Skip to content

Commit

Permalink
feat(NODE-5919): support new type option in create search index hel…
Browse files Browse the repository at this point in the history
…pers (#4060)
  • Loading branch information
baileympearson committed Apr 23, 2024
1 parent 4a62ec6 commit 3598c23
Show file tree
Hide file tree
Showing 6 changed files with 356 additions and 17 deletions.
5 changes: 4 additions & 1 deletion src/operations/search_indexes/create.ts
Expand Up @@ -8,12 +8,15 @@ import { AbstractOperation } from '../operation';
/**
* @public
*/
export interface SearchIndexDescription {
export interface SearchIndexDescription extends Document {
/** The name of the index. */
name?: string;

/** The index definition. */
definition: Document;

/** The type of the index. Currently `search` or `vectorSearch` are supported. */
type?: string;
}

/** @internal */
Expand Down
162 changes: 162 additions & 0 deletions test/manual/search-index-management.prose.test.ts
Expand Up @@ -340,5 +340,167 @@ describe('Index Management Prose Tests', function () {
.to.deep.equal({ dynamic: false });
}
);

it(
'Case 7: Driver can successfully handle search index types when creating indexes',
metadata,
async function () {
// 01. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`).
const coll0 = collection;
{
// 02. Create a new search index on `coll0` with the `createSearchIndex` helper. Use the following definition:
// ```typescript
// {
// name: 'test-search-index-case7-implicit',
// definition: {
// mappings: { dynamic: false }
// }
// }
// ```
const indexName = await coll0.createSearchIndex({
name: 'test-search-index-case7-implicit',
definition: {
mappings: { dynamic: false }
}
});
// 03. Assert that the command returns the name of the index: `"test-search-index-case7-implicit"`.
expect(indexName).to.equal('test-search-index-case7-implicit');
// 04. Run `coll0.listSearchIndexes('test-search-index-case7-implicit')` repeatedly every 5 seconds until the following
// condition is satisfied and store the value in a variable `index1`:

// - An index with the `name` of `test-search-index-case7-implicit` is present and the index has a field `queryable`
// with a value of `true`.

const [index1] = await waitForIndexes({
predicate: indexes => indexes.every(index => index.queryable),
indexNames: 'test-search-index-case7-implicit',
collection: coll0
});

// 05. Assert that `index1` has a property `type` whose value is `search`.
expect(index1).to.have.property('type', 'search');
}
{
// 06. Create a new search index on `coll0` with the `createSearchIndex` helper. Use the following definition:
// ```typescript
// {
// name: 'test-search-index-case7-explicit',
// type: 'search',
// definition: {
// mappings: { dynamic: false }
// }
// }
// ```
const indexName = await coll0.createSearchIndex({
name: 'test-search-index-case7-explicit',
type: 'search',
definition: {
mappings: { dynamic: false }
}
});
// 07. Assert that the command returns the name of the index: `"test-search-index-case7-explicit"`.
expect(indexName).to.equal('test-search-index-case7-explicit');
// 08. Run `coll0.listSearchIndexes('test-search-index-case7-explicit')` repeatedly every 5 seconds until the following
// condition is satisfied and store the value in a variable `index2`:

// - An index with the `name` of `test-search-index-case7-explicit` is present and the index has a field `queryable`
// with a value of `true`.

const [index2] = await waitForIndexes({
predicate: indexes => indexes.every(index => index.queryable),
indexNames: 'test-search-index-case7-explicit',
collection: coll0
});
// 09. Assert that `index2` has a property `type` whose value is `search`.
expect(index2).to.have.property('type', 'search');
}
{
// 10. Create a new vector search index on `coll0` with the `createSearchIndex` helper. Use the following definition:
// ```typescript
// {
// name: 'test-search-index-case7-vector',
// type: 'vectorSearch',
// definition: {
// "fields": [
// {
// "type": "vector",
// "path": "plot_embedding",
// "numDimensions": 1536,
// "similarity": "euclidean",
// },
// ]
// }
// }
// ```

const indexName = await coll0.createSearchIndex({
name: 'test-search-index-case7-vector',
type: 'vectorSearch',
definition: {
fields: [
{
type: 'vector',
path: 'plot_embedding',
numDimensions: 1536,
similarity: 'euclidean'
}
]
}
});
// 11. Assert that the command returns the name of the index: `"test-search-index-case7-vector"`.
expect(indexName).to.equal('test-search-index-case7-vector');
// 12. Run `coll0.listSearchIndexes('test-search-index-case7-vector')` repeatedly every 5 seconds until the following
// condition is satisfied and store the value in a variable `index3`:
// - An index with the `name` of `test-search-index-case7-vector` is present and the index has a field `queryable` with
// a value of `true`.
const [index3] = await waitForIndexes({
predicate: indexes => indexes.every(index => index.queryable),
indexNames: 'test-search-index-case7-vector',
collection: coll0
});

// 13. Assert that `index3` has a property `type` whose value is `vectorSearch`.
expect(index3).to.have.property('type', 'vectorSearch');
}
}
);

it('Case 8: Driver requires explicit type to create a vector search index', async function () {
// 1. Create a collection with the "create" command using a randomly generated name (referred to as `coll0`).
const coll0 = collection;

// 2. Create a new vector search index on `coll0` with the `createSearchIndex` helper. Use the following definition:
// {
// name: 'test-search-index-case8-error',
// definition: {
// fields: [
// {
// type: 'vector',
// path: 'plot_embedding',
// numDimensions: 1536,
// similarity: 'euclidean',
// },
// ]
// }
// }
const definition = {
name: 'test-search-index-case8-error',
definition: {
fields: [
{
type: 'vector',
path: 'plot_embedding',
numDimensions: 1536,
similarity: 'euclidean'
}
]
}
};
const error = await coll0.createSearchIndex(definition).catch(e => e);

// 3. Assert that the command throws an exception containing the string "Attribute mappings missing" due to the `mappings`
// field missing.
expect(error).to.match(/Attribute mappings missing/i);
});
});
});
72 changes: 68 additions & 4 deletions test/spec/index-management/createSearchIndex.json
Expand Up @@ -50,7 +50,8 @@
"mappings": {
"dynamic": true
}
}
},
"type": "search"
}
},
"expectError": {
Expand All @@ -73,7 +74,8 @@
"mappings": {
"dynamic": true
}
}
},
"type": "search"
}
],
"$db": "database0"
Expand All @@ -97,7 +99,8 @@
"dynamic": true
}
},
"name": "test index"
"name": "test index",
"type": "search"
}
},
"expectError": {
Expand All @@ -121,7 +124,68 @@
"dynamic": true
}
},
"name": "test index"
"name": "test index",
"type": "search"
}
],
"$db": "database0"
}
}
}
]
}
]
},
{
"description": "create a vector search index",
"operations": [
{
"name": "createSearchIndex",
"object": "collection0",
"arguments": {
"model": {
"definition": {
"fields": [
{
"type": "vector",
"path": "plot_embedding",
"numDimensions": 1536,
"similarity": "euclidean"
}
]
},
"name": "test index",
"type": "vectorSearch"
}
},
"expectError": {
"isError": true,
"errorContains": "Atlas"
}
}
],
"expectEvents": [
{
"client": "client0",
"events": [
{
"commandStartedEvent": {
"command": {
"createSearchIndexes": "collection0",
"indexes": [
{
"definition": {
"fields": [
{
"type": "vector",
"path": "plot_embedding",
"numDimensions": 1536,
"similarity": "euclidean"
}
]
},
"name": "test index",
"type": "vectorSearch"
}
],
"$db": "database0"
Expand Down
30 changes: 26 additions & 4 deletions test/spec/index-management/createSearchIndex.yml
Expand Up @@ -26,7 +26,7 @@ tests:
- name: createSearchIndex
object: *collection0
arguments:
model: { definition: &definition { mappings: { dynamic: true } } }
model: { definition: &definition { mappings: { dynamic: true } } , type: 'search' }
expectError:
# This test always errors in a non-Atlas environment. The test functions as a unit test by asserting
# that the driver constructs and sends the correct command.
Expand All @@ -39,15 +39,15 @@ tests:
- commandStartedEvent:
command:
createSearchIndexes: *collection0
indexes: [ { definition: *definition } ]
indexes: [ { definition: *definition, type: 'search'} ]
$db: *database0

- description: "name provided for an index definition"
operations:
- name: createSearchIndex
object: *collection0
arguments:
model: { definition: &definition { mappings: { dynamic: true } } , name: 'test index' }
model: { definition: &definition { mappings: { dynamic: true } } , name: 'test index', type: 'search' }
expectError:
# This test always errors in a non-Atlas environment. The test functions as a unit test by asserting
# that the driver constructs and sends the correct command.
Expand All @@ -60,5 +60,27 @@ tests:
- commandStartedEvent:
command:
createSearchIndexes: *collection0
indexes: [ { definition: *definition, name: 'test index' } ]
indexes: [ { definition: *definition, name: 'test index', type: 'search' } ]
$db: *database0

- description: "create a vector search index"
operations:
- name: createSearchIndex
object: *collection0
arguments:
model: { definition: &definition { fields: [ {"type": "vector", "path": "plot_embedding", "numDimensions": 1536, "similarity": "euclidean"} ] }
, name: 'test index', type: 'vectorSearch' }
expectError:
# This test always errors in a non-Atlas environment. The test functions as a unit test by asserting
# that the driver constructs and sends the correct command.
# The expected error message was changed in SERVER-83003. Check for the substring "Atlas" shared by both error messages.
isError: true
errorContains: Atlas
expectEvents:
- client: *client0
events:
- commandStartedEvent:
command:
createSearchIndexes: *collection0
indexes: [ { definition: *definition, name: 'test index', type: 'vectorSearch' } ]
$db: *database0

0 comments on commit 3598c23

Please sign in to comment.