6

私はかなり一般的なhabtm関係を持っています:

Photo has_and_belongs_to_many :tags
Tag has_and_belongs_to_many :photos

私の写真モデルには、特定の一連の tag_ids でタグ付けされた写真を見つけるために使用する「タグ付き」メソッドがあります。このクエリは、指定されたすべてのタグを持つ写真のみに一致する必要がありますが、他のタグの有無は無視します。これが私の方法です:

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)

qしかし、(1) はそもそも正しいクエリではないと思います。(2) を作成しArel::SelectManager、それが where 呼び出しに渡されると が発生しCannot visit Arel::SelectManagerます。だから、明らかに私はこれを間違っています。

更新: ここでさらに具体的に言うと、Arel ノードを返すことを検討しています。これは、検索メソッドに Arel ノードを渡すことを期待する gem (ランサック) を使用しているためです。Ransack は、複雑な検索クエリを生成する際に、この Arel ノードを他のノードと連鎖させます。

Arel の第一人者がこれを正しく行う方法を教えてくれませんか?

4

1 に答える 1

10

Arel の優れたドキュメントを見つけるのは難しいですが、@ Philip Cが便利なスライドをいくつかまとめており、この質問に対する彼の回答で参照されています。

探しているものは次のとおりです。

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))
)

これにより、 のArel::Nodes::Inように直接使用できるインスタンスが作成されPhoto.where(q)ます。


アップデート:

ドキュメントとランサックのソースの一部を調べた後、サブクエリを含むカスタム述語を定義する自然な方法はないようです。これは、あなたの場合に必要です(述語はwhere句に収まる必要があるため)。これを回避する 1 つの方法:formatterは、次のように述語が使用する を利用することです。

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

tags_subquery(tag_ids)上記のように arel ノードを生成するメソッドとして定義できますが、それを返す前にそれを置き換えarraytag_ids呼び出します.to_sql(フォーマッターはノードではなく文字列を返す必要があります)。

私はこれを試していないので、うまくいくとワクワクします!

于 2012-11-18T23:51:04.707 に答える