Build the mental model
Choosing an Elasticsearch field type looks simple, but it fundamentally determines what search and aggregation capabilities you get. A `text` field is tokenized through an analyzer for full-text search (`match`), but it cannot be used for exact-match filtering or aggregation (terms buckets) — the value gets split into small tokens, so the "original value" is no longer recoverable that way. A `keyword` field skips the analyzer and indexes the whole value as a single token, so it fits exact matches, sorting, aggregations, and filters, but it's inefficient for full-text search (even one extra space stops a match). For numeric types (`integer`, `long`, `float`, `double`), choose the smallest precision that still comfortably covers your value range, to optimize range-query and aggregation performance — using `long` when you don't need it wastes storage and memory. Indexing a `date` field in ISO 8601 format unlocks automatic date math (range queries, date histogram aggregations); indexing it as a plain string loses all of that. Think of it as a sharp knife (`keyword`, precise cuts) versus a food processor (`text`, blend everything into searchable chunks) — it's common to map the same value into both forms with a multi-field mapping (`text` plus a `.keyword` sub-field), so you get full-text search and exact matching from one source value.
Connect it to a real scenario
Design the Tutorial Platform's `title` field as `text` plus a `.keyword` sub-field: use `title` (text) with `match` for the search box, and `title.keyword` for the admin dashboard's "sort by title alphabetically" feature, since sorting on the `text` field would give an order that doesn't match true alphabetical order because of how tokens are stored. Choosing `date` for `publishedAt` lets a "last 7 days" filter feature be implemented with a single simple range query — choosing `string` would mean implementing date comparison logic manually at the application layer. Choose `integer` for `lessonCount` (a small range) since `long` is unnecessary and only wastes storage.
Try the working example
PUT /tutorials
{
"mappings": {
"properties": {
"title": {
"type": "text",
"fields": {
"keyword": { "type": "keyword", "ignore_above": 256 }
}
},
"lessonCount": { "type": "integer" },
"publishedAt": { "type": "date", "format": "strict_date_optional_time" },
"price": { "type": "float" }
}
}
}You get a mapping where `title` supports both `match` (full-text) queries and exact sorting via `title.keyword`.5-minute try-it
Write a mapping for a `products` index with four fields: `sku` (needs exact matching), `description` (needs full-text search), `stockCount` (a small integer), and `lastRestockedAt` (a date).
One important caution
Mapping a category/tag/status field (a dropdown-style value that only needs exact matching, filtering, and aggregation) as `text` — terms aggregations no longer work, and filters can return incorrect results.
Indexing a `date` field as a locale-specific string (like "08/29/2026") instead of ISO format — date range queries and date histogram aggregations stop working correctly.
Elasticsearch Guide — Field Data Types — Elastic