Filtered Search
Vector search answers the question: “Which embeddings are most similar to this embedding?”
In real products, similarity alone is rarely enough. You almost always want to constrain the search to a subset of data:
- Only documents from a specific tenant / organization
- Only items that are in_stock = true
- Only content in a specific language, time window, or category
- Only records the caller is allowed to see
Filtered search does exactly that: it runs metadata filtering together with ANN (Approximate Nearest Neighbor) search.
What Dodil does
Dodil VBase supports the same idea you may know from Milvus:
- Apply a scalar filter (metadata condition) to narrow the candidates.
- Run ANN search only within that reduced candidate set.
- Return the top‑K matches (optionally including selected metadata fields).
This typically improves:
- Relevance (you avoid “similar but wrong scope” matches)
- Performance (search works on fewer candidates)
- Cost (less compute per query)
Two ways to filter
1) Standard filtering
This is the default model: filter first, then search.
Example mindset:
“Find the top 10 most similar vectors, but only among items where
color LIKE "red%"andlikes > 50.”
Standard filtering is great when your filter is straightforward and selects a reasonable subset.
2) Iterative filtering
Some filters become expensive when they are very complex or when they match a large portion of the collection.
Iterative filtering is an alternative strategy:
- VBase searches in iterations (using a search iterator under the hood).
- Each candidate result is tested against your filter.
- The process continues until top‑K filtered results are collected.
This can reduce the total amount of scalar filtering work in highly complex cases, but it can also be slower when many candidates must be checked (because results are processed progressively).
If your workload has heavy filters and you see high latency, iterative filtering can be a useful optimization (when supported by your chosen query mode).
Filter expression basics
Filters are written as a string expression against your scalar fields.
Common patterns:
- Equality / comparison:
status == "active",price < 100,likes >= 50 - Boolean fields:
in_stock == true - Prefix matching:
color like "red%" - Membership:
category in ["shoes", "bags"] - Combine conditions:
tenant_id == "org_123" and (lang == "en" or lang == "ar")
Design tip: if you filter by tenant or user ownership on every request, make that field indexed and/or consider partitioning/partition keys (depending on your schema strategy).
Example
Assume your collection has these fields:
id(primary key)vector(embedding)color(string)likes(int)
Search with standard filtering
from dodil import Client
from dodil.vbase import VBaseConfig
# Authorize
c = Client(
service_account_id="...",
service_account_secret="...",
)
# Connect to VBase
vbase = c.vbase.connect(
VBaseConfig(
host="vbase-db-<id>.infra.dodil.cloud",
port=443,
scheme="https",
db_name="db_<id>",
)
)
query_vector = [
0.3580376395471989,
-0.6023495712049978,
0.18414012509913835,
-0.26286205330961354,
0.9029438446296592,
]
results = vbase.search(
collection_name="my_collection",
data=[query_vector],
limit=5,
filter='color like "red%" and likes > 50',
output_fields=["color", "likes"],
)
for hit in results[0]:
print(hit.id, hit.distance, hit.entity.get("color"), hit.entity.get("likes"))When to use filtered search
- Multi-tenant apps: always filter by
tenant_id. - RAG: filter by
doc_type,source,language,created_at. - E‑commerce: filter by
in_stock,price,brand,category. - Observability / security: filter by
service,env,status_code,region.
Filtered search is one of the highest-leverage features in vector applications: it’s the difference between “similar content” and “similar content that is actually usable in my current context.”