1

я получил типичный тег и все-объектных отношений: скажемКак найти записи, чьи объекты has_many через объекты включают в себя все объекты какого-либо списка?

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 

ничего особенного здесь: каждый проект помечен одним или несколькими тегами.
Приложение имеет функцию поиска: вы можете выбрать определенные теги, и мое приложение должно показать вам все проекты, отмеченные всеми упомянутыми тегами. Таким образом я получил массив необходимых tag_ids и затем застрял с такой простой проблемой

+0

каков ваш вопрос? каков ваш код на данный момент для поиска тегов? – dax

+0

@ dax, вопрос в том, что AR-запрос может помочь мне реализовать упомянутые функции поиска? –

ответ

6

чтобы сделать это в одном запросе вы хотите воспользоваться общим дважды не существует SQL запроса, который по существу делает найти X для всех Y.

В вашем случае, вы можете сделать:

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 

В качестве альтернативы, вы можете использовать счетчик, группу и имея, хотя я подозреваю, что первая версия быстрее, но не стесняйтесь бенчмарка:

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 
+0

Спасибо! Я бы никогда не составил такой сложный запрос. –

+0

Нет проблем, я добавил альтернативу, которая, как я подозреваю, будет медленнее, но разнообразие - это пряность жизни, ey. –

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 

Там может быть несколько опечатки в там, но вы получите идею

+0

спасибо! Я думал об этом, но я не уверен в выполнении операции «уменьшить» с объектом отношения. Я хочу уменьшить результат запроса на стороне БД –

Смежные вопросы