Запрос подстановочных знаков Elasticsearch с дефисами и фильтром нижнего регистра

Я хочу сделать запрос с подстановочными знаками для QNMZ-1900

Как я читал в документах и ​​пробовал сам, стандартный токенизатор Elasticsearch разбивает слова на дефисы, например QNMZ-1900 будет разбит на QNMZ и 1900.

Чтобы предотвратить такое поведение, я использую функцию not_analyzed.

curl -XPUT 'localhost:9200/test-idx' -d '{
"mappings": {
    "doc": {
        "properties": {
            "foo" : {
                "type": "string",
                "index": "not_analyzed"
            }
        }
    }
}
}'

Я помещаю что-то в свой индекс:

curl -XPUT 'localhost:9200/test-idx/doc/1' -d '{"foo": "QNMZ-1900"}'

Обновление:

curl -XPOST 'localhost:9200/test-idx/_refresh'

Теперь я могу использовать запрос с подстановочными знаками и найти QNMZ-1900 :

curl 'localhost:9200/test-idx/doc/_search?pretty=true' -d '{
"query": {
     "wildcard" : { "foo" : "QNMZ-19*" }
}

Мой вопрос:

Как выполнить запрос с подстановочными знаками с условием поиска в нижнем регистре?

Я пробовал:

curl -XDELETE 'localhost:9200/test-idx'
curl -XPUT 'localhost:9200/test-idx' -d '{
"mappings": {
    "doc": {
        "properties": {
            "foo" : {
                "type": "string",
                "index": "not_analyzed",
                "filter": "lowercase"
            }
        }
    }
}
}'
curl -XPUT 'localhost:9200/test-idx/doc/1' -d '{"foo": "QNMZ-1900"}'
curl -XPOST 'localhost:9200/test-idx/_refresh'

но мой строчный запрос:

curl 'localhost:9200/test-idx/doc/_search?pretty=true' -d '{
"query": {
     "wildcard" : { "foo" : "qnmz-19*" }
}
}'

ничего не находит.

Как это исправить?


person astropanic    schedule 22.09.2014    source источник


Ответы (2)


Одним из решений является определение пользовательского анализатора с помощью

  • токенизатор keyword (который сохраняет входное значение как есть, как если бы оно было not_analyzed)
  • фильтр токенов lowercase

Я пробовал это:

POST test-idx
{
  "index":{
    "analysis":{
      "analyzer":{
        "lowercase_hyphen":{
          "type":"custom",
          "tokenizer":"keyword",
          "filter":["lowercase"]
        }
      }
    }
  }
}

PUT test-idx/doc/_mapping
{
  "doc":{
    "properties": {
        "foo" : {
          "type": "string",
          "analyzer": "lowercase_hyphen"
        }
    }      
  }
}

POST test-idx/doc
{
  "foo":"QNMZ-1900"
}

Как вы можете видеть, используя конечную точку _analyze следующим образом:

GET test-idx/_analyze?analyzer=lowercase_hyphen&text=QNMZ-1900

выводит только один токен в нижнем регистре, но не разделенный на дефисы:

{
   "tokens": [
      {
         "token": "qnmz-1900",
         "start_offset": 0,
         "end_offset": 9,
         "type": "word",
         "position": 1
      }
   ]
}

Затем, используя тот же запрос:

POST test-idx/doc/_search
{
  "query": {
    "wildcard" : { "foo" : "qnmz-19*" }    
  }
}

У меня есть этот результат, который вы хотите:

{
   "took": 66,
   "timed_out": false,
   "_shards": {
      "total": 5,
      "successful": 5,
      "failed": 0
   },
   "hits": {
      "total": 1,
      "max_score": 1,
      "hits": [
         {
            "_index": "test-idx",
            "_type": "doc",
            "_id": "wo1yanIjQGmvgfScMg4hyg",
            "_score": 1,
            "_source": {
               "foo": "QNMZ-1900"
            }
         }
      ]
   }
}

Однако обратите внимание, что это позволит вам запрашивать только значение в нижнем регистре. Как сказал Андрей в комментарии, тот же запрос со значением QNMZ-19* ничего не вернет.

Причину можно найти в документация : во время поиска значение не анализируется.

person ThomasC    schedule 22.09.2014
comment
Не работает для POST test-idx/doc/_search { query: { wildcard : { foo : QNMZ-19* } } } - person Andrei Stefan; 22.09.2014
comment
Действительно, входной запрос с подстановочными знаками не анализируется, но таким образом поиск всегда можно выполнить по значению в нижнем регистре. Тем не менее, я обновляю свой ответ. - person ThomasC; 22.09.2014
comment
@ThomasC, этот ответ все еще действителен для ElasticSearch 6.1? Есть ли более новая и удобная функция для такого поиска? Что вы думаете о различных видах моделирования. Например, мы можем продублировать foo в поле foo_lowercase в json, которое будет содержать qnmz-1900. И тогда мы могли бы выполнить поиск по curl 'localhost:9200/test-idx/doc/_search?pretty=true' -d '{query: {wildcard: {foo: qnmz-19*}} Или вы рассматриваете такой вид моделирования как антипаттерн для elasticsearch? - person Radosław Osiński; 11.01.2018
comment
@RadosławOsiński Я еще не вникал в ES 6.1. Однако, начиная с ES 2.X, тип данных string был заменен текстом (для анализируемой строки) и ключевым словом (для неанализируемой строки). В моем ответе stringдолжно быть заменено на text в отображении. Что касается вашего предложения, на самом деле я бы его реализовал. Используйте сопоставление нескольких полей, чтобы сохранить исходное значение в основном поле и сохранить в подполях каждую проанализированную версию (по одной на каждый анализатор). Это хорошая практика, единственным недостатком является то, что эти поля занимают некоторое пространство, и индексация будет немного длиннее. Надеюсь это поможет :) - person ThomasC; 13.01.2018
comment
Спасибо за хорошее объяснение, этот ответ должен быть принят. - person Vishal Sharma; 25.08.2020

Я проверил этот подход в своем любимом проекте на основе ES 6.1. Модель данных, как показано ниже, позволяет выполнять поиск, как и ожидалось, в вопросе:

PUT test-idx
{
    "settings": {
        "analysis": {
            "analyzer": {
                "keylower": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": ["lowercase"]
                }
            }
        }
    }
}

POST /test-idx/doc/_mapping
{
    "properties": {
        "foo": {
            "type": "text",
            "fields": {
                "raw": {
                    "type": "keyword"
                },
                "lowercase_foo": {
                    "type": "text",
                    "analyzer": "keylower"
                }
            }
        }
    }
}

PUT /test-idx/doc/1
{"foo": "QNMZ-1900"}

Проверьте результаты этих двух поисков. Сначала будет один удар. Второй вернет 0 хитов.

GET /test-idx/doc/_search
{
  "query": {
     "wildcard" : { "foo.lowercase_foo" : "qnmz-19*" }
  }
}

GET /test-idx/doc/_search
{
  "query": {
     "wildcard" : { "foo" : "qnmz-19*" }
  }
}

Спасибо @ThomasC за мнение. Пожалуйста, будьте осторожны с моим ответом. Я только изучаю Elasticsearch. Я не эксперт в этой базе данных. Я не знаю, это готовый к производству совет!

person Radosław Osiński    schedule 13.01.2018