Нечеткие фразы Elasticsearch

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

{
    "query": {
        "match": {
            "content": {
                "query": "some search terms like this",
                "fuzziness": 1,
                "operator": "and"
            }
        }
    }
}

Ответ 1

В конце концов выяснилось, что мне нужно использовать комбинацию span запросов, которые дают отличную тонкую настройку для нечеткости и slop. Мне нужно было добавить функцию, чтобы вручную токенизировать мои фразы и добавлять к массиву "clauses" программным образом:

{"query":
{
  "span_near": {
    "clauses": [
      {
        "span_multi": {
          "match": {
            "fuzzy": {
              "content": {
                "fuzziness": "2",
                "value": "word"
              }
            }
          }
        }
      },
      {
        "span_multi": {
          "match": {
            "fuzzy": {
              "content": {
                "fuzziness": "2",
                "value": "another"
              }
            }
          }
        }
      }                   
    ],
    "slop": 1,
    "in_order": "true"

Ответ 2

@econgineer Отличная почта.

Я хотел попробовать это для ES-запроса, над которым мы работаем, но я слишком ленив, чтобы продолжать делать данные JSON....

Я думаю, что этот код работает... странно это заставляет jq жаловаться, но ElasticSearch работает....

import json
import pprint
from collections import defaultdict
nested_dict = lambda: defaultdict(nested_dict)
query=nested_dict()
query['span_near']['clauses']=list()
query['slop']='1'
query['in_order']="true"


words=['what','is','this']
for w in words:
    nest = nested_dict()
    nest["span_multi"]["match"]["fuzzy"]["msg"]["fuzziness"]["value"]=w
    nest["span_multi"]["match"]["fuzzy"]["msg"]["fuzziness"]["fuzziness"]="2"
    json.dumps(nest)
    query['span_near']['clauses'].append(json.loads(json.dumps(nest)))


pprint.pprint(json.loads(json.dumps(query)))

Если вы украшаете вывод

cat t2.json | tr  "\'" "\""  | jq '.'

Вы должны увидеть что-то вроде

{
  "in_order": "true",
  "slop": "1",
  "span_near": {
    "clauses": [
      {
        "span_multi": {
          "match": {
            "fuzzy": {
              "msg": {
                "fuzziness": {
                  "fuzziness": "2",
                  "value": "what"
                }
              }
            }
          }
        }
      },
      {
        "span_multi": {
          "match": {
            "fuzzy": {
              "msg": {
                "fuzziness": {
                  "fuzziness": "2",
                  "value": "is"
                }
              }
            }
          }
        }
      },
      {
        "span_multi": {
          "match": {
            "fuzzy": {
              "msg": {
                "fuzziness": {
                  "fuzziness": "2",
                  "value": "this"
                }
              }
            }
          }
        }
      }
    ]
  }
}

И затем для запроса ES это обычный нормальный

curl --silent My_ES_Server:9200:/INDEX/_search -d @t2.json

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

Ответ 3

Действительно, отличный вопрос и ответ. Я удивлен, что это "нечеткое совпадение фразы" не имеет поддержки из коробки.

Здесь протестированный код NodeJS, который генерирует блок запросов с нечеткой фразой (multi clause), в контексте многопользовательского поиска (msearch), но это должно работать одинаково с одним поиском.

Применение:

let queryBody = [];
client.msearch({
   body: queryBody
})

queryBody.push({ index: 'YOUR_INDEX' });
queryBody.push(createESFuzzyPhraseQueryBlock('YOUR PHRASE', 'YOUR_FIELD_NAME', 2));   // 2 <- fuzziness

Функции:

const createESFuzzyPhraseClauseBlock = (word, esFieldName, fuzziness) => {
    let clauseBlock = JSON.parse(
        `{
            "span_multi": {
                "match": {
                    "fuzzy": {
                        "${esFieldName}": {
                            "fuzziness": "${fuzziness}",
                            "value": "${word}"
                        }
                    }
                }
            }
        }`);

    return clauseBlock;
};


const createESFuzzyPhraseQueryBlock = (phrase, esFieldName, fuzziness) => {
    let clauses = [];

    let words = phrase.split(' ');
    words.forEach(word => clauses.push(createESFuzzyPhraseClauseBlock(word, esFieldName, fuzziness)));

    let queryBlock =
        {
            "query":
                {
                    "span_near": {
                        "clauses": clauses,
                        "slop": 1,
                        "in_order": "true"
                    }
                }
        };

    return queryBlock;
};

Ответ 4

Также рассмотрите возможность смешивания запросов, для меня базовый запрос выглядел так: для фраз длины 2 я использовал префиксный запрос, а для остальных я использовал запрос на совпадение с fuziness, установленным в AUTO.