Вложенные поиски в Lucene без дублирования ключевых слов - PullRequest
0 голосов
/ 04 ноября 2011

Я хочу настроить поиск в Lucene (фактически Lucene.NET, но я могу при необходимости конвертировать из Java), используя следующую логику:

  1. Строка поиска: A B C
  2. Найдите в одном поле индекса все, что соответствует A, B или C. (Запрос: (field1:A field1:B field1:C))
  3. Для каждого термина, который не совпадает на шаге 2, ищите его во втором поле, сохраняя результаты первого поиска (Запрос: (+(field1:A) +(field2:B field2:C)))
  4. Для каждого термина, не соответствующего шагу 3, найдите третье поле ...
  5. Продолжайте до тех пор, пока не закончатся поля, или будет поиск, который использовал каждый термин.

В настоящее время мой код может проверять, дает ли данный поиск НЕТ результатов, и объединяет ли И все те, которые действительно дают результаты. Но у меня нет возможности остановить его до того, как он проверит все поля (что излишне ограничивает результаты) - в настоящее время он заканчивается запросом вроде: (+(field1:A field1:B field1:C) +(field3:A field3:B field3:C)), когда я хочу, чтобы он был (+(field1:A field1:C) +(field3:B)). Я не могу просто посмотреть на результаты первого поиска и удалить слова из строки поиска, потому что анализатор искажает слова, когда анализирует их для поиска, и у меня нет возможности отменить их, чтобы выяснить, какой из оригинала Поисковым запросам соответствует.

Есть предложения?


Edit: Хорошо, обычно я предпочитаю описывать свои проблемы в резюме, но я думаю, что некоторая часть этого теряется в процессе, поэтому я буду более конкретным.

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

  1. Наушники
  2. Наушники Monster
  3. Наушники White Monster
  4. Белые наушники Foobar

Индекс содержит документы с семью полями - для этого примера:

  • "тип данных" : строка, представляющая, какой тип элемента представляет этот документ (продукт, категория, марка), поэтому мы знаем, как его отобразить
  • "бренд" : релевантные бренды (категории имеют несколько брендов, товары и бренды имеют по одному)
  • «путь» : путь к данной категории (т. Е. «Аудио наушники-вкладыши» для «Аудио> Наушники> наушники-вкладыши»)
  • "ключевые слова" : различные вещи, описывающие продукт, которые никуда не денутся.

Обычно логика для каждого шага поиска выглядит следующим образом:

  1. Проверьте, есть ли у нас совпадение.
  2. Если это так, отфильтруйте результаты на основе этого соответствия и продолжите анализ остальных поисковых терминов на следующем шаге.
  3. Если нет, проанализируйте условия поиска на следующем шаге.

Каждый шаг выглядит примерно так:

  • Поиск категории
  • Поиск бренда
  • Поиск по ключевым словам

Итак, вот как должны воспроизводиться эти три примера поиска:

  1. Наушники
    • Поиск по категории: +path:headphones +datatype:Category
    • Есть совпадения (категория «Наушники»), и в исходном запросе не осталось слов, поэтому мы его возвращаем.
  2. Наушники-монстры
    • Поиск категории: `+ (путь: путь монстра: наушники) + тип данных: категория
    • Найдены совпадения для path:headphones и datatype:Category, в результате чего "Monster" не имеет себе равных
    • Поиск бренда: +path:headphones +brand:monster
    • Найдены совпадения для path:headphones и brand:monster, и слова из исходного запроса не остались, поэтому мы возвращаем все наушники от Monster.
  3. Наушники White Monster
    • Поиск по категории: +(path:monster path:headphones path:white) +datatype:Category
    • Найдены совпадения для path:headphones и datatype:Category, в результате чего "White" и "Monster" не были сопоставлены
    • Поиск бренда: +path:headphones +(brand:monster +brand:white)
    • Найдены совпадения для path:headphones и brand:monster, в результате чего "White" не имеет аналогов
    • Ключевые слова для поиска: +path:headphones +brand:monster +keywords:white
    • Есть совпадения, и слова из исходного запроса не остались, поэтому мы их возвращаем.
  4. Белые наушники Foobar
    • Поиск по категории: +(path:foobar path:headphones path:white) +datatype:Category
    • Найдены совпадения для path:headphones и datatype:Category, в результате чего "White" и "Foobar" не были сопоставлены
    • Поиск бренда: +path:headphones +(brand:foobar +brand:white)
    • Ничего не найдено, поэтому мы продолжаем.
    • Поисковые ключевые слова: +path:headphones +(keywords:white keywords:foobar)
    • Найдены совпадения для path:headphones и keywords:white, в результате чего "Foobar" не имеет аналогов
    • ... (продолжить поиск в других полях, включая описание продукта) ...
    • Поисковые термины все еще не соответствуют ("Foobar"), возвращают "Ничего не найдено"

У меня двойственная проблема:

  1. Я не хочу, чтобы совпадения продолжались после того, как все сопоставлено (описания имеют только продукты, поэтому, как только он достигнет этого шага, мы никогда не вернем что-то, что не является продуктом). Я мог бы справиться с этим с помощью GetHitTerms Дениса из здесь , за исключением того, что я в конечном итоге ищу первый совпадающий термин во всех последующих полях, пока все не совпадет (т.е. в примере # 2 у меня будет +path:headphones +(brand:headphones brand:monster) ).
  2. Несмотря на мой пример выше, мой фактический поисковый запрос в поле пути выглядит как +path:headphon +datatype:Taxonomy, потому что я искажаю его для поиска. Поэтому я не могу взять соответствующий термин и просто удалить его из исходного запроса (потому что "headphon"! = "Наушники").

Надеюсь, это прояснит то, что я ищу.

Ответы [ 2 ]

1 голос
/ 04 ноября 2011

Я не понимаю ваш вариант использования, но вы говорите так, будто спрашиваете об API BooleanQuery . Вы можете получить пункты вашего запроса, позвонив по номеру getClauses.

Простой пример:

BooleanQuery bq = new BooleanQuery();
bq.add(new TermQuery(new Term("field1","a")), BooleanClause.Occur.SHOULD)
bq.add(new TermQuery(new Term("field1","b")), BooleanClause.Occur.SHOULD)

BooleanClause[] clauses = bq.getClauses();

РЕДАКТИРОВАТЬ: возможно, вы просто запрашиваете алгоритм поиска. В псевдокоде:

generate_query (qs_that_matched, qs_that_didnt_match, level):
   new_query = qs_that_matched AND level:qs_that_didnt_match
   qs_still_unmatched = ...
   qs_which_just_matched = ...
   if qs_still_unmatched != null:
      return generate_query(qs_that_matched AND qs_which_just_matched, qs_still_unmatched, level+1)
   else:
      return qs_that_matched AND qs_which_just_matched
0 голосов
/ 14 ноября 2011

В конце я создал класс QueryTree и сохранил запросы в древовидной структуре. Он хранит ссылку на функцию, которая принимает запрос, список терминов, которые нужно ввести в этот запрос, должен ли он или ИЛИ эти термины, и список дочерних элементов (которые представляют уникальные комбинации совпадающих терминов).

Чтобы выполнить следующий уровень поиска, я просто вызываю Evaluate(Func<string, QueryParser.Operator, Query> newQuery) на самых глубоких узлах моего дерева со ссылкой на функцию, которая принимает термины и оператор и возвращает правильный запрос для этого набора логики. Затем функция Evaluate проверяет этот новый запрос по списку несопоставленных терминов, которые были переданы ему, и результирующим наборам всех наследственных запросов (путем ANDing с родителем, который AND с его родителем и т. Д.). Затем он создает дочерние элементы для каждого набора совпадающих терминов, используя GetHitTerms , и передает непревзойденные термины дочернему элементу. Повторите для каждого уровня поиска.


Я подозреваю, что есть лучший способ сделать это - я даже не смотрел на Бобо, о котором упоминал Ксодарап, и я никогда не получал многогранный поиск (согласно Дени). Тем не менее, это работает, а это значит, что пришло время перейти к другим аспектам сайта.

...