| Tool | Where | Best for |
|---|---|---|
| KQL | Kibana query bar (Discover, dashboards) | Quick interactive filtering |
| Query DSL | Dev Tools, the _search API, applications |
Full control: scoring, bool logic, everything |
| Aggregations | Inside a _search body |
Summaries such as counts, top-N, histograms |
| ES|QL | Discover (ES|QL mode), Dev Tools, the _query API |
Piped queries with transforms, stats and joins |
The examples use the OPNsense data stream from the ingest section.
Kibana query bar (KQL)¶
observer.hostname : "fw01" and event.action : "block"
destination.port : (22 or 3389) and not source.ip : 10.0.0.0/8
process.name : filter*
message : "link state changed"
KQL only filters. Time range, sorting and aggregations come from the Kibana UI around it.
Query DSL¶
GET logs-opnsense-*/_search
{
"size": 20,
"sort": [{ "@timestamp": "desc" }],
"_source": ["@timestamp", "source.ip", "destination.ip", "destination.port"],
"query": {
"bool": {
"filter": [
{ "term": { "event.action": "block" } },
{ "range": { "@timestamp": { "gte": "now-24h" } } }
],
"must_not": [
{ "term": { "source.ip": "10.0.0.0/8" } }
]
}
}
}
- Use
filterfor exact yes/no conditions: they're cached and don't affect scoring. Usemustonly when relevance ranking matters. termis forkeyword,ip, numeric and date fields.matchis for analyzedtext.
Aggregations¶
Use an aggregation when the question is about a set of documents ("which source IPs are blocked most?") rather than about individual documents. Set "size": 0 to skip returning hits.
GET logs-opnsense-*/_search
{
"size": 0,
"query": { "range": { "@timestamp": { "gte": "now-7d" } } },
"aggs": {
"top_blocked_sources": {
"terms": { "field": "source.ip", "size": 10 }
},
"per_hour": {
"date_histogram": { "field": "@timestamp", "fixed_interval": "1h" }
}
}
}
ES|QL¶
ES|QL is a piped query language: each | stage transforms the previous result.
FROM logs-opnsense-*
| WHERE @timestamp > NOW() - 24 hours AND event.action == "block"
| STATS hits = COUNT(*) BY source.ip
| SORT hits DESC
| LIMIT 10
From Dev Tools or curl:
POST _query?format=txt
{
"query": "FROM logs-opnsense-* | STATS hits = COUNT(*) BY destination.port | SORT hits DESC | LIMIT 10"
}