Тип поля flattened в Elasticsearch

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

flattened тип предоставляет альтернативный подход, при котором весь объект отображается как одно поле. Для данного объекта плоское сопоставление (flattened mapping) проанализирует его конечные значения и проиндексирует их в одно поле как ключевые слова. Затем содержимое объекта можно искать с помощью простых запросов и агрегатов.

Этот тип данных может быть полезен для индексирования объектов с большим или неизвестным количеством уникальных ключей. Для всего объекта JSON создается только одно сопоставление полей, что может помочь предотвратить взрыв сопоставлений из-за слишком большого количества сопоставлений отдельных полей.

С другой стороны, плоские объектные поля представляют собой компромисс с точки зрения функциональности поиска. Разрешены только базовые запросы, без поддержки запросов числового диапазона или выделения.

Тип плоского сопоставления (flattened mapping) не следует использовать для индексации всего содержимого документа, поскольку он обрабатывает все значения как ключевые слова и не обеспечивает полную функциональность поиска. Подход по умолчанию, когда каждое подполе имеет свою собственную запись в сопоставлениях, хорошо работает в большинстве случаев.

flattened поле объекта можно создать следующим образом:

PUT bug_reports
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text"
      },
      "labels": {
        "type": "flattened"
      }
    }
  }
}

POST bug_reports/_doc/1
{
  "title": "Results are not sorted correctly.",
  "labels": {
    "priority": "urgent",
    "release": ["v1.2.5", "v1.3.0"],
    "timestamp": {
      "created": 1541458026,
      "closed": 1541457010
    }
  }
}

Во время индексирования токены создаются для каждого конечного значения в объекте JSON. Значения индексируются как строковые ключевые слова без анализа или специальной обработки чисел или дат.

При запросе к flattened полю верхнего уровня выполняется поиск всех конечных значений в объекте:

POST bug_reports/_search
{
  "query": {
    "term": {"labels": "urgent"}
  }
}

Чтобы запросить конкретный ключ в flattened объекте, используется точечная нотация объекта:

POST bug_reports/_search
{
  "query": {
    "term": {"labels.release": "v1.3.0"}
  }
}

Поддерживаемые операции

Из-за сходства в способах индексации значений, flattened поля имеют во многом те же функции сопоставления и поиска, что и поля ключевых слов (keyword fields).

В настоящее время flattened поля объектов можно использовать со следующими типами запросов:

  • term, terms, terms_set
  • prefix
  • range
  • match, multi_match
  • query_string, simple_query_string
  • exists

При запросе невозможно ссылаться на ключи полей с использованием подстановочных знаков, как в {"term": {"labels.time *": 1541457010}}. Обратите внимание, что все запросы, включая диапазон, обрабатывают значения как строковые ключевые слова. Выделение не поддерживается во flattened полях.

Можно выполнять сортировку по flattened полю объекта, а также выполнять простые агрегации в стиле ключевых слов, например terms. Как и в случае с запросами, здесь нет специальной поддержки числовых значений - все значения в объекте JSON обрабатываются как ключевые слова. При сортировке это означает, что значения сравниваются лексикографически.

flattened поля объекта в настоящее время не могут быть сохранены (stored). В сопоставлении невозможно указать параметр хранилища (store parameter).

Получение flattened полей

Значения полей и конкретные подполя можно получить с помощью параметра fields. Поскольку flattened поле отображает весь объект с потенциально множеством подполей как одно поле, ответ содержит неизмененную структуру из _source.

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

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "flattened_field": {
        "type": "flattened"
      }
    }
  }
}

PUT my-index-000001/_doc/1?refresh=true
{
  "flattened_field" : {
    "subfield" : "value"
  }
}

POST my-index-000001/_search
{
  "fields": ["flattened_field.subfield"],
  "_source": false
}

{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 1,
      "relation": "eq"
    },
    "max_score": 1.0,
    "hits": [{
      "_index": "my-index-000001",
      "_type" : "_doc",
      "_id": "1",
      "_score": 1.0,
      "fields": {
        "flattened_field.subfield" : [ "value" ]
      }
    }]
  }
}

Вы также можете использовать Painless сценарий для извлечения значений из подполей flattened полей. Вместо включения doc['<field_name>'].value в ваш Painless сценарий - используйте doc['<field_name>.<sub-field_name>'].value. Например, если у вас есть flattened поле с именем label с подполем release, ваш Painless сценарий будет иметь вид doc['labels.release'].value.

Например, предположим, что ваше отображение содержит два поля, одно из которых имеет flattened тип:

PUT my-index-000001
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text"
      },
      "labels": {
        "type": "flattened"
      }
    }
  }
}

Проиндексируйте несколько документов, содержащих ваши сопоставленные поля. Поле labels имеет три подполя:

POST /my-index-000001/_bulk?refresh
{"index":{}}
{"title":"Something really urgent","labels":{"priority":"urgent","release":["v1.2.5","v1.3.0"],"timestamp":{"created":1541458026,"closed":1541457010}}}
{"index":{}}
{"title":"Somewhat less urgent","labels":{"priority":"high","release":["v1.3.0"],"timestamp":{"created":1541458026,"closed":1541457010}}}
{"index":{}}
{"title":"Not urgent","labels":{"priority":"low","release":["v1.2.0"],"timestamp":{"created":1541458026,"closed":1541457010}}}

Поскольку метки - это плоский тип поля, весь объект отображается как одно поле. Чтобы получить значения из этого подполя в Painless сценарии, используйте формат doc['<field_name>.<sub-field_name>'].value.

"script": {
  "source": """
    if (doc['labels.release'].value.equals('v1.3.0'))
    {emit(doc['labels.release'].value)}
    else{emit('Version mismatch')}
  """


Читайте также:

Комментарии

Популярные сообщения из этого блога

Язык поисковых запросов в Graylog

Нормальные формы, пример нормализации в базе данных

Хэш-таблица: разрешение коллизий