Elasticsearch 6.8 match_phrase search N-граммовый токенизатор работает не очень хорошо

Я использую Elasticsearch N-gram tokenizer и match_phrase для нечеткого сопоставления моего индекса и тестовых данных, как показано ниже:

DELETE /m8
PUT m8
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "my_tokenizer"
        }
      },
      "tokenizer": {
        "my_tokenizer": {
          "type": "ngram",
          "min_gram": 1,
          "max_gram": 3,
          "custom_token_chars":"_."
        }
      }
    },
    "max_ngram_diff": 10
  },
  "mappings": {
    "table": {
      "properties": {
        "dataSourceId": {
          "type": "long"
        },
        "dataSourceType": {
          "type": "integer"
        },
        "dbName": {
          "type": "text",
          "analyzer": "my_analyzer",
          "fields": {
            "keyword": {
              "type": "keyword",
              "ignore_above": 256
            }
          }
        }
      }
    }
  }
}


PUT /m8/table/1
{
  "dataSourceId":1,
  "dataSourceType":2,
  "dbName":"rm.rf"
}

PUT /m8/table/2
{
  "dataSourceId":1,
  "dataSourceType":2,
  "dbName":"rm_rf"
}
PUT /m8/table/3
{
  "dataSourceId":1,
  "dataSourceType":2,
  "dbName":"rmrf"
}

проверьте _analyze:

POST m8/_analyze
{
  "tokenizer": "my_tokenizer",
  "text": "rm.rf"
}

_analyze результат:

{
  "tokens" : [
    {
      "token" : "r",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "rm",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "rm.",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "word",
      "position" : 2
    },
    {
      "token" : "m",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "word",
      "position" : 3
    },
    {
      "token" : "m.",
      "start_offset" : 1,
      "end_offset" : 3,
      "type" : "word",
      "position" : 4
    },
    {
      "token" : "m.r",
      "start_offset" : 1,
      "end_offset" : 4,
      "type" : "word",
      "position" : 5
    },
    {
      "token" : ".",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "word",
      "position" : 6
    },
    {
      "token" : ".r",
      "start_offset" : 2,
      "end_offset" : 4,
      "type" : "word",
      "position" : 7
    },
    {
      "token" : ".rf",
      "start_offset" : 2,
      "end_offset" : 5,
      "type" : "word",
      "position" : 8
    },
    {
      "token" : "r",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "word",
      "position" : 9
    },
    {
      "token" : "rf",
      "start_offset" : 3,
      "end_offset" : 5,
      "type" : "word",
      "position" : 10
    },
    {
      "token" : "f",
      "start_offset" : 4,
      "end_offset" : 5,
      "type" : "word",
      "position" : 11
    }
  ]
}

Когда я ищу "rm", ничего не найдено:

GET /m8/table/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match_phrase": {
            "dbName": "rm"
          }
        }
      ]
    }
  }
}
{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 0,
    "max_score" : null,
    "hits" : [ ]
  }
}

Но '.rf' можно найти:

{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 1.7260926,
    "hits" : [
      {
        "_index" : "m8",
        "_type" : "table",
        "_id" : "1",
        "_score" : 1.7260926,
        "_source" : {
          "dataSourceId" : 1,
          "dataSourceType" : 2,
          "dbName" : "rm.rf"
        }
      }
    ]
  }
}

Мой вопрос: почему "rm" не удалось найти, даже если _analyze разделил эту фразу?


person Rollsbean    schedule 09.10.2020    source источник


Ответы (1)


  1. my_analyzer также будет использоваться во время поиска.

    "mapping":{
     "dbName": {
      "type": "text",
      "analyzer": "my_analyzer" 
      "search_analyzer":"my_analyzer"  // <==== If you don't provide a search analyzer then what you defined in analyzer will be used during search time as well.
    
  2. Запрос Match_phrase используется для сопоставления фраз с учетом позиции анализируемого текста. например, поиск Kal ho будет соответствовать документу, имеющему Kal в позиции X, и ho в позиции X + 1 в анализируемом тексте.

  3. Когда вы ищете 'rm' (# 1), текст анализируется с помощью my_analyzer, который преобразует его в n-грамм, и в верхней части этого запроса будет использоваться фраза_search. Следовательно, результат не ожидается.

Решение:

  1. Используйте стандартный анализатор с простым запросом на совпадение

    GET /m8/_search
    {
     "query": {
     "bool": {
       "must": [
         {
           "match": {
             "dbName": {
               "query": "rm",
               "analyzer": "standard" // <=========
             }
           }
         }
       ]
     }
     }
     }
    

    ИЛИ Определить во время сопоставления и использовать запрос на сопоставление (не match_phrase)

    "mapping":{
          "dbName": {
           "type": "text",
           "analyzer": "my_analyzer" 
           "search_analyzer":"standard" //<==========
    

Дополнительный вопрос: Почему вы хотите использовать запрос match_phrase с n-граммовым токенизатором?

person Sahil Gupta    schedule 09.10.2020
comment
Мне нужно нечеткое сопоставление слова, а не фразы, точно так же, как слово abc_def, если я введу c_d, тогда этот документ будет найден. - person Rollsbean; 09.10.2020
comment
Кроме того, вы знаете, как сопоставить dat. в 1_? я ищу rm., ожидался один документ, но все документы вернулись - person Rollsbean; 09.10.2020