Skip to content

Commit 267a54d

Browse files
DRIVERS-2630: add e2e testing for search index commands and clarifications to search index spec (#1442)
1 parent 501b7a1 commit 267a54d

13 files changed

+250
-42
lines changed

source/client-side-operations-timeout/client-side-operations-timeout.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ specifications:
131131
- `Enumerating Collections <../enumerate-collections.rst>`__
132132
- `Enumerating Databases <../enumerate-databases.rst>`__
133133
- `GridFS <../gridfs/gridfs-spec.rst>`__
134-
- `Index Management <../index-management.rst>`__
134+
- `Index Management <../index-management/index-management.rst>`__
135135
- `Transactions <../transactions/transactions.rst>`__
136136
- `Convenient API for Transactions <../transactions-convenient-api/transactions-convenient-api.rst>`__
137137

source/index-management/index-management.rst

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,10 @@ Index View API
414414
* For drivers that cannot make IndexView iterable, they MUST implement this method to
415415
* return a list of indexes. In the case of async drivers, this MAY return a Future<Cursor>
416416
* or language/implementation equivalent.
417+
*
418+
* If drivers are unable to make the IndexView iterable, they MAY opt to provide the options for
419+
* listing search indexes via the `list` method instead of the `Collection.indexes` method.
420+
417421
*/
418422
list(): Cursor;
419423
@@ -873,8 +877,8 @@ search index management helpers.
873877
options as outline in the `CRUD specification <https://github.com/mongodb/specifications/blob/master/source/crud/crud.rst#read>`_. Drivers MAY combine the aggregation options with
874878
any future ``listSearchIndexes`` stage options, if that is idiomatic for a driver's language.
875879
876-
Notes
877-
-----
880+
Asynchronicity
881+
--------------
878882
879883
The search index commands are asynchronous and return from the server before the index is successfully updated, created or dropped.
880884
In order to determine when an index has been created / updated, users are expected to run the ``listSearchIndexes`` repeatedly
@@ -888,7 +892,34 @@ An example, from Javascript:
888892
while (!(await collection.listSearchIndexes({ name }).hasNext())) {
889893
await setTimeout(1000);
890894
}
891-
895+
896+
Where are read concern and write concern?
897+
-----------------------------------------
898+
899+
These commands internally proxy the search index management commands to a separate process that runs alongside an Atlas cluster. As such, read concern and
900+
write concern are not relevant for the search index management commands.
901+
902+
Consistency with Existing APIs
903+
------------------------------
904+
905+
Drivers SHOULD strive for a search index management API that is as consistent as possible with their existing index management API.
906+
907+
NamespaceNotFound Errors
908+
------------------------
909+
910+
Some drivers suppress NamespaceNotFound errors for CRUD helpers. Drivers MAY suppress NamespaceNotFound errors from
911+
the search index management helpers.
912+
913+
Drivers MUST suppress NamespaceNotFound errors for the ``dropSearchIndex`` helper. Drop operations should be idempotent:
914+
915+
.. code:: typescript
916+
917+
await collection.dropSearchIndex('my-test-index');
918+
// subsequent calls should behave the same for the user as the first call
919+
await collection.dropSearchIndex('my-test-index');
920+
await collection.dropSearchIndex('my-test-index');
921+
922+
892923
Common Interfaces
893924
-----------------
894925
@@ -902,6 +933,11 @@ Common Interfaces
902933
name: Optional<string>;
903934
}
904935
936+
interface SearchIndexOptions {
937+
// The name for this index, if present.
938+
name: Optional<string>;
939+
}
940+
905941
/**
906942
* The following interfaces are empty but are provided as placeholders for drivers that cannot
907943
* add options in a non-breaking manner, if options are added in the future.
@@ -925,8 +961,10 @@ Standard API for Search Indexes
925961
* @note Drivers MAY opt to implement this method signature, the signature that
926962
* takes an SearchIndexModel as a parameter, or for those languages with method
927963
* overloading MAY decide to implement both.
964+
*
965+
* @note Drivers MAY combine the `indexOptions` with the `createSearchIndexOptions`, if that is idiomatic for their language.
928966
*/
929-
createSearchIndex(name: String, definition: Document, options: Optional<CreateSearchIndexOptions>): String;
967+
createSearchIndex(definition: Document, indexOptions: Optional<SearchIndexOptions>, createSearchIndexOptions: Optional<CreateSearchIndexOptions>): String;
930968
931969
/**
932970
* Convenience method for creating a single index.
@@ -974,7 +1012,7 @@ Index View API for Search Indexes
9741012
/**
9751013
* Returns the search index view for this collection.
9761014
*/
977-
searchIndexes(name: Optional<String>, aggregateOptions: Optional<AggregationOptions>, options: Optional<ListSearchIndexesOptions>): SearchIndexView;
1015+
searchIndexes(name: Optional<String>, aggregateOptions: Optional<AggregationOptions>, options: Optional<ListSearchIndexOptions>): SearchIndexView;
9781016
}
9791017
9801018
interface SearchIndexView extends Iterable<Document> {
@@ -990,6 +1028,9 @@ Index View API for Search Indexes
9901028
* For drivers that cannot make SearchIndexView iterable, they MUST implement this method to
9911029
* return a list of indexes. In the case of async drivers, this MAY return a Future<Cursor>
9921030
* or language/implementation equivalent.
1031+
*
1032+
* If drivers are unable to make the SearchIndexView iterable, they MAY opt to provide the options for
1033+
* listing search indexes via the `list` method instead of the `Collection.searchIndexes` method.
9931034
*/
9941035
list(): Cursor<Document>;
9951036
@@ -1002,8 +1043,10 @@ Index View API for Search Indexes
10021043
* @note Drivers MAY opt to implement this method signature, the signature that
10031044
* takes an SearchIndexModel as a parameter, or for those languages with method
10041045
* overloading MAY decide to implement both.
1046+
*
1047+
* @note Drivers MAY combine the `indexOptions` with the `createSearchIndexOptions`, if that is idiomatic for their language.
10051048
*/
1006-
createOne(name: String, definition: Document, options: Optional<CreateSearchIndexOptions>): String;
1049+
createOne(definition: Document, indexOptions: Optional<SearchIndexOptions>, createSearchIndexOptions: Optional<CreateSearchIndexOptions>): String;
10071050
10081051
/**
10091052
* This is a convenience method for creating a single index.
@@ -1031,7 +1074,7 @@ Index View API for Search Indexes
10311074
/**
10321075
* Updates a single search index from the collection by the index name.
10331076
*/
1034-
updateOne(name: String, options: Optional<UpdateSearchIndexOptions>): Result;
1077+
updateOne(name: String, definition: Document, options: Optional<UpdateSearchIndexOptions>): Result;
10351078
}
10361079
10371080
---------
@@ -1093,3 +1136,4 @@ Changelog
10931136
:2023-05-10: Merge index enumeration and index management specs and get rid of references
10941137
to legacy server versions.
10951138
:2023-05-18: Add the search index management API.
1139+
:2023-07-27: Add search index management clarifications.

source/index-management/tests/README.rst

Lines changed: 147 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ For each of the configurations:
3131
indicated indexes act on
3232

3333
Tests
34-
-----
3534

3635
- Run the driver's method that returns a list of index names, and:
3736

@@ -45,4 +44,150 @@ Tests
4544
- verify the "unique" flags show up for the unique index
4645
- verify there are no duplicates in the returned list
4746
- if the result consists of statically defined index models that include an ``ns`` field, verify
48-
that its value is accurate
47+
that its value is accurate
48+
49+
Search Index Management Helpers
50+
-------------------------------
51+
52+
These tests are intended to smoke test the search management helpers end-to-end against a live Atlas cluster.
53+
54+
The search index management commands are asynchronous and mongod/mongos returns before the changes to a clusters' search indexes have completed. When
55+
these prose tests specify "waiting for the changes", drivers should repeatedly poll the cluster with ``listSearchIndexes``
56+
until the changes are visible. Each test specifies the condition that is considered "ready". For example, when creating a
57+
new search index, waiting until the inserted index has a status ``queryable: true`` indicates that the index was successfully
58+
created.
59+
60+
The commands tested in these prose tests take a while to successfully complete. Drivers should raise the timeout for each test to avoid timeout errors if
61+
the test timeout is too low. 5 minutes is a sufficiently large timeout that any timeout that occurs indicates a real failure, but this value is not required and can be tweaked per-driver.
62+
63+
There is a server-side limitation that prevents multiple search indexes from being created with the same name, definition and
64+
collection name. This limitation does not take into account collection uuid. Because these commands are asynchronous, any cleanup
65+
code that may run after a test (cleaning a database or dropping search indexes) may not have completed by the next iteration of the
66+
test (or the next test run, if running locally). To address this issue, each test uses a randomly generated collection name. Drivers
67+
may generate this collection name however they like, but a suggested implementation is a hex representation of an
68+
ObjectId (``new ObjectId().toHexString()`` in Node).
69+
70+
Setup
71+
~~~~~
72+
73+
These tests must run against an Atlas cluster with a 7.0+ server. `Scripts are available <https://github.com/mongodb-labs/drivers-evergreen-tools/tree/master/.evergreen/atlas>`_ in drivers-evergreen-tools which can setup and teardown
74+
Atlas clusters. To ensure that the Atlas cluster is cleaned up after each CI run, drivers should configure evergreen to run these tests
75+
as a part of a task group. Be sure that the cluster gets torn down!
76+
77+
When working locally on these tests, the same Atlas setup and teardown scripts can be used locally to provision a cluster for development.
78+
79+
Case 1: Driver can successfully create and list search indexes
80+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
81+
82+
#. Create a collection with the "create" command using a randomly generated name (referred to as ``coll0``).
83+
#. Create a new search index on ``coll0`` with the ``createSearchIndex`` helper. Use the following definition:
84+
85+
.. code:: typescript
86+
87+
{
88+
name: 'test-search-index',
89+
definition: {
90+
mappings: { dynamic: false }
91+
}
92+
}
93+
94+
#. Assert that the command returns the name of the index: ``"test-search-index"``.
95+
#. Run ``coll0.listSearchIndexes()`` repeatedly every 5 seconds until the following condition is satisfied and store the value in a variable ``index``:
96+
1. An index with the ``name`` of ``test-search-index`` is present and the index has a field ``queryable`` with a value of ``true``.
97+
98+
#. Assert that ``index`` has a property ``latestDefinition`` whose value is ``{ 'mappings': { 'dynamic': false } }``
99+
100+
Case 2: Driver can successfully create multiple indexes in batch
101+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
102+
103+
#. Create a collection with the "create" command using a randomly generated name (referred to as ``coll0``).
104+
#. Create two new search indexes on ``coll0`` with the ``createSearchIndexes`` helper. Use the following
105+
definitions when creating the indexes. These definitions are referred to as ``indexDefinitions``.
106+
107+
.. code:: typescript
108+
109+
{
110+
name: 'test-search-index-1',
111+
definition: {
112+
mappings: { dynamic: false }
113+
}
114+
}
115+
{
116+
name: 'test-search-index-2',
117+
definition: {
118+
mappings: { dynamic: false }
119+
}
120+
}
121+
122+
#. Assert that the command returns an array containing the new indexes' names: ``["test-search-index-1", "test-search-index-2"]``.
123+
#. Run ``coll0.listSearchIndexes()`` repeatedly every 5 seconds until the following condition is satisfied.
124+
1. An index with the ``name`` of ``test-search-index-1`` is present and index has a field ``queryable`` with the value of ``true``. Store result in ``index1``.
125+
2. An index with the ``name`` of ``test-search-index-2`` is present and index has a field ``queryable`` with the value of ``true``. Store result in ``index2``.
126+
#. Assert that ``index1`` and ``index2`` have the property ``latestDefinition`` whose value is ``{ "mappings" : { "dynamic" : false } }``
127+
Case 3: Driver can successfully drop search indexes
128+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
129+
130+
#. Create a collection with the "create" command using a randomly generated name (referred to as ``coll0``).
131+
#. Create a new search index on ``coll0`` with the following definition:
132+
133+
.. code:: typescript
134+
135+
{
136+
name: 'test-search-index',
137+
definition: {
138+
mappings: { dynamic: false }
139+
}
140+
}
141+
142+
#. Assert that the command returns the name of the index: ``"test-search-index"``.
143+
#. Run ``coll0.listSearchIndexes()`` repeatedly every 5 seconds until the following condition is satisfied:
144+
1. An index with the ``name`` of ``test-search-index`` is present and index has a field ``queryable`` with the value of ``true``.
145+
146+
#. Run a ``dropSearchIndex`` on ``coll0``, using ``test-search-index`` for the name.
147+
#. Run ``coll0.listSearchIndexes()`` repeatedly every 5 seconds until ``listSearchIndexes`` returns an empty array.
148+
149+
This test fails if it times out waiting for the deletion to succeed.
150+
151+
Case 4: Driver can update a search index
152+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
153+
154+
#. Create a collection with the "create" command using a randomly generated name (referred to as ``coll0``).
155+
#. Create a new search index on ``coll0`` with the following definition:
156+
157+
.. code:: typescript
158+
159+
{
160+
name: 'test-search-index',
161+
definition: {
162+
mappings: { dynamic: false }
163+
}
164+
}
165+
166+
#. Assert that the command returns the name of the index: ``"test-search-index"``.
167+
#. Run ``coll0.listSearchIndexes()`` repeatedly every 5 seconds until the following condition is satisfied:
168+
1. An index with the ``name`` of ``test-search-index`` is present and index has a field ``queryable`` with the value of ``true``.
169+
170+
#. Run a ``updateSearchIndex`` on ``coll0``, using the following definition.
171+
172+
.. code:: typescript
173+
174+
{
175+
name: 'test-search-index',
176+
definition: {
177+
mappings: { dynamic: true }
178+
}
179+
}
180+
181+
#. Assert that the command does not error and the server responds with a success.
182+
#. Run ``coll0.listSearchIndexes()`` repeatedly every 5 seconds until the following condition is satisfied:
183+
1. An index with the ``name`` of ``test-search-index`` is present. This index is referred to as ``index``.
184+
2. The index has a field ``queryable`` with a value of ``true`` and has a field ``status`` with the value of ``READY``.
185+
186+
#. Assert that an index is present with the name ``test-search-index`` and the definition has a
187+
property ``latestDefinition`` whose value is ``{ 'mappings': { 'dynamic': true } }``.
188+
189+
Case 5: ``dropSearchIndex`` suppresses namespace not found errors
190+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
191+
192+
#. Create a driver-side collection object for a randomly generated collection name. Do not create this collection on the server.
193+
#. Run a ``dropSearchIndex`` command and assert that no error is thrown.

source/index-management/tests/createSearchIndex.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@
5454
}
5555
},
5656
"expectError": {
57-
"isError": true
57+
"isError": true,
58+
"errorContains": "Search index commands are only supported with Atlas"
5859
}
5960
}
6061
],
@@ -100,7 +101,8 @@
100101
}
101102
},
102103
"expectError": {
103-
"isError": true
104+
"isError": true,
105+
"errorContains": "Search index commands are only supported with Atlas"
104106
}
105107
}
106108
],

source/index-management/tests/createSearchIndex.yml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ tests:
2828
arguments:
2929
model: { definition: &definition { mappings: { dynamic: true } } }
3030
expectError:
31-
# Search indexes are only available on 7.0+ atlas clusters. DRIVERS-2630 will add e2e testing
32-
# against an Atlas cluster and the expectError will be removed.
31+
# This test always errors in a non-Atlas environment. The test functions as a unit test by asserting
32+
# that the driver constructs and sends the correct command.
3333
isError: true
34+
errorContains: Search index commands are only supported with Atlas
3435
expectEvents:
3536
- client: *client0
3637
events:
@@ -47,9 +48,10 @@ tests:
4748
arguments:
4849
model: { definition: &definition { mappings: { dynamic: true } } , name: 'test index' }
4950
expectError:
50-
# Search indexes are only available on 7.0+ atlas clusters. DRIVERS-2630 will add e2e testing
51-
# against an Atlas cluster and the expectError will be removed.
51+
# This test always errors in a non-Atlas environment. The test functions as a unit test by asserting
52+
# that the driver constructs and sends the correct command.
5253
isError: true
54+
errorContains: Search index commands are only supported with Atlas
5355
expectEvents:
5456
- client: *client0
5557
events:

source/index-management/tests/createSearchIndexes.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
"models": []
4949
},
5050
"expectError": {
51-
"isError": true
51+
"isError": true,
52+
"errorContains": "Search index commands are only supported with Atlas"
5253
}
5354
}
5455
],
@@ -87,7 +88,8 @@
8788
]
8889
},
8990
"expectError": {
90-
"isError": true
91+
"isError": true,
92+
"errorContains": "Search index commands are only supported with Atlas"
9193
}
9294
}
9395
],
@@ -135,7 +137,8 @@
135137
]
136138
},
137139
"expectError": {
138-
"isError": true
140+
"isError": true,
141+
"errorContains": "Search index commands are only supported with Atlas"
139142
}
140143
}
141144
],

0 commit comments

Comments
 (0)