Data types and value formats

Content Advanced Search supports various data types for querying articles and pages. This document outlines each supported type and the value formats you can use.

Text

Full-text analysed string fields that are tokenised and suitable for search queries.

Fields: heroMedia.title, heroMedia.summary, content.text, content.altText, etc.

Value format:

"value": "some text"

Supported operators:

OperatorDescription
textFull-text search with tokenisation, stemming, and relevance scoring
phraseExact phrase matching (words in order)
prefixMatch values starting with a prefix
existsCheck if field exists

Characteristics:

  • Tokenised and analysed during indexing
  • Case-insensitive by default
  • Supports fuzzy matching and relevance scoring
  • Best for human-readable content and search

Examples:

{
  "text": {
    "query": "tournament final",
    "path": "heroMedia.title"
  }
}
{
  "phrase": {
    "query": "championship victory",
    "path": "content.text"
  }
}

Keyword

Exact-value string fields stored as-is without analysis. Used for filtering and sorting.

Fields: slug, language, tags, categories.id, linkedIds.sourceSystem, linkedIds.sourceSystemId, etc.

Value format:

"value": "exact-value"

Supported operators:

OperatorDescription
equalExact value matching (case-sensitive)
inMatch any value from a list
rangeLexicographic (alphabetical) range queries
prefixMatch values starting with a prefix
existsCheck if field exists

Characteristics:

  • Stored exactly as provided
  • Case-sensitive matching
  • No tokenisation or analysis
  • Best for structured data, IDs, slugs, tags, and enum values

Examples:

{
  "equal": {
    "path": "slug",
    "value": "article-slug"
  }
}
{
  "in": {
    "path": "tags",
    "values": ["breaking-news", "exclusive", "featured"]
  }
}
{
  "equal": {
    "path": "linkedIds.sourceSystem",
    "value": "OPTA_FOOTBALL_TEAM"
  }
}

Number

Numeric values including integers and floating-point numbers.

Fields: readTimeMinutes, etc.

Value format:

"value": 123

or

"value": 45.67

Supported operators:

OperatorDescription
equalExact value matching
inMatch any value from a list
rangeNumeric range queries with gte, gt, lte, lt
existsCheck if field exists

Examples:

{
  "range": {
    "path": "readTimeMinutes",
    "gte": 5,
    "lte": 15
  }
}

Boolean

True or false values.

Fields: pinned, singlePage, custom boolean fields, etc.

Value format:

"value": true

or

"value": false

Supported operators:

OperatorDescription
equalExact value matching
existsCheck if field exists

Examples:

{
  "equal": {
    "path": "pinned",
    "value": true
  }
}
{
  "equal": {
    "path": "singlePage",
    "value": false
  }
}

Date

Date and datetime values following ISO 8601 format with nanosecond precision.

Fields: publishDate, lastModified.date

Standard format: ISO 8601

YYYY-MM-DD
YYYY-MM-DDTHH:mm:ss
YYYY-MM-DDTHH:mm:ssZ
YYYY-MM-DDTHH:mm:ss.sssZ
YYYY-MM-DDTHH:mm:ss.sssssssssZ (nanosecond precision)

Date math expressions: Relative dates anchored to now or an absolute date

now
now-30d
now-1M
now+2h
now-1y+6M/d
2026-01-01||+30d
2026-01-01T00:00:00Z||-1M+5d/d

Supported operators:

OperatorDescription
equalExact date matching
inMatch any date from a list
rangeDate range queries with gte, gt, lte, lt
existsCheck if field exists

Date Math Syntax

Two anchor forms are supported:

AnchorDescription
nowResolved to the current time at query-execution time
<date>|| An absolute ISO 8601 date/datetime resolved at parse time

The anchor can be followed by zero or more +<value><unit> / -<value><unit> segments and an optional /d rounding suffix:

now[+-<value><unit>]*[/d]
<date>||[+-<value><unit>]*[/d]

Multiple segments are applied left to right: now-1y+6M-3d/d

/d rounding: Truncates the result to midnight (00:00:00) after all offsets have been applied.

Supported units:

UnitDescription
yYears
MMonths (capital M)
wWeeks
dDays
hHours
mMinutes (lowercase m)
sSeconds

Examples:

Absolute dates:

{
  "range": {
    "path": "publishDate",
    "gte": "2024-01-01",
    "lte": "2024-12-31"
  }
}

Relative dates:

{
  "range": {
    "path": "publishDate",
    "gte": "now-30d"
  }
}

Complex date math (multiple segments + rounding):

{
  "range": {
    "path": "publishDate",
    "gte": "now-1M",
    "lte": "now/d"
  }
}

Anchor date with math:

{
  "range": {
    "path": "publishDate",
    "gte": "2026-01-01||+30d",
    "lte": "2026-01-01||+60d/d"
  }
}

Common Date Patterns

PatternDescription
nowCurrent date and time
now/dToday at midnight (UTC)
now-1d1 day ago
now-7d7 days ago (last week)
now-30d30 days ago (last month)
now-1M1 month ago
now-3M3 months ago (last quarter)
now-1y1 year ago
now-24h24 hours ago
now+1d1 day from now (future)
now+1w1 week from now
now-1y+6M/d6 months ago from a year ago, at midnight
2026-01-01||Absolute anchor with no offset
2026-01-01||+30d30 days after 1 Jan 2026

Timezone Handling

  • The now keyword is evaluated using UTC timezone
  • Dates without timezone information (e.g., 2024-01-01, 2024-01-01T10:30:00) are interpreted as UTC
  • To specify a different timezone, include the timezone offset in the date string:
    • 2024-01-01T00:00:00-05:00 (EST)
    • 2024-01-01T00:00:00+01:00 (CET)
  • For consistent behaviour across all queries, it's recommended to always include timezone information or use UTC

Timezone examples:

Find articles published after midnight EST (5 hours behind UTC):

{
  "range": {
    "path": "publishDate",
    "gte": "2024-11-10T00:00:00-05:00"
  }
}

Compare with UTC equivalent (5 hours later):

{
  "range": {
    "path": "publishDate",
    "gte": "2024-11-10T05:00:00Z"
  }
}

Both queries are equivalent - the first midnight EST equals 5 AM UTC.

Use cases:

Last 7 days:

{
  "range": {
    "path": "publishDate",
    "gte": "now-7d",
    "lte": "now"
  }
}

This month:

{
  "range": {
    "path": "publishDate",
    "gte": "now-1M"
  }
}

Between specific dates:

{
  "range": {
    "path": "publishDate",
    "gte": "2024-06-01T00:00:00Z",
    "lt": "2024-07-01T00:00:00Z"
  }
}

Null

Represents the absence of a value.

Value format:

"value": null

Supported operators:

OperatorDescription
equalMatch null values
existsCheck if field exists (note: null values are considered non-existent)

Example:

{
  "equal": {
    "path": "heroMedia.summary",
    "value": null
  }
}

Note: To check if a field exists regardless of value, use the exists operator instead.

Nested

Complex objects containing multiple fields. Nested types preserve the relationship between fields within each object in an array.

Fields: categories, linkedIds, sponsors, author.socialHandles, content.linkedIds

Structure: Nested fields are arrays of objects where each object has its own set of properties:

"categories": [
  {
    "id": "5a3c1e8b-4f2d-4a1c-9b8e-1234567890ab",
    "text": "Division One"
  }
]
"linkedIds": [
  {
    "sourceSystem": "OPTA_FOOTBALL_TEAM",
    "sourceSystemId": "t123"
  }
]

Supported operators:

OperatorDescription
nestedQuery nested objects while preserving field relationships
Individual operatorsUse within nested context (text, equals, range, etc.)

When to use the nested operator:

ScenarioRequired?Reason
Multiple fields in same nested objectRequiredEnsures all conditions match on a single object
Single field in nested objectOptionale.g., just categories.id

Basic example:

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

For complete details, examples, and best practices, see the nested operator documentation.

Type Coercion and Validation

Advanced Search performs basic type validation:

Field TypeAccepted Values
Booleantrue or false
NumericNumbers (integers or floats)
DateISO 8601 format or date math expressions
KeywordAny text value (exact match)
TextAny text value (analysed)

Type mismatches will result in no matches or query errors.

Examples by type

Working with Booleans

Find pinned articles that are not pages:

{
  "query": {
    "compound": {
      "filter": [
        {
          "equal": {
            "path": "pinned",
            "value": true
          }
        },
        {
          "equal": {
            "path": "singlePage",
            "value": false
          }
        }
      ]
    }
  }
}

Working with numbers

Find articles with read time between 5 and 10 minutes:

{
  "query": {
    "range": {
      "path": "readTimeMinutes",
      "gte": 5,
      "lte": 10
    }
  }
}

Working with dates

Find articles published in the last 24 hours:

{
  "query": {
    "range": {
      "path": "publishDate",
      "gte": "now-24h"
    }
  }
}

Find articles published in a specific month:

{
  "query": {
    "range": {
      "path": "publishDate",
      "gte": "2024-10-01T00:00:00Z",
      "lt": "2024-11-01T00:00:00Z"
    }
  }
}

Working with text fields

Full-text search across title and content body:

{
  "query": {
    "text": {
      "query": "tournament championship",
      "path": ["heroMedia.title", "content.text"]
    }
  }
}

Exact phrase match:

{
  "query": {
    "phrase": {
      "query": "breaking news",
      "path": "heroMedia.title"
    }
  }
}

Working with keyword fields

Find articles with specific tags:

{
  "query": {
    "in": {
      "path": "tags",
      "values": ["breaking-news", "exclusive", "featured"]
    }
  }
}

Exact match on slug:

{
  "query": {
    "equal": {
      "path": "slug",
      "value": "article-slug-2024"
    }
  }
}

Prefix search on slug:

{
  "query": {
    "prefix": {
      "query": "article-",
      "path": "slug"
    }
  }
}

Working with null

Find articles where a field is not set:

{
  "query": {
    "equal": {
      "path": "heroMedia.summary",
      "value": null
    }
  }
}

Working with nested objects

Find articles in a specific category:

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

Find articles linked to a specific entity from a specific source:

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

Find articles with specific categories:

{
  "query": {
    "equal": {
      "path": "categories.id",
      "value": "a1b2c3d4-e5f6-4a5b-8c7d-9e0f1a2b3c4d"
    }
  }
}

Best practices

  1. Use text fields for search, keyword fields for filtering - Use text operator on analysed text fields, equal on keyword fields
  2. Know your field types - Text fields are case-insensitive and analysed, keyword fields are exact and case-sensitive
  3. Use nested queries for related fields - Always use nested operator when querying multiple fields within the same nested object
  4. Use date math for relative queries - Prefer now-7d over hardcoded dates for "last 7 days" queries
  5. Be explicit with date ranges - Use both gte and lte when you need a bounded range
  6. Match field types - Ensure your query values match the field's data type
  7. Use ISO 8601 for absolute dates - Include timezone information when possible
  8. Consider timezone handling - now uses the server's current time