Mapping
Defining field types explicitly, choosing between text and keyword, and configuring the analysis chain.
Mapping defines how each field is stored and indexed. It is the schema, and it is largely immutable once data exists.
Define it explicitly
PUT /articles
{
"mappings": {
"dynamic": "strict",
"properties": {
"title": { "type": "text", "analyzer": "english",
"fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } },
"body": { "type": "text", "analyzer": "english" },
"status": { "type": "keyword" },
"tags": { "type": "keyword" },
"views": { "type": "integer" },
"published": { "type": "date" },
"author": { "type": "object",
"properties": { "id": { "type": "keyword" }, "name": { "type": "keyword" } } }
}
}
}text versus keyword
This is the distinction that matters most:
A field you both search and facet on needs both, via a multi-field as in title above:
title for matching, title.keyword for sorting and aggregation.
Analyzers
An analyzer is a chain: character filters, then a tokenizer, then token filters. It runs at index time and at query time, and the two must agree or nothing matches.
PUT /articles
{
"settings": {
"analysis": {
"filter": {
"english_stop": { "type": "stop", "stopwords": "_english_" },
"english_stemmer": { "type": "stemmer", "language": "english" },
"synonyms": { "type": "synonym", "synonyms": ["db, database", "k8s, kubernetes"] }
},
"analyzer": {
"content": {
"type": "custom",
"char_filter": ["html_strip"],
"tokenizer": "standard",
"filter": ["lowercase", "english_stop", "synonyms", "english_stemmer"]
}
}
}
}
}Test the chain rather than reasoning about it:
POST /articles/_analyze
{ "analyzer": "content", "text": "Running <b>databases</b> in production" }The output shows exactly which terms are indexed. Most "why does this search not match" questions are answered here in seconds.
Changing a mapping
Fields worth disabling
Every enabled feature costs index size and indexing time:
{ "properties": {
"raw_payload": { "type": "object", "enabled": false }, // stored, not indexed
"internal_id": { "type": "keyword", "doc_values": false }, // not aggregatable or sortable
"body": { "type": "text", "norms": false } // no length normalisation
}}For log data in particular, disabling indexing on fields nobody queries is one of the largest available savings.