Частичное сопоставление nGram и ограничение результатов nGram в запросе с несколькими полями

#search #elasticsearch #indexing #n-gram #elasticsearch-2.0

#Поиск #elasticsearch #индексирование #n-грамм #elasticsearch-2.0

Вопрос:

Справочная информация: я реализовал частичный поиск по полю имени путем индексации маркированного имени ( name поля), а также проанализированного имени ( ngram поля) с триграммой.

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

Проблема: я пытаюсь реализовать запрос, который ограничивает совпадения nGram теми, которые соответствуют только некоторому порогу (скажем, 80%) строки запроса. Я понимаю, что minimum_should_match , похоже, это то, что я ищу, но моя проблема заключается в формировании запроса для фактического получения этих результатов.

Мои точные совпадения токенов увеличиваются до самого верха, но я по-прежнему получаю каждый документ, в котором есть одна совпадающая триграмма в ngram поле.

СУТЬ: настройки индекса и сопоставление

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

 {
  "my_index": {
    "settings": {
      "index": {
        "number_of_shards": "5",
        "max_result_window": "30000",
        "creation_date": "1475853851937",
        "analysis": {
          "filter": {
            "ngram_filter": {
              "type": "ngram",
              "min_gram": "3",
              "max_gram": "3"
            }
          },
          "analyzer": {
            "ngram_analyzer": {
              "filter": [
                "lowercase",
                "ngram_filter"
              ],
              "type": "custom",
              "tokenizer": "standard"
            }
          }
        },
        "number_of_replicas": "1",
        "uuid": "AuCjcP5sSb-m59bYrprFcw",
        "version": {
          "created": "2030599"
        }
      }
    }
  }
}
  

Сопоставления индексов

 {
  "my_index": {
    "mappings": {
      "my_type": {
        "properties": {
          "acw": {
            "type": "integer"
          },
          "pcg": {
            "type": "integer"
          },
          "date": {
            "type": "date",
            "format": "strict_date_optional_time||epoch_millis"
          },
          "dob": {
            "type": "date",
            "format": "strict_date_optional_time||epoch_millis"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "boost": 10
          },
          "ngram": {
            "type": "string",
            "analyzer": "ngram_analyzer"
          },
          "bdk": {
            "type": "integer"
          },
          "mmw": {
            "type": "integer"
          },
          "mpi": {
            "type": "integer"
          },
          "sex": {
            "type": "string",
            "index": "not_analyzed"
          }
        }
      }
    }
  }
}
  

Попытки решения

[GIST: Query Attempts] unlinkifying due to 2 link limit 🙁
(https://gist.github.com/jordancardwell/2e690013666e7e1da6ef1acee314b4e6)

I tried a multi-match query, which gives me correct search results, but I haven’t had luck omitting results for names that only match a single trigram (say «odo» trigram inside «theodophilus«)

 //this matches 'frodo' and sends results to the top, since `name` field is boosted
//  but also matches 'theodore' and 'rodolpho'

{
  "size":100,
  "from":0,
  "query":{
    "multi_match":{
      "query":"frodo",
      "fields":[
        "name",
        "ngram"
      ],
      "type":"best_fields"
    }
  }
}
  

.

 //I then tried to throw in the `minimum_must_match` option
// hoping it would filter out large strings that only had one matching trigram for instance
{
  "size":100,
  "from":0,
  "query":{
    "multi_match":{
      "query":"frodo",
      "fields":[
        "name",
        "ngram"
      ],
      "type":"best_fields",
      "minimum_should_match": "90%",
    }
  }
}
  

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

 // I then tried to contruct a custom query to just return the `minimum_should_match`d results on the ngram field
// I started with a query produced by using bodybuilder to `and` and `or` my other search criteria together
{
  "query": {
    "bool": {
      "filter": {
        "bool": {
          "must": [
            //each separate field's criteria `must`/`and`ed together
            {
              "query": {
                "bool": {
                  "filter": {
                    "bool": {
                      "should": [
                        //each critereon for a specific field `should`/`or`ed together
                        {
                         //my attempt at getting `ngram` field results.. 
                         // should theoretically only return when field 
                         // contains nothing but matching ngrams 
                         // (i.e. exact matches and other fluke matches)
                          "query": { 
                            "match": {
                              "ngram": {
                                "query": "frodo",
                                "minimum_should_match": "100%"
                              }
                            }
                          }
                        }
                        //... other critereon to be `should`/`or`ed together
                      ]
                    }
                  }
                }
              }
            }
            //... other criteria to be `must`/`and`ed together
          ]
        }
      }
    }
  }
}
  

Кто-нибудь может увидеть, что я делаю не так?

Кажется, это должно быть довольно просто выполнить, но я, должно быть, упускаю что-то очевидное.


Обновить

Я запустил запрос с _explain=true помощью (используя пользовательский интерфейс sense), чтобы попытаться понять мои результаты.

Я запросил a match в ngram поле для "frod" with minimum_should_match = 100% , но я все равно получаю каждую запись, которая соответствует хотя бы одному ngram. (например rodolpho , даже если он не содержит fro )

СУТЬ: тестовый запрос и результаты


примечание: перекрестная публикация из [discuss.elastic.co ] сделаю ссылку позже, пока не могу опубликовать больше 2 : /

(https://discuss.elastic.co/t/ngram-partial-match-limiting-ngram-results-in-multiple-field-query/62526)

Ответ №1:

Я использовал ваши настройки и сопоставления для создания индекса. И ваши запросы, похоже, работают нормально для меня. Я бы предложил выполнить explain проверку одного из «неожиданных» документов, который возвращается, и посмотреть, почему он сопоставляется и возвращается с другими результатами.

Вот что я сделал:

Запустите api analyze в своем анализаторе, чтобы увидеть, как запрос будет разделен на токены.

 curl -XGET 'localhost:9200/my_index/_analyze' -d '
{
  "analyzer" : "ngram_analyzer",
  "text" : "frodo"
}'
  

фродо будет разделен на 3 токена вашим анализатором.

 {
  "tokens": [
    {
      "token": "fro",
      "start_offset": 0,
      "end_offset": 5,
      "type": "word",
      "position": 0
    },
    {
      "token": "rod",
      "start_offset": 0,
      "end_offset": 5,
      "type": "word",
      "position": 0
    },
    {
      "token": "odo",
      "start_offset": 0,
      "end_offset": 5,
      "type": "word",
      "position": 0
    }
  ]
}
  

Я проиндексировал 3 документа для тестирования (использовалось только поле ngrams). Вот документы:

 {
  "took": 5,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 3,
    "max_score": 1,
    "hits": [
      {
        "_index": "my_index",
        "_type": "my_type",
        "_id": "2",
        "_score": 1,
        "_source": {
          "ngram": "theodore"
        }
      },
      {
        "_index": "my_index",
        "_type": "my_type",
        "_id": "1",
        "_score": 1,
        "_source": {
          "ngram": "frodo"
        }
      },
      {
        "_index": "my_index",
        "_type": "my_type",
        "_id": "3",
        "_score": 1,
        "_source": {
          "ngram": "rudolpho"
        }
      }
    ]
  }
}
  

Первый упомянутый вами запрос соответствует фродо и Теодору, но не рудольфу, как вы упомянули, что имеет смысл, поскольку rudolpho не создает никаких триграмм, которые соответствуют триграммам от фродо

 frodo -> fro, rod, odo 

rudolpho -> rud, udo, dol, olp, lph, pho
  

Используя ваш второй запрос, я возвращаю только фродо (ни один из двух других).

 {
  "took": 5,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0.53148466,
    "hits": [
      {
        "_index": "my_index",
        "_type": "my_type",
        "_id": "1",
        "_score": 0.53148466,
        "_source": {
          "ngram": "frodo"
        }
      }
    ]
  }
}
  

Затем я запустил explain ( localhost:9200/my_index/my_type/2/_explain ) в двух других документах (theodore и rudolpho), и я вижу это (я обрезал ответ)

 {
  "_index": "my_index",
  "_type": "my_type",
  "_id": "2",
  "matched": false,
  "explanation": {
    "value": 0,
    "description": "Failure to meet condition(s) of required/prohibited clause(s)",
    "details": [
      {
        "value": 0,
        "description": "no match on required clause ((ngram:fro ngram:rod ngram:odo)~2)",
        "details": [
  

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

Комментарии:

1. спасибо за быстрый и подробный ответ! И вы правы насчет rudolpho, я неправильно ввел rodolpho (было сгенерированным fakerjs именем:).. Я собираюсь проверить, что получаю те же результаты, используя только запрос ngram с моим индексом (я подозреваю, что так), затем попробуйте добавить запрос к полю name и вернуться к вам. Еще раз спасибо!

2. Пожалуйста, смотрите Суть выше и здесь для объяснения запроса соответствия, который по-прежнему возвращает каждое совпадение с одной совпадающей триграммой. Я все еще изучаю это, но, честно _explain говоря, результаты немного сбивают с толку.