Searching existing data in Elasticsearch is how operators confirm ingestion, isolate failures, and answer application questions without pulling raw documents back out of the cluster. A good search flow starts by targeting the correct index, alias, or data stream and then matching the query type to the mapped field types.
The Search API accepts a quick Lucene query-string search through the q parameter and full JSON request bodies through the Query DSL. Current Elastic docs note that q is useful for fast ad hoc checks but does not support the full Query DSL, and if a request sends both q and a JSON query body, the URI query-string search takes precedence.
Search behavior also depends on mappings. Analyzed text fields work best with match queries, while exact filters and repeatable sorting usually rely on keyword, date, or numeric fields. Current self-managed deployments typically use an authenticated HTTPS endpoint for these API calls.
$ curl -sS --fail "http://localhost:9200/_cat/indices?v&s=index&h=health,status,index,docs.count,store.size" health status index docs.count store.size green open app-events-search-2026.04 4 17.6kb green open app-metrics-2026.04 2 8.2kb
If the data lives in a data stream or alias, search that logical name instead of a hidden backing index so future rollovers do not break the query target.
$ curl -sS --fail "http://localhost:9200/app-events-search-2026.04/_mapping?pretty&filter_path=*.mappings.properties"
{
"app-events-search-2026.04" : {
"mappings" : {
"properties" : {
"event_id" : {
"type" : "keyword"
},
"level" : {
"type" : "keyword"
},
"message" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"timestamp" : {
"type" : "date"
}
}
}
}
}
Current Elastic docs still position match as the standard full-text query and term as the exact-value query for keyword fields. Sorting and exact filtering commonly use message.keyword, level, or another field with doc values.
$ curl -sS --fail -G "http://localhost:9200/app-events-search-2026.04/_search?pretty&filter_path=took,hits.total,hits.hits._id,hits.hits._source" --data-urlencode "q=timeout"
{
"took" : 4,
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"hits" : [
{
"_id" : "evt-1004",
"_source" : {
"timestamp" : "2026-04-02T06:19:44Z",
"level" : "ERROR",
"message" : "database timeout while writing session"
}
},
{
"_id" : "evt-1002",
"_source" : {
"timestamp" : "2026-04-02T06:16:52Z",
"level" : "WARN",
"message" : "upstream timeout threshold reached"
}
}
]
}
}
Use --data-urlencode so spaces and special characters in the query are escaped safely.
Current Elastic API docs note that the q parameter uses Lucene query-string syntax, does not support the full Query DSL, and overrides any JSON query body sent with the same request.
$ curl -sS --fail -H "Content-Type: application/json" -X POST "http://localhost:9200/app-events-search-2026.04/_search?pretty&filter_path=took,hits.total,hits.hits._id,hits.hits._source" -d '{
"size": 2,
"_source": ["timestamp", "level", "message"],
"sort": [
{ "timestamp": { "order": "desc" } }
],
"query": {
"bool": {
"must": [
{ "match": { "message": "timeout" } }
],
"filter": [
{ "term": { "level": "ERROR" } },
{ "range": { "timestamp": { "gte": "now-24h" } } }
]
}
}
}'
{
"took" : 3,
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"hits" : [
{
"_id" : "evt-1004",
"_source" : {
"timestamp" : "2026-04-02T06:19:44Z",
"level" : "ERROR",
"message" : "database timeout while writing session"
}
},
{
"_id" : "evt-1001",
"_source" : {
"timestamp" : "2026-04-02T06:15:00Z",
"level" : "ERROR",
"message" : "connection timeout while opening upstream socket"
}
}
]
}
}
The match query is the standard full-text query in current Elastic docs. Keep term filters on exact-value fields such as keyword, date, or numeric fields rather than analyzed text fields.
$ curl -sS --fail -H "Content-Type: application/json" -X POST "http://localhost:9200/app-events-search-2026.04/_search?pretty&filter_path=hits.total,hits.hits._id,hits.hits._source" -d '{
"from": 1,
"size": 1,
"sort": [
{ "timestamp": { "order": "desc" } }
],
"query": {
"match": { "message": "timeout" }
}
}'
{
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"hits" : [
{
"_id" : "evt-1002",
"_source" : {
"timestamp" : "2026-04-02T06:16:52Z",
"level" : "WARN",
"message" : "upstream timeout threshold reached"
}
}
]
}
}
Current Elastic docs keep the same rule: from and size are fine for shallow paging, but the default index.max_result_window limit still blocks paging past 10,000 hits. Use search_after with a fixed sort key for deep pagination.
$ curl -sS --fail -H "Content-Type: application/json" -X POST "http://localhost:9200/app-events-search-2026.04/_search?pretty&filter_path=took,hits.total" -d '{
"track_total_hits": true,
"query": {
"match": {
"message": "timeout"
}
}
}'
{
"took" : 2,
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
}
}
}
Elastic's current API reference notes that track_total_hits returns the exact hit count at extra cost. Leave it off when a fast top-N query is enough, and use filter_path or _source filtering to keep routine responses small.