Skip to content
ExponaBLOG
ELASTICSEARCH & SEARCH ENGINEERING · DAY 006 / 100

text vs keyword fields: a practical guide for developers

The most common mapping mistake.

Expona Admin6 min read

Understanding Text vs Keyword Fields: A Critical Foundation

If you've spent time debugging why your Elasticsearch filters aren't working or why sorting seems broken, you've likely encountered the distinction between text and keyword fields. Text vs keyword fields: a practical guide for developers starts here, because this single decision shapes how Elasticsearch processes, stores, and retrieves your data. Getting it wrong early is one of the most common mapping mistakes we see in production systems, and it's surprisingly easy to fix once you understand the mechanics.

A text field in Elasticsearch is designed for human language content. When you index a value into a text field, Elasticsearch runs it through an analyzer—a pipeline that breaks the text into individual terms, applies filters, and prepares it for search. This is what enables full-text search to work at all.

Let's walk through a real example. Imagine you're indexing product descriptions in an e-commerce system:

json
{
  "mappings": {
    "properties": {
      "description": {
        "type": "text",
        "analyzer": "standard"
      }
    }
  }
}

When you index a document with description: "Premium leather laptop bag with waterproof lining", the standard analyzer will:

  1. Convert to lowercase: premium leather laptop bag with waterproof lining
  2. Tokenize into terms: ["premium", "leather", "laptop", "bag", "with", "waterproof", "lining"]
  3. Remove common stop words (depending on your analyzer): ["premium", "leather", "laptop", "bag", "waterproof", "lining"]
  4. Optionally apply stemming or lemmatization

Now when a user searches for waterproof bags, Elasticsearch tokenizes that query the same way and finds your product because it matches on both waterproof and bag. This is the power of text fields—fuzzy, intelligent matching on unstructured content.

The trade-off is that text fields store the analyzed terms, not the original value. You cannot sort on them, aggregate on them, or filter for exact matches reliably. If you try, Elasticsearch will either refuse or give you unexpected results.

Keyword Fields: Exact Values and Precise Operations

A keyword field stores the exact value as-is, with no analysis. It's treated as a single, indivisible token. This makes keyword fields perfect for filtering, sorting, and aggregation.

Consider a product status field in that same e-commerce system:

json
{
  "mappings": {
    "properties": {
      "status": {
        "type": "keyword"
      }
    }
  }
}

When you index status: "in-stock", it's stored exactly as "in-stock". A filter query like status: "in-stock" will match precisely. You can also sort by status and aggregate to count how many products are in each status category.

Keyword fields are also the right choice for:

  • IDs, codes, and identifiers (user IDs, SKUs, order numbers)
  • Categorical values (status, priority, category, region)
  • Email addresses (when you need exact matches)
  • Tags and labels
  • URLs and paths

The limitation is that keyword fields don't support partial or fuzzy matching. A search for in-stoc won't find "in-stock" unless you explicitly configure wildcard queries or prefix queries, which carry a performance cost.

Filter, Sort, and Aggregate on Keyword

Here's where the practical importance becomes clear. If you map a field as text when it should be keyword, three operations become problematic or impossible.

Filtering

Let's say you want to filter products by exact status. With a text field, Elasticsearch may return inconsistent results because the filter query is also analyzed. Your filter "in-stock" becomes a term search for the token "in" and "stock" separately, and the match might not work as expected depending on stop words and tokenization.

json
{
  "query": {
    "bool": {
      "filter": {
        "term": {
          "status": "in-stock"
        }
      }
    }
  }
}

With keyword mapping, this filter is exact and deterministic. Every time.

Sorting

Elasticsearch refuses to sort on analyzed text fields in the standard way. You'll see an error like "Fielddata is disabled on text fields by default". While you can enable fielddata, it consumes memory and is considered an anti-pattern. Use keyword fields for sorting instead.

Aggregation

Aggregations on text fields produce surprising results. If you aggregate a text field with the terms aggregation, you get counts for each individual token, not each original value. A product description field aggregated as text gives you counts of individual words, not counts of unique descriptions. Use keyword fields when you want to aggregate on the actual values.

Here's what that looks like in practice:

json
{
  "aggs": {
    "status_counts": {
      "terms": {
        "field": "status",
        "size": 10
      }
    }
  }
}

This aggregation only works reliably if status is mapped as keyword.

Use Both: Multi-Fields for Maximum Flexibility

Here's the design pattern that bridges both worlds: multi-field mappings. You map a single source field to multiple Elasticsearch fields—one for full-text search and one for filtering, sorting, and aggregation.

json
{
  "mappings": {
    "properties": {
      "product_name": {
        "type": "text",
        "analyzer": "standard",
        "fields": {
          "keyword": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

Now you index once, and Elasticsearch creates two fields internally:

  • product_name (text) – for full-text search
  • product_name.keyword (keyword) – for filtering, sorting, aggregation

Your queries and aggregations now reference the appropriate field:

json
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "product_name": "waterproof laptop"
        }
      },
      "filter": {
        "term": {
          "product_name.keyword": "Premium Leather Laptop Bag"
        }
      }
    }
  }
}

This approach is common in production systems. You get the best of both: semantic search on the text field and precise operations on the keyword sub-field.

Common Mistakes

Mistake 1: Mapping everything as text. Junior developers often default to text because it seems more flexible. But text fields bloat your index, slow queries, and make filtering/sorting unreliable. Start with keyword and add text fields only when you need full-text search.

Mistake 2: Mapping categorical data as text. Status, category, priority, region—these are always keyword. They're the most common culprit in mapping decisions. If you're filtering on it, it's keyword.

Mistake 3: Forgetting to use the keyword sub-field. You set up multi-fields correctly, but your aggregation still references the text field. Reference field_name.keyword in aggregations and filters.

Mistake 4: Mixing case sensitivity assumptions. Keyword fields are case-sensitive by default. "in-stock" and "In-Stock" are different values. If you need case-insensitive matching on a keyword field, use a normalizer in your mapping.

json
{
  "mappings": {
    "properties": {
      "status": {
        "type": "keyword",
        "normalizer": "lowercase"
      }
    }
  },
  "settings": {
    "analysis": {
      "normalizer": {
        "lowercase": {
          "type": "custom",
          "filter": ["lowercase"]
        }
      }
    }
  }
}

Key Takeaways

  • Text fields are analyzed into terms and optimized for full-text search. Use them for descriptions, comments, and unstructured content where fuzzy matching matters.
  • Keyword fields store exact values and are the only reliable choice for filtering, sorting, and aggregation. Use them for IDs, status, categories, and any field where you need precise operations.
  • Multi-field mappings let you have both: map a single source field as text for search and keyword (as a sub-field) for everything else.
  • The most common mistake is mapping categorical and filterable data as text. Think operationally: if you filter on it, aggregate by it, or sort by it, use keyword.
  • Test your mappings early. Index sample data, run real queries, and verify that filtering and sorting work before deploying to production.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles

text vs keyword fields | Elasticsearch Guide | Expona