Query syntax
This guide explains the full syntax users can type into FilterQuery. It covers everything from matching a single field value to building multi-condition queries with logical operators and grouping.
How the syntax works
Every query is a sequence of conditions. Each condition targets a field by name, followed by a colon and one or more values:
status: Open
Multiple conditions separated by spaces default to AND:
status: Open assignee: john.doe priority: High
The parser converts typed input into the rule. This structure is a tree of rules connected with glue: "and" or glue: "or". Simple queries produce a single flat filtering rule, while complex queries produce nested filtering rule groups.
Field names can be either the field's id or its sanitized label. Labels appear in the input, while the value prop stores the corresponding IDs. For example, a field { id: "first_name", label: "First Name" } is typed as FirstName: and stored as first_name:.
Values containing spaces, reserved words, or special characters must be quoted:
type: "Bug Report"
status: "In Progress"
Single (') and double (") quotes both work. Use \ to escape a quote inside a string, or switch to the opposite quote type to avoid escaping.
Basic field-value pairs
Match a field to an exact value with field: value:
status: Open
assignee: jane.doe
country: Germany
Each condition produces a rule with filter: "equal":
{ "field": "status", "filter": "equal", "value": "Open" }
Multiple values and exclusions
Multiple values (OR within a field)
Comma-separate values to match any of them:
status: Open, "In Progress", Review
priority: High, Critical
This produces a single rule with includes:
{ "field": "status", "includes": ["Open", "In Progress", "Review"] }
Excluding values
Prefix a value with - to exclude it:
status: -Closed
priority: -Low, -Normal
type: -"Won't Fix"
A single exclusion produces filter: "notEqual". Multiple exclusions expand into an AND group:
{
"glue": "and",
"rules": [
{ "field": "status", "filter": "notEqual", "value": "Closed" },
{ "field": "status", "filter": "notEqual", "value": "On Hold" }
]
}
- before the field name (-status: Open) isn't valid. Negation belongs on the value side.
Text matching operators
Wildcards
Use * as a wildcard for partial text matching:
| Pattern | Meaning | Example |
|---|---|---|
value | Exact match | name: Alex |
value* | Starts with | name: Alex* |
*value | Ends with | email: *@gmail.com |
*value* | Contains | title: *urgent* |
name: Alex*
email: *@company.com
title: *crash*
Word operators
contains, starts, and ends are word-based alternatives to wildcards:
| Operator | Meaning | Example |
|---|---|---|
contains | Contains substring | name: contains Alex |
starts | Starts with | name: starts Alex |
ends | Ends with | name: ends Smith |
-contains | Does not contain | name: -contains spam |
-starts | Does not start with | name: -starts test_ |
-ends | Does not end with | email: -ends @temp.com |
description: contains "critical error"
email: -ends "@temp.com"
title: -contains deprecated
name: Alex* and name: starts Alex produce the same output: { filter: "beginsWith", value: "Alex" }.
To search for the literal word contains, starts, or ends as a field value, quote it:
keyword: "contains"
Numeric and date comparisons
Use comparison operators on number and date fields:
| Operator | Meaning | Example |
|---|---|---|
: | Equals | age: 25 |
> | Greater than | age: >25 |
>= | Greater or equal | votes: >=100 |
< | Less than | price: <50 |
<= | Less or equal | created: <=2024-01-01 |
Use .. for a bounded range (inclusive on both ends):
age: 25 .. 50
created: 2024-01-01 .. 2024-12-31
price: 100 .. 500
A range produces filter: "between" with a { start, end } value object:
{ "field": "age", "filter": "between", "value": { "start": 25, "end": 50 } }
Negative numbers require special handling. -30 is parsed as "not equal to 30", not "equal to negative 30". To match a literal negative number, quote it: temperature: "-10". Comparison operators don't have this issue: temperature: >-10 works as expected.
Date predicates
Date fields support filtering by year or month without a full date value.
Auto-inferred predicates
The parser infers precision from the value format:
| Input | Interpreted as | Matches |
|---|---|---|
start: 2024 | start.year: 2024 | All dates in 2024 |
start: 2024-06 | year-month filter | All dates in June 2024 |
start: 2024-06-15 | exact date | June 15, 2024 only |
start: 2024
start: 2024-06
start: >=2024-06
start: 2024-01 .. 2024-06
Explicit predicates
Use field.predicate: syntax to filter by year or month directly:
| Predicate | Meaning | Example |
|---|---|---|
year | Match by year only | start.year: 2024 |
month | Match by month (1–12) | start.month: 6 |
start.year: 2024
start.year: >2020
start.month: 6
start.month: 1, 2, 3
start: 2024 and start.year: 2024 produce the same output:
{ "field": "start", "predicate": "year", "filter": "equal", "value": 2024 }
All comparison operators and ranges work with predicates: start.year: 2020 .. 2024.
For fields whose labels include a dot after sanitization, the predicate attaches to the sanitized label. A field { id: "start", label: "Start Date" } sanitizes to StartDate, so the predicate syntax is StartDate.year: 2024.
Tags and free text
Tags
Prefix a value with # to match it across all fields without specifying one. Tags do exact matching:
#Urgent
#"In Progress"
-#Closed
Combine multiple tags with commas:
#bug, #feature
#Open, -Closed
A tag produces a rule with field: "*":
{ "field": "*", "filter": "equal", "value": "Urgent" }
To enable autocomplete suggestions for tags, add a "#" key to the options hash.
Free text
With parse="allowFreeText" (the default), plain words without a field name run a contains search across all fields. Each word becomes a separate rule, combined with AND:
Alex Smith
urgent bug
Prefix a word with - to exclude it:
-spam
Alex -test
Alex Smith produces:
{
"glue": "and",
"rules": [
{ "field": "*", "filter": "contains", "value": "Alex" },
{ "field": "*", "filter": "contains", "value": "Smith" }
]
}
With parse="strict", unrecognized text triggers a validation error instead.
Logical operators and grouping
AND and OR
Use and and or to combine conditions explicitly:
project: Alpha and status: Open
status: Open or status: "In Progress"
type: Bug and status: -Closed and assignee: jane.doe
Conditions without an explicit operator default to AND.
Grouping with parentheses
Use () to control evaluation order:
project: Alpha and (status: Open or status: "In Progress")
priority: High or (votes: >=100 and status: Open)
Parentheses produce nested filtering rule groups:
{
"glue": "and",
"rules": [
{ "field": "project", "filter": "equal", "value": "Alpha" },
{
"glue": "or",
"rules": [
{ "field": "status", "filter": "equal", "value": "Open" },
{ "field": "status", "filter": "equal", "value": "In Progress" }
]
}
]
}
and, or, contains, starts, and ends are reserved words. To use any of them as a literal field value, quote it: operator: "and".