1

私は典型的なタグとwhatever-objectリレーションを取得しました:

class Tag < ActiveRecord::Base
   attr_accessible :name
   has_many :tagazations
   has_many :projects, :through => :tagazations
end

class Tagazation < ActiveRecord::Base
  belongs_to :project
  belongs_to :tag
  validates :tag_id, :uniqueness => { :scope => :project_id }
end

class Project < ActiveRecord::Base
   has_many :tagazations
   has_many :tags, :through => :tagazations
end

ここでは特に何もありません。各プロジェクトは 1 つまたは複数のタグでタグ付けされています。
アプリには検索機能があります。特定のタグを選択すると、私のアプリは、言及されたすべてのタグでタグ付けされたすべてのプロジェクトを表示する必要があります。だから私は必要なtag_idsの配列を取得し、そのような簡単な問題に行き詰まりました

4

2 に答える 2

7

1 つのクエリでこれを行うには、一般的なdouble not exists SQL クエリを利用する必要があります。これは基本的に、すべての Y に対して X を検索します。

あなたのインスタンスでは、次のようにするかもしれません:

class Project < ActiveRecord::Base
  def with_tags(tag_ids)
    where("NOT EXISTS (SELECT * FROM tags
      WHERE NOT EXISTS (SELECT * FROM tagazations
        WHERE tagazations.tag_id = tags.id
        AND tagazations.project_id = projects.id)
      AND tags.id IN (?))", tag_ids)
  end
end

別の方法として、count、group、having を使用することもできますが、最初のバージョンの方が速いと思いますが、自由にベンチマークしてください。

def with_tags(tag_ids)
  joins(:tags).select('projects.*, count(tags.id) as tag_count')
    .where(tags: { id: tag_ids }).group('projects.id')
    .having('tag_count = ?', tag_ids.size)
end
于 2013-09-23T09:36:52.780 に答える
1

これは 1 つの方法ですが、決して最も効率的ではありません。

class Project < ActiveRecord::Base
   has_many :tagazations
   has_many :tags, :through => :tagazations

   def find_with_all_tags(tag_names)
     # First find the tags and join with their respective projects
     matching_tags = Tag.includes(:projects).where(:name => tag_names)
     # Find the intersection of these lists, using the ruby array intersection operator &
     matching_tags.reduce([]) {|result, tag| result & tag.projects}
   end
end

誤字脱字があるかもしれませんがご了承ください

于 2013-09-23T09:06:03.470 に答える