Вложенный поиск в SunSpot Solr - PullRequest
1 голос
/ 04 ноября 2010

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

например:

Hello Jack
  Hello Janice
  How are you?
  ..

I am Janice
  How are you?

Welcome to the Jungle
  Nothing better to do.

Поиск Janice должен вернуть следующий набор результатов:

Hello Jack # one of the child messages matches the key word
I am Janice # parent message matched the keyword)

Моя модель выглядит следующим образом:

class Message < ActiveRecord::Base    
  belongs_to :parent, :class_name => "Message"
  has_many   :replies, :class_name => "Message", :foreign_key => :parent_id      
  # content      
  searchable do
    text :content
    integer :parent_id
  end     
end

Каков синтаксис DSL для указания вложенных подзапросов типа условий?

Редактировать 1

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

class Message < ActiveRecord::Base    
  belongs_to :parent, :class_name => "Message"
  has_many   :replies, :class_name => "Message", :foreign_key => :parent_id      
  belongs_to :category
  # content      
  searchable do
    text :content
    integer :category_id
    integer :parent_id
  end     
end

В приведенной выше модели я хочу ограничить текстовый поиск данной категорией.

Ответы [ 2 ]

8 голосов
/ 08 ноября 2010

Лучший способ выполнить то, что вы ищете, - это денормализовать содержимое ответов - и любые другие поля, которые вы хотите сделать доступными для поиска, - в их родительское Сообщение.

Это довольно просто сделать в Sunspot. Другой распространенный сценарий, который вы можете исследовать в Интернете, - это поиск сообщения в блоге на основе содержания его комментариев.

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

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

class Message < ActiveRecord::Base    
  # …

  after_save :reindex_parent

  searchable do
    # …
    text :replies_content
  end

  def replies_content
    replies.collect(&:content).join(" ")
  end

  def reindex_parent
    parent.solr_index!
  end

end

(что text :replies_content может также принять встроенный lambda, если вы хотите сохранить несколько строк вместо определения нового метода. Это ваше дело.)

При таком подходе нет никаких реальных изменений в синтаксисе поиска, поскольку все содержимое ответов будет объединено с поиском по ключевым словам по умолчанию.

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

Последнее замечание: этот подход может быть немного сложным, если, например, ваши сообщения имеют много ответов. Вероятно, это хорошая идея, чтобы убедиться, что вы индексируете асинхронно, используя DelayedJob или Resque. Но это другой разговор.

Обновление 1: определение с определенным идентификатором category_id

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

У вас есть пара вариантов, которые я вижу. Я начну с самого простого, а затем опишу несколько вероятных комбинаций. Простейшим подходом было бы сделать довольно простой поиск - не беспокойтесь о денормализации или о чем-либо подобном - и реконструировать ваши родительско-дочерние сообщения с ассоциациями ActiveRecord.

@search = Message.search do
  keywords params[:q]
  with(:category_id, params[:category_id])
end
@messages = @search.results

Как видите, обзор по category_id довольно прост в Sunspot. Может быть, это основная часть вашего вопроса, и я только что ушел и сделал его более сложным, чем должно быть:)

Оттуда некоторые из этих @messages будут родителями, некоторые - ответами. Разумеется, в пределах вашей точки зрения вы можете определить, что есть что, и соответственно отобразить.

<% if message.parent %>
  …

Здесь есть несколько других подходов в зависимости от точного характера ваших требований. Вышесказанное может быть достаточно хорошим, поэтому я не буду их здесь подробно описывать. Но если вы продолжите проводить денормализацию, вы также можете включить многозначный целочисленный столбец для всех ответов сообщения 'category_id s. Что-то вроде integer :reply_category_ids, :multi => true.

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

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

0 голосов
/ 07 марта 2019

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

searchable do
  ...
  text :ip_address,  as: :ip_address_textp # nested searching
  ...
end

private

def ip_address
  Address.find(address_id).ip # retrieve attribute from parent record with FK
end
...