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