Content Advanced Search
Content Advanced Search is a simplified query language for searching articles and pages. It provides a clean, intuitive syntax that abstracts away backend complexities and focuses on common search use cases. This document and it's children explain how to use implement this, to meet the specific search requirements of your project.
Quick start
All Content Advanced Search queries follow this structure:
{
"_id": "<request-identifier>",
"language": {
"primary": "<language-code>",
"fallback": ["<language-code>", ...],
"default": <boolean>
},
"query": {
"<operator>": {
// operator-specific parameters
},
"score": <score-options>
},
"page": <number>,
"size": <number>,
"sort": [<sort-options>],
"explain": <boolean>
}Core fields:
| Field | Description | Required? | Default |
|---|---|---|---|
_id | Client-provided identifier echoed back in the response for request correlation (useful for multi-search) | No | None |
language | Language filter with optional fallback languages (top-level operator) | No | All languages |
query | The search query (operator is optional - if omitted, all documents are matched). | No | Match all |
page | Page number for pagination | No | 1 |
size | Results per page | No | 20 |
sort | Array of sort criteria | No | Relevance score |
explain | Enable explain mode for diagnostic information | No | false |
Core concepts
Complete reference of all searchable fields, their data types, and supported operators. Includes top-level fields, hero media, author, content, categories, linked IDs, and sponsors.
Learn how to specify which fields to search using the path parameter. Supports single fields, nested fields, multiple fields, and wildcards.
Example:
{
"path": "heroMedia.title"
}Understand the supported data types (text, keyword, number, boolean, date, null) and their value formats. Includes date math syntax for relative date queries like now-30d.
Example:
{
"range": {
"path": "publishDate",
"gte": "now-30d"
}
}Query operators
Operators define the type of search or filter to apply to your query.
Perform full-text search on articles using tokenisation and analysis. Ideal for natural language queries and word-based searches.
Example:
{
"query": {
"text": {
"query": "cup final",
"path": ["heroMedia.title", "content.text"]
}
}
}Find articles and pages where words appear in an exact sequence. Unlike text, the word order must match.
Example:
{
"query": {
"phrase": {
"query": "Team A FC",
"path": "heroMedia.title"
}
}
}Find articles and pages where a field exactly matches a given value. Supports Booleans, keywords, numbers, dates, and null values.
Example:
{
"query": {
"equal": {
"path": "categories.id",
"value": "5a3c1e8b-4f2d-4a1c-9b8e-1234567890ab"
}
}
}Match articles and pages where a field contains any value from a specified list.
Example:
{
"query": {
"in": {
"path": "tags",
"values": ["breaking-news", "exclusive", "featured"]
}
}
}Find articles where a field value falls within a specified range. Works with numbers, dates, and keyword fields.
Example:
{
"query": {
"range": {
"path": "publishDate",
"gte": "now-30d"
}
}
}Match articles where a field value starts with a specified prefix. Perfect for autocomplete functionality.
Example:
{
"query": {
"prefix": {
"query": "break",
"path": "tags"
}
}
}Write complex queries in a compact string format with field-specific searches, Boolean operators (AND, OR, NOT), and wildcards.
Example:
{
"query": {
"queryString": {
"query": "heroMedia.title:championship AND publishDate:[now-7d TO now]",
"defaultPath": "content.text"
}
}
}Combine multiple query clauses using Boolean logic with must, should, mustNot, and filter.
Example:
{
"query": {
"compound": {
"must": [
{
"text": {
"query": "team a",
"path": "heroMedia.title"
}
}
],
"filter": [
{
"range": {
"path": "publishDate",
"gte": "now-7d"
}
}
]
}
}
}Find articles and pages where a specified field exists and has a non-null value.
Example:
{
"query": {
"exists": {
"path": "heroMedia"
}
}
}Query nested objects within articles and pages, ensuring conditions apply to the same nested object.
Example:
{
"query": {
"nested": {
"path": "linkedIds",
"query": {
"compound": {
"must": [
{
"equal": {
"path": "linkedIds.sourceSystem",
"value": "OPTA_FOOTBALL_TEAM"
}
},
{
"equal": {
"path": "linkedIds.sourceSystemId",
"value": "t100"
}
}
]
}
}
}
}
}Top-level operators
These operators must be used at the same level as the query field and cannot be nested within other operators.
Filter articles and pages by language with optional fallback languages. Essential for multi-lingual websites where content may not be available in all languages.
Example:
{
"language": {
"primary": "en",
"fallback": ["es", "de"]
},
"query": {
"text": {
"query": "championship",
"path": "heroMedia.title"
}
}
}Response Format
Each search in the stream returns a response following the standard API response structure:
{
"_id": "<request-identifier>",
"status": "success" | "error",
"errors": [
{
"code": "<error-code>",
"message": "<error-message>",
"path": "<json-path>"
}
],
"data": [...],
"metadata": {
"createdAt": "<timestamp>",
"pageItems": <number>,
"totalItems": <number>,
"totalPages": <number>,
"pageNumber": <number>,
"pageSize": <number>,
"sort": "<sort-description>"
}
}Example: Successful response:
{
"_id": "featured",
"status": "success",
"data": [
{"id": "article-1", "heroMedia": {"title": "Championship Final"}, ...},
{"id": "article-2", "heroMedia": {"title": "Cup Results"}, ...}
],
"metadata": {
"createdAt": "2025-01-15T10:30:00Z",
"pageItems": 2,
"totalItems": 15,
"totalPages": 5,
"pageNumber": 1,
"pageSize": 3,
"sort": "publishDate:desc"
}
}Error handling
When errors occur, the response includes an errors array. Each error object contains:
| Field | Description |
|---|---|
code | Machine-readable error code |
message | Human-readable error description |
path | JSON path to the problematic field in the request |
Multiple errors can be returned for a single request, allowing the query to execute while reporting issues encountered.
Example: Error response:
{
"_id": "my-search",
"status": "error",
"errors": [
{
"code": "unknown_field",
"message": "The field \"title\" is not recognised.",
"path": "query.text.path"
}
],
"metadata": {
"createdAt": "2025-01-15T10:30:00Z"
}
}Example: Query executed with warnings:
Queries may still execute and return results alongside errors:
{
"_id": "search-with-issues",
"status": "success",
"data": [...],
"errors": [
{
"code": "unknown_field",
"message": "The field \"summary\" is not recognised.",
"path": "query.compound.should[1].text.path"
}
],
"metadata": {
"createdAt": "2025-01-15T10:30:00Z",
"pageItems": 5,
"totalItems": 42,
"totalPages": 9,
"pageNumber": 1,
"pageSize": 5,
"sort": "publishDate:desc"
}
}Example: Query with invalid field:
If you query a field that doesn't exist:
{
"_id": "search-1",
"query": { "text": { "query": "championship", "path": "title" } }
}Response:
{
"_id": "search-1",
"status": "error",
"errors": [
{
"code": "unknown_field",
"message": "The field \"title\" is not recognised.",
"path": "query.text.path"
}
],
"metadata": {
"createdAt": "2025-01-15T10:30:00Z"
}
}Common error codes:
| Code | Description |
|---|---|
unknown_field | The referenced field path does not exist in the schema |
ambiguous_field | The field path resolves to more than one distinct field alias (e.g. in sort) |
invalid_value | The value is structurally invalid for its context (e.g. an unrecognised metric) |
value_type_mismatch | The supplied value type is incompatible with the target field type |
path_required | The operator was given an empty path list |
no_compatible_fields | All resolved field paths were filtered out; none are compatible with the operator |
multiple_fields_not_supported | The operator requires a single field path but the path resolved to more than one |
field_type_incompatible | The resolved field type cannot be used with the requested operator |
invalid_expression | A score function expression was nil or empty |
unsupported_score_metric | The score reference names a metric that is not supported |
gauss_decay_invalid | The gauss decay value is outside the valid (0, 1) range |
values_required | The operator requires at least one value but was given an empty list |
query_required | The operator requires a non-empty query string but was given an empty one |
minimum_should_match_negative | The compound operator minimumShouldMatch value is negative |
validation_error | Generic request validation failure |
internal_error | Unexpected internal error |
Notes:
- Each JSON object in the stream is processed independently with its own results
- Each search must include its own
languagefilter if needed - Each search can have its own
query,page,size, andsortoptions - Results are returned as separate JSON objects in the response stream (one per search)
- Responses may arrive in a different order than requests due to parallel processing - use the
_idfield to correlate - Invalid JSON objects in the stream are skipped, and processing continues with the next object
- For combining queries with Boolean logic in a single result set, use the
compoundoperator instead
Search options
Control how articles are scored and ranked in search results using:
| Option | Description |
|---|---|
boost | Multiply the score by a factor |
constant | Replace the score with a fixed value |
function | Apply complex scoring functions |
Example:
{
"query": {
"equal": {
"path": "tags",
"value": "breaking-news",
"score": {
"boost": 2.0
}
}
}
}Control result pagination and sort order as part of your Content Advanced Search query structure.
Pagination:
- Use
pageandsizefields in your query - Default: 20 results per page
Sorting:
- Sort by one or more fields (dates, Booleans, keywords, numeric)
- Default: relevance score (descending) when search query is present
Example:
{
"query": {
"text": {
"query": "championship",
"path": "heroMedia.title"
}
},
"page": 2,
"size": 20,
"sort": [
{
"field": "publishDate",
"order": "desc"
}
]
}Enable explain mode to receive diagnostic information about query execution, including scoring details, logical structure, and performance cost estimates.
Enable with: "explain": true in query structure
Explain fields:
| Field | Description |
|---|---|
_score | Final relevance score for each result |
_explain.maxScore | Maximum possible score (for normalisation) |
_explain.logic | Boolean logic structure of your query |
_explain.cost | Performance cost breakdown |
Example:
{
"_id": "explain-example",
"data": [
{
"_score": 10.5,
"id": "article-123"
...
}
],
"_explain": {
"maxScore": 12.5,
"logic": "heroMedia.title:championship AND publishDate:[now-30d TO now]",
"cost": {
"total": 165,
"breakdown": {
"operators": 60,
"fields": 40,
"boosts": 10,
"functions": 0
}
}
}
}Use explain mode to:
- Optimise query performance
- Tune scoring and boosts
- Validate query logic
- Troubleshoot unexpected results
Additional resources
Learn recommended patterns for efficient, secure, and maintainable queries.
Optimise query performance with tips on field selection, query structure, and monitoring techniques.
Common Use Cases
Find pinned articles
{
"query": {
"equal": {
"path": "pinned",
"value": true
}
}
}Search by category
{
"query": {
"nested": {
"path": "categories",
"query": {
"equal": {
"path": "categories.id",
"value": "5a3c1e8b-4f2d-4a1c-9b8e-1234567890ab"
}
}
}
}
}Multi-lingual content with fallback
Search for articles and pages in English with Spanish fallback:
{
"language": {
"primary": "en",
"fallback": ["es"]
},
"query": {
"text": {
"query": "championship",
"path": "heroMedia.title"
}
}
}Boost recent articles
Match all articles and pages and apply time-based scoring:
{
"query": {
"score": {
"function": {
"gauss": {
"field": "publishDate",
"origin": "now",
"scale": "30d",
"decay": 0.5
}
}
}
}
}Note: When no operator is specified, all documents are matched and the score function is applied.
Updated about 2 hours ago
