Mappings: text vs keyword

Elasticsearch · Essentials · 11 min

Most Elasticsearch bugs and slow queries trace back to a field mapped as the wrong type.

A mapping is the schema of an index: each field's type and how it is indexed. If you don't define one, dynamic mapping guesses on first sight: numbers become long or float, ISO date strings date, and every other string becomes a text field with a .keyword subfield.

The crucial distinction is between the two string types:

  • text is analyzed into terms for full-text search ("Blue Pen" becomes blue, pen); you can't sort or aggregate on it efficiently,
  • keyword stores the exact value as one term, for filters, sorting, aggregations and ids ("SKU-001", "paid", email addresses).

Other common types: integer, long, float, scaled_float, double, date, boolean, ip, geo_point, object, nested, dense_vector. Multi-fields ("fields": { "raw": { "type": "keyword" } }) index one value both ways.

Define mappings explicitly for production indices: PUT /index { "mappings": { "properties": {...} } }. Set "dynamic": "strict" to reject unknown fields, or false to store but not index them; uncontrolled dynamic fields can lead to a mapping explosion (the default limit is 1,000 fields).

You can add new fields to an existing mapping, but you cannot change an existing field's type; that requires creating a new index and reindexing.

Example

PUT /orders
{
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "order_id":  { "type": "keyword" },
      "status":    { "type": "keyword" },
      "customer":  { "type": "text", "fields": { "raw": { "type": "keyword" } } },
      "total":     { "type": "scaled_float", "scaling_factor": 100 },
      "placed_at": { "type": "date" },
      "notes":     { "type": "text" }
    }
  }
}

PUT /orders/_doc/1
{ "order_id": "A-1", "status": "paid", "customer": "Ana Petrosyan", "colour": "red" }
# 400 strict_dynamic_mapping_exception: [colour] not allowed

GET /orders/_mapping

Remember

  • text = analyzed for full-text; keyword = exact value for filters, sorts, aggs
  • Dynamic mapping makes strings text + .keyword
  • Define production mappings explicitly; consider dynamic: strict
  • Existing field types can't change: reindex instead

Watch out for

  • Mapping ids, statuses or emails as text and then wondering why exact filters and aggregations misbehave.

Check yourself

  1. You need to sort products by brand name and also search brand with typos. How should brand be mapped?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html