Making KQL the Default for Kibana Discover and Dashboard Filters
Mixed Lucene and KQL filters create inconsistent dashboard results. Learn why explicit KQL predicates improve reviewability and how to avoid common mapping and wildcard traps.
19 Jan 2026, 22:28 UTC

The consistency problem nobody owns
Your checkout dashboard looks wrong. One SRE filters with status:500, another pastes a Lucene string, and a third adds a free-text search for "timeout". Three people, three hit counts, and no way to tell which result is correct. The intent was the same; the query language was not.
This is a team-consistency problem, not a technology problem. Kibana accepts multiple query syntaxes, so unless you standardize, every saved search and dashboard becomes a private dialect. The fix is to make Kibana Query Language (KQL) the default for user-facing filters and reserve Lucene or raw Elasticsearch DSL for the cases KQL cannot express.
What KQL actually does
KQL is a Kibana-side query language. When you type a filter into Discover or a dashboard search bar, Kibana translates it into Elasticsearch query DSL—the JSON structure Elasticsearch natively understands. It is not sent as raw Lucene syntax to the cluster.
That translation layer matters. KQL encourages explicit field predicates: http.response.status_code:500 instead of a bare 500. It supports boolean operators (and, or, not), parentheses, numeric and date ranges, quoted phrases, trailing wildcards, and existence checks like field:*. For operational filters where field names and value types are known, this readability is the point.
A worked example: the checkout-error filter
Imagine a dashboard meant to surface server-side checkout failures while excluding synthetic traffic. A maintainable KQL filter looks like this:
http.response.status_code >= 500 and http.response.status_code < 600 and url.domain:checkout.example.com and not user.name:synthetic-*
Enter this in the Discover search bar or a dashboard filter, using a data view that exposes your web-server logs. Keep the time boundary in the global time picker unless the saved object intentionally needs an embedded @timestamp range. Naming fields explicitly—rather than relying on free-text search—means the filter survives mapping changes and is reviewable in a pull request.
To verify the translation, open the inspection panel in Discover (the exact menu label varies by Kibana version) and view the generated Elasticsearch request. You should see a bool query combining range, term, and wildcard clauses. If you see something else, your field types or syntax need attention.
Where KQL stops
KQL is intentionally simpler than Lucene. It does not support regular expressions, fuzzy matching, or query boosting. If your team needs those, route the request to Lucene or precompute tags at ingest time rather than trying to force an unsupported construct into KQL.
Wildcards are the other trap. A trailing wildcard like synthetic-* is usually fine on a low-cardinality keyword field. A leading wildcard such as *-prod can be expensive on high-cardinality keyword fields because it forces Elasticsearch to scan many terms. Avoid leading wildcards in shared dashboards.
Field mapping changes the meaning of the same KQL string. An analyzed text field and its keyword subfield can match differently for the same value. Exact identifiers usually need a keyword or runtime field. A query that looks correct can silently miss documents if the data view exposes the analyzed field instead of the exact-match subfield.
Verify before you trust
Kibana behavior is version-sensitive. Query-language defaults, data views versus older index patterns, field formatters, and saved-object compatibility can all shift across releases. Treat the examples here as a starting assumption and confirm them in your deployment.
Pick a narrow time range, run the KQL filter, and compare hit counts against the same intent expressed as a Lucene filter. Test edge cases: missing fields, empty values, mixed case, values with spaces or punctuation, and documents that not should exclude. Open the data view field list and confirm each field type before trusting exact-match, range, or wildcard behavior.
Publish a style guide, not a mandate
Standardization sticks when it is cheap. Add a short KQL style guide to your repo and require it in dashboard and saved-search pull requests. The rules can fit on one page:
- Require explicit field filters; avoid bare free-text search in shared objects.
- Use
field:*for existence checks. - Quote only values that need phrases.
- Prefer keyword or runtime fields for exact matches.
- Avoid leading wildcards on high-cardinality fields.
- Route regex, fuzzy, or boosting needs to Lucene or ingest-time tags.
KQL will not replace every query your team writes. It will, however, make the filters that analysts and SREs see every day explicit, reviewable, and consistent.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.