Best practices

This document outlines a set of best practice recommendations to follow when implementing Content Advanced Search.

Best practices

1. Be explicit with field names

Use specific field names instead of wildcards when you know the exact fields needed. This improves query performance and reduces the risk of hitting field limits.

Example:

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

2. Simplify queries

Break complex queries into multiple simpler queries when possible. This improves maintainability and makes troubleshooting easier.

3. Use filter for non-scoring conditions

Use the filter clause instead of must for conditions that don't need to affect relevance scoring. Filters are cached and faster than scoring queries.

Less efficient - Using must for Boolean filters:

{
  "compound": {
    "must": [
      {
        "text": {
          "query": "championship",
          "path": "heroMedia.title"
        }
      },
      {
        "equal": {
          "path": "singlePage",
          "value": true
        }
      },
      {
        "equal": {
          "path": "categories.id",
          "value": "a1b2c3d4-e5f6-4a5b-8c7d-9e0f1a2b3c4d"
        }
      }
    ]
  }
}

More efficient - Using filter for Boolean conditions:

{
  "compound": {
    "must": [
      {
        "text": {
          "query": "championship",
          "path": "heroMedia.title"
        }
      }
    ],
    "filter": [
      {
        "equal": {
          "path": "singlePage",
          "value": true
        }
      },
      {
        "equal": {
          "path": "categories.id",
          "value": "a1b2c3d4-e5f6-4a5b-8c7d-9e0f1a2b3c4d"
        }
      }
    ]
  }
}

4. Isolate user-submitted queries

Keep user-submitted queries as separate, isolated conditions within a compound operator. Never merge user input with your internal filtering logic within the same condition. This applies to all operators including queryString - never concatenate internal filters with user queries in a single query string.

Dangerous - Concatenating internal filters with user query:

{
  "queryString": {
    "query": "categories.id:a1b2c3d4 AND " + userQuery
  }
}

If a user enters categories.id:a1b2c3d4 OR categories.id:b2c3d4e5, they can override your filters.

Safe - Keep user query isolated:

{
  "compound": {
    "must": [
      {
        "queryString": {
          "query": userQuery,
          "defaultPath": "content.text"
        }
      }
    ],
    "filter": [
      {
        "equal": {
          "path": "categories.id",
          "value": "a1b2c3d4-e5f6-4a5b-8c7d-9e0f1a2b3c4d"
        }
      }
    ]
  }
}

This isolation prevents user input from interacting with your internal query logic and makes it easier to validate and sanitise user queries separately.