PYTHON-3227 Clustered Indexes for all Collections (#971)

This commit is contained in:
Julius Park 2022-06-15 13:16:22 -07:00 committed by GitHub
parent f45f00b4e5
commit 02a9df69f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 223 additions and 34 deletions

View File

@ -117,7 +117,6 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
read_concern: Optional["ReadConcern"] = None,
session: Optional["ClientSession"] = None,
timeout: Optional[float] = None,
encrypted_fields: Optional[Mapping[str, Any]] = None,
**kwargs: Any,
) -> None:
"""Get / create a Mongo collection.
@ -159,13 +158,11 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
- `session` (optional): a
:class:`~pymongo.client_session.ClientSession` that is used with
the create collection command
- `encrypted_fields`: **(BETA)** Document that describes the encrypted fields for
Queryable Encryption. If provided it will be passed to the create collection command.
- `**kwargs` (optional): additional keyword arguments will
be passed as options for the create collection command
.. versionchanged:: 4.2
Added ``encrypted_fields`` parameter.
Added the ``clusteredIndex`` and ``encryptedFields`` parameters.
.. versionchanged:: 4.0
Removed the reindex, map_reduce, inline_map_reduce,
@ -222,6 +219,7 @@ class Collection(common.BaseObject, Generic[_DocumentType]):
self.__database: Database[_DocumentType] = database
self.__name = name
self.__full_name = "%s.%s" % (self.__database.name, self.__name)
encrypted_fields = kwargs.pop("encryptedFields", None)
if create or kwargs or collation:
if encrypted_fields:
common.validate_is_mapping("encrypted_fields", encrypted_fields)

View File

@ -304,7 +304,6 @@ class Database(common.BaseObject, Generic[_DocumentType]):
read_concern: Optional["ReadConcern"] = None,
session: Optional["ClientSession"] = None,
timeout: Optional[float] = None,
encrypted_fields: Optional[Mapping[str, Any]] = None,
**kwargs: Any,
) -> Collection[_DocumentType]:
"""Create a new :class:`~pymongo.collection.Collection` in this
@ -336,28 +335,6 @@ class Database(common.BaseObject, Generic[_DocumentType]):
:class:`~pymongo.collation.Collation`.
- `session` (optional): a
:class:`~pymongo.client_session.ClientSession`.
- `encrypted_fields`: **(BETA)** Document that describes the encrypted fields for
Queryable Encryption. For example::
{
"escCollection": "enxcol_.encryptedCollection.esc",
"eccCollection": "enxcol_.encryptedCollection.ecc",
"ecocCollection": "enxcol_.encryptedCollection.ecoc",
"fields": [
{
"path": "firstName",
"keyId": Binary.from_uuid(UUID('00000000-0000-0000-0000-000000000000')),
"bsonType": "string",
"queries": {"queryType": "equality"}
},
{
"path": "ssn",
"keyId": Binary.from_uuid(UUID('04104104-1041-0410-4104-104104104104')),
"bsonType": "string"
}
]
} }
- `**kwargs` (optional): additional keyword arguments will
be passed as options for the `create collection command`_
@ -389,11 +366,42 @@ class Database(common.BaseObject, Generic[_DocumentType]):
- ``pipeline`` (list): a list of aggregation pipeline stages
- ``comment`` (str): a user-provided comment to attach to this command.
This option is only supported on MongoDB >= 4.4.
- ``encryptedFields`` (dict): **(BETA)** Document that describes the encrypted fields for
Queryable Encryption. For example::
{
"escCollection": "enxcol_.encryptedCollection.esc",
"eccCollection": "enxcol_.encryptedCollection.ecc",
"ecocCollection": "enxcol_.encryptedCollection.ecoc",
"fields": [
{
"path": "firstName",
"keyId": Binary.from_uuid(UUID('00000000-0000-0000-0000-000000000000')),
"bsonType": "string",
"queries": {"queryType": "equality"}
},
{
"path": "ssn",
"keyId": Binary.from_uuid(UUID('04104104-1041-0410-4104-104104104104')),
"bsonType": "string"
}
]
}
- ``clusteredIndex`` (dict): Document that specifies the clustered index
configuration. It must have the following form::
{
// key pattern must be {_id: 1}
key: <key pattern>, // required
unique: <bool>, // required, must be true
name: <string>, // optional, otherwise automatically generated
v: <int>, // optional, must be 2 if provided
}
- ``changeStreamPreAndPostImages`` (dict): a document with a boolean field ``enabled`` for
enabling pre- and post-images.
.. versionchanged:: 4.2
Added ``encrypted_fields`` parameter.
Added the ``clusteredIndex`` and ``encryptedFields`` parameters.
.. versionchanged:: 3.11
This method is now supported inside multi-document transactions
@ -411,6 +419,7 @@ class Database(common.BaseObject, Generic[_DocumentType]):
.. _create collection command:
https://mongodb.com/docs/manual/reference/command/create
"""
encrypted_fields = kwargs.get("encryptedFields")
if (
not encrypted_fields
and self.client.options.auto_encryption_opts
@ -419,8 +428,14 @@ class Database(common.BaseObject, Generic[_DocumentType]):
encrypted_fields = self.client.options.auto_encryption_opts._encrypted_fields_map.get(
"%s.%s" % (self.name, name)
)
kwargs["encryptedFields"] = encrypted_fields
if encrypted_fields:
common.validate_is_mapping("encrypted_fields", encrypted_fields)
common.validate_is_mapping("encryptedFields", encrypted_fields)
clustered_index = kwargs.get("clusteredIndex")
if clustered_index:
common.validate_is_mapping("clusteredIndex", clustered_index)
with self.__client._tmp_session(session) as s:
# Skip this check in a transaction where listCollections is not
@ -439,7 +454,6 @@ class Database(common.BaseObject, Generic[_DocumentType]):
read_concern,
session=s,
timeout=timeout,
encrypted_fields=encrypted_fields,
**kwargs,
)

View File

@ -0,0 +1,177 @@
{
"description": "clustered-indexes",
"schemaVersion": "1.4",
"runOnRequirements": [
{
"minServerVersion": "5.3",
"serverless": "forbid"
}
],
"createEntities": [
{
"client": {
"id": "client0"
}
},
{
"database": {
"id": "database0",
"client": "client0",
"databaseName": "ts-tests"
}
},
{
"collection": {
"id": "collection0",
"database": "database0",
"collectionName": "test"
}
}
],
"initialData": [
{
"collectionName": "test",
"databaseName": "ts-tests",
"documents": []
}
],
"tests": [
{
"description": "createCollection with clusteredIndex",
"operations": [
{
"name": "dropCollection",
"object": "database0",
"arguments": {
"collection": "test"
}
},
{
"name": "createCollection",
"object": "database0",
"arguments": {
"collection": "test",
"clusteredIndex": {
"key": {
"_id": 1
},
"unique": true,
"name": "test index"
}
}
},
{
"name": "assertCollectionExists",
"object": "testRunner",
"arguments": {
"databaseName": "ts-tests",
"collectionName": "test"
}
}
]
},
{
"description": "listCollections includes clusteredIndex",
"operations": [
{
"name": "dropCollection",
"object": "database0",
"arguments": {
"collection": "test"
}
},
{
"name": "createCollection",
"object": "database0",
"arguments": {
"collection": "test",
"clusteredIndex": {
"key": {
"_id": 1
},
"unique": true,
"name": "test index"
}
}
},
{
"name": "listCollections",
"object": "database0",
"arguments": {
"filter": {
"name": {
"$eq": "test"
}
}
},
"expectResult": [
{
"name": "test",
"options": {
"clusteredIndex": {
"key": {
"_id": 1
},
"unique": true,
"name": "test index",
"v": {
"$$type": [
"int",
"long"
]
}
}
}
}
]
}
]
},
{
"description": "listIndexes returns the index",
"operations": [
{
"name": "dropCollection",
"object": "database0",
"arguments": {
"collection": "test"
}
},
{
"name": "createCollection",
"object": "database0",
"arguments": {
"collection": "test",
"clusteredIndex": {
"key": {
"_id": 1
},
"unique": true,
"name": "test index"
}
}
},
{
"name": "listIndexes",
"object": "collection0",
"expectResult": [
{
"key": {
"_id": 1
},
"name": "test index",
"clustered": true,
"unique": true,
"v": {
"$$type": [
"int",
"long"
]
}
}
]
}
]
}
]
}

View File

@ -658,7 +658,9 @@ class TestSpec(SpecRunner):
kwargs["codec_options"] = OPTS
if not data:
kwargs["write_concern"] = wc
db.create_collection(coll_name, **kwargs, encrypted_fields=encrypted_fields)
if encrypted_fields:
kwargs["encryptedFields"] = encrypted_fields
db.create_collection(coll_name, **kwargs)
coll = db[coll_name]
if data:
# Load data.

View File

@ -996,7 +996,7 @@ class UnifiedSpecTestMixinV1(IntegrationTest):
def _collectionOperation_listIndexes(self, target, *args, **kwargs):
if "batch_size" in kwargs:
self.skipTest("PyMongo does not support batch_size for list_indexes")
return target.list_indexes(*args, **kwargs)
return list(target.list_indexes(*args, **kwargs))
def _collectionOperation_listIndexNames(self, target, *args, **kwargs):
self.skipTest("PyMongo does not support list_index_names")

View File

@ -1002,8 +1002,6 @@ def parse_spec_options(opts):
if "maxCommitTimeMS" in opts:
opts["max_commit_time_ms"] = opts.pop("maxCommitTimeMS")
if "encryptedFields" in opts:
opts["encrypted_fields"] = opts.pop("encryptedFields")
if "hint" in opts:
hint = opts.pop("hint")
if not isinstance(hint, str):