Skip to content

Commit b6ff2cc

Browse files
authored
feat: Create and Update Search Index (#244)
1 parent 08203de commit b6ff2cc

File tree

4 files changed

+143
-0
lines changed

4 files changed

+143
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2+
import { DbOperationArgs, MongoDBToolBase, SearchIndexArgs } from "../mongodbTool.js";
3+
import { OperationType, ToolArgs } from "../../tool.js";
4+
5+
const SEARCH_INDEX_TYPE = "search";
6+
7+
export class CreateSearchIndexTool extends MongoDBToolBase {
8+
protected name = "create-search-index";
9+
protected description = "Create an Atlas Search index for a collection";
10+
protected argsShape = {
11+
...DbOperationArgs,
12+
name: SearchIndexArgs.name,
13+
analyzer: SearchIndexArgs.analyzer,
14+
mappings: SearchIndexArgs.mappings,
15+
};
16+
17+
protected operationType: OperationType = "create";
18+
19+
protected async execute({
20+
database,
21+
collection,
22+
name,
23+
analyzer,
24+
mappings,
25+
}: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
26+
const provider = await this.ensureConnected();
27+
const indexes = await provider.createSearchIndexes(database, collection, [
28+
{
29+
name,
30+
type: SEARCH_INDEX_TYPE,
31+
definition: {
32+
analyzer,
33+
mappings,
34+
},
35+
},
36+
]);
37+
38+
return {
39+
content: [
40+
{
41+
text: `Created the search index "${indexes[0]}" on collection "${collection}" in database "${database}"`,
42+
type: "text",
43+
},
44+
],
45+
};
46+
}
47+
}

src/tools/mongodb/mongodbTool.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,57 @@ export const DbOperationArgs = {
1010
collection: z.string().describe("Collection name"),
1111
};
1212

13+
export const SearchIndexArgs = {
14+
name: z.string().describe("The name of the index"),
15+
analyzer: z
16+
.string()
17+
.optional()
18+
.default("lucene.standard")
19+
.describe(
20+
"The analyzer to use for the index. Can be one of the built-in lucene analyzers (`lucene.standard`, `lucene.simple`, `lucene.whitespace`, `lucene.keyword`), a language-specific analyzer, such as `lucene.cjk` or `lucene.czech`, or a custom analyzer defined in the Atlas UI."
21+
),
22+
mappings: z
23+
.object({
24+
dynamic: z
25+
.boolean()
26+
.optional()
27+
.default(false)
28+
.describe(
29+
"Enables or disables dynamic mapping of fields for this index. If set to true, Atlas Search recursively indexes all dynamically indexable fields. If set to false, you must specify individual fields to index using mappings.fields."
30+
),
31+
fields: z
32+
.record(
33+
z.string().describe("The field name"),
34+
z
35+
.object({
36+
type: z
37+
.enum([
38+
"autocomplete",
39+
"boolean",
40+
"date",
41+
"document",
42+
"embeddedDocuments",
43+
"geo",
44+
"number",
45+
"objectId",
46+
"string",
47+
"token",
48+
"uuid",
49+
])
50+
.describe("The field type"),
51+
})
52+
.passthrough()
53+
54+
.describe(
55+
"The field index definition. It must contain the field type, as well as any additional options for that field type."
56+
)
57+
)
58+
.optional()
59+
.describe("The field mapping definitions. If `dynamic` is set to false, this is required."),
60+
})
61+
.describe("Document describing the index to create."),
62+
};
63+
1364
export enum VectorFieldType {
1465
VECTOR = "vector",
1566
FILTER = "filter",

src/tools/mongodb/tools.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import { DropCollectionTool } from "./delete/dropCollection.js";
1818
import { ExplainTool } from "./metadata/explain.js";
1919
import { CreateCollectionTool } from "./create/createCollection.js";
2020
import { LogsTool } from "./metadata/logs.js";
21+
import { CreateSearchIndexTool } from "./create/createSearchIndex.js";
22+
import { UpdateSearchIndexTool } from "./update/updateSearchIndex.js";
2123
import { CreateVectorIndexTool } from "./create/createVectorIndex.js";
2224
import { UpdateVectorIndexTool } from "./update/updateVectorIndex.js";
2325
import { CollectionSearchIndexesTool } from "./read/collectionSearchIndexes.js";
@@ -45,6 +47,8 @@ export const MongoDbTools = [
4547
ExplainTool,
4648
CreateCollectionTool,
4749
LogsTool,
50+
CreateSearchIndexTool,
51+
UpdateSearchIndexTool,
4852
CreateVectorIndexTool,
4953
UpdateVectorIndexTool,
5054
DropSearchIndexTool,
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
2+
import { DbOperationArgs, MongoDBToolBase, SearchIndexArgs } from "../mongodbTool.js";
3+
import { OperationType, ToolArgs } from "../../tool.js";
4+
5+
export class UpdateSearchIndexTool extends MongoDBToolBase {
6+
protected name = "update-search-index";
7+
protected description = "Updates an Atlas Search index for a collection";
8+
protected argsShape = {
9+
...DbOperationArgs,
10+
name: SearchIndexArgs.name,
11+
analyzer: SearchIndexArgs.analyzer,
12+
mappings: SearchIndexArgs.mappings,
13+
};
14+
15+
protected operationType: OperationType = "update";
16+
17+
protected async execute({
18+
database,
19+
collection,
20+
name,
21+
analyzer,
22+
mappings,
23+
}: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
24+
const provider = await this.ensureConnected();
25+
// @ts-expect-error: Interface expects a SearchIndexDefinition. However,
26+
// passing analyzer/mappings at the root for the definition is necessary for the call to succeed.
27+
await provider.updateSearchIndex(database, collection, name, {
28+
analyzer,
29+
mappings,
30+
});
31+
32+
return {
33+
content: [
34+
{
35+
text: `Successfully updated search index "${name}" on collection "${collection}" in database "${database}"`,
36+
type: "text",
37+
},
38+
],
39+
};
40+
}
41+
}

0 commit comments

Comments
 (0)