2012-11-16 8 views
6

に変換:私の写真のモデルではのActiveRecordのHABTMのクエリは、私はかなり一般的<code>habtm</code>関係していAREL

Photo has_and_belongs_to_many :tags 
Tag has_and_belongs_to_many :photos 

を私がでタグ付けされた写真を見つけるために使用「のタグで」メソッドを持っています指定されたtag_idのセットこのクエリは、指定されたすべてのタグを持つ写真だけを照合する必要がありますが、他のタグの有無は無視してください。ここに私の方法です:

def self.with_terms(array) 
    select('distinct photos.*').joins(:tags).where('tags.id' => array).group("photos." + self.column_names.join(', photos.')).having("count(*) = #{array.size}") 
end 

これは期待どおりです。

ここで私が使用している他のいくつかのライブラリとこれを統合するために、私はArelでこれを書き直す必要があります。 (それをArelノードにしますか?あなたが通常これを何と呼んでいるかはわかりません)。

私はこれを実験してきましたが、以前はArelを使用しようとしたことはありませんでしたので、少し失われています。私は、コンソールで実験し、試みられてきた:

t = Photo.arel_table 
q = t.join(:tags).on(t[:tags_id].in(array)) 
Photo.where(q) 

しかし、(1)私はqが最初の場所で右のクエリであるとは思わない、と(2)それが渡されたArel::SelectManagerを作成しますどこに呼び出しが発生するかCannot visit Arel::SelectManager。だから、明らかに私はこれを間違っている。

更新:Arelノードを検索メソッドに渡すことを期待している宝石(ransack)で作業しているので、私はArelノードを返すようにしています。 Ransackは、このArelノードを複雑な検索クエリを生成する際に他のノードと連鎖させます。

私はどのようにこれを正しく行うことができますか?

+0

これは答えではありませんが、Squeelを試してみてください!ARのデフォルトはかなり不自然ですが、ARELははるかに強力ですが、悲惨なことに文書化されています。このようなことをするには、豪華な宝石がとてもいいです! –

答えて

9

良いArelのドキュメントを見つけるのは難しいですが、@Philip Cは、このquestionの回答で参照されている便利なslidesをまとめました。

次はあなたが探しているものでなければなりません:

photos = Arel::Table.new(:photos) 
tags = Arel::Table.new(:tags) 
photo_tags = Arel::Table.new(:photo_tags) 

q = photos[:id].in(
    photos.project(photos[:id]) 
    .join(photo_tags).on(photos[:id].eql(photo_tags[:photo_id])) 
    .join(tags).on(photo_tags[:tag_id].eql(tags[:id])) 
    .where(tags[:id].in(array)) 
    .group(photos.columns) 
    .having(tags[:id].count.eq(array.length)) 
) 

これは、あなたがPhoto.where(q)のように直接使用することができるはずArel::Nodes::Inインスタンスになります。


UPDATE:

ドキュメントと掻き回すためのソースの一部を見た後、あなたに必要である、サブクエリを含むカスタム述語を定義するための任意の自然な方法があるようには思えません(述語はwhere句に収まる必要があるため)。

Ransack.configure do |config| 
    config.add_predicate 'with_tag_ids', 
        :arel_predicate => 'in', 
        :formatter => proc {|tag_ids| tags_subquery(tag_ids) }, 
        :validator => proc {|v| v.present?}, 
        :compounds => true 
end 

あなたは上記のようARELノードを生成しますが、tag_idsarray置き換え、.to_sqlを呼び出す方法としてtags_subquery(tag_ids)を定義することができます。次のようにこの問題を回避するための一つの方法は、あなたの述語が使用:formatterを活用することであるかもしれませんそれを返す前に(フォーマッタはノードではなく文字列を返す必要があります)。

私はこれを試していないので、動作すれば興奮します!

+0

素晴らしい - このクエリは動作しますが、Arelノードとして返すと 'where'節に直接渡すことができますか?理由は、Arelに依存するライブラリで使用できるクエリメソッドを記述しようとしていることです。具体的には、Arelノードを返す必要があります。つまり、 'Photo.where(q)'が正しい結果を返した場合、私が探しているものを満たすでしょう。 – Andrew

+0

'q'のクエリはかなり複雑です。 'where'節に強制したいのであれば、それは副問い合わせを伴います。それがうまくいけば、これを達成する一つの方法は、 'q'を' '写真 '"の形式に変更することです。 "id" IN(サブクエリが写真のIDを返す) '。同等のArelについて上記の編集を参照してください。 – cdesrosiers

+0

ああ!それは有望だと思って、テストし、それが動作するかどうかを確認します。 – Andrew

関連する問題