# 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 ```json { "query": { // your query }, "explain": true } ``` ## Example ```json { "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. ```json { "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. ```json { "_explain": { "maxScore": 12.5 } } ``` **Example normalisation:** ```javascript 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. ```json { "_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. ```json { "_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 ```json { "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 ```json { "_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: ```json { "_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: ```json [ { "_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: ```json { "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** ```json // Before (high weight) "path": "content.*" // After (lower weight) "path": ["content.text", "content.altText"] ``` 2. **Remove fuzzy matching** ```json // Before { "text": { "query": "...", "fuzzy": true } } // After { "text": { "query": "..." } } ``` 3. **Flatten nested compounds** ```json // Before (deeply nested) { "compound": { "must": [{ "compound": { ... }}]}} // After (flattened) { "compound": { "must": [...], "filter": [...] }} ``` 4. **Use filter instead of must** ```json // 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