Spec-Zone.ru › Elasticsearch 8
›Elasticsearch Guide [8.17] ›Mapping ›Metadata fields

_source поле

Поле _source содержит исходный документ JSON, который был передан во время индексирования. Само поле _source не индексируется (и, следовательно, не может быть проиндексировано), но оно хранится, чтобы его можно было возвращать при выполнении запросов fetch, таких как get или search.

Если для вас важен объём дискового пространства, рассмотрите следующие варианты:

  • Использование синтетического _source, которое воссоздаёт содержимое источника во время извлечения вместо его хранения на диске. Это уменьшает использование дискового пространства, но замедляет доступ к _source в запросах Get и Search.
  • Отключение поля _source полностью. Это уменьшает использование дискового пространства, но отключает функции, которые полагаются на _source.

Синтетическая _source

Несмотря на удобство использования, поле источника занимает значительное место на диске. Вместо хранения исходных документов на диске точно так, как вы их отправляете, Elasticsearch может реконструировать содержимое источника на лету при получении. Для включения этой функции подписки используйте значение synthetic для настройки индекса index.mapping.source.mode:

resp = client.indices.create(
    index="idx",
    settings={
        "index": {
            "mapping": {
                "source": {
                    "mode": "synthetic"
                }
            }
        }
    },
)
print(resp)
const response = await client.indices.create({
  index: "idx",
  settings: {
    index: {
      mapping: {
        source: {
          mode: "synthetic",
        },
      },
    },
  },
});
console.log(response);
PUT idx
{
  "settings": {
    "index": {
      "mapping": {
        "source": {
          "mode": "synthetic"
        }
      }
    }
  }
}

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

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

Синтетическое поле _source поддерживается всеми типами полей. В зависимости от реализации, типы полей имеют разные свойства при использовании с синтетическим полем _source.

Большинство типов полей строят синтетическое поле _source, используя имеющиеся данные, чаще всего doc_values и сохранённые поля. Для этих типов полей дополнительного места для хранения содержимого поля _source не требуется. Из-за структуры хранения doc_values, сгенерированное поле _source претерпевает изменения по сравнению с исходным документом.

Для всех остальных типов полей исходное значение поля хранится как есть, так же, как и поле _source в несинтетическом режиме. В этом случае изменений нет, и данные поля в _source такие же, как в исходном документе. Аналогично, некорректные значения полей, использующих ignore_malformed или ignore_above, необходимо хранить как есть. Такой подход менее эффективен с точки зрения хранения, поскольку данные, необходимые для реконструкции _source, хранятся дополнительно к другим данным, необходимым для индексации поля (например, doc_values).

Ограничения синтетического _source

Некоторые типы полей имеют дополнительные ограничения. Эти ограничения описаны в разделе синтетического _source документации типа поля documentation.

Изменения синтетического _source

При включенном синтетическом _source извлеченные документы претерпевают некоторые изменения по сравнению с исходным JSON.

Массивы перемещены в листья полей

Синтетические _source массивы перемещаются в листья. Например:

resp = client.index(
    index="idx",
    id="1",
    document={
        "foo": [
            {
                "bar": 1
            },
            {
                "bar": 2
            }
        ]
    },
)
print(resp)
response = client.index(
  index: 'idx',
  id: 1,
  body: {
    foo: [
      {
        bar: 1
      },
      {
        bar: 2
      }
    ]
  }
)
puts response
const response = await client.index({
  index: "idx",
  id: 1,
  document: {
    foo: [
      {
        bar: 1,
      },
      {
        bar: 2,
      },
    ],
  },
});
console.log(response);
PUT idx/_doc/1
{
  "foo": [
    {
      "bar": 1
    },
    {
      "bar": 2
    }
  ]
}

Преобразуется в:

{
  "foo": {
    "bar": [1, 2]
  }
}

Это может привести к исчезновению некоторых массивов:

resp = client.index(
    index="idx",
    id="1",
    document={
        "foo": [
            {
                "bar": 1
            },
            {
                "baz": 2
            }
        ]
    },
)
print(resp)
response = client.index(
  index: 'idx',
  id: 1,
  body: {
    foo: [
      {
        bar: 1
      },
      {
        baz: 2
      }
    ]
  }
)
puts response
const response = await client.index({
  index: "idx",
  id: 1,
  document: {
    foo: [
      {
        bar: 1,
      },
      {
        baz: 2,
      },
    ],
  },
});
console.log(response);
PUT idx/_doc/1
{
  "foo": [
    {
      "bar": 1
    },
    {
      "baz": 2
    }
  ]
}

Преобразуется в:

{
  "foo": {
    "bar": 1,
    "baz": 2
  }
}
Поля имеют имена, соответствующие схеме

Синтетические поля источника именуются в соответствии с именами в схеме. При использовании динамической схемы поля с точками (.) в своих именах по умолчанию интерпретируются как несколько объектов, в то время как точки в именах полей сохраняются внутри объектов, у которых отключены subobjects. Например:

resp = client.index(
    index="idx",
    id="1",
    document={
        "foo.bar.baz": 1
    },
)
print(resp)
const response = await client.index({
  index: "idx",
  id: 1,
  document: {
    "foo.bar.baz": 1,
  },
});
console.log(response);
PUT idx/_doc/1
{
  "foo.bar.baz": 1
}

Преобразуется в:

{
  "foo": {
    "bar": {
      "baz": 1
    }
  }
}

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

"script": { "source": """  emit(params._source['foo.bar.baz'])  """ }

Вместо этого ссылки на источник должны соответствовать структуре схемы:

"script": { "source": """  emit(params._source['foo']['bar']['baz'])  """ }

или просто

"script": { "source": """  emit(params._source.foo.bar.baz)  """ }

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

"script": { "source": """  emit(field('foo.bar.baz').get(null))   """ }
"script": { "source": """  emit($('foo.bar.baz', null))   """ }
Сортировка по алфавиту

Синтетические _source поля сортируются по алфавиту. JSON RFC определяет объекты как «неопределенный набор нуля или более пар имя/значение», поэтому приложениям не должно быть важно, но без синтетического _source исходный порядок сохраняется, и некоторые приложения могут, вопреки спецификации, делать что-то с этим порядком.

Представление диапазонов

Значения полей диапазонов (например, long_range) всегда представляются как включающие обе стороны с соответствующим корректированием границ. Смотрите примеры.

Уменьшенная точность значений geo_point

Значения полей geo_point представлены в синтетическом _source с уменьшенной точностью. Смотрите примеры.

Минимизация изменений источника

Можно избежать изменений синтетического источника для определенного объекта или поля за счет дополнительной стоимости хранения. Это регулируется параметром synthetic_source_keep со следующим вариантом:

  • none: синтетический источник отличается от исходного источника, как описано выше (по умолчанию).
  • arrays: массивы соответствующего поля или объекта сохраняют исходный порядок элементов и дублируемые элементы. Синтетический фрагмент источника для таких массивов не гарантирует точного соответствия исходному источнику, например, массив [1, 2, [5], [[4, [3]]], 5] может отображаться как есть или в эквивалентном формате, как [1, 2, 5, 4, 3, 5]. Точный формат может измениться в будущем, чтобы сократить издержки хранения этого варианта.
  • all: источник для одиночных экземпляров и массивов соответствующего поля или объекта записывается. При применении к объектам записывается источник всех подобъектов и подполей. Кроме того, исходный источник массивов записывается и отображается в синтетическом источнике без изменений.

Например:

resp = client.indices.create(
    index="idx_keep",
    settings={
        "index": {
            "mapping": {
                "source": {
                    "mode": "synthetic"
                }
            }
        }
    },
    mappings={
        "properties": {
            "path": {
                "type": "object",
                "synthetic_source_keep": "all"
            },
            "ids": {
                "type": "integer",
                "synthetic_source_keep": "arrays"
            }
        }
    },
)
print(resp)
const response = await client.indices.create({
  index: "idx_keep",
  settings: {
    index: {
      mapping: {
        source: {
          mode: "synthetic",
        },
      },
    },
  },
  mappings: {
    properties: {
      path: {
        type: "object",
        synthetic_source_keep: "all",
      },
      ids: {
        type: "integer",
        synthetic_source_keep: "arrays",
      },
    },
  },
});
console.log(response);
PUT idx_keep
{
  "settings": {
    "index": {
      "mapping": {
        "source": {
          "mode": "synthetic"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "path": {
        "type": "object",
        "synthetic_source_keep": "all"
      },
      "ids": {
        "type": "integer",
        "synthetic_source_keep": "arrays"
      }
    }
  }
}
resp = client.index(
    index="idx_keep",
    id="1",
    document={
        "path": {
            "to": [
                {
                    "foo": [
                        3,
                        2,
                        1
                    ]
                },
                {
                    "foo": [
                        30,
                        20,
                        10
                    ]
                }
            ],
            "bar": "baz"
        },
        "ids": [
            200,
            100,
            300,
            100
        ]
    },
)
print(resp)
const response = await client.index({
  index: "idx_keep",
  id: 1,
  document: {
    path: {
      to: [
        {
          foo: [3, 2, 1],
        },
        {
          foo: [30, 20, 10],
        },
      ],
      bar: "baz",
    },
    ids: [200, 100, 300, 100],
  },
});
console.log(response);
PUT idx_keep/_doc/1
{
  "path": {
    "to": [
      { "foo": [3, 2, 1] },
      { "foo": [30, 20, 10] }
    ],
    "bar": "baz"
  },
  "ids": [ 200, 100, 300, 100 ]
}

возвращает исходный источник без дедупликации и сортировки массивов:

{
  "path": {
    "to": [
      { "foo": [3, 2, 1] },
      { "foo": [30, 20, 10] }
    ],
    "bar": "baz"
  },
  "ids": [ 200, 100, 300, 100 ]
}

Вариант захвата источника массивов может быть применён на уровне индекса, установив index.mapping.synthetic_source_keep в arrays. Это относится ко всем объектам и полям в индексе, за исключением тех, у которых явно переопределено synthetic_source_keep в none. В этом случае издержки хранения растут пропорционально количеству и размерам массивов, присутствующих в источнике каждого документа.

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

Следующие типы полей поддерживают синтетический источник, используя данные из doc_values или сохранённых полей, и не требуют дополнительного места для хранения поля _source.

Если вы включите настройки ignore_malformed или ignore_above, то для хранения игнорируемых значений полей этих типов потребуется дополнительное хранилище.

  • aggregate_metric_double
  • annotated-text
  • binary
  • boolean
  • byte
  • date
  • date_nanos
  • dense_vector
  • double
  • flattened
  • float
  • geo_point
  • half_float
  • histogram
  • integer
  • ip
  • keyword
  • long
  • range типов
  • scaled_float
  • short
  • text
  • version
  • wildcard

Отключение поля _source

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

resp = client.indices.create(
    index="my-index-000001",
    mappings={
        "_source": {
            "enabled": False
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'my-index-000001',
  body: {
    mappings: {
      _source: {
        enabled: false
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "my-index-000001",
  mappings: {
    _source: {
      enabled: false,
    },
  },
});
console.log(response);
PUT my-index-000001
{
  "mappings": {
    "_source": {
      "enabled": false
    }
  }
}

Подумайте, прежде чем отключать поле _source

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

  • API update, update_by_query и reindex.
  • В приложении Kibana Discover данные полей не будут отображаться.
  • Dinamic выделение.
  • Возможность повторной индексации из одного индекса Elasticsearch в другой, для изменения отображений или анализа, или для обновления индекса до новой основной версии.
  • Возможность отладки запросов или агрегаций, просматривая исходный документ, используемый во время индексации.
  • Возможно, в будущем — возможность автоматического исправления повреждения индекса.

Если проблема в объёме дискового пространства, то лучше увеличить уровень сжатия, чем отключать поле _source.

Включение/исключение полей из _source

Функция, доступная только специалистам, — возможность обрезать содержимое поля _source после индексации документа, но перед сохранением поля _source.

Удаление полей из _source имеет схожие недостатки с отключением _source, особенно то, что вы не можете повторно индексировать документы из одного индекса Elasticsearch в другой. Вместо этого рассмотрите использование фильтрации источника.

Параметры includes/excludes (которые также поддерживают подстановочные знаки) могут быть использованы следующим образом:

resp = client.indices.create(
    index="logs",
    mappings={
        "_source": {
            "includes": [
                "*.count",
                "meta.*"
            ],
            "excludes": [
                "meta.description",
                "meta.other.*"
            ]
        }
    },
)
print(resp)

resp1 = client.index(
    index="logs",
    id="1",
    document={
        "requests": {
            "count": 10,
            "foo": "bar"
        },
        "meta": {
            "name": "Some metric",
            "description": "Some metric description",
            "other": {
                "foo": "one",
                "baz": "two"
            }
        }
    },
)
print(resp1)

resp2 = client.search(
    index="logs",
    query={
        "match": {
            "meta.other.foo": "one"
        }
    },
)
print(resp2)
response = client.indices.create(
  index: 'logs',
  body: {
    mappings: {
      _source: {
        includes: [
          '*.count',
          'meta.*'
        ],
        excludes: [
          'meta.description',
          'meta.other.*'
        ]
      }
    }
  }
)
puts response

response = client.index(
  index: 'logs',
  id: 1,
  body: {
    requests: {
      count: 10,
      foo: 'bar'
    },
    meta: {
      name: 'Some metric',
      description: 'Some metric description',
      other: {
        foo: 'one',
        baz: 'two'
      }
    }
  }
)
puts response

response = client.search(
  index: 'logs',
  body: {
    query: {
      match: {
        'meta.other.foo' => 'one'
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "logs",
  mappings: {
    _source: {
      includes: ["*.count", "meta.*"],
      excludes: ["meta.description", "meta.other.*"],
    },
  },
});
console.log(response);

const response1 = await client.index({
  index: "logs",
  id: 1,
  document: {
    requests: {
      count: 10,
      foo: "bar",
    },
    meta: {
      name: "Some metric",
      description: "Some metric description",
      other: {
        foo: "one",
        baz: "two",
      },
    },
  },
});
console.log(response1);

const response2 = await client.search({
  index: "logs",
  query: {
    match: {
      "meta.other.foo": "one",
    },
  },
});
console.log(response2);
PUT logs
{
  "mappings": {
    "_source": {
      "includes": [
        "*.count",
        "meta.*"
      ],
      "excludes": [
        "meta.description",
        "meta.other.*"
      ]
    }
  }
}

PUT logs/_doc/1
{
  "requests": {
    "count": 10,
    "foo": "bar" 
  },
  "meta": {
    "name": "Some metric",
    "description": "Some metric description", 
    "other": {
      "foo": "one", 
      "baz": "two" 
    }
  }
}

GET logs/_search
{
  "query": {
    "match": {
      "meta.other.foo": "one" 
    }
  }
}

Эти поля будут удалены из сохранённого поля _source.

Мы всё ещё можем выполнять поиск по этому полю, даже если оно отсутствует в сохранённом поле _source.

© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/mapping-source-field.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API