Elasticsearch - сортировка по количеству совпадений массивов в нескольких массивах - PullRequest
2 голосов
/ 18 марта 2020

Индексированные документы

{
  "book_id":"book01",
  "pages":[
    { "page_id":1, "words":["1", "2", "xx"] }
    { "page_id":2, "words":["4", "5", "xx"] }
    { "page_id":3, "words":["7", "8", "xx"] }
  ]
}
{
  "book_id":"book02",
  "pages":[
    { "page_id":1, "words":["1", "xx", "xx"] }
    { "page_id":2, "words":["4", "xx", "xx"] }
    { "page_id":3, "words":["7", "xx", "xx"] }
  ]
}

Входные данные

{
  "book_id":"book_new",
  "pages":[
    { "page_id":1, "words":["1", "2", "3"] }
    { "page_id":2, "words":["4", "5", "6"] }
    { "page_id":3, "words":["xx", "xx", "xx"] }
  ]
}

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

Порог

  1. min_word_match_score: 2 (минимальный балл words совпадение между две страницы)
  2. min_page_match_score: 2 (минимальное количество similar pages между двумя книгами)

Ключевые термины

  1. аналог страницы: две страницы, содержащие не менее min_word_match_score одинаковых слов
  2. похожая книга: две книги, содержащие не менее min_page_match_score похожих страниц

ожидаемый результат

Исходя из указанных порогов, правильный возврат должен быть только book01, поскольку

  1. book01-1 и book_new-1 имеют оценку 2 (> = min_word_match_score, totalScore ++)
  2. book01-2 и book_new-2 имеют 2 балла (> = min_word_match_score, totalScore ++)
  3. book01 и book_new имеют 2 суммарных балла (totalScore> = min_page_match_score)

Плохой поисковый запрос (не работает)

"bool" : {
   "should" : [
     {
        "match" : { "book_pages.visual_words" : {"query" : "1", "operator" : "OR"} },
        "match" : { "book_pages.visual_words" : {"query" : "2", "operator" : "OR"} },
        "match" : { "book_pages.visual_words" : {"query" : "3", "operator" : "OR"} }
     }
   ],
   "minimum_should_match" : 2
   "adjust_pure_negative" : true,
   "boost" : 1.0
 }
}

Сначала я попытался мак Часть, если запрос на соответствие страницы, но это не поиск по массиву, а просто поиск по словам всех страниц. И я не совсем уверен, как управлять двумя различными показателями - слова-совпадения-оценки и страницы-совпадения-оценки.

Должен ли я копать в innerHit? Пожалуйста помоги!

1 Ответ

0 голосов
/ 19 марта 2020

Не будь лучшим, но мои два цента !!

Я не думаю, что 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, и посмотрите, сможете ли вы глубже понять, почему результаты возвращают так, как они есть.

Не уверен, что это так, но я надеюсь, что это поможет!

...