Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions pymongo/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ def __init__(
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.
Expand Down Expand Up @@ -159,13 +158,11 @@ def __init__(
- `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,
Expand Down Expand Up @@ -222,6 +219,7 @@ def __init__(
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)
Expand Down
66 changes: 40 additions & 26 deletions pymongo/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,6 @@ def create_collection(
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
Expand Down Expand Up @@ -336,28 +335,6 @@ def create_collection(
: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`_
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given this description of kwargs, I wonder if it would be more consistent to not add an explicit "clustered_index" parameter and instead document clusteredIndex as an acceptable kwarg below. If so, we probably do the same for encrypted_fields.

We would make the same change to Collection.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM!


Expand Down Expand Up @@ -389,11 +366,42 @@ def create_collection(
- ``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
Expand All @@ -411,6 +419,7 @@ def create_collection(
.. _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
Expand All @@ -419,8 +428,14 @@ def create_collection(
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
Expand All @@ -439,7 +454,6 @@ def create_collection(
read_concern,
session=s,
timeout=timeout,
encrypted_fields=encrypted_fields,
**kwargs,
)

Expand Down
177 changes: 177 additions & 0 deletions test/collection_management/clustered-indexes.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
}
]
}
]
}
]
}
4 changes: 3 additions & 1 deletion test/test_encryption.py
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,9 @@ def setup_scenario(self, scenario_def):
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.
Expand Down
2 changes: 1 addition & 1 deletion test/unified_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,7 @@ def _collectionOperation_count(self, target, *args, **kwargs):
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")
Expand Down
2 changes: 0 additions & 2 deletions test/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down