Advanced
Metadata filters
Filter retrieval by structured metadata and user context.
Metadata filters constrain vector search before documents reach the model. Use them for tenant boundaries, product scopes, visibility rules, freshness, status, and other retrieval-time constraints.
Filters are not prompt instructions. They are application data-access rules attached to the search request.
Store Filterable Metadata
Add metadata when embedding documents:
import { embedDocuments } from "@anvia/core/embeddings";
const embedded = await embedDocuments(embeddingModel, documents, {
id: (document) => document.id,
content: (document) => document.text,
metadata: (document) => ({
tenantId: document.tenantId,
product: document.product,
visibility: document.visibility,
priority: document.priority,
published: document.status === "published",
}),
});
Metadata values can be strings, numbers, booleans, or null. Keep metadata flat. Store ids and flags, not large nested records. Prefer non-null values for fields you plan to filter across production vector adapters.
Filter A Search
import { vectorFilter } from "@anvia/core/vector-store";
const filter = vectorFilter.eq("tenantId", tenant.id);
const results = await index.search({
query: "billing limits",
topK: 5,
filter,
});
The filter runs before results are returned. Documents without matching metadata are excluded.
Combine Conditions
const tenantBillingFilter = vectorFilter.and(
vectorFilter.eq("tenantId", tenant.id),
vectorFilter.eq("product", "billing"),
);
const results = await index.search({
query: "invoice settings",
topK: 5,
threshold: 0.72,
filter: tenantBillingFilter,
});
Use and(...) for required constraints and or(...) for allowed alternatives:
const publicOrTenantFilter = vectorFilter.or(
vectorFilter.eq("visibility", "public"),
vectorFilter.eq("tenantId", tenant.id),
);
Range Filters
Use gt(...) and lt(...) for values with meaningful ordering:
const filter = vectorFilter.and(
vectorFilter.eq("product", "support"),
vectorFilter.gt("priority", 2),
);
Number comparisons are the portable production case. Core’s in-memory store also supports string and boolean ordering, but vector database adapters may narrow range filters to numeric metadata. Use string, boolean, or null filter values only after checking the adapter you deploy.
Dynamic Context Filters
Attach filters to dynamic context:
import { AgentBuilder } from "@anvia/core";
import { vectorFilter } from "@anvia/core/vector-store";
const supportContextFilter = vectorFilter.and(
vectorFilter.eq("tenantId", tenant.id),
vectorFilter.eq("visibility", "support"),
);
const agent = new AgentBuilder("tenant-support", model)
.dynamicContext(tenantDocsIndex, {
topK: 6,
threshold: 0.72,
filter: supportContextFilter,
})
.build();
Build the filter from authenticated product state, not from model output. The model can decide how to use retrieved facts, but it should not decide which tenant’s facts are eligible.
Search Tool Filters
Filters also apply to vector search tools:
const searchDocs = docsIndex.asTool({
name: "search_docs",
description: "Search documentation available to the current tenant.",
topK: 4,
filter: supportContextFilter,
});
If a tool is exposed to the model, apply the same permission filter you would apply to automatic context. Tool descriptions can explain scope, but they should not be the enforcement mechanism.
Production Guidance
Prefer explicit metadata over parsing ids or source paths at query time. For example, store tenantId, product, and visibility as metadata fields even if those values also appear in the document id.
Test filters with permission-sensitive prompts:
- a tenant asking about its own private document
- a tenant asking about another tenant’s private document
- a public document that should be visible to all tenants
- archived or draft material that should not be retrieved
If a document should never be visible to a model, do not index it into a shared corpus without a reliable filter path.
