Модель поиска, которая has_and_belongs_to_many :tags

Мне нужна небольшая помощь с Rails. Я переношу серверную систему служб поиска данных с PHP на RoR. Что я нахожу удивительно кратким.

Мне нужно иметь возможность искать модель по ее отношению к модели тега. Каждой модели присвоена ассоциация has_and_belongs_to_many.

Когда поиск передается со списком значений, разделенных запятыми, мне нужно выполнить поиск «И» по тегам, чтобы я мог найти все ресурсы, в которых переданы все теги (например, Лондон И мосты).

Также он должен быть нечувствительным к регистру. В настоящее время в модели ресурсов выполняется только один поиск тегов.

def self.search(search)
    where('LOWER(tags.name) = ?', search.downcase).joins(:tags)
end

Все мои попытки использовать массив с использованием where, find или all не увенчались успехом.

PHP-способ выполнения поиска AND в таблице соединений кажется довольно хакерским, я надеюсь, что Rails может предоставить более элегантное решение.

Любая помощь будет принята с благодарностью.


person Ode    schedule 01.03.2012    source источник
comment
Может быть, попробовать act_as_taggable_on — драгоценный камень, который предоставляет множество отличных помощников. Благодаря этому камню вы сэкономите много времени.   -  person zachar    schedule 01.03.2012


Ответы (1)


Если вам нужен AND, вам нужно убедиться, что количество совпадающих тегов точно соответствует запрошенному вами количеству, так что это также включает COUNT. Запрос, к которому вы стремитесь, будет включать IN.

Было бы удобнее, если бы вы могли убедиться, что ваши теги всегда хранятся в нижнем регистре. Выполнение таких операций, как LOWER(...) в запросе, будет проблематичным, если у вас много данных, поскольку это приводит к автоматическому сканированию таблицы и не может быть проиндексировано.

Во-вторых, я бы не стал использовать has_and_belongs_to_many, так как это пережиток времен Rails 1.0, который не так гибок и надежен, как альтернатива has_many :through. Единственная разница между этими двумя подходами с точки зрения базы данных заключается в том, что таблица соединений :through имеет уникальный идентификатор для каждой записи и представлена ​​первоклассной моделью. Система has_and_belongs_to_many в большинстве случаев причудлива до бесполезности.

То, что вы, вероятно, хотите, выглядит примерно так:

def self.with_all_tags(*tags)
  tags = tags.flatten.collect(&:downcase)
  where('tags.name IN (?)', tags).having('COUNT(tags.id)=?', tags.length).joins(:tags)
end

def self.with_any_tags(*tags)
  tags = tags.flatten.collect(&:downcase)
  where('tags.name IN (?)', tags).joins(:tags)
end

Возможно, вам придется поэкспериментировать, чтобы получить правильное поведение. Ваш пример не был таким конкретным.

person tadman    schedule 01.03.2012
comment
Я надеялся объяснить, что если строка London,bridges будет передана в качестве условия поиска, будут возвращены все ресурсы, которые имеют теги London и bridges. Они должны иметь и то, и другое, чтобы их можно было вернуть. - person Ode; 01.03.2012
comment
В этом случае вам нужно WHERE tags.name IN ('london', 'bridges') с HAVING COUNT(tags.id)=2, чтобы убедиться, что оба совпадают. Для этого может потребоваться GROUP BY main_model.id, где это имя таблицы вашей основной модели. - person tadman; 01.03.2012
comment
Спасибо, это то же самое, что и в PHP, но в итоге получилось гораздо более подробно. Мне просто нужно было небольшое направление, чтобы выполнить это в RoR. - person Ode; 01.03.2012