Elasticsearch Mappings and Analyzers
How field types and the analyzer pipeline decide search accuracy and indexing cost.
A mapping defines field representation, and an analyzer produces tokens for full-text search.
The mapping decides search quality first
In Elasticsearch, a mapping is the schema that defines how each field of a JSON document is stored and indexed. It resembles a table schema in a relational database, but it also controls how the underlying Lucene engine analyzes and indexes each field. The same string can end up as full-text search material or as exact-match-only data depending on the mapping.
Getting a field type wrong means the query you want either does not work at all or wastes memory. No amount of query tuning compensates for a mismatched mapping. Working out the expected search patterns during design is the safer approach.
Dynamic and static mappings
There are two ways to create a mapping. Dynamic mapping infers a type from the shape of the value when a document arrives with fields that were never declared. It speeds up ingestion in the early stage, before the data model is settled.
Unexpected fields or date-like strings can produce unwanted mappings. Numeric strings become numeric fields only when numeric_detection is enabled; its default is false. The dynamic mapping documentation defines these rules.
| Strategy | Characteristics | Caveat |
|---|---|---|
| Dynamic mapping | Fast initial ingestion | Unintended field growth and type misjudgment |
| Static mapping | Easy field control | Requires more design time |
| Multi-field | Keeps text and keyword together | Increases storage cost |
Static mapping declares field types, analyzers, and multi-field structure all at once when the index is created. The three strategies in the table are not mutually exclusive: the usual approach fixes the core fields statically and allows dynamic mapping in a limited way for the rest.
Common field types
Beyond strings, the type follows the search intent.
- text: full-text search material. Passes through an analyzer and is split into tokens.
- keyword: used for exact matching, aggregation, and sorting. Stores the value whole.
- date: used for time-based filtering and analysis.
- numeric (integer, long, and so on): used for range filters and numeric aggregation.
- dense_vector: used for embedding-based vector search.
The deciding factor is what you intend to do with the field. If you need to find words inside it, as with body text, text is right. If you need to filter or group by the value itself, as with a status field, keyword is right.
text, keyword, and multi-fields
The difference between text and keyword is whether an analyzer runs. A text field goes through the analyzer pipeline and is split into multiple tokens, which suits match queries that look for words inside a sentence. A keyword field is indexed whole without splitting, which suits exact matching, aggregation, and sorting.
When one field has to serve both purposes, use a multi-field. The same string is indexed as text and simultaneously as a keyword subfield.
The following is a Kibana Dev Tools Console request.
PUT /products
{
"mappings": {
"properties": {
"name": {
"type": "text",
"fields": {
"keyword": { "type": "keyword" }
}
},
"price": { "type": "integer" },
"created_at": { "type": "date" }
}
}
}With this setup, name serves full-text search while name.keyword serves exact matching and aggregation. Since the same value is indexed twice, storage cost goes up, so apply it only to fields that genuinely need both.
| Search intent | Field | Query or operation |
|---|---|---|
| Words within a product name | name | match analyzes the query |
| Exact product name | name.keyword | term compares the indexed value |
| Group by product name | name.keyword | terms aggregation |
A term query does not analyze its input, so sending original text to a text field can miss its stored tokens.
The analyzer pipeline
An analyzer turns raw text into searchable tokens. It runs three components in order.
- Character filter: cleans up the string before it reaches the tokenizer, doing work such as stripping HTML tags or substituting characters.
- Tokenizer: breaks the string into tokens, the units of meaning. The standard tokenizer follows Unicode word-boundary rules.
- Token filter: processes the resulting tokens. Lowercasing, stopword removal, synonym merging, and stemming happen here.
Only tokens that pass through this pipeline enter the inverted index. The inverted index maps each token to the documents it appears in ahead of time, so a lookup returns the document list for a token directly instead of scanning every document. That structure is why full-text search stays fast at scale.
Index-time and search-time tokens must be compatible, but the analyzers need not be identical. A separate search_analyzer can support query-time synonyms or autocomplete. Check token output against representative queries, as shown in the search_analyzer documentation.
Korean search and nori
The standard tokenizer does not perform Korean morphological analysis. It can retain a particle in '삼성전자의' and produce a different token from '삼성전자'. Korean search often uses a morphological analyzer.
nori is the official Elasticsearch Korean morphological analysis plugin (analysis-nori). nori_tokenizer splits sentences into morphemes, and the nori_part_of_speech filter removes parts of speech such as particles and endings. Self-managed clusters require the plugin on every node.
PUT /stocks
{
"settings": {
"analysis": {
"analyzer": {
"korean": {
"type": "custom",
"tokenizer": "nori_tokenizer",
"filter": ["lowercase", "nori_part_of_speech"]
}
}
}
},
"mappings": {
"properties": {
"name": { "type": "text", "analyzer": "korean" }
}
}
}Autocomplete follows different tokenization rules than body search. It has to match on partial input, so it deserves a dedicated field and a dedicated analyzer. Making the body-text analyzer carry autocomplete as well leaves both jobs half done.
Design criteria in practice
Derive field types backwards from search patterns. Decide what you will do with a value, and the type follows.
- User names, status values, tags: keyword
- Body text, descriptions, reviews: text
- Strings that need both search and aggregation: multi-field
- Autocomplete: a dedicated field with a dedicated analyzer
Changing index-time analysis does not rewrite tokens already stored. Rebuild the index when existing documents need the new tokenization. Updating search_analyzer alone can change query processing without reindexing, but still needs relevance checks.
| Change | Existing documents | Operational step |
|---|---|---|
| Index-time tokenizer | Stored tokens remain unchanged | Reindex into the new mapping |
| Search-time analyzer | Stored tokens remain unchanged | Update search_analyzer and test queries |
| Add a multi-field | Old documents lack the new subfield | Reprocess existing documents |
The required work depends on whether the change affects stored tokens or only new queries.
Summary
Mappings define field representation, while analyzers produce tokens for full-text search. Use text for analyzed queries and keyword for exact matching or aggregation, combining them when both are needed.
Korean morphology and autocomplete require tokenization suited to their queries. Keep index-time and search-time tokens compatible, and reindex when existing documents need different stored tokens.