Intervals query

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 nested query: not supported yet — intervals clauses are rejected with an HTTP 400 naming the offending clause. Use phrase with slop for 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, default false) Whether the tokens must appear in the given order.
  • analyzer — (Optional, string) Analyzer override for query; 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_of only, default -1) Total gap budget between the sub-spans.
  • ordered — (all_of only, default false) 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, default AUTO) AUTO adapts 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" } } }
PredicateKeeps spans that …
containingcontain a filter span
not_containingcontain no filter span
contained_byare inside a filter span
not_contained_byare inside no filter span
overlappingoverlap a filter span
not_overlappingoverlap no filter span
beforelie entirely before a filter span
afterlie 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. match with max_gaps and ordered is a phrase you can loosen on one axis at a time — the playground’s get search with max_gaps: 0 finds the 25 GET /search lines, exactly what phrase returns, while cluster left with max_gaps: 2 still matches the 25 cluster node left the region unexpectedly lines across the node in between.
  • Unordered co-occurrence in one window. all_of without ordered and with a finite max_gaps bounds how far apart the pieces may drift — “these terms, within a window, any order”, which phrase cannot say.
  • Disjunctions of positional rules. any_of of two match rules is “either wording, positionally” — one clause, not a bool of two phrases that would lose the shared gap budget.
  • Pattern leaves inside proximity. prefix/wildcard/fuzzy/ regexp leaves participate in gap and order logic — upstream* within 2 positions of reset, composed instead of post-filtered.
  • Contextual negation. not_containing / not_contained_by remove matches that sit inside (or around) another rule’s spans — “error near restart, but not inside a stack-trace span”.
  • 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; its slop corresponds to a match rule’s gap budget.
  • fuzzy, wildcard, regexp, range — the standalone (non-positional) forms of the multi-term leaves.
Calendar October 6, 2026
Edit Edit this page