Columnar index mode
When you turn on columnar mode, Elasticsearch becomes a full analytical and search columnar store.
For a conceptual introduction to when and why to use it, go to Columnar index mode. This page describes how to turn it on and configure index sorting and provides more details about_source modes and limitations.
You activate a set of changes that collectively align the Elasticsearch storage model with dedicated columnar stores:
- Fields are stored once, as doc values only. Non-text fields are not indexed by default, eliminating the storage cost of maintaining redundant index structures. Text fields remain indexed by default to support full-text search.
- For non-indexed fields, doc values skippers are enabled by default. Doc values skippers are compact skip lists with metadata (for example, minimum and maximum values) to avoid scanning large blocks of data when executing a query.
- Mappings are always flat, and object and passthrough fields in mappings are always auto-flattened. Nested fields are not auto flattened.
- Depending on your license, you can choose between two
_sourcemodes. If using synthetic source, a flattened or columnar representation of the source is generated automatically when it's requested at query time. Alternatively, this columnar source can be generated at index time and stored to the disk as doc values. - New multi-value semantics: the original ordering of multiple values per field per document (for example, in arrays) is preserved by default. Optionally, fields in mappings can be configured to only allow one value per document.
- Fields in mappings can be configured to reject documents that have no value for them.
- Metadata fields like
_routingand_idare stored using doc values as well. - An optimized doc values format is used by default, further reducing storage footprint, especially when combined with index sorting.
Together with index sorting, columnar mode brings Elasticsearch's storage footprint and columnar access in line with dedicated columnar stores while retaining its full search and aggregation capabilities.
Two columnar index modes are available:
columnar- A general-purpose columnar store with no use-case-specific defaults. Use this mode for bare indices and data streams that do not fit the logging paradigm.
logsdb_columnar-
A columnar store with logging-oriented defaults. It inherits all behavior of the
columnarmode and additionally:- Applies a default mapping that includes a
@timestampfield (and optionallyhost.name). - Enables index sorting if
@timestampandhost.namemappings exist.
Use this mode for log data.
- Applies a default mapping that includes a
Both modes are strictly columnar: they reject mapping-level runtime fields and prevent turning off _source.
Set mode index setting at index creation time. The setting cannot be changed after the index is created.
PUT my-index
{
"settings": {
"mode": "columnar"
}
}
For log data, use a component template to enable the logsdb_columnar mode for all logs-*-* data streams:
PUT _component_template/logs@custom
{
"template": {
"settings": {
"mode": "logsdb_columnar"
}
}
}
By default, all data streams matching with logs-*-* use logsdb, but with the custom component template in place, all data streams matching with logs-*-* will use logsdb_columnar.
Or set it directly at index creation:
PUT my-logs-index
{
"settings": {
"mode": "logsdb_columnar"
}
}
When you set up columnar index mode, you must determine the index sort fields. Good index sorting fields facilitate more efficient data storage and improved query response times. Good index sorting fields are dependent on the use case and the data.
The logsdb_columnar index mode, like logsdb index mode, uses by default the host.name field in ascending order and @timestamp field in descending order as the index sort fields.
Hosts typically emit similar logs, so storing log entries from the same host sequentially on disk improves the efficiency of compression techniques such as run-length and delta encoding.
The @timestamp field is also used as the index sort field with recent log entries appearing first, improving query performance for queries over recent data.
The columnar index mode doesn't enable index sorting by default. If you collect logs from different agents, sorting by agent ID and timestamp might be a good choice:
PUT my-index
{
"settings": {
"mode": "columnar",
"sort.field": [ "agent.id", "@timestamp" ],
"sort.order": [ "asc", "desc" ]
}
}
If query latency is more important than storage efficiency, sorting by just @timestamp might improve query response times:
PUT my-logs-index
{
"settings": {
"mode": "logsdb_columnar",
"sort.field": [ "@timestamp" ],
"sort.order": [ "desc" ]
}
}
With the columnar modes, fields that are not referenced in static mappings get dynamically mapped as follows:
- Whole numbers are mapped as a long field type.
- Decimal numbers are mapped as a double type.
- Strings are mapped as a keyword field type.
- Objects and arrays are mapped as one or more leaf fields (depending on the number of unmapped field paths). Mappings are flattened in columnar mode and that applies to unmapped objects too. Each leaf field under the unmapped object will be mapped as a separate leaf field. No object fields are added to the mappings.
Dynamically mapped fields are configured with doc values turned on and indexes turned off by default, in line with the columnar premise of storing each field once using doc values.
Dynamic mapping behavior is controlled by the dynamic configuration parameter, which can be set to:
true(default): Turns on the dynamic mapping behavior as described in the preceding paragraphs.false: Unmapped fields are not mapped or stored. Data in unmapped fields are lost.strict: Documents containing unmapped fields don't get indexed, raising indexing errors instead.
Note that the runtime option is not supported in columnar mode.
If you configure "dynamic": false, then only fields that are explicitly mapped in the mappings are stored.
Fields that are not explicitly mapped in the mappings are not stored and therefore lost.
If you use columnar mode, mappings are always flattened. When you define mappings, object and passthrough field mappers are removed and leaf field mappings are created for each field path. The same applies to dynamic mapping updates during indexing.
During object flattening, the enabled and dynamic settings are preserved and separately tracked.
Same applies to passthrough fields, along with their priority setting.
For example, given a mapping with an attributes object (dynamic: false) and a labels passthrough field (priority: 10):
PUT my-index
{
"settings": {
"mode": "columnar"
},
"mappings": {
"properties": {
"attributes": {
"type": "object",
"dynamic": false,
"properties": {
"host": { "type": "keyword" },
"ip": { "type": "ip" }
}
},
"labels": {
"type": "passthrough",
"priority": 10,
"properties": {
"env": { "type": "keyword" }
}
}
}
}
}
The processed mapping shows the object mappers removed and their settings captured under prefix_properties. For example, GET my-index/_mapping returns:
{
"my-index": {
"mappings": {
"prefix_properties": {
"attributes": {
"dynamic": "false"
},
"labels": {
"passthrough": 10
}
},
"properties": {
"attributes.host": { "type": "keyword" },
"attributes.ip": { "type": "ip" },
"labels.env": { "type": "keyword" }
}
}
}
}
The attributes and labels object mappers are gone from properties; only the flat dotted-path leaf fields remain. The dynamic: false from attributes is preserved under prefix_properties.attributes.dynamic, preventing new fields under attributes.* from being auto-mapped at index time. The priority: 10 from labels is preserved under prefix_properties.labels.passthrough.
Columnar index modes don't store the original JSON _source on disk. Two _source modes are supported:
- Synthetic columnar
_source - Reconstructs a flattened representation of
_sourcefrom doc values at query time. Requires an appropriate license for synthetic columnar_source. For more information, refer to Synthetic_source. - Columnar stored
_source - Materializes and stores the columnar
_sourcerepresentation on disk at index time as doc values. Used automatically when synthetic columnar_sourceis not licensed, and can also be configured explicitly to speed up_sourceretrieval. For more information, see Columnar source.
Both modes return the same content. columnar_stored changes only when the columnar _source is built (at index time instead of at query time), not what it contains.
Columnar _source is reconstructed from the values of mapped fields, over an auto-flattened mapping. It reflects how your data is stored rather than the exact content you sent at index time, so _source differs from the indexed document in the following ways:
- Field paths are flat
- Objects are returned as dotted leaf paths, so a document indexed as
{"host": {"name": "host-1"}}is returned as{"host.name": "host-1"}. Use the dotted paths in_sourceinclude and exclude filters as well. - Leaf arrays are preserved
- A multi-valued leaf field is returned as it was indexed, including the original order of its values and any duplicates.
- Arrays of objects lose their grouping
- Auto-flattening turns an array of objects into one multi-valued leaf field per property. Which values belonged to which object is not retained and can't be recovered.
- Nested fields keep their objects
nestedfields are not auto-flattened, so an array of nested objects is returned as an array of objects, with each object's values intact. Columnar modes support only a single level of nesting.- Unmapped content is dropped
- Anything in a document that doesn't reach a mapped field is lost: fields skipped because of
dynamic: false, subtrees underenabled: false, and dynamic fields skipped because a field limit was reached. Values that a mapped field accepts but can't index normally are still returned in_source.
There's no way to opt a single field or object into keeping its original source. The synthetic_source_keep mapping parameter, the index.mapping.synthetic_source_keep index setting, and store: true are all rejected in columnar modes.
For example, take this document:
{
"@timestamp": "2026-09-18T10:00:00.000Z",
"host": { "name": "host-1" },
"tags": [ "prod", "web", "prod" ],
"links": [
{ "trace_id": "t1", "span_id": "s1" },
{ "trace_id": "t2", "span_id": "s2" }
]
}
In a logsdb_columnar index where links is a plain object, _source comes back as:
{
"@timestamp": "2026-09-18T10:00:00.000Z",
"host.name": "host-1",
"tags": [ "prod", "web", "prod" ],
"links.trace_id": [ "t1", "t2" ],
"links.span_id": [ "s1", "s2" ]
}
The tags array survives unchanged, including the duplicate prod and the original ordering. The links array is gone: the pairing of each trace_id with its span_id can no longer be recovered. If necessary, mapping links as nested would keep the array of objects.
Both logsdb and the columnar modes default to synthetic _source, but they make different trade-offs, so migrating an index from logsdb to logsdb_columnar can change the _source you get back:
logsdb |
logsdb_columnar |
|
|---|---|---|
| Object structure | Objects as named in the mapping; dotted input is expanded into objects | Always flat, dotted leaf paths |
| Leaf arrays | Preserved by default; order and duplicates are lost where synthetic_source_keep is set to none |
Always preserved |
| Arrays of objects | Preserved | Flattened into one multi-valued field per property, grouping lost |
| Nested fields | Preserved, any depth | Preserved, single level of nesting only |
Unmapped, dynamic: false, or enabled: false content |
Retained in _source |
Dropped |
| Per-field source retention | synthetic_source_keep, store |
Not available |
logsdb achieves its higher fidelity by defaulting index.mapping.synthetic_source_keep to arrays and by retaining the parts of a document that synthetic source can't otherwise reconstruct, at the cost of extra storage. Columnar modes trade that fidelity for a smaller storage footprint and improved indexing throughput.
If a consumer of your data depends on an object array being returned faithfully, map that field as nested before migrating to a columnar mode.
The following features are not supported in columnar index modes:
- Nested field type: The nested field type is supported in a limited fashion in the columnar index modes. Nesting of nested field types is not supported.
- Mapping-level runtime fields: Defining runtime fields in the index mappings is rejected. Runtime fields can still be defined on individual search requests.
- Turning off doc values: Mapped fields cannot turn off doc values. Setting
doc_valuestofalseleads to mapping errors. The only exception is multi-fields, where it's often desirable to store doc values for just one and use different index configurations for the rest. - Multi-field independence from parent constraints: A parent field's
doc_values.multi_value: falseornullability: falseconstraint is not inherited by its sub-fields. Each multi-field applies its owndoc_valuesconfiguration independently. As a result, a value rejected (and redirected to._on_failure) by the parent may still be indexed by the multi-field, so the two can hold different value sets. Configuredoc_values: { multi_value: false }on the sub-field itself if you need the same constraint there. - Incompatible field types: Field types that don't support doc values are not supported in columnar mode (for example,
search_as_you_type). - Turning off
_source: Setting"_source": {"enabled": false}is not allowed. - Stored source mode: The traditional
storedsource mode is not supported; only synthetic columnar and columnar stored modes are available. See Columnar_source. dynamic: falseandenabled: falseare lossy: Settingdynamic: falseon an object prevents unmapped sub-fields from being stored; their data is permanently lost. Settingenabled: falseignores the entire object subtree; its data is permanently lost.- Per-field source retention: The
synthetic_source_keepmapping parameter, theindex.mapping.synthetic_source_keepindex setting, andstore: trueare rejected. A field or object can't opt into keeping its original source, so the flattening described in What columnar_sourcepreserves can't be avoided for individual fields. - Default query fields: The
index.query.default_fieldindex setting in columnar mode will by default only include fields that are indexed (by default text based fields are indexed).