Статьи

Анатомия запроса DisMax

При отладке функциональности нового анализатора запросов у меня было 

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

избежать моей судьбы
 извлечь выгоду из моего нового понимания.

Наше обсуждение здесь будет сосредоточено на композиции запросов, выполненных с помощью анализатора запросов Solr DisMax (DisMaxQParser). Это был код, с которым я в основном работал, но он служит отличным примером, потому что он затрагивает все наиболее важные типы запросов, с которыми вы можете регулярно иметь дело: булевы запросы, запросы DisMax, запросы фраз и термины запросов.

Запрос

Так что без дальнейших действий давайте рассмотрим наш пример запроса:

localhost:8983/solr/select
?q=captain james kirk
&defType=edismax
&qf=BodyTitle^1.5
&pf=BodyTitle
&mm=2
&debugQuery=true

(Те, кто следил за нашим блогом в последнее время, могут понять, что здесь мы используем наш любимый демонстрационный набор данных — данные SciFi StackExchange. Работать с ним легко и интересно. Если вы хотите попробовать, наша система  индексирования StackOverflow в записи Solr  поможет вам начать работу через 10 минут.) Здесь мы ищем сообщения, содержащие  капитана james kirk  ( q), мы используем синтаксический анализатор edismax ( defType), мы ищем чистые совпадения терминов в основной части и заголовке. поля (обратите внимание на повышение заголовка,  qf), и мы ищем совпадения фраз в тех же двух полях ( pf). Мы настаиваем на том, что как минимум 2 условия должны совпадать (mm) и, наконец, поскольку нам интересно понять, как Solr думает о нашем запросе и о скоринге, мы включаем отладочный вывод ( debugQuery).

Первое, на что интересно взглянуть, это раздел парсера запроса. Это показывает нам, как Solr разбивает запрос, который мы отправили:

+(
  ( DisjunctionMaxQuery((Body:captain | Title:captain^1.5))
    DisjunctionMaxQuery((Body:james | Title:james^1.5))
    DisjunctionMaxQuery((Body:kirk | Title:kirk^1.5)) )~2
)
DisjunctionMaxQuery((Body:"captain james kirk"))
DisjunctionMaxQuery((Title:"captain james kirk"))

Булевы Запросы

Здесь у нас есть примеры всех часто используемых запросов: Boolean, DisjunctionMax, Term и Phrase. Давайте разберемся с этим. На самом внешнем уровне у нас есть логический запрос с тремя предложениями:

  1. +(( DisjunctionMaxQuery((Body:captain | Title:captain^1.5)) DisjunctionMaxQuery((Body:james | Title:james^1.5)) DisjunctionMaxQuery((Body:kirk | Title:kirk^1.5)) )~2)
  2. DisjunctionMaxQuery((Body:"captain james kirk"))
  3. DisjunctionMaxQuery((Title:"captain james kirk"))

Теперь вы можете спросить себя: «Где AND и OR?» Ну, это не совсем то, как работают булевы запросы Lucene. Вместо этого булевский запрос Lucene состоит из набора предложений, которые модифицируются либо с помощью  MUST match , MUST_NOT match , либо  SHOULD match, ДОЛЖНО совпадение по умолчанию, ОБЯЗАТЕЛЬНО совпадение указывается в вышеупомянутом запросе плюсом в начале предложения, а совпадение MUST_NOT, хотя и не присутствует в этом примере, указывается минусом в начале предложения. Таким образом, для этого запроса мы настаиваем на том, чтобы первое предложение ДОЛЖНО совпадать в каждом документе, возвращенном в результате нашего поиска, остальные два ДОЛЖНЫ совпадать, но если они этого не делают, это нормально (документ просто получает более низкий балл). И что касается оценки, то каждое соответствующее условие булева запроса оценивается, и окончательный результат булева запроса является суммой очков его предложений.

Давайте углубимся на один уровень глубже в первом предложении логического запроса. Здесь мы на самом деле находим еще один логический запрос, и он имеет три предложения совпадения (все эти предложения — DisjunctionMaxQueries… держите лошадей, мы добираемся). В этом логическом запросе следует отметить одну интересную вещь:  ~2 конец запроса. Это потому, что вы ранее указали  mm=2 в исходном запросе. Это  ~2 указывает на то, что из трех необязательных СЛЕДУЕТ сопоставлять подзапросы, мы настаиваем на том, что по крайней мере два из этих запросов действительно совпадают, в противном случае мы игнорируем этот документ.

DisMax Queries

Теперь давайте внимательнее посмотрим на первый фактический запрос DisMax: DisjunctionMaxQuery((Body:captain | Title:captain^1.5)). DisjunctionMax — забавное имя … но никто не смеется. Это означает, что мы должны искать рассматриваемый термин по  дизъюнкции  возможных полей и возвращать максимальный  балл по всем этим подзапросам. Таким образом, в этом случае, если  капитан  соответствует в названии и соответствует в теле, мы найдем результат в обоих случаях и вернем максимальное из этих двух значений в качестве результата для предложения DisMax.

Теперь, почему вы хотите вернуть только максимальное количество баллов подзапроса? Рассмотрим пример: допустим, вы хотите найти  Гарри Поттера  в полях «Заголовок» и «Тело». Если мы возьмем сумму подзапросов вместо максимума, то документ, в заголовке которого указан «Гарри Поттер», но не в теле, будет иметь ту же оценку, что и документ, в котором в заголовке и теле есть «Гарри». Но «Поттер» никуда. Это не то, что вы хотите! Если максимизация все еще кажется слишком жесткой для вашего варианта использования, то найдите параметр «связать», который позволяет вам что-то делать между максимизацией и суммированием баллов по пунктам.

Срок запроса

Переход на один уровень глубже сейчас, взгляд давайте в первом предложении:  Body:captain. Выглядит довольно просто, верно? Просто искали  капитана  в поле Тела. На самом деле, это одна из самых сложных частей запроса. Оценка этого запроса зависит от нескольких вещей, среди которых:

  • Насколько часто встречается термин «  капитан»  в этом документе (термин «частота»)
  • Насколько распространен термин «  капитан»  во всех документах (частота документов)
  • Насколько велико поле в этом документе (Field Norm)
  • Насколько этот запрос повышен

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

Фразовые Запросы

Это оставляет один последний кусок, запрос Фраза. Пересекая вверх по дереву синтаксического разбора запроса, следующего уровень верхних Логический подпункт является запросом DisMax с одним пунктом:  DisjunctionMaxQuery((Body:"captain james kirk")). Как запрос DisMax, это несколько вырожденный случай, потому что, как вы помните, DisMax получает максимальную оценку всех своих подпунктов; в этом случае есть только один пункт. Несмотря на это, это подпункт, который нас интересует прямо сейчас Body:"captain james kirk":; Это Фразовый запрос. В основном он ищет все документы, которые содержат  все указанных условий, а затем выбрасывает документы, в которых эти термины не являются смежными друг с другом и в этом порядке. Оценка происходит аналогично запросам терминов, так как оценка пропорциональна числу появлений фразы в этом документе и обратно пропорциональна количеству появлений этих терминов во всем корпусе.

Целостное понимание запроса DisMax

Итак, теперь, когда мы разбили запрос DisMax и рассмотрели все маленькие части, давайте создадим его обратно и еще раз посмотрим на запрос DisMax в целом.

+(
  ( DisjunctionMaxQuery((Body:captain | Title:captain^1.5))
    DisjunctionMaxQuery((Body:james | Title:james^1.5))
    DisjunctionMaxQuery((Body:kirk | Title:kirk^1.5)) )~2
)
DisjunctionMaxQuery((Body:"captain james kirk"))
DisjunctionMaxQuery((Title:"captain james kirk"))

По-английски:

  • В первую очередь мы ищем документы, содержащие как минимум два из трех терминов:  капитанджеймс и  кирк . Это должно соответствовать!

    • Мы осуществляем поиск только в полях «Тело» и «Заголовок», и документы не получают более высокую оценку за сопоставление любого из этих терминов в обоих полях.

      • Ох … и Название действительно важно, поэтому его счет умножается на 1,5.
  • Далее, если в каком-либо из документов из последней маркировки в тексте также есть фраза «капитан джеймс кирк», мы дадим им более высокий балл.
  • Опять же, если какой-либо из документов из последней маркировки также содержит в заголовке фразу «капитан джеймс кирк», тогда мы дадим им более высокий балл.

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