> ## Documentation Index
> Fetch the complete documentation index at: https://algolia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Optional filters

> What optional filters are and how you can use filter scoring to improve relevance.

export const Records = () => <Tooltip tip="A record is a searchable object in an Algolia index. Each record consists of named attributes." cta="Algolia records" href="/doc/guides/sending-and-managing-data/prepare-your-data#algolia-records">
    records
  </Tooltip>;

Unlike [filters](/doc/guides/managing-results/refine-results/filtering),
**optional filters don't remove <Records /> from your search results when your query doesn't match them**.
Instead, they divide your records into two sets: the results that match the optional filter and those that don't.

Use this to [boost results](/doc/guides/managing-results/rules/merchandising-and-promoting/how-to/how-to-promote-with-optional-filters)
you want to show before others or *bury* results you want to show last.

## Optional filters, replicas and sorting

If you're using a [sort-by attribute](/doc/guides/managing-results/refine-results/sorting/how-to/sort-by-attribute)
with standard replicas, you can't use optional filters to boost or bury records.
The sort-by attribute always takes precedence.

## Filter scoring

You can add extra nuance to your ranking by [specifying scores](/doc/guides/managing-results/refine-results/filtering/in-depth/filter-scoring) for different optional filters. For example, you want to display your matching products in the following order:

* Apple products first
* Samsung products second
* Huawei products last

In this scenario, products from any other brand appear after Apple and Samsung products and before Huawei products.
You can achieve this by using scored optional filters to boost Apple and Samsung products and negative filters to bury Huawei products.

To run the code examples on this page, [install the latest API client](/doc/libraries/sdk/install).

<CodeGroup>
  ```cs C# theme={"system"}
  var response = await client.SearchSingleIndexAsync<Hit>(
    "INDEX_NAME",
    new SearchParams(
      new SearchParamsObject
      {
        OptionalFilters = new OptionalFilters(
          new List<OptionalFilters>
          {
            new OptionalFilters("brand:Apple<score=3>"),
            new OptionalFilters("brand:Samsung<score=2>"),
            new OptionalFilters("brand:-Huawei"),
          }
        ),
      }
    )
  );
  ```

  ```dart Dart theme={"system"}
  final response = await client.searchSingleIndex(
    indexName: "INDEX_NAME",
    searchParams: SearchParamsObject(
      optionalFilters: [
        "brand:Apple<score=3>",
        "brand:Samsung<score=2>",
        "brand:-Huawei",
      ],
    ),
  );
  ```

  ```go Go theme={"system"}
  response, err := client.SearchSingleIndex(client.NewApiSearchSingleIndexRequest(
    "INDEX_NAME").WithSearchParams(search.SearchParamsObjectAsSearchParams(
    search.NewEmptySearchParamsObject().SetOptionalFilters(search.ArrayOfOptionalFiltersAsOptionalFilters(
      []search.OptionalFilters{
        *search.StringAsOptionalFilters("brand:Apple<score=3>"),
        *search.StringAsOptionalFilters("brand:Samsung<score=2>"),
        *search.StringAsOptionalFilters("brand:-Huawei"),
      },
    )))))
  if err != nil {
    // handle the eventual error
    panic(err)
  }
  ```

  ```java Java theme={"system"}
  SearchResponse response = client.searchSingleIndex(
    "INDEX_NAME",
    new SearchParamsObject().setOptionalFilters(
      OptionalFilters.of(
        Arrays.asList(
          OptionalFilters.of("brand:Apple<score=3>"),
          OptionalFilters.of("brand:Samsung<score=2>"),
          OptionalFilters.of("brand:-Huawei")
        )
      )
    ),
    Hit.class
  );
  ```

  ```js JavaScript theme={"system"}
  const response = await client.searchSingleIndex({
    indexName: 'indexName',
    searchParams: { optionalFilters: ['brand:Apple<score=3>', 'brand:Samsung<score=2>', 'brand:-Huawei'] },
  });
  ```

  ```kotlin Kotlin theme={"system"}
  var response =
    client.searchSingleIndex(
      indexName = "INDEX_NAME",
      searchParams =
        SearchParamsObject(
          optionalFilters =
            OptionalFilters.of(
              listOf(
                OptionalFilters.of("brand:Apple<score=3>"),
                OptionalFilters.of("brand:Samsung<score=2>"),
                OptionalFilters.of("brand:-Huawei"),
              )
            )
        ),
    )
  ```

  ```php PHP theme={"system"}
  $response = $client->searchSingleIndex(
      'INDEX_NAME',
      ['optionalFilters' => [
          'brand:Apple<score=3>',

          'brand:Samsung<score=2>',

          'brand:-Huawei',
      ],
      ],
  );
  ```

  ```python Python theme={"system"}
  response = client.search_single_index(
      index_name="INDEX_NAME",
      search_params={
          "optionalFilters": [
              "brand:Apple<score=3>",
              "brand:Samsung<score=2>",
              "brand:-Huawei",
          ],
      },
  )
  ```

  ```ruby Ruby theme={"system"}
  response = client.search_single_index(
    "INDEX_NAME",
    Algolia::Search::SearchParamsObject.new(
      optional_filters: ["brand:Apple<score=3>", "brand:Samsung<score=2>", "brand:-Huawei"]
    )
  )
  ```

  ```scala Scala theme={"system"}
  val response = Await.result(
    client.searchSingleIndex(
      indexName = "INDEX_NAME",
      searchParams = Some(
        SearchParamsObject(
          optionalFilters = Some(
            OptionalFilters(
              Seq(
                OptionalFilters("brand:Apple<score=3>"),
                OptionalFilters("brand:Samsung<score=2>"),
                OptionalFilters("brand:-Huawei")
              )
            )
          )
        )
      )
    ),
    Duration(100, "sec")
  )
  ```

  ```swift Swift theme={"system"}
  let response: SearchResponse<Hit> = try await client.searchSingleIndex(
      indexName: "INDEX_NAME",
      searchParams: SearchSearchParams
          .searchSearchParamsObject(SearchSearchParamsObject(optionalFilters: SearchOptionalFilters
                  .arrayOfSearchOptionalFilters([
                      SearchOptionalFilters.string("brand:Apple<score=3>"),
                      SearchOptionalFilters.string("brand:Samsung<score=2>"),
                      SearchOptionalFilters.string("brand:-Huawei"),
                  ])))
  )
  ```
</CodeGroup>

When you don't specify a score for an optional filter, it defaults to 1.

<Note>
  For performance reasons, **you shouldn't use filter scoring on searches that may return more than 100,000 results**.
</Note>

## Score calculation

The engine has three ways of calculating scores:

* Calculate the sum of optional filters
* Calculate the maximum score for optional filters
* A combination of both.

By default, the engine sums the score of each optional filter. The engine determines this score by taking the maximum score of each optional filter.

### Simple optional filters

Here's an example of how the engine calculates an [`optionalFilters`](/doc/api-reference/api-parameters/optionalFilters) score:

<CodeGroup>
  ```cs C# theme={"system"}
  var response = await client.SearchSingleIndexAsync<Hit>(
    "INDEX_NAME",
    new SearchParams(
      new SearchParamsObject
      {
        OptionalFilters = new OptionalFilters(
          new List<OptionalFilters>
          {
            new OptionalFilters("brand:Apple<score=2>"),
            new OptionalFilters("type:tablet"),
          }
        ),
      }
    )
  );
  ```

  ```dart Dart theme={"system"}
  final response = await client.searchSingleIndex(
    indexName: "INDEX_NAME",
    searchParams: SearchParamsObject(
      optionalFilters: [
        "brand:Apple<score=2>",
        "type:tablet",
      ],
    ),
  );
  ```

  ```go Go theme={"system"}
  response, err := client.SearchSingleIndex(client.NewApiSearchSingleIndexRequest(
    "INDEX_NAME").WithSearchParams(search.SearchParamsObjectAsSearchParams(
    search.NewEmptySearchParamsObject().SetOptionalFilters(search.ArrayOfOptionalFiltersAsOptionalFilters(
      []search.OptionalFilters{*search.StringAsOptionalFilters("brand:Apple<score=2>"), *search.StringAsOptionalFilters("type:tablet")})))))
  if err != nil {
    // handle the eventual error
    panic(err)
  }
  ```

  ```java Java theme={"system"}
  SearchResponse response = client.searchSingleIndex(
    "INDEX_NAME",
    new SearchParamsObject().setOptionalFilters(
      OptionalFilters.of(Arrays.asList(OptionalFilters.of("brand:Apple<score=2>"), OptionalFilters.of("type:tablet")))
    ),
    Hit.class
  );
  ```

  ```js JavaScript theme={"system"}
  const response = await client.searchSingleIndex({
    indexName: 'indexName',
    searchParams: { optionalFilters: ['brand:Apple<score=2>', 'type:tablet'] },
  });
  ```

  ```kotlin Kotlin theme={"system"}
  var response =
    client.searchSingleIndex(
      indexName = "INDEX_NAME",
      searchParams =
        SearchParamsObject(
          optionalFilters =
            OptionalFilters.of(
              listOf(
                OptionalFilters.of("brand:Apple<score=2>"),
                OptionalFilters.of("type:tablet"),
              )
            )
        ),
    )
  ```

  ```php PHP theme={"system"}
  $response = $client->searchSingleIndex(
      'INDEX_NAME',
      ['optionalFilters' => [
          'brand:Apple<score=2>',

          'type:tablet',
      ],
      ],
  );
  ```

  ```python Python theme={"system"}
  response = client.search_single_index(
      index_name="INDEX_NAME",
      search_params={
          "optionalFilters": [
              "brand:Apple<score=2>",
              "type:tablet",
          ],
      },
  )
  ```

  ```ruby Ruby theme={"system"}
  response = client.search_single_index(
    "INDEX_NAME",
    Algolia::Search::SearchParamsObject.new(optional_filters: ["brand:Apple<score=2>", "type:tablet"])
  )
  ```

  ```scala Scala theme={"system"}
  val response = Await.result(
    client.searchSingleIndex(
      indexName = "INDEX_NAME",
      searchParams = Some(
        SearchParamsObject(
          optionalFilters =
            Some(OptionalFilters(Seq(OptionalFilters("brand:Apple<score=2>"), OptionalFilters("type:tablet"))))
        )
      )
    ),
    Duration(100, "sec")
  )
  ```

  ```swift Swift theme={"system"}
  let response: SearchResponse<Hit> = try await client.searchSingleIndex(
      indexName: "INDEX_NAME",
      searchParams: SearchSearchParams
          .searchSearchParamsObject(SearchSearchParamsObject(optionalFilters: SearchOptionalFilters
                  .arrayOfSearchOptionalFilters([
                      SearchOptionalFilters.string("brand:Apple<score=2>"),
                      SearchOptionalFilters.string("type:tablet"),
                  ])))
  )
  ```
</CodeGroup>

The engine starts by calculating the maximum score for each optional filter you send.
In the preceding example, the engine first checks if a record's `brand` attribute has `Apple` as its value.
If this is the case, the record gets a score of 2 on this filter and 0 otherwise.
Finally, it checks the `type` attribute to see if it matches `tablet` and assigns a score of 1.

In the following records, the first record matches both the `brand:Apple<score=2>` and `type:tablet` optional filters, which score 2 and 1 (for a total score of 3).
However, the second record only matches the `brand:Apple<score=2>` optional filter, so the record gets a total score of 2.
Therefore, the iPad Air record shows up higher than the iPhone one.

```json JSON icon=braces theme={"system"}
[
  {
    "name": "iPad Air",
    "brand": "Apple",
    "type": "tablet"
  },
  {
    "name": "iPhone 17",
    "brand": "Apple",
    "type": "phone"
  }
]
```

With these optional filters, the engine ranks results as follows:

1. Apple tablets (score: 3)
2. Other Apple products (score: 2)
3. Tablets by other brands (score: 1)
4. Other products by other brands (score: 0)

### Complex optional filters

The engine calculates the maximum score for each element of your optional filters,
so you can combine filters to perform more complex score calculations.

For example, you want to promote red and blue products but avoid over-promoting products that are both red and blue.
To do so, use the following optional filters:

```json JSON icon=braces theme={"system"}
[["color:red<score=2>", "color:blue"], ["type:-jeans"]]
```

And the following record:

```json JSON icon=braces theme={"system"}
{
  "name": "Long-sleeved shirt",
  "type": "shirt",
  "color": ["red", "blue"]
}
```

1. The engine calculates the maximum score for the first optional filter. In this case, since it's an array (`["color:red<score=2>", "color:blue"]`), the engine checks all nested optional filters and uses the highest matching score.
2. Since the record contains red and blue colors, it scores 2 (match on red) and 1 (match on blue). The engine uses the highest matching score instead of a sum, meaning **this record gets a score of 2 on this filter (not 3)**.
3. The engine matches the second optional filter (`type:-jeans`). The record also matches (it's not "jeans"), so it scores 1. Adding this to the previous optional filter's score, the total score for this record is 3.

With these optional filters, the engine ranks results as follows:

1. Products that have "red" listed as color and aren't jeans (score: 3)
2. Jeans that have "red" listed as color (score: 2), and products that have "blue" listed as color but aren't jeans (score: 2)
3. Jeans that have "blue" listed as color (score: 1), and products that aren't jeans (score: 1)
4. Jeans in colors other than "red" and "blue" (score: 0)

<Info>
  When several records have a similar score,
  the engine ranks them according to the ranking formula, as with any other search.
</Info>

### `sumOrFiltersScores`

The previous example used filter scoring to boost products that are either red or blue and not jeans.
Records that are both red and blue are also prevented from getting a higher score than other records.

You can change the engine's behavior by adding the [`sumOrFiltersScores`](/doc/api-reference/api-parameters/sumOrFiltersScores) parameter to a search. This makes the engine add all scores together, regardless of the structure of your filters.

In the preceding example, this means that products that are both "red" and "blue" would show up even higher:

1. Products that have "red" and "blue" listed as color and aren't jeans (score: 4)
2. Products that have "red" and "blue" listed as color and are jeans (score: 3)
3. Jeans that have only "red" listed as color (score: 2), and products that have only "blue" listed as color and aren't jeans (score: 2)
4. Jeans that have only "blue" listed as color (score: 1), and products that aren't jeans (score: 1)
5. Jeans in colors other than "red" and "blue" (score: 0)

<Note>
  The [`sumOrFiltersScores`](/doc/api-reference/api-parameters/sumOrFiltersScores) parameter only changes the scoring for `OR` optional filters, which you create with a nested array of optional filters (`optionalFilters: [["attr:val"]]`). The engine always sums `AND` filter scores (`optionalFilters: ["attr:val1", "attr:val2"]`).
</Note>

## Performance

Optional filters are powerful but have some significant performance limitations. Here are a few recommendations:

* Define [`optionalFilters`](/doc/api-reference/api-parameters/optionalFilters) with the [`filterOnly`](/doc/api-reference/api-parameters/attributesForFaceting#param-filter-only) modifier, which tells the engine to treat the attribute as a filter instead of a facet. For example:

  <CodeGroup>
    ```cs C# theme={"system"}
    var response = await client.SetSettingsAsync(
      "INDEX_NAME",
      new IndexSettings { AttributesForFaceting = new List<string> { "filterOnly(brand)" } }
    );
    ```

    ```dart Dart theme={"system"}
    final response = await client.setSettings(
      indexName: "INDEX_NAME",
      indexSettings: IndexSettings(
        attributesForFaceting: [
          "filterOnly(brand)",
        ],
      ),
    );
    ```

    ```go Go theme={"system"}
    response, err := client.SetSettings(client.NewApiSetSettingsRequest(
      "INDEX_NAME",
      search.NewEmptyIndexSettings().SetAttributesForFaceting(
        []string{"filterOnly(brand)"})))
    if err != nil {
      // handle the eventual error
      panic(err)
    }
    ```

    ```java Java theme={"system"}
    UpdatedAtResponse response = client.setSettings(
      "INDEX_NAME",
      new IndexSettings().setAttributesForFaceting(Arrays.asList("filterOnly(brand)"))
    );
    ```

    ```js JavaScript theme={"system"}
    const response = await client.setSettings({
      indexName: 'INDEX_NAME',
      indexSettings: { attributesForFaceting: ['filterOnly(brand)'] },
    });
    ```

    ```kotlin Kotlin theme={"system"}
    var response =
      client.setSettings(
        indexName = "INDEX_NAME",
        indexSettings = IndexSettings(attributesForFaceting = listOf("filterOnly(brand)")),
      )
    ```

    ```php PHP theme={"system"}
    $response = $client->setSettings(
        'INDEX_NAME',
        ['attributesForFaceting' => [
            'filterOnly(brand)',
        ],
        ],
    );
    ```

    ```python Python theme={"system"}
    response = client.set_settings(
        index_name="INDEX_NAME",
        index_settings={
            "attributesForFaceting": [
                "filterOnly(brand)",
            ],
        },
    )
    ```

    ```ruby Ruby theme={"system"}
    response = client.set_settings(
      "INDEX_NAME",
      Algolia::Search::IndexSettings.new(attributes_for_faceting: ["filterOnly(brand)"])
    )
    ```

    ```scala Scala theme={"system"}
    val response = Await.result(
      client.setSettings(
        indexName = "INDEX_NAME",
        indexSettings = IndexSettings(
          attributesForFaceting = Some(Seq("filterOnly(brand)"))
        )
      ),
      Duration(100, "sec")
    )
    ```

    ```swift Swift theme={"system"}
    let response = try await client.setSettings(
        indexName: "INDEX_NAME",
        indexSettings: IndexSettings(attributesForFaceting: ["filterOnly(brand)"])
    )
    ```
  </CodeGroup>

* Prefer attributes with a low cardinality for your [`optionalFilters`](/doc/api-reference/api-parameters/optionalFilters). For example, it's preferable to use it with attributes like "brand" rather than "product\_name", as there are usually more unique product names than brands.

* Minimize the number of [`optionalFilters`](/doc/api-reference/api-parameters/optionalFilters) as much as possible.

* Aim for queries to return a smaller result set.

* Depending on your plan, it may be possible to apply [sharding and other custom settings](/doc/guides/scaling/scaling-to-larger-datasets) to optimize your implementation. For more information, contact the [support team](https://www.algolia.com/contactus/).

## See also

To learn more, see these blog articles:

* [Optional filters](https://www.algolia.com/blog/engineering/feature-spotlight-optional-filters/)
* [Optional filters and fuzzy matching](https://www.algolia.com/blog/engineering/discover-what-fuzzy-search-is-with-fuzzy-matching/)
