Skip to content

Commit 3598c23

Browse files
feat(NODE-5919): support new type option in create search index helpers (#4060)
1 parent 4a62ec6 commit 3598c23

File tree

6 files changed

+356
-17
lines changed

6 files changed

+356
-17
lines changed

src/operations/search_indexes/create.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ import { AbstractOperation } from '../operation';
88
/**
99
* @public
1010
*/
11-
export interface SearchIndexDescription {
11+
export interface SearchIndexDescription extends Document {
1212
/** The name of the index. */
1313
name?: string;
1414

1515
/** The index definition. */
1616
definition: Document;
17+
18+
/** The type of the index. Currently `search` or `vectorSearch` are supported. */
19+
type?: string;
1720
}
1821

1922
/** @internal */

test/manual/search-index-management.prose.test.ts

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,5 +340,167 @@ describe('Index Management Prose Tests', function () {
340340
.to.deep.equal({ dynamic: false });
341341
}
342342
);
343+
344+
it(
345+
'Case 7: Driver can successfully handle search index types when creating indexes',
346+
metadata,
347+
async function () {
348+
// 01. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`).
349+
const coll0 = collection;
350+
{
351+
// 02. Create a new search index on `coll0` with the `createSearchIndex` helper. Use the following definition:
352+
// ```typescript
353+
// {
354+
// name: 'test-search-index-case7-implicit',
355+
// definition: {
356+
// mappings: { dynamic: false }
357+
// }
358+
// }
359+
// ```
360+
const indexName = await coll0.createSearchIndex({
361+
name: 'test-search-index-case7-implicit',
362+
definition: {
363+
mappings: { dynamic: false }
364+
}
365+
});
366+
// 03. Assert that the command returns the name of the index: `"test-search-index-case7-implicit"`.
367+
expect(indexName).to.equal('test-search-index-case7-implicit');
368+
// 04. Run `coll0.listSearchIndexes('test-search-index-case7-implicit')` repeatedly every 5 seconds until the following
369+
// condition is satisfied and store the value in a variable `index1`:
370+
371+
// - An index with the `name` of `test-search-index-case7-implicit` is present and the index has a field `queryable`
372+
// with a value of `true`.
373+
374+
const [index1] = await waitForIndexes({
375+
predicate: indexes => indexes.every(index => index.queryable),
376+
indexNames: 'test-search-index-case7-implicit',
377+
collection: coll0
378+
});
379+
380+
// 05. Assert that `index1` has a property `type` whose value is `search`.
381+
expect(index1).to.have.property('type', 'search');
382+
}
383+
{
384+
// 06. Create a new search index on `coll0` with the `createSearchIndex` helper. Use the following definition:
385+
// ```typescript
386+
// {
387+
// name: 'test-search-index-case7-explicit',
388+
// type: 'search',
389+
// definition: {
390+
// mappings: { dynamic: false }
391+
// }
392+
// }
393+
// ```
394+
const indexName = await coll0.createSearchIndex({
395+
name: 'test-search-index-case7-explicit',
396+
type: 'search',
397+
definition: {
398+
mappings: { dynamic: false }
399+
}
400+
});
401+
// 07. Assert that the command returns the name of the index: `"test-search-index-case7-explicit"`.
402+
expect(indexName).to.equal('test-search-index-case7-explicit');
403+
// 08. Run `coll0.listSearchIndexes('test-search-index-case7-explicit')` repeatedly every 5 seconds until the following
404+
// condition is satisfied and store the value in a variable `index2`:
405+
406+
// - An index with the `name` of `test-search-index-case7-explicit` is present and the index has a field `queryable`
407+
// with a value of `true`.
408+
409+
const [index2] = await waitForIndexes({
410+
predicate: indexes => indexes.every(index => index.queryable),
411+
indexNames: 'test-search-index-case7-explicit',
412+
collection: coll0
413+
});
414+
// 09. Assert that `index2` has a property `type` whose value is `search`.
415+
expect(index2).to.have.property('type', 'search');
416+
}
417+
{
418+
// 10. Create a new vector search index on `coll0` with the `createSearchIndex` helper. Use the following definition:
419+
// ```typescript
420+
// {
421+
// name: 'test-search-index-case7-vector',
422+
// type: 'vectorSearch',
423+
// definition: {
424+
// "fields": [
425+
// {
426+
// "type": "vector",
427+
// "path": "plot_embedding",
428+
// "numDimensions": 1536,
429+
// "similarity": "euclidean",
430+
// },
431+
// ]
432+
// }
433+
// }
434+
// ```
435+
436+
const indexName = await coll0.createSearchIndex({
437+
name: 'test-search-index-case7-vector',
438+
type: 'vectorSearch',
439+
definition: {
440+
fields: [
441+
{
442+
type: 'vector',
443+
path: 'plot_embedding',
444+
numDimensions: 1536,
445+
similarity: 'euclidean'
446+
}
447+
]
448+
}
449+
});
450+
// 11. Assert that the command returns the name of the index: `"test-search-index-case7-vector"`.
451+
expect(indexName).to.equal('test-search-index-case7-vector');
452+
// 12. Run `coll0.listSearchIndexes('test-search-index-case7-vector')` repeatedly every 5 seconds until the following
453+
// condition is satisfied and store the value in a variable `index3`:
454+
// - An index with the `name` of `test-search-index-case7-vector` is present and the index has a field `queryable` with
455+
// a value of `true`.
456+
const [index3] = await waitForIndexes({
457+
predicate: indexes => indexes.every(index => index.queryable),
458+
indexNames: 'test-search-index-case7-vector',
459+
collection: coll0
460+
});
461+
462+
// 13. Assert that `index3` has a property `type` whose value is `vectorSearch`.
463+
expect(index3).to.have.property('type', 'vectorSearch');
464+
}
465+
}
466+
);
467+
468+
it('Case 8: Driver requires explicit type to create a vector search index', async function () {
469+
// 1. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`).
470+
const coll0 = collection;
471+
472+
// 2. Create a new vector search index on `coll0` with the `createSearchIndex` helper. Use the following definition:
473+
// {
474+
// name: 'test-search-index-case8-error',
475+
// definition: {
476+
// fields: [
477+
// {
478+
// type: 'vector',
479+
// path: 'plot_embedding',
480+
// numDimensions: 1536,
481+
// similarity: 'euclidean',
482+
// },
483+
// ]
484+
// }
485+
// }
486+
const definition = {
487+
name: 'test-search-index-case8-error',
488+
definition: {
489+
fields: [
490+
{
491+
type: 'vector',
492+
path: 'plot_embedding',
493+
numDimensions: 1536,
494+
similarity: 'euclidean'
495+
}
496+
]
497+
}
498+
};
499+
const error = await coll0.createSearchIndex(definition).catch(e => e);
500+
501+
// 3. Assert that the command throws an exception containing the string "Attribute mappings missing" due to the `mappings`
502+
// field missing.
503+
expect(error).to.match(/Attribute mappings missing/i);
504+
});
343505
});
344506
});

test/spec/index-management/createSearchIndex.json

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@
5050
"mappings": {
5151
"dynamic": true
5252
}
53-
}
53+
},
54+
"type": "search"
5455
}
5556
},
5657
"expectError": {
@@ -73,7 +74,8 @@
7374
"mappings": {
7475
"dynamic": true
7576
}
76-
}
77+
},
78+
"type": "search"
7779
}
7880
],
7981
"$db": "database0"
@@ -97,7 +99,8 @@
9799
"dynamic": true
98100
}
99101
},
100-
"name": "test index"
102+
"name": "test index",
103+
"type": "search"
101104
}
102105
},
103106
"expectError": {
@@ -121,7 +124,68 @@
121124
"dynamic": true
122125
}
123126
},
124-
"name": "test index"
127+
"name": "test index",
128+
"type": "search"
129+
}
130+
],
131+
"$db": "database0"
132+
}
133+
}
134+
}
135+
]
136+
}
137+
]
138+
},
139+
{
140+
"description": "create a vector search index",
141+
"operations": [
142+
{
143+
"name": "createSearchIndex",
144+
"object": "collection0",
145+
"arguments": {
146+
"model": {
147+
"definition": {
148+
"fields": [
149+
{
150+
"type": "vector",
151+
"path": "plot_embedding",
152+
"numDimensions": 1536,
153+
"similarity": "euclidean"
154+
}
155+
]
156+
},
157+
"name": "test index",
158+
"type": "vectorSearch"
159+
}
160+
},
161+
"expectError": {
162+
"isError": true,
163+
"errorContains": "Atlas"
164+
}
165+
}
166+
],
167+
"expectEvents": [
168+
{
169+
"client": "client0",
170+
"events": [
171+
{
172+
"commandStartedEvent": {
173+
"command": {
174+
"createSearchIndexes": "collection0",
175+
"indexes": [
176+
{
177+
"definition": {
178+
"fields": [
179+
{
180+
"type": "vector",
181+
"path": "plot_embedding",
182+
"numDimensions": 1536,
183+
"similarity": "euclidean"
184+
}
185+
]
186+
},
187+
"name": "test index",
188+
"type": "vectorSearch"
125189
}
126190
],
127191
"$db": "database0"

test/spec/index-management/createSearchIndex.yml

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ tests:
2626
- name: createSearchIndex
2727
object: *collection0
2828
arguments:
29-
model: { definition: &definition { mappings: { dynamic: true } } }
29+
model: { definition: &definition { mappings: { dynamic: true } } , type: 'search' }
3030
expectError:
3131
# This test always errors in a non-Atlas environment. The test functions as a unit test by asserting
3232
# that the driver constructs and sends the correct command.
@@ -39,15 +39,15 @@ tests:
3939
- commandStartedEvent:
4040
command:
4141
createSearchIndexes: *collection0
42-
indexes: [ { definition: *definition } ]
42+
indexes: [ { definition: *definition, type: 'search'} ]
4343
$db: *database0
4444

4545
- description: "name provided for an index definition"
4646
operations:
4747
- name: createSearchIndex
4848
object: *collection0
4949
arguments:
50-
model: { definition: &definition { mappings: { dynamic: true } } , name: 'test index' }
50+
model: { definition: &definition { mappings: { dynamic: true } } , name: 'test index', type: 'search' }
5151
expectError:
5252
# This test always errors in a non-Atlas environment. The test functions as a unit test by asserting
5353
# that the driver constructs and sends the correct command.
@@ -60,5 +60,27 @@ tests:
6060
- commandStartedEvent:
6161
command:
6262
createSearchIndexes: *collection0
63-
indexes: [ { definition: *definition, name: 'test index' } ]
63+
indexes: [ { definition: *definition, name: 'test index', type: 'search' } ]
64+
$db: *database0
65+
66+
- description: "create a vector search index"
67+
operations:
68+
- name: createSearchIndex
69+
object: *collection0
70+
arguments:
71+
model: { definition: &definition { fields: [ {"type": "vector", "path": "plot_embedding", "numDimensions": 1536, "similarity": "euclidean"} ] }
72+
, name: 'test index', type: 'vectorSearch' }
73+
expectError:
74+
# This test always errors in a non-Atlas environment. The test functions as a unit test by asserting
75+
# that the driver constructs and sends the correct command.
76+
# The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages.
77+
isError: true
78+
errorContains: Atlas
79+
expectEvents:
80+
- client: *client0
81+
events:
82+
- commandStartedEvent:
83+
command:
84+
createSearchIndexes: *collection0
85+
indexes: [ { definition: *definition, name: 'test index', type: 'vectorSearch' } ]
6486
$db: *database0

0 commit comments

Comments
 (0)