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
_scoreThe 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
maxScoreThe 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% relevanceQuery logic
_explain.logic
_explain.logicA 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
mustandfilterclauses) - OR: Any condition can match (from
shouldclauses) - NOT: Condition must not match (from
mustNotclauses)
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
_explain.weightProvides 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:
| Operator | Weight (points) |
|---|---|
text (with fuzzy) | 50 |
text (no fuzzy) | 30 |
phrase | 35 |
nested | 40 |
compound | 20 + (10 × clauses) |
range | 15 |
equal | 10 |
in | 10 |
prefix | 10 |
exists | 10 |
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 type | Weight (points) |
|---|---|
| Analysed text | 20 per field |
| Keyword | 10 per field |
| Nested | 25 per field |
| Wildcard matches | 30 × number of matched fields |
Example: Searching heroMedia.title and content.text (both analysed) = 20 + 20 = 40 points
Boosts weight
Weight of score modifications:
| Modification | Weight (points) |
|---|---|
Each boost | 10 |
Each function | 30 |
| Gaussian decay | 20 |
Functions weight
Weight of complex score functions:
| Function | Weight (points) |
|---|---|
multiply | 15 |
add | 15 |
| Nested functions | Cumulative |
Weight interpretation
| Weight range | Performance level | Recommendation |
|---|---|---|
| 0-100 | Low weight | Fast query, optimal performance |
| 101-300 | Moderate weight | Acceptable performance for most use cases |
| 301-500 | High weight | May impact performance, consider optimisation |
| 500+ | Very high weight | Requires 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):
compoundwith 3 clauses: 20 + 30 = 50text: 30 (no fuzzy)range: 15equal: 10- Total: Would be higher, but some overlap
Fields (40 points):
heroMedia.title(analysed): 20content.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.titleandcontent.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:
-
Reduce fields searched
// Before (high weight) "path": "content.*" // After (lower weight) "path": ["content.text", "content.altText"] -
Remove fuzzy matching
// Before { "text": { "query": "...", "fuzzy": true } } // After { "text": { "query": "..." } } -
Flatten nested compounds
// Before (deeply nested) { "compound": { "must": [{ "compound": { ... }}]}} // After (flattened) { "compound": { "must": [...], "filter": [...] }} -
Use filter instead of must
// Before (contributes to scoring weight) "must": [{ "range": { ... }}] // After (no scoring overhead) "filter": [{ "range": { ... }}] -
Limit wildcard usage
- Avoid
*wildcards when possible - Prefer explicit field lists
- Avoid
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
Updated 13 days ago
