PYTHON-4037 Avoid Appending Write/Read Concern in Atlas Search Index Helper Commands (#1570)
This commit is contained in:
parent
44e47304ff
commit
c154c6b67b
@ -41,6 +41,8 @@ PyMongo 4.7 brings a number of improvements including:
|
|||||||
:attr:`pymongo.monitoring.ConnectionReadyEvent.duration` properties.
|
:attr:`pymongo.monitoring.ConnectionReadyEvent.duration` properties.
|
||||||
- Added the ``type`` and ``kwargs`` arguments to :class:`~pymongo.operations.SearchIndexModel` to enable
|
- Added the ``type`` and ``kwargs`` arguments to :class:`~pymongo.operations.SearchIndexModel` to enable
|
||||||
creating vector search indexes in MongoDB Atlas.
|
creating vector search indexes in MongoDB Atlas.
|
||||||
|
- Fixed a bug where ``read_concern`` and ``write_concern`` were improperly added to
|
||||||
|
:meth:`~pymongo.collection.Collection.list_search_indexes` queries.
|
||||||
|
|
||||||
|
|
||||||
Unavoidable breaking changes
|
Unavoidable breaking changes
|
||||||
|
|||||||
@ -72,6 +72,7 @@ from pymongo.operations import (
|
|||||||
_IndexList,
|
_IndexList,
|
||||||
_Op,
|
_Op,
|
||||||
)
|
)
|
||||||
|
from pymongo.read_concern import DEFAULT_READ_CONCERN, ReadConcern
|
||||||
from pymongo.read_preferences import ReadPreference, _ServerMode
|
from pymongo.read_preferences import ReadPreference, _ServerMode
|
||||||
from pymongo.results import (
|
from pymongo.results import (
|
||||||
BulkWriteResult,
|
BulkWriteResult,
|
||||||
@ -81,7 +82,7 @@ from pymongo.results import (
|
|||||||
UpdateResult,
|
UpdateResult,
|
||||||
)
|
)
|
||||||
from pymongo.typings import _CollationIn, _DocumentType, _DocumentTypeArg, _Pipeline
|
from pymongo.typings import _CollationIn, _DocumentType, _DocumentTypeArg, _Pipeline
|
||||||
from pymongo.write_concern import WriteConcern, validate_boolean
|
from pymongo.write_concern import DEFAULT_WRITE_CONCERN, WriteConcern, validate_boolean
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
@ -119,7 +120,6 @@ if TYPE_CHECKING:
|
|||||||
from pymongo.collation import Collation
|
from pymongo.collation import Collation
|
||||||
from pymongo.database import Database
|
from pymongo.database import Database
|
||||||
from pymongo.pool import Connection
|
from pymongo.pool import Connection
|
||||||
from pymongo.read_concern import ReadConcern
|
|
||||||
from pymongo.server import Server
|
from pymongo.server import Server
|
||||||
|
|
||||||
|
|
||||||
@ -2364,7 +2364,10 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
|
|||||||
pipeline = [{"$listSearchIndexes": {"name": name}}]
|
pipeline = [{"$listSearchIndexes": {"name": name}}]
|
||||||
|
|
||||||
coll = self.with_options(
|
coll = self.with_options(
|
||||||
codec_options=DEFAULT_CODEC_OPTIONS, read_preference=ReadPreference.PRIMARY
|
codec_options=DEFAULT_CODEC_OPTIONS,
|
||||||
|
read_preference=ReadPreference.PRIMARY,
|
||||||
|
write_concern=DEFAULT_WRITE_CONCERN,
|
||||||
|
read_concern=DEFAULT_READ_CONCERN,
|
||||||
)
|
)
|
||||||
cmd = _CollectionAggregationCommand(
|
cmd = _CollectionAggregationCommand(
|
||||||
coll,
|
coll,
|
||||||
|
|||||||
@ -50,8 +50,7 @@
|
|||||||
"mappings": {
|
"mappings": {
|
||||||
"dynamic": true
|
"dynamic": true
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"type": "search"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"expectError": {
|
"expectError": {
|
||||||
@ -74,8 +73,7 @@
|
|||||||
"mappings": {
|
"mappings": {
|
||||||
"dynamic": true
|
"dynamic": true
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"type": "search"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"$db": "database0"
|
"$db": "database0"
|
||||||
@ -99,8 +97,7 @@
|
|||||||
"dynamic": true
|
"dynamic": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"name": "test index",
|
"name": "test index"
|
||||||
"type": "search"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"expectError": {
|
"expectError": {
|
||||||
@ -124,68 +121,7 @@
|
|||||||
"dynamic": true
|
"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"
|
"$db": "database0"
|
||||||
|
|||||||
@ -83,8 +83,7 @@
|
|||||||
"mappings": {
|
"mappings": {
|
||||||
"dynamic": true
|
"dynamic": true
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"type": "search"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -108,8 +107,7 @@
|
|||||||
"mappings": {
|
"mappings": {
|
||||||
"dynamic": true
|
"dynamic": true
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
"type": "search"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"$db": "database0"
|
"$db": "database0"
|
||||||
@ -134,8 +132,7 @@
|
|||||||
"dynamic": true
|
"dynamic": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"name": "test index",
|
"name": "test index"
|
||||||
"type": "search"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -160,70 +157,7 @@
|
|||||||
"dynamic": true
|
"dynamic": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"name": "test index",
|
"name": "test index"
|
||||||
"type": "search"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"$db": "database0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "create a vector search index",
|
|
||||||
"operations": [
|
|
||||||
{
|
|
||||||
"name": "createSearchIndexes",
|
|
||||||
"object": "collection0",
|
|
||||||
"arguments": {
|
|
||||||
"models": [
|
|
||||||
{
|
|
||||||
"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"
|
"$db": "database0"
|
||||||
|
|||||||
252
test/index_management/searchIndexIgnoresReadWriteConcern.json
Normal file
252
test/index_management/searchIndexIgnoresReadWriteConcern.json
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
{
|
||||||
|
"description": "search index operations ignore read and write concern",
|
||||||
|
"schemaVersion": "1.4",
|
||||||
|
"createEntities": [
|
||||||
|
{
|
||||||
|
"client": {
|
||||||
|
"id": "client0",
|
||||||
|
"useMultipleMongoses": false,
|
||||||
|
"uriOptions": {
|
||||||
|
"readConcernLevel": "local",
|
||||||
|
"w": 1
|
||||||
|
},
|
||||||
|
"observeEvents": [
|
||||||
|
"commandStartedEvent"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"database": {
|
||||||
|
"id": "database0",
|
||||||
|
"client": "client0",
|
||||||
|
"databaseName": "database0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collection": {
|
||||||
|
"id": "collection0",
|
||||||
|
"database": "database0",
|
||||||
|
"collectionName": "collection0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"runOnRequirements": [
|
||||||
|
{
|
||||||
|
"minServerVersion": "7.0.0",
|
||||||
|
"topologies": [
|
||||||
|
"replicaset",
|
||||||
|
"load-balanced",
|
||||||
|
"sharded"
|
||||||
|
],
|
||||||
|
"serverless": "forbid"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"description": "createSearchIndex ignores read and write concern",
|
||||||
|
"operations": [
|
||||||
|
{
|
||||||
|
"name": "createSearchIndex",
|
||||||
|
"object": "collection0",
|
||||||
|
"arguments": {
|
||||||
|
"model": {
|
||||||
|
"definition": {
|
||||||
|
"mappings": {
|
||||||
|
"dynamic": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expectError": {
|
||||||
|
"isError": true,
|
||||||
|
"errorContains": "Atlas"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expectEvents": [
|
||||||
|
{
|
||||||
|
"client": "client0",
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"commandStartedEvent": {
|
||||||
|
"command": {
|
||||||
|
"createSearchIndexes": "collection0",
|
||||||
|
"indexes": [
|
||||||
|
{
|
||||||
|
"definition": {
|
||||||
|
"mappings": {
|
||||||
|
"dynamic": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"$db": "database0",
|
||||||
|
"writeConcern": {
|
||||||
|
"$$exists": false
|
||||||
|
},
|
||||||
|
"readConcern": {
|
||||||
|
"$$exists": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "createSearchIndexes ignores read and write concern",
|
||||||
|
"operations": [
|
||||||
|
{
|
||||||
|
"name": "createSearchIndexes",
|
||||||
|
"object": "collection0",
|
||||||
|
"arguments": {
|
||||||
|
"models": []
|
||||||
|
},
|
||||||
|
"expectError": {
|
||||||
|
"isError": true,
|
||||||
|
"errorContains": "Atlas"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expectEvents": [
|
||||||
|
{
|
||||||
|
"client": "client0",
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"commandStartedEvent": {
|
||||||
|
"command": {
|
||||||
|
"createSearchIndexes": "collection0",
|
||||||
|
"indexes": [],
|
||||||
|
"$db": "database0",
|
||||||
|
"writeConcern": {
|
||||||
|
"$$exists": false
|
||||||
|
},
|
||||||
|
"readConcern": {
|
||||||
|
"$$exists": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "dropSearchIndex ignores read and write concern",
|
||||||
|
"operations": [
|
||||||
|
{
|
||||||
|
"name": "dropSearchIndex",
|
||||||
|
"object": "collection0",
|
||||||
|
"arguments": {
|
||||||
|
"name": "test index"
|
||||||
|
},
|
||||||
|
"expectError": {
|
||||||
|
"isError": true,
|
||||||
|
"errorContains": "Atlas"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expectEvents": [
|
||||||
|
{
|
||||||
|
"client": "client0",
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"commandStartedEvent": {
|
||||||
|
"command": {
|
||||||
|
"dropSearchIndex": "collection0",
|
||||||
|
"name": "test index",
|
||||||
|
"$db": "database0",
|
||||||
|
"writeConcern": {
|
||||||
|
"$$exists": false
|
||||||
|
},
|
||||||
|
"readConcern": {
|
||||||
|
"$$exists": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "listSearchIndexes ignores read and write concern",
|
||||||
|
"operations": [
|
||||||
|
{
|
||||||
|
"name": "listSearchIndexes",
|
||||||
|
"object": "collection0",
|
||||||
|
"expectError": {
|
||||||
|
"isError": true,
|
||||||
|
"errorContains": "Atlas"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expectEvents": [
|
||||||
|
{
|
||||||
|
"client": "client0",
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"commandStartedEvent": {
|
||||||
|
"command": {
|
||||||
|
"aggregate": "collection0",
|
||||||
|
"pipeline": [
|
||||||
|
{
|
||||||
|
"$listSearchIndexes": {}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"writeConcern": {
|
||||||
|
"$$exists": false
|
||||||
|
},
|
||||||
|
"readConcern": {
|
||||||
|
"$$exists": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "updateSearchIndex ignores the read and write concern",
|
||||||
|
"operations": [
|
||||||
|
{
|
||||||
|
"name": "updateSearchIndex",
|
||||||
|
"object": "collection0",
|
||||||
|
"arguments": {
|
||||||
|
"name": "test index",
|
||||||
|
"definition": {}
|
||||||
|
},
|
||||||
|
"expectError": {
|
||||||
|
"isError": true,
|
||||||
|
"errorContains": "Atlas"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"expectEvents": [
|
||||||
|
{
|
||||||
|
"client": "client0",
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"commandStartedEvent": {
|
||||||
|
"command": {
|
||||||
|
"updateSearchIndex": "collection0",
|
||||||
|
"name": "test index",
|
||||||
|
"definition": {},
|
||||||
|
"$db": "database0",
|
||||||
|
"writeConcern": {
|
||||||
|
"$$exists": false
|
||||||
|
},
|
||||||
|
"readConcern": {
|
||||||
|
"$$exists": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -30,6 +30,8 @@ from test.utils import AllowListEventListener, EventListener
|
|||||||
from pymongo import MongoClient
|
from pymongo import MongoClient
|
||||||
from pymongo.errors import OperationFailure
|
from pymongo.errors import OperationFailure
|
||||||
from pymongo.operations import SearchIndexModel
|
from pymongo.operations import SearchIndexModel
|
||||||
|
from pymongo.read_concern import ReadConcern
|
||||||
|
from pymongo.write_concern import WriteConcern
|
||||||
|
|
||||||
_TEST_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "index_management")
|
_TEST_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "index_management")
|
||||||
|
|
||||||
@ -257,6 +259,33 @@ class TestSearchIndexProse(SearchIndexIntegrationBase):
|
|||||||
# Run a ``dropSearchIndex`` command and assert that no error is thrown.
|
# Run a ``dropSearchIndex`` command and assert that no error is thrown.
|
||||||
coll0.drop_search_index("foo")
|
coll0.drop_search_index("foo")
|
||||||
|
|
||||||
|
def test_case_6(self):
|
||||||
|
"""Driver can successfully create and list search indexes with non-default readConcern and writeConcern."""
|
||||||
|
# Create a collection with the "create" command using a randomly generated name (referred to as ``coll0``).
|
||||||
|
coll0 = self.db[f"col{uuid.uuid4()}"]
|
||||||
|
coll0.insert_one({})
|
||||||
|
|
||||||
|
# Apply a write concern ``WriteConcern(w=1)`` and a read concern with ``ReadConcern(level="majority")`` to ``coll0``.
|
||||||
|
coll0 = coll0.with_options(
|
||||||
|
write_concern=WriteConcern(w="1"), read_concern=ReadConcern(level="majority")
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a new search index on ``coll0`` with the ``createSearchIndex`` helper.
|
||||||
|
name = "test-search-index-case6"
|
||||||
|
model = {"name": name, "definition": {"mappings": {"dynamic": False}}}
|
||||||
|
resp = coll0.create_search_index(model)
|
||||||
|
|
||||||
|
# Assert that the command returns the name of the index: ``"test-search-index-case6"``.
|
||||||
|
self.assertEqual(resp, name)
|
||||||
|
|
||||||
|
# Run ``coll0.listSearchIndexes()`` repeatedly every 5 seconds until the following condition is satisfied and store the value in a variable ``index``:
|
||||||
|
# - An index with the ``name`` of ``test-search-index-case6`` is present and the index has a field ``queryable`` with a value of ``true``.
|
||||||
|
index = self.wait_for_ready(coll0, name)
|
||||||
|
|
||||||
|
# Assert that ``index`` has a property ``latestDefinition`` whose value is ``{ 'mappings': { 'dynamic': false } }``
|
||||||
|
self.assertIn("latestDefinition", index)
|
||||||
|
self.assertEqual(index["latestDefinition"], model["definition"])
|
||||||
|
|
||||||
def test_case_7(self):
|
def test_case_7(self):
|
||||||
"""Driver handles index types."""
|
"""Driver handles index types."""
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user