Pass a filter= argument to your vector query. The database applies the predicate during the search, not as a post-processing step in your application, so you always get top_k results that are both nearest to your query vector and match the filter. Predicates use MongoDB-style operators: {"year": {"$gte": 2000}} keeps rows whose year field is at least 2000, and the shorthand {"genre": "crime"} means genre equals "crime". Any metadata field you stored alongside your vectors is filterable — no schema declaration or secondary index setup required.
Here it is end to end, using the 5,000-film sample corpus bundled with the SDK (pip install "veep[samples]"):
from veep import VP, samples
df = samples.dataframe() # 5,000 films: id, title, year, genre, plot, vector
vp = VP.from_creds()
vp.collections.create("metadata-filter-demo", tier="hot")
vp.vectors.upsert("metadata-filter-demo", dataframe=df)
Query without a filter first:
qv = samples.encode("a small team plans an elaborate casino heist")
for hit in vp.vectors.query("metadata-filter-demo", vector=qv, top_k=3):
print(f"{hit.score:.4f} {hit.metadata['title']} ({hit.metadata['year']}, {hit.metadata['genre']})")
0.4782 Ocean's Eleven (2001, crime)
0.4048 Lookin' to Get Out (1982, comedy)
0.3917 Ocean's 11 (1960, crime drama)
Semantic search alone reaches across eras — the 1960 Rat Pack original ranks right behind the 2001 remake. Now the same query, scoped to this century:
for hit in vp.vectors.query("metadata-filter-demo", vector=qv, top_k=3,
filter={"year": {"$gte": 2000}}):
print(f"{hit.score:.4f} {hit.metadata['title']} ({hit.metadata['year']}, {hit.metadata['genre']})")
0.4782 Ocean's Eleven (2001, crime)
0.3369 Ocean's Thirteen (2007, comedy, crime)
0.3132 Ocean's Twelve (2004, comedy, crime)
The filter changed which rows were eligible, and the vector similarity ranked what remained: the modern Ocean's trilogy, in score order.
The operator set
Eight leaf operators filter on a single field: $eq, $ne, $gt, $gte, $lt, $lte, $in (value in a list), and $nin (value not in a list). Combine them with $and, $or, and $not for compound predicates:
for hit in vp.vectors.query("metadata-filter-demo", vector=qv, top_k=3,
filter={"$and": [{"year": {"$gte": 2000}},
{"genre": {"$ne": "comedy, crime"}}]}):
print(f"{hit.score:.4f} {hit.metadata['title']} ({hit.metadata['year']}, {hit.metadata['genre']})")
0.4782 Ocean's Eleven (2001, crime)
0.2919 Saw VI (2009, horror)
0.2884 Inside Man (2006, crime drama)
Every operator, with a runnable example for each, is covered in Filter syntax in depth. If you're coming from LangChain, the same filter shapes pass through the filter= kwarg of VectorPandaStore unchanged.
vp.collections.delete("metadata-filter-demo")
