- Build filters using the
FieldandFilterBuilderAPI. - Use every filter type: equality, range, datetime, geo, text, array, and null checks.
- Apply boolean logic:
must,should,must_not, andmin_should. - Compose operators (
&,|, and~) on conditions and builders. - Use standalone conditions:
has_id,has_vector,is_empty,is_null, andnested. - Integrate filters with
points.search,points.query,points.count,points.delete, andpoints.set_payload.
Environment setup
Setup: Create a collection and ingest sample data
Step 1: Configure and initialize
Step 2: Create the collection
Expected output
get_or_create call is idempotent — safe to run on every application startup without creating duplicate collections.
Step 3: Ingest sample products
Expected output
Helper functions
The following helpers are used throughout this tutorial:Equality filters
Exact match with Field.eq
Field.eq matches a string, integer, or boolean value exactly.
Expected output
Field.eq also works on boolean fields:
Expected output
Field.eq works on boolean fields without any special syntax — True and False are matched directly. The out-of-stock product (id=1, TrailMaster discontinued=True) is excluded.
Full-text match with Field.text
Field.text performs token-based matching against text-indexed fields. It requires a TextIndexParams payload index on the field and matches documents that contain the given token anywhere in the indexed content.
Expected output
tags array but not in its text field, so it is excluded by this filter.
IN list with Field.any_of
Field.any_of matches any value in a provided list, equivalent to a SQL IN clause.
Expected output
any_of is equivalent to writing multiple should conditions on the same field but in a single, readable call.
NOT IN list with Field.except_of
Field.except_of excludes any point whose field value matches an entry in the provided list, equivalent to a SQL NOT IN clause.
Expected output
except_of is the complement of any_of: it rejects any document whose field value appears in the list.
Numeric range filters
Single-bound range
Expected output
gte, lt, and lte:
Expected output
Closed range with Field.between
Field.between filters within both a lower and upper bound in a single call. Set inclusive=True for a closed range that includes the endpoints.
Expected output
between is inclusive on both ends.
Flexible range with Field.range
Field.range lets you combine any mix of inclusive and exclusive bounds. At least one bound is required.
Expected output
lt, not lte). The remaining three products — UrbanStep (4.2), TrailRunner (4.3), SpeedRunner (4.7) — all fall within the half-open interval [4.0, 4.8).
Datetime filters
Datetime filters work the same way as numeric range filters but operate on timestamp fields.Single-bound datetime filter
Expected output
Datetime range with datetime_between
Expected output
Geo filters
Geo filters restrict results to points within a geographic area. The payload field must store{"lat": ..., "lon": ...} objects.
Radius with geo_radius
geo_radius finds all points within a given radius (in metres) of a center coordinate. Use it when you want results within a circular area around a specific location.
The following code restricts the search to products whose location field falls within 500 km of New York City. Running it returns only the single product stored with NYC coordinates:
Expected output
geo_radius uses the Haversine formula over the location payload field.
Bounding box with geo_bounding_box
geo_bounding_box finds all points within a rectangular geographic region defined by top-left and bottom-right corners. It is faster than a polygon check and useful when the region maps naturally to a lat/lon rectangle.
The following code restricts the search to products located within the Continental US bounding box. Running it returns four products and excludes the Oxford stored in London:
Expected output
Polygon with geo_polygon
geo_polygon finds all points within an arbitrary polygon defined by an ordered list of (lat, lon) vertices. The polygon closes automatically, so the first point does not need to be repeated.
The following code restricts the search to products located within a polygon covering the Northeast US. Running it returns only the single product stored with New York City coordinates:
Expected output
geo_polygon checks whether the stored coordinate falls within the closed polygon defined by the exterior ring. Points outside the polygon — including those with no location payload — are excluded before scoring.
Array cardinality filter
Filter by number of values with values_count
values_count keeps only points whose array field has a number of elements that satisfies the given bounds. Use it to filter by the size of an array such as tags or reviews.
Expected output
gte and lte to the same value:
Expected output
values_count supports lt, gt, gte, and lte — at least one bound is required.
Null and empty checks
Check for null with is_null
is_null matches points where a payload field is present but explicitly set to null. Use it to find records that have a field key but no value assigned to it.
Expected output
is_null matches points where the field is explicitly set to null in the payload. Products where clearance_note is absent entirely are not matched — use is_empty for that case. The distinction matters when distinguishing “field set to null” from “field not present”.
Check for empty or missing with is_empty
is_empty matches points where an array or string field contains no elements, or where the field key is absent entirely.
Expected output
reviews array ([]). is_empty matches fields that are either absent from the payload, set to null, or set to an empty array or string. It is broader than is_null and useful for finding records with missing or unpopulated fields.
Point-level conditions
Restrict search to specific IDs with has_id
has_id narrows a search to a known subset of point IDs. Use it to apply a pre-filtered allowlist — for example, IDs returned by a business rules layer before the vector search runs.
Expected output
has_id restricts the search to exactly the listed point IDs regardless of payload content. This is useful for re-ranking or rescoring a known set of candidates — for example, re-searching a shortlist from a previous query.
Check for a named vector with has_vector
has_vector restricts results to points that carry a particular named vector. Pass an empty string to target the default (unnamed) vector.
Expected output
has_vector is most useful in named-vector collections where some points may be missing a specific vector space — for example, filtering to only products that have an image embedding.
Nested filters
Thenested condition filters on fields within nested objects. Each item in an array of objects is evaluated independently — all conditions must be satisfied by the same object, not spread across different objects in the array.
Expected output
reviews array is empty.
Boolean logic
FilterBuilder supports four clause types that control how conditions combine.
| Method | Meaning | SQL equivalent |
|---|---|---|
.must() | All conditions must match | AND |
.should() | At least one condition should match | OR |
.must_not() | Exclude any points that match | NOT |
.min_should(conds, N) | At least N conditions match | N-of-M |
Require all conditions with must (AND)
must requires every condition in the clause to be satisfied. Points that fail any one condition are excluded. Use it when multiple constraints must all hold simultaneously.
The following code requires color == "blue", in_stock == True, and price < 150 to all be true. Running it returns only the SpeedRunner running shoe, which is the only product satisfying all three conditions:
Expected output
must applies AND logic — every condition must hold or the point is excluded.
Accept alternatives with should (OR)
should requires at least one condition in the clause to match. Use it when multiple alternative values are acceptable and any of several criteria is sufficient.
The following code accepts products where color is "blue" or "red". Running it returns three products and excludes the white and black products:
Expected output
should applies OR logic: a point is included if it satisfies at least one condition. The white and black products are excluded.
Exclude results with must_not (NOT)
must_not removes any point that matches the condition from the results. Use it to suppress categories or attribute values that should never appear in the output.
The following code requires the footwear category and then excludes both discontinued and formal products. Running it returns four in-catalogue, non-formal products:
Expected output
must_not excludes any point matching the condition. The discontinued TrailMaster (id=1) is excluded by the first condition; the ClassicFit formal shoe (id=3) is excluded by the second. The remaining three products — UrbanStep, SpeedRunner, TrailRunner — pass both exclusion checks.
Match at least N conditions with min_should
min_should qualifies a point when it satisfies at least min_count of the supplied conditions. Use it when partial matches are acceptable and you want to control the minimum number of criteria that must be met.
The following code defines four independent conditions and requires at least three of them to be satisfied. Running it returns the two products that satisfy three or more of the four conditions:
Expected output
id=4 matches 4/4: blue, TrailMaster, price 129.99 < 170, rating 4.5 >= 4.5 (but brand is SpeedRunner, not TrailMaster).
Combining clauses
Expected output
must, should, and must_not in a single FilterBuilder call lets you express complex business rules as a single filter object.
Operator composition
Python operators let you combine conditions and builders without calling clause methods directly.Condition operators
Expected output
& operator combines two Field conditions with AND logic. SpeedRunner is the only blue product priced below 159.99 and is excluded by the price condition.
Expected output
| operator combines conditions with OR logic. SpeedRunner matches both conditions (blue and running category). TrailMaster matches blue. TrailRunner matches the running category. Products matching either condition are included.
Expected output
~ prefix operator negates a condition, equivalent to wrapping it in must_not. The discontinued TrailMaster (id=1, discontinued=True) is excluded. All four non-discontinued products pass.
FilterBuilder operators
Expected output
| operator on FilterBuilder objects creates a top-level should clause combining the two sub-filters.
Expected output
& directly on FilterBuilder instances is syntactic sugar for chaining .must() calls — the compiled filter is identical.
Expected output
~ to a FilterBuilder instance wraps its conditions in must_not. The result is identical to calling .must_not() explicitly. This operator form is useful when building filters programmatically where the negation decision is made separately from the condition definition.
Using filters with different endpoints
The sameFilterBuilder objects work across all points.* methods.
With points.search
Expected output
filter parameter on points.search is applied server-side before scoring, so only the two brand-matching products are ever scored.
With points.query
Expected output
points.query with query=vec behaves identically to points.search for single-vector queries but supports the full query DSL including prefetch, fusion, and order_by. The filter is applied at the same stage in both cases.
With points.count
Note:points.count()is not supported in VectorAI DB 1.0.0 and raisesUnimplementedError. Usepoints.scroll()with Pythonlen()as a workaround.
Expected output
points.count() with a filter is not implemented in VectorAI DB 1.0.0 (raises 501), so points.scroll(limit=1000) followed by a Python sum() is the supported alternative.
With points.delete
Expected output
Filter to points.delete is more efficient than fetching IDs first and then deleting by list, especially at scale.
With points.set_payload
points.set_payload with a filter updates every matching point in a single call. Both TrailMaster products (id=1 and id=4) receive the featured: True field without touching their vectors or other payload fields.
Expected output
Utility methods
Check whether a builder has conditions with is_empty
FilterBuilder.is_empty() returns True if no conditions have been added.
Expected output
FilterBuilder() reports is_empty() == True because no conditions have been added. After calling .must(), is_empty() returns False. This lets you skip passing a filter object to search when no conditions are active — sending an empty filter is valid but slightly wasteful.
Branch filter logic with copy
FilterBuilder.copy() creates a shallow copy so you can derive multiple filter variants from a shared base without mutating the original.
Expected output
copy() creates an independent clone so that modifications to Branch A or Branch B do not affect the base builder or each other. Without copy(), all three variables would reference the same mutable object and adding conditions to one would affect all.
Test truthiness with bool
FilterBuilder evaluates to True when it has at least one condition and False when empty. The following code checks an empty builder and prints a message when no filters have been configured:
bool(fb) returns False when the builder has no conditions, letting you guard the filter= parameter with a simple if fb: check. When bool(fb) is False, passing filter=fb.build() to search would send an empty filter object; omitting the filter entirely is cleaner and slightly more efficient.
Expected output
Cleanup
Complete filter reference
Field conditions
| Method | Type | Example |
|---|---|---|
eq(value) | Exact match (str, int, bool) | Field("color").eq("blue") |
text(value) | Full-text token match | Field("text").text("waterproof") |
any_of(values) | IN list | Field("color").any_of(["blue", "red"]) |
except_of(values) | NOT IN list | Field("brand").except_of(["X", "Y"]) |
gt(value) | Greater than | Field("price").gt(100.0) |
gte(value) | Greater than or equal | Field("rating").gte(4.5) |
lt(value) | Less than | Field("price").lt(200.0) |
lte(value) | Less than or equal | Field("rating").lte(5.0) |
between(lo, hi) | Closed/open range | Field("price").between(50, 150) |
range(gt=, gte=, lt=, lte=) | Flexible bounds | Field("price").range(gte=50, lt=200) |
datetime_gt(dt) | After datetime | Field("created_at").datetime_gt(dt) |
datetime_gte(dt) | At or after datetime | Field("created_at").datetime_gte(dt) |
datetime_lt(dt) | Before datetime | Field("created_at").datetime_lt(dt) |
datetime_lte(dt) | At or before datetime | Field("created_at").datetime_lte(dt) |
datetime_between(lo, hi) | Datetime range | Field("created_at").datetime_between(lo, hi) |
values_count(gte=, lte=, ...) | Array cardinality | Field("tags").values_count(gte=3) |
geo_radius(lat, lon, r) | Circle (metres) | Field("loc").geo_radius(40.7, -74.0, 5000) |
geo_bounding_box(tl, br) | Rectangle | Field("loc").geo_bounding_box((49,-125),(25,-66)) |
geo_polygon(exterior) | Polygon | Field("loc").geo_polygon([(a,b),(c,d),...]) |
Standalone conditions
| Function | Purpose | Example |
|---|---|---|
is_null(key) | Field is null | is_null("notes") |
is_empty(key) | Field is empty or missing | is_empty("reviews") |
has_id(ids) | Point ID in list | has_id([0, 1, 2]) |
has_vector(name) | Named vector exists | has_vector("") |
nested(key, filter) | Nested object filter | nested("reviews", inner_fb) |
FilterBuilder clauses
| Method | Logic | Effect |
|---|---|---|
.must(cond) | AND | All must conditions required |
.should(cond) | OR | At least one should condition |
.must_not(cond) | NOT | Exclude matching |
.min_should(conds, N) | N-of-M | At least N conditions match |
Operators
| Operator | On condition | On FilterBuilder | |
|---|---|---|---|
a & b | must=[a, b] | Merge lists | |
| `a \ | b` | should=[a, b] | Nested sub-filters in should |
~a | must_not=[a] | Swap must and must_not |
Next steps
- Similarity search fundamentals — Learn the core retrieval workflow
- Reranking search results — Improve relevance with cross-encoder and reciprocal rank fusion reranking
- Retrieval quality — Measure and optimize search accuracy using precision, recall, and MRR
- Open-source embedding models — Integrate open-source models like Sentence Transformers and BGE