Эластичные сопоставления поиска для пробелов и дефисов

#elasticsearch

#elasticsearch

Вопрос:

У меня есть индекс поиска для продуктов, и некоторые из продуктов имеют формат, подобный so:

 product1-mk1
product1-mk2
product1-mk3
product2-mk1
...
  

Очевидно, что эластичный поиск может очень хорошо соответствовать, если кто-то ищет, используя точную фазу product1-mk1 но если пользователь использует пробел вместо дефиса product1 mk1 , результаты поиска кажутся дикими, и «продукт-mk1» найден, но он находится довольно далеко внизу результатов.

Могу ли я что-нибудь сделать в своих сопоставлениях, чтобы учесть это? Мои сопоставления выглядят следующим образом:

         'mappings' => [
            'products' => [
                'name' => [
                    'type' => 'text',
                    'include_in_all' => true,
                    'search_analyzer' => 'standard'
                ],
  

Мои настройки индекса такие:

         'settings' => [
            'analysis' => [
                'filter' => [
                    'ngram_filter' => [
                        "type" => "edge_ngram",
                        "min_gram" => 2,
                        "max_gram" => 6
                    ]
                ],
                'analyzer' => [
                    'ngram_analyzer' => [
                        "type" => "custom",
                        "tokenizer" => "word_split",
                        "filter" => [
                            "lowercase",
                            "ngram_filter"
                        ]
                    ]
                ],
                'tokenizer' => [
                    'word_split' => [
                        'type' => 'ngram',
                        'min_gram' => 2,
                        'max_gram' => 6,
                        'token_chars' => [
                            'letter',
                            'digit',
                            'punctuation',
                            'symbol'
                        ]
                    ]
                ]
            ]
        ],
  

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

Ответ №1:

Вы можете использовать .keyword поле, если используете динамическое сопоставление для своего индекса, или создать поле ключевого слова, а затем использовать запрос bool с предложением should для text и keyword для получения ожидаемых результатов.

Рабочий пример использования динамического сопоставления, которое создает .keyword поле для каждого текстового поля.

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

 {
  "product" : "product1-mk1"
}
{
  "product" : "product1-mk2"
}
{
  "product" : "product1-mk3"
}
  

Поисковый запрос с пробелами

 {
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "product": "product1 mk1" 
          }
        },
        {
          "match": {
            "product.keyword": "product1 mk1" 
          }
        }
      ]
    }
  }
}
  

Результаты поиска, вы можете заметить, что первый результат имеет в ~ 10 раз лучший результат

 hits": [
      {
        "_index": "hyphen",
        "_type": "_doc",
        "_id": "1",
        "_score": 1.1143606,
        "_source": {
          "product": "product1-mk1"
        }
      },
      {
        "_index": "hyphen",
        "_type": "_doc",
        "_id": "2",
        "_score": 0.13353139,
        "_source": {
          "product": "product1-mk2"
        }
      },
      {
        "_index": "hyphen",
        "_type": "_doc",
        "_id": "3",
        "_score": 0.13353139,
        "_source": {
          "product": "product1-mk3"
        }
      }
    ]
  

Ответ №2:

я недавно столкнулся с этим, вам нужен пользовательский анализатор, пользовательский сопоставитель фильтров символов и пользовательский анализатор поиска:

  "analyzer": {
      "ngram_index_analyzer": {
        "tokenizer": "keyword",
        "type": "custom",
        "char_filter": [
          "ngram_mappings_char_filter"
        ],
        "filter": [
          "lowercase",
          "trim",
          "ngram_filter"
        ]
      },
      "ngram_search_analyzer": {
        "tokenizer": "keyword",
        "char_filter": [
          "ngram_mappings_char_filter"
        ],
        "filter": [
          "trim"
        ]
      }
    },
    "char_filter": {
      "ngram_mappings_char_filter": {
        "type": "mapping",
        "mappings" : ["-=>_"]
      }
    },
    "filter": {
       "ngram_filter": {
       "type": "ngram",
       "min_gram": 1,
       "max_gram": 10
      }
    }
  

для пробелов вы можете добавить еще одно сопоставление фильтра символов