Сочетание результатов различных запросов в Elasticsearch обычно достигается с помощью запроса bool
. Изменения в способе их объединения можно внести с помощью запроса function_score
.
В случае, если вам нужно объединить различные функции оценки для каждого поля (также известные как сходство ), например, выполнить тот же запрос с BM25
и DFR
и объединить их результаты, индексирование одного и того же поля несколько раз с помощью fields
может помочь.
Теперь позвольте мне объяснить, как эта штука работает.
Найти официальный сайт Дэвида Гилмора
Давайте представим, что у нас есть индекс со следующими картами и примерами документов:
PUT mysim
{
"mappings": {
"_doc": {
"properties": {
"url": {
"type": "keyword"
},
"title": {
"type": "text"
},
"abstract": {
"type": "text"
}
}
}
}
}
PUT mysim/_doc/1
{
"url": "https://en.wikipedia.org/wiki/David_Bowie",
"title": "David Bowie - Wikipedia",
"abstract": "David Robert Jones (8 January 1947 – 10 January 2016), known professionally as David Bowie was an English singer-songwriter and actor. He was a leading ..."
}
PUT mysim/_doc/2
{
"url": "https://www.davidbowie.com/",
"title": "David Bowie | The official website of David Bowie | Out Now ...",
"abstract": "David Bowie | The official website of David Bowie | Out Now Glastonbury 2000."
}
PUT mysim/_doc/3
{
"url": "https://www.youtube.com/channel/UC8YgWcDKi1rLbQ1OtrOHeDw",
"title": "David Bowie - YouTube",
"abstract": "This is the official David Bowie channel. Features official music videos and live videos from throughout David's career, including Space Oddity, Changes, Ash..."
}
PUT mysim/_doc/4
{
"url": "www.davidgilmour.com/",
"title": "David Gilmour | The Voice and Guitar of Pink Floyd | Official Website",
"abstract": "David Gilmour is a guitarist and vocalist with British rock band Pink Floyd, and was voted No. 1 in Fender's Greatest Players poll in the February 2006 Guitarist ..."
}
Практически, у нас есть официальный сайт Дэвида Гилмора, тот, что Дэвид Боуи, и две другие страницы о Дэвиде Боуи.
Давайте попробуем найти официальный сайт Дэвида Гилмора:
POST mysim/_search
{
"query": {
"match": {
"abstract": "david gilmour official"
}
}
}
На моей машине это возвращает следующие результаты:
"hits": [
...
"_score": 1.111233,
"_source": {
"title": "David Bowie | The official website of David Bowie | Out Now ...",
...
"_score": 0.752356,
"_source": {
"title": "David Gilmour | The Voice and Guitar of Pink Floyd | Official Website",
...
"_score": 0.68324494,
"_source": {
"title": "David Bowie - YouTube",
...
Почему-то страница Дэвида Гилмора не первая.
Если мы возьмем 30% терминов из первого запроса, как в оригинальном сообщении (давайте хитро выберем gilmour
, чтобы наш пример блестел), мы увидим улучшение:
POST mysim/_search
{
"query": {
"match": {
"abstract": "gilmour"
}
}
}
Теперь Elasticsearch возвращает только один удар:
"hits": [
...
"_score": 0.5956734,
"_source": {
"title": "David Gilmour | The Voice and Guitar of Pink Floyd | Official Website",
Допустим, мы не хотим отбрасывать все остальные результаты, просто хотим изменить порядок, чтобы веб-сайт Дэвида Гилмора был выше в результатах. Что мы можем сделать?
Используйте простые bool
query
Цель запроса bool
- объединить результаты нескольких запросов в режиме OR
, AND
или NOT
. В нашем случае мы могли бы пойти с OR
:
POST mysim/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"abstract": "david gilmour official"
}
},
{
"match": {
"abstract": "gilmour"
}
}
]
}
}
}
Это похоже на работу (на моей машине):
"hits": [
...
"_score": 1.3480294,
"_source": {
"title": "David Gilmour | The Voice and Guitar of Pink Floyd | Official Website",
...
"_score": 1.111233,
"_source": {
"title": "David Bowie | The official website of David Bowie | Out Now ...",
...
"_score": 0.68324494,
"_source": {
"title": "David Bowie - YouTube",
...
То, что делает запрос bool
под капотом, просто суммирует оценки по каждому подзапросу. В этом случае оценка наибольшего попадания 1.3480294
представляет собой сумму оценки документа по двум отдельным запросам, которые мы выполняли выше:
>>> 0.752356 + 0.5956734
1.3480294000000002
Но этого может быть недостаточно. Что если мы хотим объединить эти оценки с разными коэффициентами?
Объединение запросов с разными коэффициентами
Для этого мы можем использовать function_score
запрос.
POST mysim/_search
{
"query": {
"bool": {
"should": [
{
"function_score": {
"query": {
"match": {
"abstract": "david gilmour official"
}
},
"boost": 0.8
}
},
{
"function_score": {
"query": {
"match": {
"abstract": "gilmour"
}
},
"boost": 0.2
}
}
]
}
}
}
Здесь мы реализуем формулу из исходного поста с λ = 0.8
.
"hits": [
...
"_score": 0.8889864,
"_source": {
"title": "David Bowie | The official website of David Bowie | Out Now ...",
...
"_score": 0.7210195,
"_source": {
"title": "David Gilmour | The Voice and Guitar of Pink Floyd | Official Website",
...
На моей машине это все еще производит "неправильный" порядок.
Но изменение λ
на 0,4, кажется, делает работу! Ура!
Что делать, если я хочу объединить разные сходства?
В случае, если вам нужно углубиться и иметь возможность изменить способ, которым Elasticsearch вычисляет релевантность для каждого поля (которое называется схожесть ), это можно сделать путем определения пользовательской модели оценки .
В случае, который я с трудом представляю, вы можете объединить, скажем, BM25
и DFR
подсчет очков. Elasticsearch допускает только одну модель подсчета для каждого поля, но также позволяет анализировать одно и то же поле несколько раз с помощью нескольких полей .
Отображение может выглядеть следующим образом:
PUT mysim
{
"mappings": {
"_doc": {
"properties": {
"url": {
"type": "keyword"
},
"title": {
"type": "text"
},
"abstract": {
"type": "text",
"similarity": "BM25",
"fields": {
"dfr": {
"type": "text",
"similarity": "my_similarity"
}
}
}
}
}
},
"settings": {
"index": {
"similarity": {
"my_similarity": {
"type": "DFR",
"basic_model": "g",
"after_effect": "l",
"normalization": "h2",
"normalization.h2.c": "3.0"
}
}
}
}
}
Обратите внимание, что здесь мы определили новое сходство под названием my_similarity
, которое эффективно вычисляет DFR (пример взят из документации ).
Теперь мы сможем выполнить bool
запрос с комбинацией сходств следующим образом:
POST mysim/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"abstract": "david gilmour official"
}
},
{
"match": {
"abstract.dfr": "david gilmour official"
}
}
]
}
}
}
Обратите внимание, что мы делаем один и тот же запрос к двум разным полям. Здесь abstract.dfr
- это «виртуальное» поле с моделью оценки, установленной в DFR.
Что еще мне следует рассмотреть?
В Elasticsearch вычисляются баллы на шард , что может привести к неожиданным результатам. Например, IDF вычисляется не по всему индексу, а только по подмножеству документов, находящихся в одном и том же сегменте.
Здесь вы можете прочитать, как Lucene, основа Elasticsearch, вычисляет баллы релевантности.
Надеюсь, это поможет!