The Elasticsearch exists query matches documents where the named field has at least one indexed, non-null value. It is the standard way to test for field presence (exists) and, when nested in a must_not clause, for field absence. The query operates on the inverted index and doc values, not on _source, so its result depends on the field's mapping and on options like ignore_above and ignore_malformed.
Syntax
GET /index/_search
{
"query": {
"exists": {
"field": "field_name"
}
}
}
Parameters
| Parameter | Description | Required | Default |
|---|---|---|---|
field |
Name of the field to test. Dot notation is supported for object sub-fields. | Yes | - |
The exists query exposes no other top-level parameters. To negate it, wrap it in a bool.must_not.
Examples
Find documents that have a user field with at least one indexed value:
GET /events/_search
{
"query": {
"exists": { "field": "user" }
}
}
Find documents missing a field, using bool.must_not:
GET /events/_search
{
"query": {
"bool": {
"must_not": [
{ "exists": { "field": "user.email" } }
]
}
}
}
Combine with a filter so the absence check is unscored and cacheable:
GET /events/_search
{
"query": {
"bool": {
"filter": [
{ "range": { "@timestamp": { "gte": "now-7d/d" } } }
],
"must_not": [
{ "exists": { "field": "user_id" } }
]
}
}
}
Probe a nested object's field (use a nested query for true nested mappings):
GET /orders/_search
{
"query": {
"nested": {
"path": "shipments",
"query": {
"exists": { "field": "shipments.tracking_number" }
}
}
}
}
Performance and Use Notes
A field is considered to exist if it has any indexed value. The official docs list four conditions under which a field is reported as missing: the source value was null or []; the field has index: false and doc_values: false; a value was longer than ignore_above and was skipped; or the value was malformed and ignore_malformed is enabled. Empty strings ("") and a placeholder like "-" count as existing values - they are real terms in the index.
The query is cheap. It is backed by the _field_names index field (for indexed fields) and by doc-values existence (for fields with doc_values: true). Both data structures are present by default for the standard text/keyword/numeric/date types, so exists on a normal field is roughly as fast as a term query. Run it in filter context to enter the per-node query cache.
Mapping or ingest issues sometimes cause a field to appear in _source but never get indexed - making it invisible to exists. Pulse monitors Elasticsearch indices for mapping drift, dropped fields, and ignore_malformed/ignore_above events, so silent data-quality regressions surface before they break dashboards.
Common Mistakes
- Expecting
existsto read_source. It checks indexed values. A field withindex: falseanddoc_values: falseis invisible to the query even when present in the JSON. - Storing explicit
nulland expectingexiststo match. JSONnulland[]are treated as missing - use a sentinel value if you must distinguish "explicit null" from "absent". - Forgetting that
ignore_above(default unlimited forkeyword) andignore_malformedcause values to be dropped silently from the index. - Running
existsagainst the parent path of anestedfield. Wrap it in a nested query instead. - Using
existsinmustinstead offilterfor negative-presence checks. The score adds nothing; usefilter/must_notto keep the query cacheable.
Frequently Asked Questions
Q: How do I find documents where a field does not exist?
A: Wrap the exists query in a bool.must_not. Elasticsearch removed the standalone missing query in 5.0, so this combination is the supported pattern. Place the must_not inside bool and add a filter clause to scope the negative match to a reasonable set.
Q: Does the exists query treat empty strings as missing?
A: No. Empty strings ("") and placeholders like "-" are indexed as real terms and count as existing. Only JSON null, an empty array [], fields with no indexed/doc value, and values dropped by ignore_above/ignore_malformed are missing.
Q: How does the exists query behave with arrays?
A: An array that contains at least one non-null element counts as existing - ["foo", null] matches. An empty array [] or [null] does not.
Q: Can I use the exists query in filter context?
A: Yes, and you should. It needs no relevance score, so running it in bool.filter (or bool.must_not for the negative case) lets it enter the per-node query cache.
Q: Why does exists return zero hits when the field clearly exists in _source?
A: The field is not indexed. Likely causes: index: false plus doc_values: false in the mapping, an ignore_above truncation, a malformed value with ignore_malformed: true, or a dynamic mapping that placed the field under an unexpected path.
Q: How is exists different from a term query on the field?
A: exists answers "does this document have any value here", while a term query answers "does this document have this specific value". They are also implemented differently - exists uses the _field_names field or doc-values existence, not the term dictionary.
Related Reading
- Elasticsearch Query Language: overview of the query DSL.
- Bool Query: wraps
existsfor absence and combined logic. - Terms Query: match specific indexed values rather than presence.
- Range Query: another common
filterclause to combine withexists. - Nested Query: use when the field lives inside a nested object.