Overview
The ELIPS Python API provides a powerful and expressive Filter class for querying metadata associated with vectors. It supports fluent chaining of predicates, boolean combinators, and works seamlessly with both vector similarity search (collection.query) and row retrieval (collection.get).
Type Aliases used in this API:
MetaValue = Union[bool, int, float, str]PayloadLike = Mapping[str, MetaValue]
Fluent Chain
The primary way to construct filters is using the fluent builder pattern. Calling Filter() creates an empty filter that matches everything. You can chain predicates by specifying a field with .field(name)and then an operation. Multiple chained conditions are implicitly AND-ed together.
.equals(value).not_equals(value).lt(value)(less than).le(value)(less than or equal).gt(value)(greater than).gte(value)(greater than or equal).one_of(values)(matches any of the values in the list).contains(substring)(metadata substring match)
import elips
# Basic fluent chain (predicates are AND-ed)
f = (
elips.Filter()
.field("category").equals("tech")
.field("score").gte(0.8)
.field("country").one_of(["US", "GB"])
)Combinators
For more complex logic, you can combine filters using boolean operators.
.and_(other): Returns a new filter combining this filter andotherwith an AND operator..or_(other): Returns a new filter combining this filter andotherwith an OR operator.Filter.not_(inner): Static method returning a filter that negates the giveninnerfilter.
# OR combinator
either = (
elips.Filter().field("tier").equals("pro")
.or_(elips.Filter().field("year").gte(2023))
)
# NOT combinator
excluded = elips.Filter.not_(
elips.Filter().field("status").equals("archived")
)Static Factories
In addition to the fluent builder, Filter provides static factories for creating single-predicate filters directly.
Filter.compare(field, op, value): Uses theelips.Comparatorenum.Filter.in_set(field, values): Equivalent tofield().one_of().Filter.has_substring(field, substring): Equivalent tofield().contains().
The Comparator enum includes: eq, ne, lt, le, gt, gte.
# Static compare with Comparator enum
f2 = elips.Filter.compare("price", elips.Comparator.lt, 100.0)
# in_set
f3 = elips.Filter.in_set("region", ["eu-west", "us-east"])
# has_substring (full-text substring match on metadata)
f4 = elips.Filter.has_substring("title", "design")Runtime Evaluation
Filters can be evaluated against regular Python dictionaries without interacting with the database. This is useful for testing or client-side filtering.
.matches(payload: dict) -> bool: Tests if the filter matches a dictionary..matches_all() -> bool: ReturnsTrueif the filter is completely empty (matches anything).
# Runtime eval without DB
record = {"category": "tech", "score": 0.9, "country": "US"}
print(f.matches(record)) # True
print(elips.Filter().matches_all()) # TrueMetadata Acceleration
ELIPS utilizes a MetadataIndex to accelerate similarity searches by narrowing down candidates before executing the Approximate Nearest Neighbor (ANN) search. Only equality constraints (.equals() and .one_of() / in_set()) can be used for index pre-filtering. Range predicates (lt, le, gt, gte) and substring matches (contains, has_substring) are applied during a post-filtering phase after ANN candidates are retrieved.
You can inspect a filter to see what constraints can be pushed down to the index:
.exact_constraints() -> Optional[list[tuple[str, list[MetaValue]]]]: Returns a list of equality constraints if acceleratable, otherwiseNone.
# Check accelerability
constraints = f.exact_constraints()
# Returns [("category", ["tech"]), ("country", ["US", "GB"])]
# The gte(0.8) predicate is NOT equality, so it's not in exact_constraintsQuery Integration
Filters are heavily used when querying a Collection. Both vector similarity search (collection.query) and row retrieval (collection.get) accept a where argument, as does collection.delete.
# Use in query (vector similarity search)
hits = collection.query(query_embeddings=[1.0, 0.0], n_results=10, where=f)
# Use in get (row retrieval by predicate)
rows = collection.get(where=f, offset=0, limit=100)
# Use in delete (remove every matching record)
removed = collection.delete(where=f)Explain Query
To understand how your filter interacts with the query planner, you can use collection.explain(). This returns a QueryPlan object detailing whether metadata acceleration was utilized.
# Explain the plan
plan = collection.explain([1.0, 0.0], n_results=10, where=f)
print(f"Accelerated: {plan.metadata_accelerated}")
print(f"Strategy: {plan.strategy.name}")Examples
Here is an example combining complex multi-condition, OR, NOT, and tenant scoping logic:
tenant_filter = elips.Filter().field("tenant_id").equals("org_123")
active_items = (
elips.Filter()
.field("status").equals("active")
.field("visibility").one_of(["public", "internal"])
)
no_drafts = elips.Filter.not_(
elips.Filter().field("state").equals("draft")
)
# Combine them all
final_filter = tenant_filter.and_(active_items).and_(no_drafts)Pitfalls
- Chained predicates are AND-ed: Calling
.field(x).equals(y).field(a).equals(b)creates an AND filter. It does NOT overwrite or OR them. - Empty filters are not errors: An empty
elips.Filter()matches everything. This is usually intended but can be unexpected. Usematches_all()to detect empty filters if you need to validate user input. - Substring is metadata match:
.contains()andhas_substring()perform substring matches on the metadata text, not semantic searches on the document content. Usecollection.query(query_texts=...)(or a hybridquery_embeddings+query_textscall) for semantic text search. - Immutability in combinators:
.or_()and.and_()return new filters. Callingf.or_(other)does not mutatef. - Acceleration limitations:
exact_constraints()returnsNoneif ANY predicate in the filter is non-equality (like ranges or substrings). The whole filter will still work correctly (and results will be accurate), but the index cannot accelerate it entirely before the ANN step.