Не будь лучшим, но мои два цента !!
Я не думаю, что Elasticsearch предоставляет точное решение из коробки для этого варианта использования. Ближайший способ сделать то, что вы хотите, это использовать запрос More Like This .
Этот запрос, по сути, помогает вам найти документы, аналогичные самому документу , который вы указали бы в качестве ввода.
По сути, алгоритм выглядит следующим образом:
- Найти верхние термины K с самым высоким tf-idf из входного документа.
- Вы можете указать из входных данных, что min_term_frequency of слова должны быть 1 или 2, и, глядя на ваш вариант использования, это будет
1
. Имеется в виду только те слова из входного документа, частота слова которых 1
. - Построение N числа дизъюнктивных запросов на основе этих терминов или, скорее, логического оператора ИЛИ
- Эти N числа настраиваются в запросе запроса, по умолчанию это
25
и свойство max_query_terms
- Выполните запросы внутренне и верните наиболее похожие документы.
Точнее от по этой ссылке ,
Запрос MLT просто извлекает текст из входного документа, анализирует его, обычно используя тот же анализатор в поле, а затем выбирает верхние K терминов с самым высоким tf-idf, чтобы сформировать дизъюнктивный запрос этих терминов.
Давайте посмотрим, как мы можем достичь некоторых вариантов использования, которые вы упомянули.
Вариант использования 1: Найти документы страницы с min_word_match_score 2.
Обратите внимание, что поле pages
должно иметь тип nested
. Иначе, используя object
type, это было бы невозможно для этого сценария. Я предлагаю вам go через вышеупомянутые ссылки, чтобы узнать больше об этом.
Допустим, у меня есть два индекса
- my_book_index - здесь будут документы для поиска
- my_book_index_input - Это будет иметь документы, используемые в качестве входных документов
Оба будут иметь структуру отображения, как показано ниже:
{
"mappings": {
"properties": {
"book_id":{
"type": "keyword"
},
"pages":{
"type": "nested"
}
}
}
}
Образцы документов для my_book_index:
POST my_book_index/_doc/1
{
"book_id":"book01",
"pages":[
{ "page_id":1, "words":["11", "12", "13", "14", "105"] },
{ "page_id":2, "words":["21", "22", "23", "24", "205"] },
{ "page_id":3, "words":["31", "32", "33", "34", "305"] },
{ "page_id":4, "words":["41", "42", "43", "44", "405"] }
]
}
POST my_book_index/_doc/2
{
"book_id":"book02",
"pages":[
{ "page_id":1, "words":["11", "12", "13", "104", "105"] },
{ "page_id":2, "words":["21", "22", "23", "204", "205"] },
{ "page_id":3, "words":["301", "302", "303", "304", "305"] },
{ "page_id":4, "words":["401", "402", "403", "404", "405"] }
]
}
POST my_book_index/_doc/3
{
"book_id":"book03",
"pages":[
{ "page_id":1, "words":["11", "12", "13", "100", "105"] },
{ "page_id":2, "words":["21", "22", "23", "200", "205"] },
{ "page_id":3, "words":["301", "302", "303", "300", "305"] },
{ "page_id":4, "words":["401", "402", "403", "400", "405"] }
]
}
Пример документа для my_book_index_input:
POST my_book_index_input/_doc/1
{
"book_id":"book_new",
"pages":[
{ "page_id":1, "words":["11", "12", "13", "14", "15"] },
{ "page_id":2, "words":["21", "22", "23", "24", "25"] }
]
}
Больше похоже на этот запрос:
Вариант использования: В основном мне интересно найти документы, которые были бы похожи на вышеупомянутые документы, имеющие 4 matches in page 1
или 4 matches in page 2
POST my_book_index/_search
{
"size": 10,
"_source": "book_id",
"query": {
"nested": {
"path": "pages",
"query": {
"more_like_this" : {
"fields" : ["pages.words"],
"like" : [
{
"_index": "my_book_index_input",
"_id": 1
}
],
"min_term_freq" : 1,
"min_doc_freq": 1,
"max_query_terms" : 25,
"minimum_should_match": 4
}
},
"inner_hits": {
"_source": ["pages.page_id", "pages.words"]
}
}
}
}
В основном я хочу найти в my_book_index
все документы, похожие на _doc:1
в индексе my_book_index_input
.
Обратите внимание на каждый параметр в запросе. Я бы посоветовал вам go построчно понять все это.
Обратите внимание на ответ ниже, когда вы выполняете этот запрос:
Ответ:
{
"took" : 71,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 6.096043,
"hits" : [
{
"_index" : "my_book_index",
"_type" : "_doc",
"_id" : "1",
"_score" : 6.096043,
"_source" : {
"book_id" : "book01" <---- Document 1 returns
},
"inner_hits" : {
"pages" : {
"hits" : {
"total" : {
"value" : 2, <---- Number of pages hit for this document
"relation" : "eq"
},
"max_score" : 6.096043,
"hits" : [
{
"_index" : "my_book_index",
"_type" : "_doc",
"_id" : "1",
"_nested" : {
"field" : "pages",
"offset" : 0
},
"_score" : 6.096043,
"_source" : {
"page_id" : 1, <---- Page 1 returns as it has 4 matches
"words" : [
"11",
"12",
"13",
"14",
"105"
]
}
},
{
"_index" : "my_book_index",
"_type" : "_doc",
"_id" : "1",
"_nested" : {
"field" : "pages",
"offset" : 1
},
"_score" : 6.096043,
"_source" : {
"page_id" : 2, <--- Page 2 returns as it also has 4 matches
"words" : [
"21",
"22",
"23",
"24",
"205"
]
}
}
]
}
}
}
}
]
}
}
Обратите внимание, что вернулся только документ с book_id: 1. Причина проста. Я упомянул следующие свойства в запросе:
"min_term_freq" : 1,
"min_doc_freq": 1,
"max_query_terms" : 25,
"minimum_should_match": 4
По сути, для поиска во входном документе учитываются только те термины, у которых термин freq равен 1, который доступен как минимум в 1 документе, и количество совпадения в одном вложенном документе должны быть 4.
Измените параметры, например, min_doc_freq
на 3
и min_should_match
на 3
, вы должны увидеть еще несколько документов.
Обратите внимание, что вы не увидите весь документ, удовлетворяющий указанным выше свойствам, то есть из-за способа его реализации. Помните шаги, которые я упомянул в начале. Возможно, поэтому.
Вариант использования 2: Вариант использования 1 + Возврат только тех, у кого min page match
равно 2
Я не уверен, поддерживается ли это, т.е. adding filter to inner_hits based on _count of inner_hits
, однако я считаю, что это то, что вы можете добавить его на вашем прикладном уровне. В основном получите вышеуказанный ответ, рассчитайте inner_hits.pages.hits.total_value
и тем самым верните потребителю только эти документы. Ниже приведен пример потока ответов на ваш запрос:
For Request: Client Layer (UI) ---> Service Layer --> Elasticsearch
For Response: Elasticsearch ---> Service Layer (filter logic for n pages match) --> Client Layer (or UI)
Возможно, это не лучшее решение, и иногда оно может дать вам результаты, которые могут отличаться от ожидаемых, но я бы посоветовал, по крайней мере, попробовать его, поскольку единственное другое решение вместо использования этого запроса к сожалению, писать свой собственный клиентский код, который будет использовать TermVectorAPI , как указано в по этой ссылке .
Запомните алгоритм, как работает запрос MLT, и посмотрите, сможете ли вы глубже понять, почему результаты возвращают так, как они есть.
Не уверен, что это так, но я надеюсь, что это поможет!