Elasticsearch возвращает фонетический токен с поиском

Я использую плагин для фонетического анализа от elastic search для сопоставления строк благодаря фонетическому преобразованию.

Моя проблема заключается в том, как получить фонетическое преобразование, обработанное эластичным поиском в результате запроса?.

Сначала я создаю индекс с преобразованием metaphone:

request_body = {
    'settings': {
        'index': {
            'analysis': {
                'analyzer': {
                    'metaphone_analyzer': {
                        'tokenizer':
                        'standard',
                        'filter': [
                            'ascii_folding_filter', 'lowercase',
                            'metaphone_filter'
                        ]
                    }
                },
                'filter': {
                    'metaphone_filter': {
                        'type': 'phonetic',
                        'encoder': 'metaphone',
                        'replace': False
                    },
                    'ascii_folding_filter': {
                        'type': 'asciifolding',
                        'preserve_original': True
                    }
                }
            }
        }
    },
    'mappings': {
        'person_name': {
            'properties': {
                'full_name': {
                    'type': 'text',
                    'fields': {
                        'metaphone_field': {
                            'type': 'string',
                            'analyzer': 'metaphone_analyzer'
                        }
                    }
                }
            }
        }
    }
}

res = es.indices.create(index="my_index", body=request_body)

Затем я добавляю некоторые данные:

# Add some data
names = [{
    "full_name": "John Doe"
}, {
    "full_name": "Bob Alice"
}, {
    "full_name": "Foo Bar"
}]

for name in names:
    res = es.index(index="my_index",
                   doc_type='person_name',
                   body=name,
                   refresh=True)

И, наконец, я запрашиваю имя:

es.search(index="my_index",
          body={
              "size": 5,
              "query": {
                  "multi_match": {
                      "query": "Jon Doe",
                      "fields": "*_field"
                  }
              }
          })

Поиск возвращает:

{
    'took': 1,
    'timed_out': False,
    '_shards': {
        'total': 5,
        'successful': 5,
        'skipped': 0,
        'failed': 0
    },
    'hits': {
        'total':
        1,
        'max_score':
        0.77749264,
        'hits': [{
            '_index': 'my_index',
            '_type': 'person_name',
            '_id': 'AWwYjl4Mqo63y_hLp5Yl',
            '_score': 0.77749264,
            '_source': {
                'full_name': 'John Doe'
            }
        }]
    }
}

В результатах поиска я хотел бы получить фонетическое преобразование имен в эластичном поиске (также из имени запроса, но это менее важно) при выполнении поиска.

Я знаю, что мог бы использовать explain API, но я хотел бы избежать второго запроса, и, кроме того, explain API кажется немного «излишним» для того, чего я хочу достичь.

Спасибо !


person Thomas    schedule 22.07.2019    source источник


Ответы (1)


Не похоже, чтобы это было легко реализовать в запросе Elasticsearch, но вы можете попробовать анализировать API и скриптовые поля с fielddata и векторы терминов может пригодиться. Вот как.

Получить токены из произвольного запроса

Analyze API — отличный инструмент, если вы хотите понять, как именно Elasticsearch токенизирует ваш запрос.

Используя ваше сопоставление, вы можете сделать, например:

GET myindex/_analyze
{
  "analyzer": "metaphone_analyzer",
  "text": "John Doe"
}

И получить в результате что-то вроде этого:

{
  "tokens": [
    {
      "token": "JN",
      "start_offset": 0,
      "end_offset": 4,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "john",
      "start_offset": 0,
      "end_offset": 4,
      "type": "<ALPHANUM>",
      "position": 0
    },
    {
      "token": "T",
      "start_offset": 5,
      "end_offset": 8,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "doe",
      "start_offset": 5,
      "end_offset": 8,
      "type": "<ALPHANUM>",
      "position": 1
    }
  ]
}

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

Получить токены из поля документа

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

На практике Elasticsearch не будет хранить токены text только что проанализированное поле: fielddata отключено по умолчанию. Нам нужно включить его:

PUT /myindex
{
  "mappings": {
    "person_name": {
      "properties": {
        "full_name": {
          "fields": {
            "metaphone_field": {
              "type": "text", 
              "analyzer": "metaphone_analyzer",
              "fielddata": true
            }
          }, 
          "type": "text"
        }
      }
    }
  }, 
  "settings": {
    ...
  }
}

Теперь мы можем использовать поля со сценариями, чтобы попросить Elasticsearch вернуть эти токены.

Запрос может выглядеть так:

POST myindex/_search
{
  "script_fields": {
    "my tokens": {
      "script": {
        "lang": "painless",
        "source": "doc[params.field].values",
        "params": {
          "field": "full_name.metaphone_field"
        }
      }
    }
  }
}

И ответ будет выглядеть так:

{
  "hits": {
    "total": 1,
    "max_score": 1,
    "hits": [
      {
        "_index": "myindex",
        "_type": "person_name",
        "_id": "123",
        "_score": 1,
        "fields": {
          "my tokens": [
            "JN",
            "T",
            "doe",
            "john"
          ]
        }
      }
    ]
  }
}

Как видите, те самые жетоны (но в случайном порядке).

Можем ли мы также получить информацию о расположении этих токенов в документе?

Получение токенов с их позициями

векторы терминов могут помочь. Чтобы иметь возможность использовать их, нам на самом деле не нужно, чтобы fielddata был включен. Мы могли бы искать векторы терминов для документа:

GET myindex/person_name/123/_termvectors
{
  "fields" : ["full_name.metaphone_field"],
  "offsets" : true,
  "positions" : true
}

Это вернет что-то вроде этого:

{
  "_index": "myindex",
  "_type": "person_name",
  "_id": "123",
  "_version": 1,
  "found": true,
  "took": 1,
  "term_vectors": {
    "full_name.metaphone_field": {
      "field_statistics": {
        "sum_doc_freq": 4,
        "doc_count": 1,
        "sum_ttf": 4
      },
      "terms": {
        "JN": {
          "term_freq": 1,
          "tokens": [
            {
              "position": 0,
              "start_offset": 0,
              "end_offset": 4
            }
          ]
        },
        "T": {
          "term_freq": 1,
          "tokens": [
            {
              "position": 1,
              "start_offset": 5,
              "end_offset": 8
            }
          ]
        },
        "doe": {
          "term_freq": 1,
          "tokens": [
            {
              "position": 1,
              "start_offset": 5,
              "end_offset": 8
            }
          ]
        },
        "john": {
          "term_freq": 1,
          "tokens": [
            {
              "position": 0,
              "start_offset": 0,
              "end_offset": 4
            }
          ]
        }
      }
    }
  }
}

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

К сожалению, насколько мне известно, нет возможности объединить эти три запроса в один. Также fielddata следует использовать с осторожностью, так как он использует много памяти.


Надеюсь это поможет!

person Nikolay Vasiliev    schedule 22.07.2019
comment
Я не видел уведомления о вашем ответе. Это мне очень поможет. - person Thomas; 29.01.2020