Intervals query #
The intervals query is a rule language for term positions: it matches
documents whose analyzed token stream satisfies proximity rules you
express as a tree — fixed token sequences, maximum gaps, required
order, unions, multi-term leaves, and positional filters between
sibling rules. It covers everything
phrase expresses plus the shapes a phrase
cannot: unordered combinations with bounded width, disjunctions of
positional rules, and prefix/wildcard/fuzzy/regexp leaves inside
positional composition.
Requires a position index (text fields analyzed with positions — the default).
Inside a
nestedquery: not supported yet — intervals clauses are rejected with an HTTP 400 naming the offending clause. Usephrasewithslopfor positional logic on nested fields.
How it works #
Evaluation is two-phase. First the rule tree compiles per segment:
match texts are tokenized with the field’s analyzer (or the rule’s
analyzer), and every multi-term leaf (prefix, wildcard, fuzzy,
regexp, range) resolves to the set of dictionary terms its
pattern matches. The leaves that sit outside every any_of form the
candidate set — a conjunction (bool-must over all of those terms), a
cheap inverted-index superset of the answer. Phase two decodes each
candidate’s token positions and evaluates the interval grammar
exactly: leaves yield one-position spans at every occurrence,
all_of picks one span per sub-rule and checks order plus the gap
budget, any_of unions, and filter predicates retain or drop spans
positionally. Match-ness is exact; only the BM25 frequency is capped
(at 1024 combinations per node), so pathological rules cost score
resolution, never correctness. The same evaluator runs on every
serving path (block producer, realtime segment, disk segment).
Two grammar details worth knowing: a match rule is an implicit
all_of whose tokens may never share one position (so "search search" needs two real occurrences), and max_gaps counts the
uncovered positions inside the combined span — (end − start + 1)
minus the covered positions — not the
distance between the outer terms.
Rule family #
A rule object sets exactly one of: match, all_of, any_of,
prefix, wildcard, fuzzy, regexp, or range — plus its own
optional filter.
match
#
Analyzes query and matches its tokens as one combination:
POST /my-collection/_search
{
"query": {
"intervals": {
"message": {
"match": {
"query": "cluster node restart",
"max_gaps": 2,
"ordered": true
}
}
}
}
}
query— (Required, string) Text to analyze.max_gaps— (Optional, integer, default-1) Maximum number of positions between the matched tokens;0= adjacent (a phrase),-1= unlimited.ordered— (Optional, boolean, defaultfalse) Whether the tokens must appear in the given order.analyzer— (Optional, string) Analyzer override forquery; defaults to the field’s analyzer.filter— (Optional, filter object) See filters.
all_of / any_of
#
all_of combines sub-rules conjunctively (all must produce a span);
any_of disjunctively (any one suffices):
POST /my-collection/_search
{
"query": {
"intervals": {
"message": {
"all_of": {
"intervals": [
{ "match": { "query": "connection reset" } },
{ "any_of": {
"intervals": [
{ "match": { "query": "peer" } },
{ "prefix": { "prefix": "upstream" } }
]
} }
],
"ordered": true
}
}
}
}
}
intervals— (Required, array of rules) The sub-rules.max_gaps— (all_ofonly, default-1) Total gap budget between the sub-spans.ordered— (all_ofonly, defaultfalse) Sub-spans must appear in list order and not overlap.
prefix / wildcard / regexp
#
Multi-term leaves: any dictionary term matching the pattern produces a one-position span at every occurrence:
{ "prefix": { "prefix": "rest" } }
{ "wildcard": { "pattern": "node?1" } }
{ "regexp": { "pattern": "err(o)?r" } }
wildcard supports ? (one character) and * (zero or more). All
three accept filter.
fuzzy
#
Terms within an edit distance of term:
{ "fuzzy": { "term": "recieve", "fuzziness": "AUTO" } }
fuzziness— (Optional, defaultAUTO)AUTOadapts by term length; an integer pins the distance.prefix_length,transpositions— only the default values (0/true) are accepted; other values are rejected with a named error rather than silently mis-matching.
range
#
Dictionary terms between two analyzed bounds:
{ "range": { "gte": "info", "lte": "warning" } }
Both bounds are required; each must be exactly one of the inclusive
(gte/lte) or exclusive (gt/lt) form. A bound must analyze to a
single term — multi-term bounds are rejected.
Filters #
Any rule carries a filter object that keeps only the spans passing
positional predicates against another rule’s spans:
"filter": { "containing": { "match": { "query": "username" } } }
| Predicate | Keeps spans that … |
|---|---|
containing | contain a filter span |
not_containing | contain no filter span |
contained_by | are inside a filter span |
not_contained_by | are inside no filter span |
overlapping | overlap a filter span |
not_overlapping | overlap no filter span |
before | lie entirely before a filter span |
after | lie entirely after a filter span |
The script filter is not supported — rejected with a named error.
Strict gates #
Unsupported knobs are rejected with named errors instead of being
ignored: use_field pointing at a field other than the query field,
filter script, and fuzzy prefix_length/transpositions other than
their defaults.
Use cases #
- Phrases with room.
matchwithmax_gapsandorderedis a phrase you can loosen on one axis at a time — the playground’sget searchwithmax_gaps: 0finds the 25GET /searchlines, exactly whatphrasereturns, whilecluster leftwithmax_gaps: 2still matches the 25cluster node left the region unexpectedlylines across thenodein between. - Unordered co-occurrence in one window.
all_ofwithoutorderedand with a finitemax_gapsbounds how far apart the pieces may drift — “these terms, within a window, any order”, whichphrasecannot say. - Disjunctions of positional rules.
any_ofof twomatchrules is “either wording, positionally” — one clause, not aboolof two phrases that would lose the shared gap budget. - Pattern leaves inside proximity.
prefix/wildcard/fuzzy/regexpleaves participate in gap and order logic —upstream*within 2 positions ofreset, composed instead of post-filtered. - Contextual negation.
not_containing/not_contained_byremove matches that sit inside (or around) another rule’s spans — “error near restart, but not inside a stack-trace span”.
Related #
span— the other positional grammar: clause composition (span_near/span_or/span_multi) instead of a declarative rule tree. Intervals expresses the same shapes more compactly and adds the filter predicates; spans are the older, lower-level family.phrase— the single fixed-sequence special case; itsslopcorresponds to amatchrule’s gap budget.fuzzy,wildcard,regexp,range— the standalone (non-positional) forms of the multi-term leaves.