Skip to main content

Filter expression syntax (KQL)

Some Query APIs accept an optional filter.expression argument for advanced filtering, using Kibana Query Language (KQL). This page documents the syntax and behavior that's common across every API that supports it. For the fields you can actually reference in expression — which differ per API — see that API's own reference page:

(More APIs will be added to this list as expression support rolls out to them.)

expression is not the same language as searchTerm. searchTerm uses Lucene query_string syntax and contributes to relevance scoring (it affects result order). filter.expression uses KQL and only decides which items match — it never affects their order. Use the separate sort argument to control ordering.

expression is not validated ahead of time — an invalid KQL string is only caught when the query runs, and comes back as an error in the GraphQL response rather than an upfront validation message. For the full KQL grammar, treat the official KQL reference as authoritative.

Basic syntax

NOTE:

Reserved characters — \ ( ) : < > " * — must be escaped with a backslash when used literally (e.g. inside an unquoted value), unless the whole value is wrapped in double quotes.

Leading wildcards (e.g. *ing) are disabled by default, same as in searchTerm.

ConstructExampleMeaning
Equality / containsstatus: ACTIVEField contains the value
Exact matchbrand.keyword: "PnP Foods"Field equals the value exactly (see .keyword fields)
AND / OR / NOTstatus: ACTIVE AND NOT brand: PnPBoolean combinators (case-insensitive keywords)
Grouping(status: ACTIVE OR status: DISCONTINUED) AND brand: PnPParentheses control precedence
Multi-value shorthandtype: (GTIN13 GTIN14)Same as type: GTIN13 OR type: GTIN14. The values inside ( ) must be bare and space-separated — type: (GTIN13 OR GTIN14) (a literal OR inside the list) is a parse error. To use OR explicitly, repeat the field name on both sides: type: GTIN13 OR type: GTIN14.
RangenetContent >= 1 AND netContent <= 5Also >, <
ExistenceconditionIds: *Field has any value set
WildcarditemCategoryId.keyword: default-dataset-cat** matches zero or more characters; ? is not supported (unlike searchTerm)
Grouped matchadditionalProperties: { id: Color AND value: Blue }Match against a single entry of a list field — see Grouped matching on list fields

.keyword vs regular fields

Every text field has a paired .keyword variant, exactly like searchTerm's .keyword fields:

  • name: "paper towel" — matches if that phrase appears anywhere in name.
  • name.keyword: "paper towel" — matches only if name is exactly that string (case-sensitive).
  • Wildcards should generally target .keyword fields (e.g. itemCategoryId.keyword: cat-*) to get predictable prefix/substring behavior.

Some .keyword fields have a maximum length they'll match against — check the field reference of the specific API you're using.

Grouped matching on list fields

Many entities have fields that are lists of objects — for example, additionalProperties, a set of customer-specific key/value pairs found on several entities. For these, group the conditions that must apply to the same list entry inside <field>: { ... }:

additionalProperties: { id: Color AND value: Blue } AND additionalProperties: { id: Size AND (value: S OR value: M) }

This matches items that have one additional property with id: Color, value: Blue and (possibly a different) additional property with id: Size and value of S or M.

NOTE:

The query is repeated per entry — writing additionalProperties: { id: Color AND value: Blue AND id: Size AND value: S } would incorrectly require a single entry to have two different ids at once.

Not every list-of-objects field supports this grouping syntax — check the specific API's field reference for which ones do.

Escaping

expression accepts plain KQL text. KQL uses double quotes for phrase matches (e.g. description: "heavy duty fabric") and backslashes to escape reserved characters (e.g. a literal \() — send those characters as-is, with whatever escaping your GraphQL client normally applies for a string value (e.g. \" and \\ in a JSON variables payload), and nothing more.

Concretely, to filter on the exact phrase description: "heavy duty fabric", send this in your GraphQL variables:

{
"expression": "description: \"heavy duty fabric\""
}

Some APIs may still accept an older, no-longer-required extra level of escaping for backward compatibility — check that API's own reference page if you're unsure which format to use for a new integration (plain escaping, as shown above, is always the right choice for new code).

Case sensitivity

  • Matches against .keyword fields, and fields holding fixed values (e.g. status or type enums), are case-sensitivestatus: active will not match an item whose status is ACTIVE.
  • Matches against regular (non-.keyword) fields are effectively case-insensitive.

Dates and time zones

Date comparisons and date math (e.g. created > now-2d) are evaluated in UTC. Prefer explicit dates over relative date math to avoid ambiguity.

A bare date like validFrom <= 2026-07-17 needs no quoting (it contains no reserved characters). A full timestamp like 2026-07-17T00:00:00.000Z contains :, which is a reserved KQL character, so it must be quoted: validFrom <= "2026-07-17T00:00:00.000Z" — see Escaping for how to send the quotes.

Bare terms

A term with no field: prefix (e.g. expression: "paper") is matched against a default set of fields rather than one you chose. This is both imprecise and comparatively expensive. Always qualify expression terms with an explicit field name.

Limitations that apply to every API

  1. No upfront validation. An invalid expression isn't caught until the query runs — it comes back as an error in the GraphQL response, not a schema-level validation message.
  2. Match-only, no relevance scoring. expression cannot influence result order or apply a boost; it only decides matches. Use sort to control ordering.
  3. Different language from searchTerm. KQL has no fuzzy matching (~), proximity search ("..."~N), boosting (^), or regex (/…/) — those are searchTerm-only features. Don't mix the two syntaxes.
  4. Leading wildcards disabled (e.g. *ing), same restriction as searchTerm.
  5. field: (A OR B) is invalid. A literal OR inside the multi-value shorthand list is a parse error — see the multi-value shorthand row above.
  6. Older filter arguments may combine additively with expression. Where an API's filter input still has older, single-purpose fields alongside expression (typically marked deprecated), supplying both means they're AND-ed together, which can produce confusing double-filtering. Prefer expressing everything through expression alone.

Each API may have additional limitations of its own — check that API's field reference page.

See also