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

# Filtering

> Composable filter conditions with AND, OR, and NOT logic

Filters scope a [search](/ai/rag/search) to rows that match conditions on your `metadata` fields (plus `id` and `text`). A filter is either a single condition or a logical combination of conditions, and the whole structure is typed against your `metadata`.

```typescript theme={null}
const results = await table.search("machine learning", {
  filter: { field: "category", op: "=", value: "AI" },
});
```

## Conditions

A single condition names a `field`, an operator `op`, and (for most operators) a `value`. The `field` is one of your `metadata` keys, or `id` / `text` — names resolve to the underlying columns automatically.

```typescript theme={null}
{ field: "category", op: "=", value: "AI" }
```

### Operators

| `op`                        | `value`                | Meaning                            |
| --------------------------- | ---------------------- | ---------------------------------- |
| `=`                         | `string \| number`     | Equals.                            |
| `>` `>=` `<` `<=`           | `string \| number`     | Comparison.                        |
| `IN`                        | `(string \| number)[]` | Value is in the list.              |
| `LIKE`                      | `string`               | SQL `LIKE` pattern (`%` wildcard). |
| `NOT LIKE`                  | `string`               | Negated `LIKE` pattern.            |
| `IS NULL` / `IS NOT NULL`   | —                      | Field is (not) null.               |
| `IS TRUE` / `IS NOT TRUE`   | —                      | Boolean truthiness.                |
| `IS FALSE` / `IS NOT FALSE` | —                      | Boolean falsiness.                 |

The `IS …` operators take no `value`.

```typescript theme={null}
// Comparison
{ field: "score", op: ">=", value: 0.8 }

// Membership
{ field: "category", op: "IN", value: ["AI", "Database"] }

// Pattern
{ field: "title", op: "LIKE", value: "Intro%" }

// Null and boolean checks
{ field: "summary", op: "IS NOT NULL" }
{ field: "published", op: "IS TRUE" }
```

## Combining conditions

Wrap conditions in `AND`, `OR`, or `NOT` to build compound filters. These nest freely, so you can express any boolean logic.

### AND / OR

```typescript theme={null}
{
  AND: [
    { field: "category", op: "=", value: "AI" },
    { field: "score", op: ">=", value: 0.7 },
  ],
}
```

```typescript theme={null}
{
  OR: [
    { field: "category", op: "=", value: "AI" },
    { field: "category", op: "=", value: "Database" },
  ],
}
```

### NOT

`NOT` negates a single nested filter (which may itself be a condition or a group).

```typescript theme={null}
{ NOT: { field: "title", op: "LIKE", value: "%draft%" } }
```

### Nesting

```typescript theme={null}
const results = await table.search("machine learning", {
  filter: {
    AND: [
      { field: "category", op: "=", value: "AI" },
      {
        OR: [
          { field: "score", op: ">=", value: 0.9 },
          { field: "title", op: "LIKE", value: "Intro%" },
        ],
      },
      { NOT: { field: "title", op: "LIKE", value: "%draft%" } },
    ],
  },
});
```

## Types

```typescript theme={null}
type FilterField<T> = keyof T["metadata"] | "id" | "text";

type FilterCondition<T> =
  | { field: FilterField<T>; op: ">" | ">=" | "<" | "<=" | "="; value: string | number }
  | { field: FilterField<T>; op: "IN"; value: (string | number)[] }
  | { field: FilterField<T>; op: "LIKE" | "NOT LIKE"; value: string }
  | { field: FilterField<T>; op: "IS NULL" | "IS NOT NULL" }
  | { field: FilterField<T>; op: "IS TRUE" | "IS NOT TRUE" | "IS FALSE" | "IS NOT FALSE" };

type Filter<T> =
  | FilterCondition<T>
  | { AND: Filter<T>[] }
  | { OR: Filter<T>[] }
  | { NOT: Filter<T> };
```

Because filters are typed against `T["metadata"]`, an unknown field name or a `value` of the wrong type is a compile-time error.

<Tip>
  Filters compile to SQL predicates over the `metadata.<field>` columns. For fields you filter on frequently, add a scalar index with [`createIndex()`](/ai/rag/vector-table#createindex) so the engine can skip non-matching rows instead of scanning them.
</Tip>
