Skip to content

Query Builder - Usage guide

All available operators

General operators

Operator Description
= Equal to
!= Not equal to
< Less than
<= Less than or equal
> Greater than
>= Greater than or equal
between Between two values
like SQL LIKE pattern matching
ilike SQL LIKE pattern matching (case insensitive)
not like SQL NOT LIKE pattern matching
not ilike SQL NOT LIKE pattern matching (case insensitive)
starts with Starts with text
ends with Ends with text
not starts with Does not start with text
not ends with Does not end with text
contains Contains text (case sensitive)
icontains Contains text (case insensitive)
not contains Does not contain text (case sensitive)
not icontains Does not contain text (case insensitive)
match Full-text search match
in Value is in list
not in Value is not in list
is true Boolean field is true
is false Boolean field is false
is empty Field is null or empty
is not empty Field has a value
any The relation carries a record matching the sub-filter
not any The relation carries no record matching the sub-filter

Vector field operators

Operator Description
l1 distance L1/Manhattan distance
l1 distance < L1 distance less than
l1 distance <= L1 distance less than or equal
l1 distance > L1 distance greater than
l1 distance >= L1 distance greater than or equal
l2 distance L2/Euclidean distance
l2 distance < L2 distance less than
l2 distance <= L2 distance less than or equal
l2 distance > L2 distance greater than
l2 distance >= L2 distance greater than or equal
cosine distance Cosine distance
cosine distance < Cosine distance less than
cosine distance <= Cosine distance less than or equal
cosine distance > Cosine distance greater than
cosine distance >= Cosine distance greater than or equal
inner product Inner product
inner product < Inner product less than
inner product <= Inner product less than or equal
inner product > Inner product greater than
inner product >= Inner product greater than or equal

Spatial field operators

Operator Description
spatial distance Calculate spatial distance between points
spatial distance < Spatial distance less than
spatial distance <= Spatial distance less than or equal
spatial distance > Spatial distance greater than
spatial distance >= Spatial distance greater than or equal
spatial within distance Point within specified distance
spatial contains Geometry contains another geometry
spatial within Geometry within another geometry
spatial intersects Geometries intersect
spatial equals Geometries are equal
spatial disjoint Geometries are disjoint
spatial touches Geometries touch
spatial crosses Geometries cross
spatial overlaps Geometries overlap

Filter syntax

# Simple rule: ["field", "operator", "value"]
GET /api/products/
X-Filter: ["name", "=", "Laptop"]

# AND conditions: ["&", [rule1, rule2, ...]]
GET /api/products/
X-Filter: ["&", [["price", ">=", 100], ["is_active", "is true"]]]

# OR conditions: ["|", [rule1, rule2, ...]]
GET /api/products/
X-Filter: ["|", [["category", "=", "electronics"], ["category", "=", "books"]]]

Available operators

CharField, TextField

Operators: =, !=, like, ilike, not like, not ilike, starts with, ends with, not starts with, not ends with, contains, icontains, not contains, not icontains, match, in, not in, is empty, is not empty

Example:

GET /api/products/
X-Filter: ["description", "icontains", "smartphone"]

IntegerField, FloatField, DecimalField

Operators: =, !=, <, <=, >, >=, between, in, not in, is empty, is not empty

Example:

GET /api/products/
X-Filter: ["price", "between", [100, 500]]

BooleanField

Operators: is true, is false

Example:

GET /api/products/
X-Filter: ["is_featured", "is true"]

DateField, DateTimeField

Operators: =, !=, <, <=, >, >=, between, is empty, is not empty

Example:

GET /api/orders/
X-Filter: ["created_at", ">=", "2024-01-01T00:00:00Z"]

ChoiceField

Operators: =, !=, in, not in, is empty, is not empty

Example:

GET /api/orders/
X-Filter: ["status", "in", ["pending", "processing"]]

ForeignKey, OneToOne

Operators: =, !=, in, not in, is empty, is not empty, any, not any

Example:

GET /api/products/
X-Filter: ["category.name", "=", "Electronics"]

ManyToMany, OneToMany

Operators: in, not in, is empty, is not empty, any, not any

Example:

GET /api/products/
X-Filter: ["tags", "not in", [1, 2, 3]]

UUIDField

Operators: =, !=, like, ilike, not like, not ilike, starts with, ends with, not starts with, not ends with, contains, icontains, not contains, not icontains, match, in, not in, is empty, is not empty

Example:

GET /api/users/
X-Filter: ["uuid", "starts with", "550e8400"]

VectorField

Operators: l1 distance, l1 distance <, l1 distance <=, l1 distance >, l1 distance >=, l2 distance, l2 distance <, l2 distance <=, l2 distance >, l2 distance >=, cosine distance, cosine distance <, cosine distance <=, cosine distance >, cosine distance >=, inner product, inner product <, inner product <=, inner product >, inner product >=

Example:

GET /api/embeddings/
X-Filter: ["vector", "cosine distance <", [0.1, [0.2, 0.3, 0.4]]]

PointField

Operators: spatial distance, spatial distance <, spatial distance <=, spatial distance >, spatial distance >=, spatial within distance, spatial contains, spatial within, spatial intersects, spatial equals, spatial disjoint, spatial touches, spatial crosses, spatial overlaps, is empty, is not empty

Examples:

# Find stores within 5km of a point (longitude, latitude)
GET /api/stores/
X-Filter: ["location", "spatial within distance", [[2.3522, 48.8566], 5000]]

# Find stores at exact location
GET /api/stores/
X-Filter: ["location", "spatial equals", [2.3522, 48.8566]]

# Find stores within a specific distance
GET /api/stores/
X-Filter: ["location", "spatial distance <", [[2.3522, 48.8566], 10000]]

Note: Distance operators require a tuple with the reference point and distance value: [[longitude, latitude], distance_in_meters]

Filtering on a relation

Two rules over the same relation are independent, each satisfied by a related record of its own: A & B is the intersection of A and B, whatever rules sit next to them. This product matches as soon as it carries a red tag and an urgent tag, the same one or not:

GET /api/products/
X-Filter: ["&", [["tags.color", "=", "red"], ["tags.name", "=", "urgent"]]]

any is the other reading: a single related record satisfying a whole sub-filter. Its value is a filter of the model the relation reaches, written against that model's own fields:

GET /api/products/
X-Filter: ["tags", "any", ["&", [["color", "=", "red"], ["name", "=", "urgent"]]]]

not any negates it, and a record carrying nothing related satisfies it:

GET /api/orders/
X-Filter: ["lines", "not any", ["state", "=", "out_of_stock"]]

An empty sub-filter asks only what the relation carries, so ["tags", "any", null] reads as is not empty and ["tags", "not any", null] as is empty (the short forms read better, and stay).

Named alone, a relation that fans out compares the key of its related records: ["tags", "in", [1, 2]] is ["tags.id", "in", [1, 2]], and so are not in, is empty and is not empty.

A reverse relation is filtered on by the name its foreign key declares with related_name. The name Edgy makes up when none is declared (<model>s_set) is refused, in the API as in the code, as it stays out of the API schemas.

A path crosses at most filter_max_depth relations (8 by default), those leading to the sub-filter it sits in counted: ["category", "any", ["products.tags.name", "=", "red"]] crosses three. A relation and its inverse come back to where they started, so nothing else ends a path that walks them in turn; one going deeper is refused with a 422.

A path only crosses the rows the request could read through the model it reaches: in a workspace, that workspace and the rows keyed to it by their workspace, then what the global filters of the model let through. An any block, an or branch and an ordering on a relation that fans out cross the same rows. global_query, and a rule allowed to reach the excluded fields (the global filters themselves), answer for the system and cross everything.

Complex filtering

# Nested AND/OR
GET /api/products/
X-Filter: ["&", [
  ["is_active", "is true"],
  ["|", [["price", "<", 50], ["category.slug", "=", "sale"]]]
]]

Error responses

  • 422: Invalid field, operator, or JSON format
  • 422: A path crossing more relations than filter_max_depth
  • 422: Type conversion errors

Programmatic usage

For advanced use cases where you need to build filters programmatically in Python code, check out the advanced guide.

Advanced Usage

Back to Overview