Почему анализатор ключевых слов, примененный к текстовому полю, не возвращает результатов, если шаблон содержит тире в поисковом запросе Regexp?

Я создал небольшой пример, чтобы продемонстрировать конкретную проблему, с которой я столкнулся. Вкратце, когда я создаю сопоставление с несколькими полями, используя тип поля «Текст» и анализатор ключевых слов, никакие документы не возвращаются из поискового запроса Elasticsearch Regexp, содержащего знаки препинания. Я использую тире в следующем примере, чтобы продемонстрировать проблему.

Я использую Elasticsearch 7.10.2. Индекс, на который я ориентируюсь, уже заполнен миллионами документов. Поле типа Text, где мне нужно запустить некоторые регулярные выражения, использует анализатор Standard (по умолчанию). Я так понимаю, поскольку анализатор Standard токенизирует поле, следующий запрос:

POST _analyze
{
  "analyzer" : "default",
  "text" : "The number is: 123-4576891-73.\n\n"
}

даст три слова: the, number, is и три группы чисел: 123, 4567891, 73. Очевидно, что регулярное выражение, основанное на знаках препинания, такое как это, которое содержит два буквальных дефиса:

"(.*[^a-z0-9_])?[0-9]{3}-[0-9]{7}-[0-9]{2}([^a-z0-9_].*)?" 

не вернет результат. Обратите внимание: для тех, кто не знаком с этим, ярлыки регулярных выражений не работают для запросов Elasticsearch на основе Lucene (по крайней мере, пока). Вот ссылка: https://www.elastic.co/guide/en/elasticsearch/reference/current/regexp-syntax.html. Кроме того, использование границ слов, которые я показываю в своих примерах (.*[^a-z0-9_])? и ([^a-z0-9_].*)?, взято из этого сообщения: Word граница в регулярном выражении Lucene.

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

PUT /index-01
{
  "settings": {
    "number_of_shards": 1
  },
  "mappings": {
    "properties": {
      "text": { "type": "text" }
    }
  }
}
POST index-01/_doc/
{
  "text": "The number is: 123-4576891-73.\n\n"
}

Следующий поисковый запрос Regexp ничего не вернет из-за проблемы с токенизацией, описанной ранее:

POST index-01/_search
{
  "size": 1,
  "query": {
    "regexp": {
      "text": {
        "value": "(.*[^a-z0-9_])?[0-9]{3}-[0-9]{7}-[0-9]{2}([^a-z0-9_].*)?",
        "flags": "ALL",
        "case_insensitive": true,
        "max_determinized_states": 100000
      }
    }
  },
  "_source": false,
  "highlight": {
    "fields": {
      "text": {}
    }
  }
}

Большинство сообщений предполагают, что быстрое исправление будет состоять в том, чтобы настроить таргетинг на несколько полей типа ключевого слова вместо текстового поля. Поле с несколькими типами ключевых слов создается автоматически, как показано ниже:

GET index-01/_mapping/field/text

отклик:

{
  "index-01" : {
    "mappings" : {
      "text" : {
        "full_name" : "text",
        "mapping" : {
          "text" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    }
  }
}

Ориентируясь на поле ключевого слова, я получаю результаты для следующего поискового запроса Regexp:

POST index-01/_search
{
  "size": 1,
  "query": {
    "regexp": {
      "text.keyword": {
        "value": "(.*[^a-z0-9_])?[0-9]{3}-[0-9]{7}-[0-9]{2}([^a-z0-9_].*)?",
        "flags": "ALL",
        "case_insensitive": true,
        "max_determinized_states": 100000
      }
    }
  },
  "_source": false,
  "highlight": {
    "fields": {
      "text.keyword": {}
    }
  }
}

вот часть результата, выделенная хитом:

...
         "highlight" : {
          "text.keyword" : [
            "<em>This is my number 123-4576891-73. Thanks\n\n</em>"
          ]
        }
...

Поскольку некоторые документы содержат большое количество текста, я отрегулировал размер поля text.keyword параметром ignore_above:

PUT /index-01/_mapping
{
  "properties": {
    "text": {
      "type": "text",
      "fields": {
        "keyword": {
          "type": "keyword",
          "ignore_above": 32766
        }
      }
    }
  }
}

Однако при этом будут пропущены некоторые документы, поскольку целевой индекс содержит текстовые поля большего размера, чем верхняя граница для ключевого слова типа поля. Кроме того, согласно документации Elasticsearch здесь: https://www.elastic.co/guide/en/elasticsearch/reference/current/keyword.html этот тип поля действительно предназначен для структурированных данных, постоянных значений и запросов с подстановочными знаками.

Следуя этому руководству, я назначил анализатору ключевых слов новый тип поля Text (text.raw), внеся следующее обновление в сопоставление:

PUT /index-01/_mapping
{
  "properties": {
    "text": {
      "type": "text",
      "fields": {
        "keyword": {
          "type": "keyword",
          "ignore_above": 32766
        },
        "raw": {
          "type": "text",
          "analyzer": "keyword",
          "index": true
        }
      }
    }
  }
}

Теперь вы можете увидеть дополнительное сопоставление text.raw с этим запросом:

GET index-01/_mapping/field/text

отклик:

{
  "index-01" : {
    "mappings" : {
      "text" : {
        "full_name" : "text",
        "mapping" : {
          "text" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 32766
              },
              "raw" : {
                "type" : "text",
                "analyzer" : "keyword"
              }
            }
          }
        }
      }
    }
  }
}

Затем я проверил, что данные на самом деле были сопоставлены с несколькими полями:

POST index-01/_search
{
  "query": 
  {
    "match_all": {}
  },
  "fields": ["text", "text.keyword", "text.raw"]
}

отклик:

...
    "hits" : [
      {
        "_index" : "index-01",
        "_type" : "_doc",
        "_id" : "2R-OgncBn-TNB4PjXYAh",
        "_score" : 1.0,
        "_source" : {
          "text" : "The number is: 123-4576891-73.\n\n"
        },
        "fields" : {
          "text" : [
            "The number is: 123-4576891-73.\n\n"
          ],
          "text.keyword" : [
            "The number is: 123-4576891-73.\n\n"
          ],
          "text.raw" : [
            "The number is: 123-4576891-73.\n\n"
          ]
        }
      }
    ]
...

Я также проверил, что анализатор ключевых слов, примененный к полю text.raw, содержит один токен, как показано в следующем запросе:

POST _analyze
{
  "analyzer" : "keyword",
  "text" : "The number is: 123-4576891-73.\n\n"
}

отклик:

{
  "tokens" : [
    {
      "token" : "The number is: 123-4576891-73.\n\n",
      "start_offset" : 0,
      "end_offset" : 32,
      "type" : "word",
      "position" : 0
    }
  ]
}

Однако точно такой же поисковый запрос Regexp, нацеленный на поле text.raw, ничего не возвращает:

POST index-01/_search
{
  "size": 1,
  "query": {
    "bool": {
      "must": [
        {
          "regexp": {
            "text.raw":   {
              "value": "(.*[^a-z0-9_])?[0-9]{3}-[0-9]{7}-[0-9]{2}([^a-z0-9_].*)?",
              "flags": "ALL",
              "case_insensitive": true,
              "max_determinized_states": 100000
            }
          }
        }
      ]
    }
  },
  "_source": false,
  "highlight" : {
    "fields" : {
        "text.raw": {}
    }
  }
}

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


person ewilan    schedule 08.02.2021    source источник