Explain mode

Explain mode provides detailed diagnostic information about query execution, including scoring details, logical query structure, and performance weight estimation. This is essential for understanding query behaviour, optimising performance, and troubleshooting unexpected results. This document explains how to use it.

Enabling explain mode

Explain mode is enabled by including the explain field in your query structure.

Syntax

{
  "query": {
    // your query
  },
  "explain": true
}

Example

{
  "query": {
    "text": {
      "query": "championship",
      "path": "heroMedia.title"
    }
  },
  "explain": true
}

Explain fields

When explain mode is enabled, additional fields prefixed with underscore (_) provide diagnostic information.

Score information

_score

The final relevance score for each result after all boosts and functions have been applied.

{
  "results": [
    {
      "_score": 8.745,
      "id": "article-123"
      ...
    }
  ]
}

Use cases:

  • Compare relative relevance between results
  • Understand impact of boost values
  • Validate scoring behaviour
  • Normalise scores for display

maxScore

The maximum possible score for the query, useful for normalising scores to a 0-1 or 0-100 scale.

{
  "_explain": {
    "maxScore": 12.5
  }
}

Example normalisation:

const normalisedScore = (result._score / _explain.maxScore) * 100;
// Result: 69.96% relevance

Query logic

_explain.logic

A human-readable representation of how the query was logically processed, showing the boolean structure using standard operators.

{
  "_explain": {
    "logic": "(heroMedia.title:championship OR content.text:championship) AND categories.id:5a3c1e8b AND NOT tags:app-only"
  }
}

Logic operators

  • AND: All conditions must match (from must and filter clauses)
  • OR: Any condition can match (from should clauses)
  • NOT: Condition must not match (from mustNot clauses)

Example patterns

Simple text search:

"heroMedia.title:football"

Multi-field search:

"(heroMedia.title:cup OR content.text:cup)"

Compound with must:

"heroMedia.title:championship AND pinned:true"

With exclusions:

"tags:featured AND NOT tags:app-only"

Complex compound:

"(tags:A OR tags:B) AND publishDate:[now-7d TO now]"

Nested queries:

"linkedIds:(sourceSystem:OPTA AND sourceSystemId:t123)"

Query weight

_explain.weight

Provides an estimated weight score indicating query complexity and resource usage. Higher weights generally correlate with longer execution times.

Note: Weight values represent query complexity and help identify patterns that may impact performance. The calculation methodology is still being refined, so use weights for relative comparison between queries rather than as absolute benchmarks.

{
  "_explain": {
    "weight": {
      "total": 245,
      "breakdown": {
        "operators": 80,
        "fields": 65,
        "boosts": 40,
        "functions": 60
      }
    }
  }
}

Weight breakdown components

Operators weight

Weight based on the search operators used:

OperatorWeight (points)
text (with fuzzy)50
text (no fuzzy)30
phrase35
nested40
compound20 + (10 × clauses)
range15
equal10
in10
prefix10
exists10

Example: A compound query with must, filter, and should (3 clauses) = 20 + (10 × 3) = 50 points

Fields weight

Weight based on fields searched and their types:

Field typeWeight (points)
Analysed text20 per field
Keyword10 per field
Nested25 per field
Wildcard matches30 × number of matched fields

Example: Searching heroMedia.title and content.text (both analysed) = 20 + 20 = 40 points

Boosts weight

Weight of score modifications:

ModificationWeight (points)
Each boost10
Each function30
Gaussian decay20

Functions weight

Weight of complex score functions:

FunctionWeight (points)
multiply15
add15
Nested functionsCumulative

Weight interpretation

Weight rangePerformance levelRecommendation
0-100Low weightFast query, optimal performance
101-300Moderate weightAcceptable performance for most use cases
301-500High weightMay impact performance, consider optimisation
500+Very high weightRequires optimisation, may cause timeouts

Complete example

Request

{
  "query": {
    "compound": {
      "must": [
        {
          "text": {
            "query": "championship final",
            "path": ["heroMedia.title", "content.text"]
          }
        }
      ],
      "filter": [
        {
          "range": {
            "path": "publishDate",
            "gte": "now-30d"
          }
        }
      ],
      "should": [
        {
          "equal": {
            "path": "pinned",
            "value": true,
            "score": {
              "boost": {
                "value": 2.0
              }
            }
          }
        }
      ]
    }
  },
  "explain": true
}

Explain output

{
  "_explain": {
    "maxScore": 15.2,
    "logic": "(heroMedia.title:championship final OR content.text:championship final) AND publishDate:[now-30d TO now]",
    "weight": {
      "total": 165,
      "breakdown": {
        "operators": 60,
        "fields": 40,
        "boosts": 10,
        "functions": 0
      }
    }
  },
  "total": 2,
  "page": 1,
  "size": 10
}

Weight analysis

Operators (60 points):

  • compound with 3 clauses: 20 + 30 = 50
  • text: 30 (no fuzzy)
  • range: 15
  • equal: 10
  • Total: Would be higher, but some overlap

Fields (40 points):

  • heroMedia.title (analysed): 20
  • content.text (analysed): 20
  • Total: 40

Boosts (10 points):

  • One boost on equal: 10

Functions (0 points):

  • No score functions used

Total: 165 points - Moderate weight, acceptable performance

Use cases

Performance optimisation

Use weight breakdown to identify expensive query components:

{
  "_explain": {
    "weight": {
      "total": 450,
      "breakdown": {
        "operators": 150,
        "fields": 240,
        "boosts": 30,
        "functions": 30
      }
    }
  }
}

Analysis: High field weight (240) suggests too many fields being searched. Consider reducing wildcard usage or limiting fields.

Score tuning

Compare _score values to validate boost effectiveness:

[
  { "_score": 15.2, "id": "1", "pinned": true },
  { "_score": 8.6, "id": "2", "pinned": false }
]

The pinned article scores approximately 77% higher, confirming the boost is working as intended.

Query validation

Verify logical structure matches intent:

{
  "logic": "(heroMedia.title:championship OR content.text:championship) AND publishDate:[now-30d TO now]"
}

Confirms that:

Interpretation:

  • Text search is across both heroMedia.title and content.text
  • Date filter is applied correctly
  • Should clause (pinned) is optional

Troubleshooting

Examine the logic string to debug unexpected results:

Expected: Match articles with tag A AND tag B
Actual logic: "(tags:A OR tags:B)"
Issue: Using should instead of must - both tags not required

Best practices

Development vs production

  • Enable in development: Use explain mode when building and testing queries
  • Enable in staging: Validate query behaviour before production deployment
  • Disable in production: Explain mode adds overhead

Weight monitoring

Target: Keep total weight under 300 for optimal performance

If weight is high (300+), consider:

  1. Reduce fields searched

    // Before (high weight)
    "path": "content.*"
    
    // After (lower weight)
    "path": ["content.text", "content.altText"]
  2. Remove fuzzy matching

    // Before
    { "text": { "query": "...", "fuzzy": true } }
    
    // After
    { "text": { "query": "..." } }
  3. Flatten nested compounds

    // Before (deeply nested)
    { "compound": { "must": [{ "compound": { ... }}]}}
    
    // After (flattened)
    { "compound": { "must": [...], "filter": [...] }}
  4. Use filter instead of must

    // Before (contributes to scoring weight)
    "must": [{ "range": { ... }}]
    
    // After (no scoring overhead)
    "filter": [{ "range": { ... }}]
  5. Limit wildcard usage

    • Avoid * wildcards when possible
    • Prefer explicit field lists

Limitations

  • Weight calculation methodology is subject to change as Content Advanced Search is tuned for performance
  • Logic string representation is simplified and may not show all internal optimisations
  • Explain mode is not recommended in production due to overhead
  • Weight values should be used for relative comparison rather than absolute benchmarking