Elasticsearch — сортировка по количеству совпадений массивов в нескольких массивах

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

{
  "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? Пожалуйста помоги!


comment
Ваше поле pages имеет тип nested или object?   -  person Opster ES Ninja - Kamal    schedule 18.03.2020
comment
Это тип объекта   -  person Devon    schedule 18.03.2020


Ответы (1)


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

Я не думаю, что Elasticsearch предоставляет точное готовое решение для этого варианта использования. Самый близкий способ сделать то, что вы хотите, — это использовать Больше лайков.

Этот запрос помогает найти документы, похожие на сам документ, который вы предоставляете в качестве входных данных.

В основном алгоритм такой:

  • Найдите лучшие K терминов с самым высоким tf-idf из входного документа.
  • Вы можете указать из ввода, что min_term_частота слов должна быть 1 или 2, и, глядя на ваш вариант использования, это будет 1. Смысл рассматривать только те слова из входного документа, частота терминов которых равна 1.
  • Построить N дизъюнктивных запросов на основе этих терминов, а точнее логического оператора ИЛИ
  • Эти N номеров настраиваются в запросе запроса, по умолчанию это 25, а свойство max_query_terms.
  • Выполняйте запросы внутренне и возвращайте наиболее похожие документы.

Более точно по этой ссылке< / а>,

Запрос MLT просто извлекает текст из входного документа, анализирует его, обычно используя тот же анализатор, что и поле, а затем выбирает K лучших терминов с наивысшим значением tf-idf для формирования дизъюнктивного запроса этих терминов.

Давайте посмотрим, как мы можем достичь некоторых вариантов использования, которые вы упомянули.

Вариант использования 1: Найдите документы страницы, имеющей min_word_match_score 2.

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

Скажем, у меня есть два индекса

  • 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.

Обратите внимание на каждый параметр в запросе. Я предлагаю вам пройти построчно, чтобы понять все это.

Обратите внимание на ответ ниже при выполнении этого запроса:

Ответ:

{
  "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

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

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

person Opster ES Ninja - Kamal    schedule 19.03.2020