Ruby on Rails: поиск в одной таблице, где несколько строк должны присутствовать в другой таблице - PullRequest
1 голос
/ 10 февраля 2012

Я пытаюсь создать поиск, в котором одна запись должна иметь несколько записей в другой таблице (связанных по идентификаторам и операторам has_many), чтобы быть включенной в результат.

У меня есть таблицы users, skill_lists, skill_maps.

пользователи сопоставляются с индивидуальными навыками с помощью отдельных записей в таблице skill_maps. Многие пользователи могут использовать один навык, а один пользователь может иметь несколько навыков через несколько записей в таблице skill_maps.

, например

User_id | Skill_list_id
2       | 9
2       | 15
3       | 9

У пользователя 2 есть навыки 9 и 15
пользователь 3 имеет только навык 9

Я пытаюсь создать поиск, который возвращает хэш всех пользователей, имеющих набор навыков. Набор обязательных skill_ids отображается в виде массива в параметрах.

Вот код, который я использую:

skill_selection_user_ids = SkillMap.find_all_by_skill_list_id(params[:skill_ids]).map(&:user_id) 

@results = User.find(:all, :conditions => {:id => skill_selection_user_ids})

Проблема в том, что возвращаются все пользователи, которые ЛЮБЫЕ из этих навыков, а не пользователи, которые имеют ВСЕ их.

Кроме того, моя таблица пользователей связана с таблицей skill_lists :through => :skill_maps и наоборот, так что я могу звонить @user.skill_list и т. Д. *

Я уверен, что это настоящий вопрос новичка, я совершенно новичок в рельсах (и программировании). Я искал и искал решение, но не мог ничего найти. Я действительно не знаю, как объяснить проблему в одном поисковом запросе.

Ответы [ 2 ]

1 голос
/ 11 февраля 2012

Вам, вероятно, придется использовать некоторый пользовательский SQL для получения идентификаторов пользователя.Я проверил этот запрос на аналогичные отношения HABTM, и он, кажется, работает:

SELECT DISTINCT(user_id) FROM skill_maps AS t1 WHERE (SELECT COUNT(skill_list_id) FROM skill_maps AS t2 WHERE t2.user_id = t1.user_id AND t2.skill_list_id IN (1,2,3)) = 3

Хитрость в подзапросе.Для каждой строки во внешнем запросе он находит количество записей для этой строки, которые соответствуют любым навыкам, которые вас интересуют. Затем он проверяет, является ли темcount соответствует общему количеству навыков, которые вас интересуют. Если есть совпадение, то пользователь должен обладать всеми навыками, которые вы искали.

Вы можете выполнить этов Rails, используя find_by_sql:

sql = 'SELECT DISTINCT(user_id) FROM skill_maps AS t1 WHERE (SELECT COUNT(skill_list_id) FROM skill_maps AS t2 WHERE t2.user_id = t1.user_id AND t2.skill_list_id IN (?)) = ?'
skill_ids = params[:skill_ids]
user_ids = SkillMap.find_by_sql([sql, skill_ids, skill_ids.size])

Извините, если имена таблиц и столбцов не совсем верны, но, надеюсь, это в поле зрения.

1 голос
/ 11 февраля 2012

Лично я не знаю, как это сделать, используя интерфейс запросов ActiveRecord.Проще всего было бы получить списки пользователей, которые имеют каждый отдельный навык, и затем пересечь эти списки, возможно, используя Set:

require 'set'

skills = [5, 10, 19] # for example
user_ids = skills.map { |s| Set.new(SkillMap.find_all_by_skill_list_id(s).map(&:user_id)) }.reduce(:&)
users = User.where(:id => user_ids.to_a)

Для (вероятно) более высокой производительности вы могли бы "сверните свой собственный SQL и позвольте движку БД делать свою работу.Возможно, я смогу придумать немного SQL для вас, если вам нужна высокая производительность здесь.(Или, если кто-то другой может, отредактируйте этот ответ!)

Кстати, вам, вероятно, следует поставить индекс на skill_maps.skill_list_id, чтобы обеспечить хорошую производительность, даже если таблица skill_maps становится очень большой.См. Документацию ActiveMigration: http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

...