collection.vectorSearch() executes a MongoDB $vectorSearch query and returns a stable score envelope for every match.
This API requires a MongoDB deployment that supports Vector Search and a Search index of type vectorSearch that already exists. A Search index is a MongoDB Search index whose type is full-text search or vectorSearch. Since v3.4.0 you can manage that index with the Search index lifecycle API. Configure the index and make its vector dimensions, numeric representation, and indexed path match the embeddings you send to the API. See the MongoDB $vectorSearch reference for deployment and index requirements.
$vectorSearch must be the first stage of any pipeline it appears in. Current MongoDB documentation still does not allow it inside a $lookup sub-pipeline or a $facet stage (manual, Vector Search stage). Use vectorSearch() as the search entry point and enrich the resulting documents afterwards when needed.
collection.vectorSearch<TDocument = TSchema>(options: VectorSearchOptions)
: Promise<Array<VectorSearchHit<TDocument>>>;
model.vectorSearch(options: ModelVectorSearchOptions)
: Promise<Array<VectorSearchHit<ModelDocument<TDocument>>>>;interface VectorSearchOptions {
index: string;
path: string;
queryVector: number[];
limit: number;
numCandidates?: number;
exact?: boolean;
filter?: Record<string, unknown>;
projection?: Record<string, unknown> | string[];
aggregateOptions?: VectorSearchAggregateOptions;
}
interface VectorSearchHit<TDocument> {
document: TDocument;
score: number;
}
interface ModelVectorSearchOptions extends VectorSearchOptions {
withDeleted?: boolean;
onlyDeleted?: boolean;
}document contains the returned source document. score is kept in a separate envelope, so it cannot overwrite an application document field.
For the default ANN mode, numCandidates is required and must be a positive integer greater than or equal to limit.
type Article = {
_id: string;
title: string;
summary: string;
status: 'draft' | 'published';
};
const articles = runtime.collection<Article>('articles');
const queryEmbedding = [0.12, -0.08, 0.44];
const hits = await articles.vectorSearch({
index: 'articles_embedding_index',
path: 'embedding',
queryVector: queryEmbedding,
limit: 5,
numCandidates: 100,
filter: { status: 'published' },
projection: ['title', 'summary', 'status'],
aggregateOptions: {
maxTimeMS: 5_000,
comment: 'related-articles',
},
});
for (const { document, score } of hits) {
console.log(score, document.title);
}filter is a Vector Search pre-filter, not a later $match stage. Create the relevant filter fields on the Search index (vectorSearch type) before relying on them.
Set exact: true for exact nearest-neighbor search and omit numCandidates.
const hits = await runtime.collection('articles').vectorSearch({
index: 'articles_embedding_index',
path: 'embedding',
queryVector: queryEmbedding,
limit: 5,
exact: true,
});model.vectorSearch() uses the same index, ANN/ENN validation, and result envelope as the Collection method. Its only extra responsibilities are Model visibility and hydration:
- A soft-deleted Model merges its default visibility predicate into
$vectorSearch.filter; it does not insert a preceding$match. withDeleted: truekeeps the caller filter unchanged.onlyDeleted: truesearches only soft-deleted documents unlesswithDeletedis also set.- Hit documents are hydrated Model documents. For example,
hit.document.populate('author')is available after the search. The server-provided score and hit order are preserved.
const hits = await runtime.model<Article>('articles').vectorSearch({
index: 'articles_embedding_index',
path: 'embedding',
queryVector: queryEmbedding,
limit: 5,
numCandidates: 100,
filter: { tenantId: 'tenant-a' },
});
await hits[0]?.document.populate('author');The method validates options locally before it contacts MongoDB:
indexandpathmust be non-empty strings.queryVectormust be a non-empty array of finite numbers.limitmust be a positive integer.- ANN requires
numCandidates >= limit; ENN rejectsnumCandidates. filter,projection, andaggregateOptionsmust have supported shapes.
Invalid vector options throw INVALID_VECTOR_SEARCH, with field/reason details. The Model-only withDeleted and onlyDeleted flags must be booleans. The call runs through monSQLize's existing aggregate execution path, but aggregateOptions.meta, aggregateOptions.stream, and aggregateOptions.explain are rejected because vectorSearch() always resolves to VectorSearchHit[].
Deployment, index, and other MongoDB execution failures are rethrown with their original driver code and message. When the error object is writable, monSQLize adds error.monsqlize.operation === 'vectorSearch' and the target namespace for diagnosis.
Index management for a Search index (including type vectorSearch) is on the Search index lifecycle page: createSearchIndex, updateSearchIndex, dropSearchIndex, listSearchIndexes, and waitForSearchIndex.
- A default local
mongodb-memory-serverinstance does not provide Vector Search. Unit coverage verifies pipeline construction and validation. End-to-end verification needs Atlas,mongodb/mongodb-atlas-local, or Community/Enterprise 8.2+ withmongot. The runnable walkthrough isexamples/docs/vector-search.ts. SetMONSQLIZE_VECTOR_SEARCH_URIorMONSQLIZE_ATLAS_LOCAL_URI. When neither is set, that example exits without calling Search sonpm run test:examplescan still invoke it. - Vector Search ranking is produced by MongoDB. monSQLize returns that score and does not re-rank results locally.
- Keep the embedding model, vector dimensions, and index configuration synchronized across writers and readers.