Сопоставьте все точные слова в запросе

Я хочу создать запрос с использованием ElasticSearch Java API, который соответствует только (1) полным словам и (2) всем словам из поискового запроса. Вот пример для этого:

Текст:

hello wonderful world

Они должны соответствовать:

hello
hello wonderful
hello world
wonderful world
hello wonderful world
wonderful
world

Они не должны совпадать:

  1. hell

  2. hello fniefsgbsugbs

Я попробовал следующие параметры для запроса на сопоставление, но он по-прежнему соответствует двум приведенным выше примерам.

Это код для генерации запроса с использованием ElasticSearch 7.7.1 Java API:

import org.elasticsearch.index.query.QueryBuilders
...

QueryBuilders.matchQuery(field, query)
            .autoGenerateSynonymsPhraseQuery(false)
            .fuzziness(0)
            .prefixLength(0)
            .fuzzyTranspositions(false)
            .operator(Operator.AND)
            .minimumShouldMatch("100%")

Что сгенерирует этот запрос:

{
  "size": 100,
  "query": {
    "bool": {
      "filter": [
        {
          "match": {
            "searchableText": {
              "query": "hell",
              "operator": "AND",
              "fuzziness": "0",
              "prefix_length": 0,
              "max_expansions": 50,
              "minimum_should_match": "100%",
              "fuzzy_transpositions": false,
              "lenient": false,
              "zero_terms_query": "NONE",
              "auto_generate_synonyms_phrase_query": false,
              "boost": 1
            }
          }
        }
      ]
    }
  }
}

Может ли кто-нибудь помочь мне найти хорошее решение для этого?

Редактировать: вот настройки и сопоставления (я удалил все, что не относится к searchableText, чтобы сделать его как можно меньше):

{
    "settings": {
      "analysis": {
        "normalizer": {
          "lowercase_normalizer": {
            "type": "custom",
            "filter": [
              "lowercase"
            ]
          }
        },
        "filter": {
          "german_stemmer": {
            "type": "stemmer",
            "language": "light_german"
          },
          "ngram_filter": {
            "type": "shingle",
            "max_shingle_size": 4,
            "min_shingle_size": 2,
            "output_unigrams": false,
            "output_unigrams_if_no_shingles": false
          }
        },
        "analyzer": {
          "german": {
            "tokenizer": "standard",
            "filter": [
              "lowercase",
              "german_synonyms",
              "german_stop",
              "german_keywords",
              "german_no_stemming",
              "german_stemmer"
            ]
          },
          "german_ngram": {
            "tokenizer": "standard",
            "filter": [
              "lowercase",
              "german_synonyms",
              "german_keywords",
              "german_no_stemming",
              "german_stemmer",
              "ngram_filter"
            ]
          }
        }
      }
    },
    "mappings": {
      "properties": {
        "description": {
          "type": "text",
          "copy_to": "searchableText",
          "analyzer": "german"
        },
        "name": {
          "type": "text",
          "copy_to": "searchableText",
          "analyzer": "german"
        },
        "userTags": {
          "type": "keyword",
          "copy_to": "searchableText",
          "normalizer": "lowercase_normalizer"
        },
        "searchableText": {
          "type": "text",
          "analyzer": "german",
          "fields": {
            "ngram": {
              "type": "text",
              "analyzer": "german_ngram"
            }
          }
        },
        "searches": {
          "type": "keyword",
          "copy_to": "searchableText",
          "normalizer": "lowercase_normalizer"
        }
      }
    }
  }

Изменить 2. Это упомянутые фильтры:

"filter": {
    "german_stop": {
      "type": "stop",
      "stopwords": "_german_"
    },
    "german_stemmer": {
      "type": "stemmer",
      "language": "light_german"
    },
    "ngram_filter": {
      "type": "shingle",
      "max_shingle_size": 4,
      "min_shingle_size": 2,
      "output_unigrams": false,
      "output_unigrams_if_no_shingles": false
    }
}

person Peter    schedule 17.11.2020    source источник
comment
Добавьте сопоставление индекса, в частности, для поля searchableText, а также любые связанные с ним настройки.   -  person Nishant    schedule 18.11.2020
comment
Спасибо за ответ, добавил настройки. Надеюсь, это поможет.   -  person Peter    schedule 18.11.2020
comment
@Peter, я попробовал вашу карту и образец документа, и он работает так, как вы хотите, пожалуйста, посмотрите мой ответ для получения более подробной информации.   -  person user156327    schedule 18.11.2020


Ответы (2)


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

"german_synonyms",
"german_stop",
"german_keywords",
"german_no_stemming",

После этого я проиндексировал ваш образец документа hello wonderful world и использовал ваш поисковый запрос, но он работает нормально, как вы и ожидали, и не возвращает результат для hell или hello fniefsgbsugbs, как показано ниже.

{
    "size": 100,
    "query": {
        "bool": {
            "filter": [
                {
                    "match": {
                        "searchableText": {
                            "query": "hello fniefsgbsugbs",
                            "operator": "AND",
                            "fuzziness": "0",
                            "prefix_length": 0,
                            "max_expansions": 50,
                            "minimum_should_match": "100%",
                            "fuzzy_transpositions": false,
                            "lenient": false,
                            "zero_terms_query": "NONE",
                            "auto_generate_synonyms_phrase_query": false,
                            "boost": 1
                        }
                    }
                }
            ]
        }
    }
}

И он возвращается

"hits": {
        "total": {
            "value": 0,
            "relation": "eq"
        },
        "max_score": null,
        "hits": []
    }

То же самое с hell , в то время как он возвращает результат с hello, hello wonderful и другими терминами, которые, как ожидается, будут совпадать.

EDIT: вы используете соответствует анализируемому запросу, т. е. анализирует условие поиска, применяет тот же анализатор, который применяет время индексирования к полю, и сопоставляет маркеры времени поиска с маркерами времени индексирования.

Для правильного устранения подобных проблем используйте проанализировать API и проверить маркеры проиндексированных документов и маркеры условий поиска.

person user156327    schedule 18.11.2020
comment
Спасибо за ваш ответ, моя проблема в том, что я не хочу, чтобы hell совпадало с hello. Вы знаете решение для этого? - person Peter; 18.11.2020
comment
@Peter, hell не соответствует hello, я попробовал еще раз :), можете ли вы указать недостающие фильтры, я думаю, эти фильтры генерируют разные токены, что вызывает проблему - person user156327; 18.11.2020
comment
@Peter, или вы можете просто попробовать еще раз с моими настройками и сопоставлением, вы можете увидеть, как они работают в соответствии с вашими требованиями. - person user156327; 18.11.2020
comment
Я думаю, вы правы, что мои настройки являются причиной того, что он не работает. Я также добавил фильтры к моему вопросу. Не могли бы вы взглянуть на них? Может быть, это фильтр ngram_filter? - person Peter; 18.11.2020
comment
@Peter, я просмотрел ваши обновленные настройки, но если вы заметили, что вы не используете ngram_filter в анализаторе german, а этот анализатор german вы используете для searchableText, в то время как анализатор german_ngram, который использует ngram_filter, используется в его подполе с именем ngram, которым вы не являетесь используя в запросе (по крайней мере, запрос, который вы указали, не включен). - person user156327; 19.11.2020
comment
@Peter, пожалуйста, ознакомьтесь с частью моего ответа, где я добавил больше информации. - person user156327; 19.11.2020

для полей, проиндексированных как ключевые слова, я часто предпочитаю QueryString Query DSL вместо Match Query. например:

{
    "query" : {
        "query_string" : {
            "query" : "my_field:('hello', 'wonderful', 'world')"
        }
    }
}

будут соответствовать всем тем комбинациям, которые вы написали, должны совпадать, а не тем, которые вам не нужны. отношение терминов в круглых скобках похоже на SQL IN, поэтому любой термин, который появляется в поле, будет соответствовать документу. кроме того, этот формат дает вам большую гибкость при создании сложных поисков. Позвольте мне знать, если это помогает.

person Tom Elias    schedule 18.11.2020
comment
Спасибо, я попробовал ваше предложение, но hell по-прежнему соответствует hello, а это не то, что мне нужно. Знаете ли вы какой-нибудь способ, чтобы части слова не совпадали? - person Peter; 18.11.2020