jongkwan.dev
Development · Essay №059

Elasticsearch Mappings and Analyzers

How field types and the analyzer pipeline decide search accuracy and indexing cost.

Jongkwan Lee2026년 3월 6일6 min read
Contents

A mapping decides what type a field is stored as, and an analyzer decides how text is split into tokens. Those two design choices largely determine search accuracy and indexing cost.

The mapping decides search quality first

In Elasticsearch, a mapping is the schema that defines what type each field of a JSON document takes in the inverted index. 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.

The problem shows up in production. A type misjudgment such as an all-digit string being taken as an integer breaks data integrity and can make queries fail. That is why declaring indices explicitly with static mapping is considered the best practice in production.

StrategyCharacteristicsCaveat
Dynamic mappingFast initial ingestionUnintended field growth and type misjudgment
Static mappingEasy field controlRequires more design time
Multi-fieldKeeps text and keyword togetherIncreases 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.

json
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.

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 default standard tokenizer splits on whitespace and punctuation.
  • 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.

The critical part is whether text is interpreted by the same rules at index time and at search time. If the two analyzers diverge, documents that clearly exist will not be found.

Korean search and nori

The standard tokenizer splits on whitespace and punctuation, which serves Korean poorly. It treats 'samsungjeonja-ui' (Samsung Electronics plus a possessive particle) and 'samsungjeonja' as two unrelated tokens, so any word carrying a particle is missed. Korean therefore needs 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. The plugin has to be installed separately.

json
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

The most expensive mistake is changing an analyzer later. Changing the analyzer changes the rules that produced the stored tokens, which requires a full reindex. Reindexing a live index costs time and resources, so it is safer to settle the mapping against expected search patterns before ingestion begins.

Summary

Mappings decide field types and analyzers decide tokenization rules. text goes through an analyzer for full-text search, keyword is stored whole for exact matching and aggregation, and a multi-field covers both when both are needed. An analyzer runs a character filter, a tokenizer, and a token filter to build the inverted index. Index-time and search-time rules must agree, or results go missing.

Korean search needs a morphological analyzer such as nori to strip particles before matching becomes accurate. Autocomplete tokenizes differently from body search, so give it a dedicated field and analyzer. Changing an analyzer forces a full reindex, so settle the mapping against expected search patterns before ingestion.