Let's get started with a Microservice Architecture with Spring Cloud:
Difference Between keyword and text in Elasticsearch
Last updated: February 7, 2026
1. Overview
One of the most common challenges when working with Elasticsearch is selecting the appropriate field type for the data to be stored. In particular, string fields require special attention. Elasticsearch offers two primary options: keyword and text. Selecting the correct one is essential. Otherwise, we risk unexpected search results, broken aggregations, or even performance issues.
In this article, we’ll explore how keyword and text fields differ. Then, we’ll explain when to use each type so we can design more reliable and efficient Elasticsearch mappings.
2. text Field Type
In Elasticsearch, the text field type is used for full-text search. Before indexing, Elasticsearch analyzes the content by tokenizing it, converting it to lowercase, and, depending on the analyzer, applying steps like synonym handling and stop-word removal.
Let’s take the next document as an example for our search cases:
{
"type": "article",
"title": "Using Elasticsearch with Spring Boot",
"status": "IN_PROGRESS"
}
We will map the title column as a text field type and the other two columns as a keyword type. After the analysis process, the index result for this column will look as follows:
["using", "elasticsearch", "with", "spring", "boot"]
And we can use the match operator for full-text search behavior:
{
"query": {
"match": {
"title": "spring elasticsearch"
}
}
}
To perform a full-text search using the Elasticsearch Java API Client, we can start by defining a model that represents the document to index. With Java, a record is a great fit for this case:
public record Article(String type, String title, String status) {
}
Next, we’ll define the search query using the client.
SearchResponse<Article> response = client.search(s -> s
.index("index")
.query(q -> q
.match(m -> m
.field("title")
.query("spring elasticsearch")
.operator(Operator.And) // require both terms
)
),
Article.class
);
This query searches on the title field using full-text analysis and requires both terms spring and elasticsearch to be present.
The text field type has some limitations for string values. For example, it does not support sorting or aggregations. This is because text fields are optimized for search and don’t store the necessary data structures that enable efficient sorting and aggregation. In addition, text fields are not suitable for exact matching, since Elasticsearch stores analyzed tokens rather than the original values.
3. keyword Field Type
When we define a field as keyword, Elasticsearch stores the value exactly as provided, without applying any analysis such as tokenization or lowercasing. This makes keyword fields ideal for exact matching, filtering, sorting, and aggregations.
For instance, let’s define the status field as a keyword type. Using our example document, Elasticsearch will index the value as follows:
["IN_PROGRESS"]
To search for documents with a specific status, we use the term query, which performs exact matching:
{
"query": {
"term": {
"status": "IN_PROGRESS"
}
}
}
Now, let’s define the query using the Java client:
SearchResponse<Article> response = client.search(s -> s
.index("index")
.query(q -> q
.term(t -> t
.field("status")
.value("IN_PROGRESS")
)
),
Article.class
);
An important thing to note is that since keyword fields are case-sensitive and require exact matches, searching for “in_progress” (lowercase) would return no results, even though the actual value is “IN_PROGRESS”. This behavior is crucial to understand when working with keyword fields.
Using the keyword type allows us to perform aggregations over the field and get accurate information about the documents. For example, if we want to get the count of documents by status, we can define the next query:
{
"size": 0,
"aggs": {
"by_status": {
"terms": {
"field": "status"
}
}
}
}
And using the Java client:
SearchResponse<Void> response = elasticsearchClient.search(s -> s.index("index")
.size(0)
.aggregations("by_status", a -> a.terms(t -> t.field("status"))), Void.class);
response.aggregations()
.get("by_status")
.sterms()
.buckets()
.array()
.forEach(b -> System.out.println(b.key()
.stringValue() + " -> " + b.docCount()));
This query groups documents by their exact status values and returns the count for each group, making it easy to generate reports or dashboards based on categorical data
4. Choosing Between text and keyword
Now that we have explored the differences between the two types, we can infer which one will suit us better for our particular cases. For instance, if we need to perform a full-text search, text type will be the best option. If we need to search by exact matching or perform aggregations based on the data, the keyword type will be a better fit.
Sorting is another reason to use the keyword type. Elasticsearch can only sort on fields that have a single comparable value per document. Since keyword fields are stored as-is (not tokenized), they work naturally for sorting. Let’s see the query:
{
"sort": [
{
"status": "asc"
}
]
}
And using the Java client:
SearchResponse<Article> response = elasticsearchClient.search(s -> s.index("index")
.sort(so -> so.field(f -> f.field("status")
.order(SortOrder.Asc))), Article.class);
Similarly, while the text type is not intended for aggregations, the keyword type is not suitable for long, free-form text. Using it in this way only increases the index size and provides no relevance scoring.
Understanding these differences helps us build more reliable Elasticsearch indices, achieve better performance, and produce accurate search results and aggregations.
5. Multi-Fields Typing
A common and recommended pattern is to index the same field as both text and keyword using multi-fields. This approach allows full-text search while still supporting exact matches, sorting, and aggregations on the same logical field.
The next is an example mapping:
{
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
With this mapping, Elasticsearch creates two indexed versions of the title field and gives us the flexibility to use the same field in different ways. For example, we can perform a full-text search on the analyzed version:
{
"query": {
"match": {
"title": "spring elasticsearch"
}
}
}
And sort the results using the keyword version:
{
"query": {
"match": {
"title": "spring elasticsearch"
}
},
"sort": [
{
"title.keyword": "asc"
}
]
}
Using the Java client, we can also combine both capabilities:
SearchResponse<Article> response = client.search(s -> s
.index("index")
.query(q -> q
.match(m -> m
.field("title")
.query("spring elasticsearch")
)
)
.sort(so -> so
.field(f -> f
.field("title.keyword")
.order(SortOrder.Asc)
)
),
Article.class
);
Similarly, we can aggregate on the keyword version to get exact value counts:
SearchResponse<Article> response = client.search(s -> s
.index("index")
.size(0)
.aggregations("popular_titles", a -> a
.terms(t -> t
.field("title.keyword")
.size(10)
)
),
Article.class
);
response.aggregations()
.get("popular_titles")
.sterms()
.buckets()
.array()
.forEach(b -> System.out.println(
b.key().stringValue() + " -> " + b.docCount()
));
This pattern is especially useful for fields like product names, article titles, or user-generated content, where we need both search and analytical capabilities. However, we need to keep in mind that multi-fields increase storage requirements since Elasticsearch indexes the data twice.
6. Conclusion
In this article, we explored the key differences between text and keyword field types in Elasticsearch. We reviewed how searches work for each type, discussed their appropriate use cases, and explained how choosing the correct field type leads to more accurate results and faster queries.
We also examined the multi-fields pattern, which allows us to index the same field as both text and keyword. This approach provides the flexibility to perform full-text searches while still supporting exact matching, sorting, and aggregations on the same logical field.
As always, the complete code for this tutorial is available over on GitHub.
















