More operators

This document explains some of the operators available in Content Advanced Search, including: queryString, compound, regexp, exists, nested, and language. Additional operators are detailed in a separate document, titled "Operators"

queryString operator

Description

The queryString operator allows you to write complex search queries in a compact string format. It supports field-specific searches, Boolean operators (AND, OR, NOT), wildcards, and grouping with parentheses.

This operator is recommended for user-submitted searches, particularly for advanced users who want to create intricate search conditions using the query syntax. It provides a powerful and flexible way to search across multiple fields with complex Boolean logic.

Syntax

{
  "queryString": {
    "query": "(<field-to-search>: (<search-values>) AND|OR (<search-values>)) AND|OR (<search-values>)",
    "defaultPath": "<field-to-search>",
    "score": <score-options>
  }
}

Options

FieldTypeDescriptionRequired?
querystringThe query string.Yes
defaultPathstringThe default field to search if no field is specified in the query.No
defaultOperatorstringDefault boolean operator for terms without explicit operators: "AND" (all terms required) or "OR" (any term). Default: "AND".No
scoreobjectScore modification options. See Score modification .No

Query syntax

The queryString operator searches one or more fields using colon-delimited field-value pairs. For example, to search the heroMedia.title field for "championship", use heroMedia.title:championship.

Combining search criteria

Combine multiple fields and values using the following operators:

OperatorDescriptionExample
ANDBoolean AND. All search values must be present for a document to be included in results.heroMedia.title:team AND pinned:true
ORBoolean OR. At least one search value must be present for a document to be included in results.tags:breaking OR tags:exclusive
NOTBoolean NOT. Specified search value must be absent for a document to be included in results.pinned:true NOT tags:draft
-Alternative NOT syntax. Excludes documents matching the value.pinned:true -tags:draft
TORange operator. Use [] for inclusive, {} for exclusive, or {] and [} for half-open ranges.publishDate:[2024-01-01 TO 2024-12-31]
()Grouping. Use parentheses to group fields and values into subqueries.(heroMedia.title:team OR heroMedia.title:club) AND pinned:true

Wildcards

Run wildcard queries using the following:

OperatorDescriptionExample
?Matches any single character.te?m matches "team" or "term"
*Matches zero or more characters.team* matches "team", "teams", "teammate"

Important: The queryString operator does not support wildcard queries with * as the first character. Any characters preceding the * are treated as a prefix that must be matched exactly.

Phrases

Use double quotes to search for exact phrases:

SyntaxDescriptionExample
"..."Exact phrase match with words in order."team a fc" matches exactly

Examples

Field-specific search:

  • heroMedia.title:championship - Search for "championship" in the title field

Combined searches:

  • tags:breaking AND publishDate:[now-7d TO now] - Articles tagged "breaking" published in last 7 days

Grouping with boolean logic:

  • (team OR club) AND final - Articles containing ("team" OR "club") AND "final"

Range queries:

  • publishDate:[2024-01-01 TO 2024-12-31] - Inclusive date range
  • readTimeMinutes:{5 TO *} - More than 5 minutes read time (exclusive lower bound)

Wildcards:

  • team* - Matches "team", "teams", "teammate"
  • te?m - Matches "team", "term"

Phrases:

  • heroMedia.title:"team a fc" - Exact phrase in title field

Examples

Basic field search

Search for "championship" in the title field:

{
  "query": {
    "queryString": {
      "query": "heroMedia.title:championship"
    }
  }
}

Multiple fields with Boolean logic

Search for articles about "team a" in title or content, published in the last 7 days:

{
  "query": {
    "queryString": {
      "query": "(heroMedia.title:\"team a\" OR content.text:\"team a\") AND publishDate:[now-7d TO now]"
    }
  }
}

Using default field

Search for "breaking news" in the default field (tags):

{
  "query": {
    "queryString": {
      "query": "breaking news",
      "defaultPath": "tags"
    }
  }
}

Complex query with wildcards and grouping

{
  "query": {
    "queryString": {
      "query": "(heroMedia.title:champion* OR heroMedia.title:final*) AND tags:sport* AND NOT tags:draft"
    }
  }
}

Using defaultOperator

The defaultOperator controls how terms are combined when no explicit boolean operator is specified.

Default behaviour (AND):

{
  "query": {
    "queryString": {
      "query": "team championship",
      "defaultPath": "heroMedia.title"
    }
  }
}

This requires both "team" AND "championship" to be present (default behaviour).

Using OR as default operator:

{
  "query": {
    "queryString": {
      "query": "team championship",
      "defaultPath": "heroMedia.title",
      "defaultOperator": "OR"
    }
  }
}

This matches articles with "team" OR "championship" (or both).

Important: The defaultOperator only affects terms without explicit operators. Terms connected with explicit AND/OR operators always respect those operators:

{
  "query": {
    "queryString": {
      "query": "team championship AND final",
      "defaultOperator": "OR"
    }
  }
}

This evaluates as: (team OR championship) AND final

  • "team" and "championship" are combined with OR (defaultOperator)
  • "final" is required (explicit AND operator)

Best practices

Never embed user input directly in query strings

Bad - User input embedded in query string:

{
  "queryString": {
    "query": "categories.id:a1b2c3d4 AND " + userInput
  }
}

This causes problems because user input can interfere with your base query logic.

Examples of query interference:

If a user enters: heroMedia.title:championship OR categories.id:b2c3d4e5

The resulting query becomes:

categories.id:a1b2c3d4 AND heroMedia.title:championship OR categories.id:b2c3d4e5

Due to operator precedence, this effectively becomes:

(categories.id:a1b2c3d4 AND heroMedia.title:championship) OR categories.id:b2c3d4e5

Now your categories.id:a1b2c3d4 filter is bypassed, and results from b2c3d4e5 are included.

Other problematic inputs:

  • User enters heroMedia.title:foo) OR (categories.id:c3d4e5f6 - breaks out of boolean grouping
  • User enters NOT categories.id:a1b2c3d4 - inverts your filter logic
  • User enters special characters or malformed syntax - causes query parsing errors

Good - User input isolated:

{
  "compound": {
    "must": [
      <user-submitted-query>
    ],
    "filter": [
      {
        "queryString": {
          "query": "categories.id:a1b2c3d4"
        }
      }
    ]
  }
}

Operator precedence

Boolean operators are evaluated with the following precedence (highest to lowest):

  1. Parentheses () - Highest precedence, evaluated first
  2. NOT / - - Negation
  3. AND - Conjunction (higher than OR)
  4. OR - Disjunction (lowest precedence)

Example:

categories.id:a1b2c3d4 AND heroMedia.title:championship OR categories.id:b2c3d4e5

Is evaluated as:

(categories.id:a1b2c3d4 AND heroMedia.title:championship) OR categories.id:b2c3d4e5

Why this matters:

Without understanding precedence, the query above might unexpectedly include other category results. To ensure both conditions are required, use explicit parentheses:

categories.id:a1b2c3d4 AND (heroMedia.title:championship OR categories.id:b2c3d4e5)

Best practice: Always use parentheses to make boolean logic explicit and avoid relying on operator precedence.

Notes

  • This operator does not support boosting syntax (e.g., heroMedia.title:championship^2)
  • Special characters in query strings must be escaped: + - = && || > < ! ( ) { } [ ] ^ " ~ * ? : \ /
  • Invalid query syntax will result in a search error
  • Wildcard queries can be slow on large datasets
  • This operator searches analysed text fields, so searches are case-insensitive and apply text analysis
  • Wildcard support: The queryString operator supports wildcards (* and ?) within field values in the query string itself (e.g., heroMedia.title:champ*). The defaultPath parameter does NOT support wildcards - you must specify an exact field name
  • The default defaultOperator is "AND", requiring all terms without explicit operators to be present
  • The defaultOperator only affects implicit term combinations; explicit AND/OR operators in the query string always take precedence

compound operator

Description

The compound operator combines multiple query clauses using Boolean logic to create complex search queries. It allows you to specify which conditions must match, should match, or must not match, giving you fine-grained control over search results.

Use this operator when:

  • Combining multiple search criteria
  • Creating complex filtering logic
  • Requiring some conditions whilst making others optional
  • Excluding specific content from results
  • Influencing scoring through must/should clauses

Syntax

{
  "compound": {
    "must": [<query>, ...],
    "should": [<query>, ...],
    "mustNot": [<query>, ...],
    "filter": [<query>, ...],
    "minimumShouldMatch": <number>,
    "score": <score-options>
  }
}

Options

FieldTypeDescriptionRequired?
mustarrayArray of queries that must all match. Contributes to scoring.Conditional
shouldarrayArray of queries where at least one should match. Contributes to scoring.Conditional
mustNotarrayArray of queries that must not match. Does not contribute to scoring.Conditional
filterarrayArray of queries that must match but do not contribute to scoring (for filtering only).Conditional
minimumShouldMatchnumberMinimum number of should clauses that must match. Default is 1 if no must or filter present, otherwise 0.No
scoreobjectScore modification options. See Score.No

Clause Behaviour

ClauseMatching RequirementAffects Score?
mustAll queries in this array must matchYes
shouldAt least one query should match if no must or filter clauses are present. When combined with must or filter, becomes optional but boosts score of matching documentsYes
mustNotNone of these queries should match. Used to exclude articlesNo
filterAll queries must match, but does not affect scoring. Use for exact filteringNo

Note: At least one of must, should, mustNot, or filter must be specified.

Examples

Must and should combination

Find articles about "team a" that should also mention "trophy" and are in the "5a3c1e8b-4f2d-4a1c-9b8e-1234567890ab" category:

{
  "query": {
    "compound": {
      "must": [
        {
          "text": {
            "query": "team a",
            "path": "heroMedia.title"
          }
        }
      ],
      "should": [
        {
          "text": {
            "query": "trophy",
            "path": "content.text"
          }
        }
      ],
      "filter": [
        {
          "equal": {
            "path": "categories.id",
            "value": "5a3c1e8b-4f2d-4a1c-9b8e-1234567890ab"
          }
        }
      ]
    }
  }
}

Note: Filtering by keyword fields like categories.id should be done using the filter clause for better performance.

Exclude specific tags

Find articles excluding platform-related tags:

{
  "query": {
    "compound": {
      "mustNot": [
        {
          "in": {
            "path": "tags",
            "values": ["app-only", "maintenance"]
          }
        }
      ]
    }
  }
}

Filter by date range without affecting score

Find recent articles about "transfers" published in the last 30 days:

{
  "query": {
    "compound": {
      "must": [
        {
          "text": {
            "query": "transfers",
            "path": ["heroMedia.title", "content.text"]
          }
        }
      ],
      "filter": [
        {
          "range": {
            "path": "publishDate",
            "gte": "now-30d"
          }
        }
      ]
    }
  }
}

Complex multi-criteria search

Find articles that:

  • Filter by pinned
  • Filtered by specific category
  • Should mention "final" or "cup" in the title
  • Must not have certain tags
{
  "query": {
    "compound": {
      "should": [
        {
          "text": {
            "query": "final",
            "path": "heroMedia.title"
          }
        },
        {
          "text": {
            "query": "cup",
            "path": "heroMedia.title"
          }
        }
      ],
      "mustNot": [
        {
          "in": {
            "path": "tags",
            "values": ["app-only", "maintenance"]
          }
        }
      ],
      "filter": [
        {
          "equal": {
            "path": "pinned",
            "value": true
          }
        },
        {
          "equal": {
            "path": "categories.id",
            "value": "5a3c1e8b-4f2d-4a1c-9b8e-1234567890ab"
          }
        }
      ]
    }
  }
}

Tag-based filtering

Find articles with specific tags, excluding certain tags:

{
  "query": {
    "compound": {
      "mustNot": [
        {
          "equal": {
            "path": "tags",
            "value": "app-only"
          }
        }
      ],
      "filter": [
        {
          "in": {
            "path": "tags",
            "values": ["breaking-news", "exclusive"]
          }
        }
      ]
    }
  }
}

Nested compound queries

Combine compound queries for even more complex logic that:

  • Must match either "team a" in "5a3c1e8b-4f2d-4a1c-9b8e-1234567890ab"
  • Or "team b" in "7b4d2f9c-5e3a-4b2d-8c9f-2345678901bc"
{
  "query": {
    "compound": {
      "should": [
        {
          "compound": {
            "must": [
              {
                "text": {
                  "query": "team a",
                  "path": "heroMedia.title"
                }
              }
            ],
            "filter": [
              {
                "equal": {
                  "path": "categories.id",
                  "value": "5a3c1e8b-4f2d-4a1c-9b8e-1234567890ab"
                }
              }
            ]
          }
        },
        {
          "compound": {
            "must": [
              {
                "text": {
                  "query": "team b",
                  "path": "heroMedia.title"
                }
              }
            ],
            "filter": [
              {
                "equal": {
                  "path": "categories.id",
                  "value": "7b4d2f9c-5e3a-4b2d-8c9f-2345678901bc"
                }
              }
            ]
          }
        }
      ]
    }
  }
}

Per-field boosting

Find articles about "championship" that should also mention "winner" or "runner-up" to boost their relevance:

{
  "query": {
    "compound": {
      "must": [
        {
          "text": {
            "query": "championship",
            "path": ["heroMedia.title", "content.text"]
          }
        }
      ],
      "should": [
        {
          "text": {
            "query": "winner",
            "path": ["heroMedia.title", "content.text"],
            "score": {
              "boost": {
                "value": 10.0
              }
            }
          }
        },
        {
          "text": {
            "query": "runner-up",
            "path": ["heroMedia.title", "content.text"],
            "score": {
              "boost": {
                "value": 5.0
              }
            }
          }
        }
      ]
    }
  }
}

Minimum should match

Find articles that should mention at least two of the following: "breaking news", "title", or published in the last 24 hours, while filtering by specific categories:

{
  "query": {
    "compound": {
      "should": [
        {
          "equal": {
            "path": "pinned",
            "value": true
          }
        },
        {
          "text": {
            "query": "breaking news",
            "path": "heroMedia.title"
          }
        },
        {
          "range": {
            "path": "publishDate",
            "gte": "now-24h"
          }
        }
      ],
      "filter": [
        {
          "in": {
            "path": "categories.id",
            "values": [
              "5a3c1e8b-4f2d-4a1c-9b8e-1234567890ab",
              "7b4d2f9c-5e3a-4b2d-8c9f-2345678901bc"
            ]
          }
        }
      ],
      "minimumShouldMatch": 2
    }
  }
}

This requires at least 50% of the should clauses (2 out of 4) to match.

Clause interaction reference

The following table clarifies how must, should, filter, and mustNot interact:

Clause PresentMust Match?Affects Score?Use Case
must onlyAll must clausesYesRequired conditions that affect relevance
should onlyAt least 1 (or minimumShouldMatch)YesOptional conditions, any can match
filter onlyAll filter clausesNoRequired conditions, no scoring needed
mustNot onlyNone can matchNoExclusion only
must + shouldAll must + 0+ should (optional)YesRequired + optional boost
filter + shouldAll filter + 0+ should (optional)Yes (should only)Required filter + optional boost
must + filterAll must + all filterYes (must only)Required scoring + required filtering
must + mustNotAll must + none mustNotYes (must only)Include + exclude with scoring
Any + mustNot(depends) + none mustNot(depends)Any pattern + exclusions

Key insights:

  • should clauses are required when alone, optional when combined with must or filter
  • filter never affects scoring, even when combined with other clauses
  • mustNot never affects scoring (pure exclusion)
  • Use minimumShouldMatch to require multiple should clauses (default is 1 when alone, 0 when with must/filter)

Notes

  • Use filter instead of must when you don't need scoring (better performance)
  • should clauses only affect scoring when combined with must or filter
  • mustNot is useful for excluding irrelevant content by tags or categories
  • Compound queries can be nested for complex boolean logic
  • Keep queries simple when possible for better performance
  • should clauses can include scoring boosts to influence relevance
  • Use minimumShouldMatch to require multiple optional conditions whilst maintaining flexibility

regexp operator

Description

The regexp operator finds articles where a field value matches a specified regular expression pattern. This provides powerful pattern matching capabilities for complex search requirements where simple text matching isn't sufficient.

Use this operator when:

  • Searching for values that match a specific pattern (e.g., article codes, IDs)
  • Finding values with specific character sequences or formats
  • Implementing advanced text matching with wildcards and alternation

Syntax

{
  "regexp": {
    "query": "<regex-pattern>",
    "path": "<field-to-search>",
    "score": <score-options>
  }
}

Options

FieldTypeDescriptionRequired?
querystringThe regular expression pattern to match.Yes
pathstringThe field to search. See Query path.Yes
scoreobjectScore modification options. See Score modification .No

Examples

Basic pattern matching

Find articles with tags matching a specific pattern:

{
  "query": {
    "regexp": {
      "query": "foot.*",
      "path": "tags"
    }
  }
}

This will match:

  • "football"
  • "footgolf"
  • "foot-racing"

Matches: "art-1234", "art-5678", etc.

Alternation

Find articles from multiple specific providers:

{
  "query": {
    "regexp": {
      "query": "(web|mobile|tablet)-.*",
      "path": "content.platforms"
    }
  }
}

Matches: "web-app", "mobile-native", "tablet-responsive", etc.

Character classes

Find tags with specific character patterns:

{
  "query": {
    "regexp": {
      "query": "[a-z]+-[0-9]+",
      "path": "tags"
    }
  }
}

Matches: "team-123", "player-456", etc.

Case-insensitive matching

Find slugs with optional prefixes:

{
  "query": {
    "regexp": {
      "query": "(article|story)-.*",
      "path": "slug"
    }
  }
}

Matches: "article-2024-final", "story-breaking-news", etc.

Search with score boost

Boost articles with specific ID patterns:

{
  "query": {
    "regexp": {
      "query": "breaking-.*",
      "path": "tags",
      "score": {
        "boost": {
          "value": 2.5
        }
      }
    }
  }
}

Nested field pattern matching

Find articles with linked IDs matching a pattern:

{
  "query": {
    "nested": {
      "path": "linkedIds",
      "query": {
        "regexp": {
          "query": "t[0-9]{3,}",
          "path": "linkedIds.sourceSystemId"
        }
      }
    }
  }
}

Matches nested IDs: "t123", "t4567", etc.

Complex pattern with score modification

Find articles with specific category slug patterns and apply a constant score:

{
  "query": {
    "nested": {
      "path": "categories",
      "query": {
        "regexp": {
          "query": "division-(one|two|three)",
          "path": "categories.id",
          "score": {
            "constant": {
              "value": 5.0
            }
          }
        }
      }
    }
  }
}

Leading Wildcards

Leading wildcards (patterns starting with .* or .+) are not supported because they require scanning every term in the index, which can cause severe performance degradation.

Instead of:

{
  "regexp": {
    "query": ".*-sport",
    "path": "tags"
  }
}

Consider alternative approaches:

  • If searching for a suffix, restructure your data to support prefix searches
  • Use compound queries with multiple specific patterns
  • Use the text operator for general word matching

Regex Syntax

The following regex syntax is supported:

PatternDescription
.Any character
*Zero or more of the preceding element
+One or more of the preceding element
?Zero or one of the preceding element
[abc]Character class (matches a, b, or c)
[a-z]Character range (matches a through z)
[^abc]Negated character class
(abc)Grouping
a||bAlternation (matches a or b)
{n}Exactly n occurrences
{n,}n or more occurrences
{n,m}Between n and m occurrences

Note: The search engine uses a specific regular expression syntax that may differ from other regex implementations. Some advanced features (like lookahead or lookbehind) are not supported.

exists operator

Description

The exists operator finds articles where a specified field exists and has a non-null value. This is useful for filtering content based on the presence or absence of specific fields.

Use this operator when:

  • Finding articles that have a specific field populated
  • Filtering out articles missing certain data
  • Checking if optional fields are present

Syntax

{
  "exists": {
    "path": "<field-to-check>",
    "score": <score-options>
  }
}

Options

FieldTypeDescriptionRequired?
pathstringThe field to check for existence. See Query Path.Yes
scoreobjectScore modification options. See Score.No

What counts as "exists"

A field is considered to exist if:

  • The field is present and has a value
  • The field is an empty string ""
  • Arrays containing at least one null or non-null value, such as [null, "foo"]

A field does NOT exist if:

  • The field is not present in the article
  • The field is explicitly set to null
  • The field is an empty array []

Examples

Check if field exists

Find articles that have hero media:

{
  "query": {
    "exists": {
      "path": "heroMedia"
    }
  }
}

Check nested fields

Find articles that have author information:

{
  "query": {
    "exists": {
      "path": "author.name"
    }
  }
}

Combined with mustNot to find missing fields

Find articles that don't have text content:

{
  "query": {
    "compound": {
      "mustNot": [
        {
          "exists": {
            "path": "content.text"
          }
        }
      ]
    }
  }
}

Find articles with tags

Find all articles that have at least one tag:

{
  "query": {
    "exists": {
      "path": "tags"
    }
  }
}

Find articles with linked data

Find articles that have linked player or team data:

{
  "query": {
    "exists": {
      "path": "linkedIds"
    }
  }
}

Multiple field or wildcard matching

Find articles with tags or any category field:

{
  "query": {
    "exists": {
      "path": ["tags", "categories.*"]
    }
  }
}

Notes

  • Empty strings are considered as "exists"
  • null values and empty arrays are considered as "does not exist"
  • Useful in combination with compound queries to include or exclude articles based on field presence
  • Can be used to validate data completeness across your content
  • Checking nested fields (e.g., author.name) only verifies that the nested path exists

nested operator

Description

The nested operator queries nested objects within articles. Nested objects are commonly used to store structured data like categories, or linked entities where each item has multiple related fields that need to be queried together.

Use this operator when:

  • Querying arrays of objects where field relationships matter
  • Working with structured nested data
  • Need to match multiple fields within the same nested object
  • Ensuring query conditions apply to a single nested object, not across the array

Syntax

{
  "nested": {
    "path": "<nested-field-path>",
    "query": <query-operator>,
    "score": <score-options>
  }
}

Options

FieldTypeDescriptionRequired?
pathstringThe path to the nested field.Yes
queryobjectThe query to apply to nested objects. Can be any Content Advanced Search operator.Yes
scoreobjectScore modification options. See Score modification.No

When to use nested vs regular queries

Regular arrays (use standard operators)

If you have a simple array of values:

{
  "tags": ["football", "premier-league", "breaking-news"]
}

Use: equal, in, etc.

Nested objects (use nested operator)

If you have an array of objects with multiple fields:

{
  "linkedIds": [
    { "sourceSystem": "OPTA_FOOTBALL_TEAM", "sourceSystemId": "t123" },
    { "sourceSystem": "OPTA_FOOTBALL_TEAM", "sourceSystemId": "t456" }
  ]
}

Use: nested operator

When is the nested wrapper required?

Required when:

  • Querying multiple fields within the same nested object where field relationships must be preserved
  • You need to ensure all conditions match on a single nested object rather than across the array

Optional (provides no benefit) when:

  • Querying only a single field within a nested object (e.g., just categories.id)
  • The query will work correctly either way

Example: Single field (nested optional):

These queries are functionally identical:

// Without nested wrapper
{
  "equal": {
    "path": "categories.id",
    "value": "5a3c1e8b-4f2d-4a1c-9b8e-1234567890ab"
  }
}

// With nested wrapper (no additional benefit)
{
  "nested": {
    "path": "categories",
    "query": {
      "equal": {
        "path": "categories.id",
        "value": "5a3c1e8b-4f2d-4a1c-9b8e-1234567890ab"
      }
    }
  }
}

Example: Multiple fields (nested required):

// Correct - ensures both conditions match same object
{
  "nested": {
    "path": "linkedIds",
    "query": {
      "compound": {
        "must": [
          {
            "equal": {
              "path": "linkedIds.sourceSystem",
              "value": "OPTA_FOOTBALL_TEAM"
            }
          },
          {
            "equal": {
              "path": "linkedIds.sourceSystemId",
              "value": "t123"
            }
          }
        ]
      }
    }
  }
}

Why nested matters

Given the article:

{
  "linkedIds": [
    { "sourceSystem": "OPTA_FOOTBALL_TEAM", "sourceSystemId": "123" },
    { "sourceSystem": "OPTA_FOOTBALL_PLAYER", "sourceSystemId": "456" }
  ]
}

Querying for articles with sourceSystem "OPTA_FOOTBALL_TEAM" and sourceSystemId "456":

Without nested (incorrect - would match above article):

{
  "compound": {
    "must": [
      {
        "equal": {
          "path": "linkedIds.sourceSystem",
          "value": "OPTA_FOOTBALL_TEAM"
        }
      },
      {
        "equal": {
          "path": "linkedIds.sourceSystemId",
          "value": "456"
        }
      }
    ]
  }
}

This matches because "OPTA_FOOTBALL_TEAM" exists AND "456" exists (even though they're in different objects).

With nested (correct - would not match):

{
  "nested": {
    "path": "linkedIds",
    "query": {
      "compound": {
        "must": [
          {
            "equal": {
              "path": "linkedIds.sourceSystem",
              "value": "OPTA_FOOTBALL_TEAM"
            }
          },
          {
            "equal": {
              "path": "linkedIds.sourceSystemId",
              "value": "456"
            }
          }
        ]
      }
    }
  }
}

This only matches if "OPTA_FOOTBALL_TEAM" AND "456" are in the SAME nested object.

Examples

Query nested linked entities

Find articles linked to a specific OPTA team:

{
  "query": {
    "nested": {
      "path": "linkedIds",
      "query": {
        "compound": {
          "filter": [
            {
              "equal": {
                "path": "linkedIds.sourceSystem",
                "value": "OPTA_FOOTBALL_TEAM"
              }
            },
            {
              "equal": {
                "path": "linkedIds.sourceSystemId",
                "value": "t123"
              }
            }
          ]
        }
      }
    }
  }
}

Without nested, this could incorrectly match an article where OPTA exists and ANY sourceSystemId matches.

Query specific source system

Find articles with any OPTA linked entities:

{
  "query": {
    "nested": {
      "path": "linkedIds",
      "query": {
        "equal": {
          "path": "linkedIds.sourceSystem",
          "value": "OPTA_FOOTBALL_TEAM"
        }
      }
    }
  }
}

Query multiple linked entities

Find articles linked to multiple specific OPTA entities:

{
  "query": {
    "nested": {
      "path": "linkedIds",
      "query": {
        "compound": {
          "filter": [
            {
              "equal": {
                "path": "linkedIds.sourceSystem",
                "value": "OPTA_FOOTBALL_TEAM"
              }
            },
            {
              "in": {
                "path": "linkedIds.sourceSystemId",
                "values": ["t123", "p456", "c789"]
              }
            }
          ]
        }
      }
    }
  }
}

Combine nested with other queries

Find recent articles linked to a specific OPTA entity:

{
  "query": {
    "compound": {
      "filter": [
        {
          "nested": {
            "path": "linkedIds",
            "query": {
              "compound": {
                "filter": [
                  {
                    "equal": {
                      "path": "linkedIds.sourceSystem",
                      "value": "OPTA_FOOTBALL_TEAM"
                    }
                  },
                  {
                    "equal": {
                      "path": "linkedIds.sourceSystemId",
                      "value": "t100"
                    }
                  }
                ]
              }
            }
          }
        },
        {
          "range": {
            "path": "publishDate",
            "gte": "now-7d"
          }
        }
      ]
    }
  }
}

Query content-level linked entities

Find articles with specific content by source system:

{
  "query": {
    "compound": {
      "filter": [
        {
          "equal": {
            "path": "content.sourceSystem",
            "value": "OPTA_FOOTBALL_TEAM"
          }
        },
        {
          "equal": {
            "path": "content.sourceSystemId",
            "value": "p12345"
          }
        }
      ]
    }
  }
}

With score boost

Boost articles linked to high-priority entities:

{
  "query": {
    "nested": {
      "path": "linkedIds",
      "query": {
        "compound": {
          "must": [
            {
              "equal": {
                "path": "linkedIds.sourceSystem",
                "value": "OPTA_FOOTBALL_TEAM"
              }
            },
            {
              "equal": {
                "path": "linkedIds.sourceSystemId",
                "value": "t100"
              }
            }
          ]
        }
      },
      "score": {
        "boost": {
          "value": 2.0
        }
      }
    }
  }
}

Scoring and multiple matches

When multiple nested objects match:

If an article has multiple nested objects that match the query, the article is returned once with a relevance score. The score is calculated based on the best-matching nested object, not a sum of all matches.

Example:

// Article with multiple linkedIds
{
  "linkedIds": [
    { "sourceSystem": "OPTA_FOOTBALL_TEAM", "sourceSystemId": "t123" },
    { "sourceSystem": "OPTA_FOOTBALL_TEAM", "sourceSystemId": "t456" }
  ]
}

// Query matching both objects
{
  "nested": {
    "path": "linkedIds",
    "query": {
      "equal": {
        "path": "linkedIds.sourceSystem",
        "value": "OPTA_FOOTBALL_TEAM"
      }
    }
  }
}

This article matches (both objects satisfy the condition) and is returned once. The score is based on the best-matching nested document.

Notes

  • The nested operator ensures all query conditions apply to the same nested object
  • Field paths within nested queries include the full path (e.g., linkedIds.sourceSystem)
  • Nested queries can contain any search operator (text, equals, range, compound, etc.)
  • Without using nested, array queries match across all array elements independently
  • Nested fields must be properly mapped as nested type in the backend index
  • When multiple nested objects match, the article is returned once with scoring based on the best match

language operator

The language operator filters articles by language with optional fallback languages. This is essential for multi-lingual websites where content may not be available in all languages.

When fallback languages are specified, the operator:

  1. Attempts to match the primary language first
  2. Falls back to the first fallback language if the primary is unavailable
  3. Continues through the fallback list in order
  4. If none match, behaviour is determined by the default flag:
    • If default is true, articles in their source language are returned
    • If default is false or omitted, no results are returned

Syntax

{
  "language": {
    "primary": "<language-code>",
    "fallback": ["<language-code>", ...],
    "default": <boolean>
  },
  "query": {
    // your search query
  }
}

Options

FieldTypeDescriptionRequired?
primarystringPrimary language code (ISO 639-1 two-letter or BCP 47 locale, e.g., en, es, fr-ca)Yes
fallbackarrayOrdered list of fallback language codes (two-letter or BCP 47 locale). Priority determined by list order.No
defaultbooleanWhen true, returns articles in their source language if primary and fallback don't match. When false, returns nothing if no match. Default: false.No

Supported language codes

  • Two-letter codes (ISO 639-1): en, fr, es, de, it, etc.
  • Locale codes (BCP 47): en-us, fr-ca, es-mx, pt-br, etc.

Both formats can be mixed in the same query.

Examples

Basic language filter (no fallback)

Return only English articles:

{
  "language": {
    "primary": "en"
  },
  "query": {
    "text": {
      "query": "championship",
      "path": "heroMedia.title"
    }
  }
}

Language with single fallback

Return English articles, falling back to Spanish if English unavailable:

{
  "language": {
    "primary": "en",
    "fallback": ["es"]
  },
  "query": {
    "text": {
      "query": "championship",
      "path": "heroMedia.title"
    }
  }
}

Language with multiple fallbacks

Return articles in order of preference: English → Spanish → German → French:

{
  "language": {
    "primary": "en",
    "fallback": ["es", "de", "fr"]
  },
  "query": {
    "text": {
      "query": "championship",
      "path": "heroMedia.title"
    }
  }
}

Result: Returns English translation if available. If not, tries Spanish, then German, then French. If none match, returns no results.

Using locale codes

Return Canadian French with fallback to France French, then English:

{
  "language": {
    "primary": "fr-ca",
    "fallback": ["fr-fr", "en"]
  },
  "query": {
    "text": {
      "query": "hockey",
      "path": "heroMedia.title"
    }
  }
}

Result: Returns Canadian French if available. If not, tries France French, then English. If none match, returns no results.

Language filter with complex search

Combine language filtering with compound search queries:

{
  "language": {
    "primary": "de",
    "fallback": ["en"]
  },
  "query": {
    "compound": {
      "must": [
        {
          "text": {
            "query": "football",
            "path": "heroMedia.title"
          }
        }
      ],
      "filter": [
        {
          "range": {
            "path": "publishDate",
            "gte": "now-7d"
          }
        }
      ]
    }
  },
  "page": 1,
  "size": 20
}

Language with sorting

{
  "language": {
    "primary": "fr",
    "fallback": ["en", "es"]
  },
  "query": {
    "equal": {
      "path": "pinned",
      "value": true
    }
  },
  "sort": [
    {
      "field": "publishDate",
      "order": "desc"
    }
  ]
}

Match all articles in a language

Return all articles in Italian with English fallback:

{
  "language": {
    "primary": "it",
    "fallback": ["en"]
  },
  "query": {}
}

Result: Returns Italian translation if available, otherwise English. If neither exists, returns no results.

Note: Empty query object matches all documents, filtered by language.

Fallback to source language

Return English articles, falling back to Spanish, then to source language if neither available:

{
  "language": {
    "primary": "en",
    "fallback": ["es"],
    "default": true
  },
  "query": {
    "text": {
      "query": "championship",
      "path": "heroMedia.title"
    }
  }
}

Result: Returns English if available. If not, tries Spanish. If neither exists, returns article in its original source language (could be German, French, etc.).

Strict language matching (no fallback to source)

Return only English or Spanish articles, exclude articles without these translations:

{
  "language": {
    "primary": "en",
    "fallback": ["es"],
    "default": false
  },
  "query": {
    "text": {
      "query": "championship",
      "path": "heroMedia.title"
    }
  }
}

Result: Returns English if available, otherwise Spanish. If neither exists, the article is excluded from results (even if it exists in other languages).

Notes

  • The language operator is mutually exclusive with direct language field filtering in your query. When using language, it is not recommended to also filter by the language field within your query.
  • Language codes are case-sensitive and should follow standard conventions.
  • Locale codes use hyphens (e.g., fr-ca, not fr_ca)
  • You can mix two-letter and four-character codes (e.g., "primary": "fr-ca", "fallback": ["fr", "en"])
  • The source language is the language in which the article was originally authored