Skip to content

Commit c0f3f67

Browse files
committed
chore: check that a vector search index exists with indexCheck
1 parent fe38463 commit c0f3f67

File tree

3 files changed

+113
-5
lines changed

3 files changed

+113
-5
lines changed

src/common/search/vectorSearchEmbeddingsManager.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,26 @@ export class VectorSearchEmbeddingsManager {
4848
this.embeddings.delete(embeddingDefKey);
4949
}
5050

51+
async indexExists({
52+
database,
53+
collection,
54+
indexName,
55+
}: {
56+
database: string;
57+
collection: string;
58+
indexName: string;
59+
}): Promise<boolean> {
60+
const provider = await this.atlasSearchEnabledProvider();
61+
if (!provider) {
62+
return false;
63+
}
64+
65+
const searchIndexesWithName = await provider.getSearchIndexes(database, collection, indexName);
66+
console.log(">>>>>", searchIndexesWithName);
67+
68+
return searchIndexesWithName.length >= 1;
69+
}
70+
5171
async embeddingsForNamespace({
5272
database,
5373
collection,

src/tools/mongodb/read/aggregate.ts

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,23 @@ export class AggregateTool extends MongoDBToolBase {
9090

9191
// Check if aggregate operation uses an index if enabled
9292
if (this.config.indexCheck) {
93-
await checkIndexUsage(provider, database, collection, "aggregate", async () => {
94-
return provider
95-
.aggregate(database, collection, pipeline, {}, { writeConcern: undefined })
96-
.explain("queryPlanner");
97-
});
93+
const usesVectorSearchIndex = await this.isVectorSearchIndexUsed({ database, collection, pipeline });
94+
switch (usesVectorSearchIndex) {
95+
case "no-vector-search-query":
96+
await checkIndexUsage(provider, database, collection, "aggregate", async () => {
97+
return provider
98+
.aggregate(database, collection, pipeline, {}, { writeConcern: undefined })
99+
.explain("queryPlanner");
100+
});
101+
break;
102+
case false:
103+
throw new MongoDBError(
104+
ErrorCodes.AtlasVectorSearchIndexNotFound,
105+
"Could not find provided vector search index."
106+
);
107+
case true:
108+
// nothing to do, everything is correct so ready to run the query
109+
}
98110
}
99111

100112
pipeline = await this.replaceRawValuesWithEmbeddingsIfNecessary({
@@ -269,6 +281,41 @@ export class AggregateTool extends MongoDBToolBase {
269281
return pipeline;
270282
}
271283

284+
private async isVectorSearchIndexUsed({
285+
database,
286+
collection,
287+
pipeline,
288+
}: {
289+
database: string;
290+
collection: string;
291+
pipeline: Document[];
292+
}): Promise<boolean | "no-vector-search-query"> {
293+
// check if the pipeline contains a $vectorSearch stage
294+
let usesVectorSearch = false;
295+
let indexName: string = "default";
296+
297+
for (const stage of pipeline) {
298+
if ("$vectorSearch" in stage) {
299+
const { $vectorSearch: vectorSearchStage } = stage as z.infer<typeof VectorSearchStage>;
300+
usesVectorSearch = true;
301+
indexName = vectorSearchStage.index;
302+
break;
303+
}
304+
}
305+
306+
if (!usesVectorSearch) {
307+
return "no-vector-search-query";
308+
}
309+
310+
const indexExists = await this.session.vectorSearchEmbeddingsManager.indexExists({
311+
database,
312+
collection,
313+
indexName,
314+
});
315+
316+
return indexExists;
317+
}
318+
272319
private generateMessage({
273320
aggResultsCount,
274321
documents,

tests/integration/tools/mongodb/read/aggregate.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,45 @@ describeWithMongoDB(
394394
await integration.mongoClient().db(integration.randomDbName()).collection("databases").drop();
395395
});
396396

397+
it("should throw an exception when using an index that does not exist", async () => {
398+
await waitUntilSearchIsReady(integration.mongoClient());
399+
400+
const collection = integration.mongoClient().db(integration.randomDbName()).collection("databases");
401+
402+
await collection.insertOne({ name: "mongodb", description_embedding: [1, 2, 3, 4] });
403+
await integration.connectMcpClient();
404+
const response = await integration.mcpClient().callTool({
405+
name: "aggregate",
406+
arguments: {
407+
database: integration.randomDbName(),
408+
collection: "databases",
409+
pipeline: [
410+
{
411+
$vectorSearch: {
412+
index: "non_existing",
413+
path: "description_embedding",
414+
queryVector: "example",
415+
numCandidates: 10,
416+
limit: 10,
417+
embeddingParameters: {
418+
model: "voyage-3-large",
419+
outputDimension: 256,
420+
},
421+
},
422+
},
423+
{
424+
$project: {
425+
description_embedding: 0,
426+
},
427+
},
428+
],
429+
},
430+
});
431+
432+
const responseContent = getResponseContent(response);
433+
expect(responseContent).toContain("Error running aggregate: Could not find provided vector search index.");
434+
});
435+
397436
for (const [dataType, embedding] of Object.entries(DOCUMENT_EMBEDDINGS)) {
398437
for (const similarity of ["euclidean", "cosine", "dotProduct"]) {
399438
describe.skipIf(!process.env.TEST_MDB_MCP_VOYAGE_API_KEY)(
@@ -406,6 +445,7 @@ describeWithMongoDB(
406445
.mongoClient()
407446
.db(integration.randomDbName())
408447
.collection("databases");
448+
409449
await collection.insertOne({ name: "mongodb", description_embedding: embedding });
410450

411451
await createVectorSearchIndexAndWait(
@@ -674,6 +714,7 @@ describeWithMongoDB(
674714
voyageApiKey: process.env.TEST_MDB_MCP_VOYAGE_API_KEY ?? "",
675715
maxDocumentsPerQuery: -1,
676716
maxBytesPerQuery: -1,
717+
indexCheck: true,
677718
}),
678719
downloadOptions: { search: true },
679720
}

0 commit comments

Comments
 (0)