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

# Filtering language

> How to construct complex and nested filter expressions for the Search and Browse APIs, including comparison operators, boolean groups, and field selectors.

## Filter basics

The `filter_group` API parameter refines results by selecting only the products that match the given expressions and running the query on those products. The `filter_group` expects a **filter expression**.

```json theme={null}
{
  "filter_group": {
    "conditional": "AND",
    "expressions": [
      {
        "property": "product_type",
        "operator": "eq",
        "values": ["Sweater"]
      },
      {
        "property": "options.color",
        "operator": "eq",
        "values": ["RED"]
      }
    ]
  }
}
```

## Expressions and operators

Expressions are the core of the filter language.

```json theme={null}
{
  "property": "product_type",
  "operator": "eq",
  "values": ["Sweaters"]
}
```

* `property` is the attribute of the field you want to filter on.
* `operator` is the logical operator to apply (see full list below).
* `values` is an array of values the `operator` compares against in the `property`.

### Supported operators

| Operator           | Description                                                                                                    |
| ------------------ | -------------------------------------------------------------------------------------------------------------- |
| `eq`               | Equal to a single value                                                                                        |
| `neq`              | Not equal to a single value                                                                                    |
| `in`               | Matches any value in the array                                                                                 |
| `notIn`            | Does not match any value in the array                                                                          |
| `gt`               | Greater than                                                                                                   |
| `gte`              | Greater than or equal to                                                                                       |
| `lt`               | Less than                                                                                                      |
| `lte`              | Less than or equal to                                                                                          |
| `between`          | Within a range (pass exactly two values)                                                                       |
| `notBetween`       | Outside a range (pass exactly two values)                                                                      |
| `contains`         | String contains the given value (case-insensitive)                                                             |
| `doesNotContain`   | String does not contain the given value (case-insensitive)                                                     |
| `beginsWith`       | String starts with the given value (case-insensitive)                                                          |
| `endsWith`         | String ends with the given value (case-insensitive)                                                            |
| `doesNotBeginWith` | String does not start with the given value (case-insensitive)                                                  |
| `doesNotEndWith`   | String does not end with the given value (case-insensitive)                                                    |
| `null`             | Value is null or does not exist                                                                                |
| `notNull`          | Value is not null (exists)                                                                                     |
| `after`            | Date/timestamp value is strictly after the given bound (pass exactly one value)                                |
| `before`           | Date/timestamp value is strictly before the given bound (pass exactly one value)                               |
| `inLast`           | Date/timestamp value falls within the last *N* days, evaluated at query time (pass a single integer day count) |
| `geoRadius`        | Geo attribute lies within a radius of an origin (see [Geospatial filtering](/engine/geospatial-filtering))     |
| `geoBoundingBox`   | Geo attribute lies inside a bounding box                                                                       |
| `geoPolygon`       | Geo attribute matches a GeoJSON polygon                                                                        |

### Examples of basic conditions

A condition to find products of type `Sweaters`:

```json theme={null}
{
  "property": "product_type",
  "operator": "eq",
  "values": ["Sweaters"]
}
```

A condition to find products created after 1 January 2020 (UNIX Epoch time):

```json theme={null}
{
  "property": "created_at",
  "operator": "gt",
  "values": [1577836800]
}
```

A condition to exclude gift card products:

```json theme={null}
{
  "property": "is_gift_card",
  "operator": "eq",
  "values": [false]
}
```

A condition to find bundle products (products with variants that require components):

```json theme={null}
{
  "property": "has_variants_that_require_components",
  "operator": "eq",
  "values": [true]
}
```

A condition to find products within a price range (e.g., between $10 and $50):

```json theme={null}
{
  "property": "variants.price",
  "operator": "between",
  "values": [10, 50]
}
```

A condition to find products with variants available at specific location IDs (e.g., locations 1001 and 1002):

<Warning>
  The `variants.in_stock_location_ids` field is no longer filterable. It is now a **sort-only** field used for location-based product demotion in sort orders. See [Sorting](/platform/sorting) for details on using this field in priority rules.
</Warning>

```json theme={null}
{
  "property": "variants.in_stock_location_ids",
  "operator": "in",
  "values": [1001, 1002]
}
```

A condition to find products whose title contains "organic":

```json theme={null}
{
  "property": "title",
  "operator": "contains",
  "values": ["organic"]
}
```

A condition to exclude products without a vendor (null check):

```json theme={null}
{
  "property": "vendor",
  "operator": "notNull",
  "values": []
}
```

A condition to find products NOT in a price range (e.g., outside $10 to $50):

```json theme={null}
{
  "property": "variants.price",
  "operator": "notBetween",
  "values": [10, 50]
}
```

A condition to find products published in the last 30 days. `inLast` is evaluated against `NOW()` at query time, so the window stays evergreen — you can save it once on a sort order or merchandising rule and it tracks the calendar without re-saving:

```json theme={null}
{
  "property": "published_at",
  "operator": "inLast",
  "values": [30]
}
```

A condition to find products created strictly after a fixed date:

```json theme={null}
{
  "property": "created_at",
  "operator": "after",
  "values": ["2025-01-01"]
}
```

A condition to find products created strictly before a fixed date:

```json theme={null}
{
  "property": "created_at",
  "operator": "before",
  "values": ["2025-01-01"]
}
```

<Note>
  `inLast`, `after`, and `before` are designed for attributes whose value type is **date** or **timestamp**. Bounds are cast to the column's type by Postgres, so both `"2025-01-01"` and `"2025-01-01T00:00:00Z"` are accepted.
</Note>

## Complex filter expressions

You can build advanced filter groups by combining expressions using `AND` and `OR`.

### Example of complex expression

To include both `Accessories` and products from `SUPREME` published after 1 January 2020:

```json theme={null}
{
  "conditional": "OR",
  "expressions": [
    {
      "conditional": "AND",
      "expressions": [
        {
          "property": "product_type",
          "operator": "eq",
          "values": ["Accessories"]
        },
        {
          "property": "vendor",
          "operator": "eq",
          "values": ["Supreme"]
        }
      ]
    },
    {
      "conditional": "AND",
      "expressions": [
        {
          "property": "published_at",
          "operator": "gt",
          "values": ["1577836800"]
        }
      ]
    }
  ]
}
```

## Best practices

When crafting filter expressions:

* Test complex expressions in a controlled environment before deployment.
* String operators (`contains`, `doesNotContain`, `beginsWith`, `endsWith`, `doesNotBeginWith`, `doesNotEndWith`) are case-insensitive. For example, filtering where title `contains` "organic" also matches "Organic" and "ORGANIC".
* Use the `between` operator instead of combining `gte` and `lte` for range queries.
* Nested filter groups support up to two levels of depth.

## Troubleshooting

If your filter expression isn't returning the expected results:

* Verify that operator names use the exact camelCase format shown above (e.g., `notIn`, not `not_in`).
* Confirm that attribute names and values match exactly with the data.
* Ensure the `values` field is always an array, even for single-value operators like `eq`.
* Check that the `conditional` field uses uppercase `AND` or `OR`.
