Skip to content

Commit ee2badf

Browse files
authored
PYTHON-3524 Support passing list of strings to create_index (#1121)
1 parent 0c6aacb commit ee2badf

File tree

7 files changed

+72
-20
lines changed

7 files changed

+72
-20
lines changed

doc/changelog.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
Changelog
22
=========
33

4+
Changes in Version 4.4
5+
-----------------------
6+
7+
- Added support for passing a list containing (key, direction) pairs
8+
or keys to :meth:`~pymongo.collection.Collection.create_index`.
9+
10+
Issues Resolved
11+
...............
12+
13+
See the `PyMongo 4.4 release notes in JIRA`_ for the list of resolved issues
14+
in this release.
15+
16+
.. _PyMongo 4.4 release notes in JIRA: https://jira.mongodb.org/secure/ReleaseNote.jspa?projectId=10004&version=34354
17+
418
Changes in Version 4.3.3
519
------------------------
620

pymongo/collection.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
NoReturn,
2727
Optional,
2828
Sequence,
29-
Tuple,
3029
Union,
3130
)
3231

@@ -62,6 +61,8 @@
6261
ReplaceOne,
6362
UpdateMany,
6463
UpdateOne,
64+
_IndexKeyHint,
65+
_IndexList,
6566
)
6667
from pymongo.read_preferences import ReadPreference, _ServerMode
6768
from pymongo.results import (
@@ -85,9 +86,6 @@
8586
UpdateOne,
8687
UpdateMany,
8788
]
88-
# Hint supports index name, "myIndex", or list of index pairs: [('x', 1), ('y', -1)]
89-
_IndexList = Sequence[Tuple[str, Union[int, str, Mapping[str, Any]]]]
90-
_IndexKeyHint = Union[str, _IndexList]
9189

9290

9391
class ReturnDocument(object):
@@ -1948,7 +1946,9 @@ def create_index(
19481946
) -> str:
19491947
"""Creates an index on this collection.
19501948
1951-
Takes either a single key or a list of (key, direction) pairs.
1949+
Takes either a single key or a list containing (key, direction) pairs
1950+
or keys. If no direction is given, :data:`~pymongo.ASCENDING` will
1951+
be assumed.
19521952
The key(s) must be an instance of :class:`basestring`
19531953
(:class:`str` in python 3), and the direction(s) must be one of
19541954
(:data:`~pymongo.ASCENDING`, :data:`~pymongo.DESCENDING`,
@@ -1964,7 +1964,7 @@ def create_index(
19641964
ascending we need to use a list of tuples::
19651965
19661966
>>> my_collection.create_index([("mike", pymongo.DESCENDING),
1967-
... ("eliot", pymongo.ASCENDING)])
1967+
... "eliot"])
19681968
19691969
All optional index creation parameters should be passed as
19701970
keyword arguments to this method. For example::
@@ -2025,6 +2025,9 @@ def create_index(
20252025
- `**kwargs` (optional): any additional index creation
20262026
options (see the above list) should be passed as keyword
20272027
2028+
.. versionchanged:: 4.4
2029+
Allow passing a list containing (key, direction) pairs
2030+
or keys for the ``keys`` parameter.
20282031
.. versionchanged:: 4.1
20292032
Added ``comment`` parameter.
20302033
.. versionchanged:: 3.11

pymongo/cursor.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ def close(self):
146146
self.sock = None
147147

148148

149-
_Sort = Sequence[Tuple[str, Union[int, str, Mapping[str, Any]]]]
149+
_Sort = Sequence[Union[str, Tuple[str, Union[int, str, Mapping[str, Any]]]]]
150150
_Hint = Union[str, _Sort]
151151

152152

@@ -832,15 +832,16 @@ def sort(
832832
"""Sorts this cursor's results.
833833
834834
Pass a field name and a direction, either
835-
:data:`~pymongo.ASCENDING` or :data:`~pymongo.DESCENDING`::
835+
:data:`~pymongo.ASCENDING` or :data:`~pymongo.DESCENDING`.::
836836
837837
for doc in collection.find().sort('field', pymongo.ASCENDING):
838838
print(doc)
839839
840-
To sort by multiple fields, pass a list of (key, direction) pairs::
840+
To sort by multiple fields, pass a list of (key, direction) pairs.
841+
If just a name is given, :data:`~pymongo.ASCENDING` will be inferred::
841842
842843
for doc in collection.find().sort([
843-
('field1', pymongo.ASCENDING),
844+
'field1',
844845
('field2', pymongo.DESCENDING)]):
845846
print(doc)
846847

pymongo/helpers.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ def _index_list(key_or_list, direction=None):
8080
Takes such a list, or a single key, or a single key and direction.
8181
"""
8282
if direction is not None:
83+
if not isinstance(key_or_list, str):
84+
raise TypeError("Expected a string and a direction")
8385
return [(key_or_list, direction)]
8486
else:
8587
if isinstance(key_or_list, str):
@@ -88,7 +90,12 @@ def _index_list(key_or_list, direction=None):
8890
return list(key_or_list)
8991
elif not isinstance(key_or_list, (list, tuple)):
9092
raise TypeError("if no direction is specified, key_or_list must be an instance of list")
91-
return key_or_list
93+
values = []
94+
for item in key_or_list:
95+
if isinstance(item, str):
96+
item = (item, ASCENDING)
97+
values.append(item)
98+
return values
9299

93100

94101
def _index_document(index_list):
@@ -108,7 +115,10 @@ def _index_document(index_list):
108115
raise ValueError("key_or_list must not be the empty list")
109116

110117
index: SON[str, Any] = SON()
111-
for (key, value) in index_list:
118+
for item in index_list:
119+
if isinstance(item, str):
120+
item = (item, ASCENDING)
121+
key, value = item
112122
if not isinstance(key, str):
113123
raise TypeError("first item in each key pair must be an instance of str")
114124
if not isinstance(value, (str, int, abc.Mapping)):

pymongo/operations.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
from pymongo.helpers import _gen_index_name, _index_document, _index_list
2323
from pymongo.typings import _CollationIn, _DocumentType, _Pipeline
2424

25+
# Hint supports index name, "myIndex", or list of either strings or index pairs: [('x', 1), ('y', -1), 'z'']
26+
_IndexList = Sequence[Union[str, Tuple[str, Union[int, str, Mapping[str, Any]]]]]
27+
_IndexKeyHint = Union[str, _IndexList]
28+
2529

2630
class InsertOne(Generic[_DocumentType]):
2731
"""Represents an insert_one operation."""
@@ -55,10 +59,6 @@ def __ne__(self, other: Any) -> bool:
5559
return not self == other
5660

5761

58-
_IndexList = Sequence[Tuple[str, Union[int, str, Mapping[str, Any]]]]
59-
_IndexKeyHint = Union[str, _IndexList]
60-
61-
6262
class DeleteOne(object):
6363
"""Represents a delete_one operation."""
6464

@@ -435,7 +435,9 @@ def __init__(self, keys: _IndexKeyHint, **kwargs: Any) -> None:
435435
436436
For use with :meth:`~pymongo.collection.Collection.create_indexes`.
437437
438-
Takes either a single key or a list of (key, direction) pairs.
438+
Takes either a single key or a list containing (key, direction) pairs
439+
or keys. If no direction is given, :data:`~pymongo.ASCENDING` will
440+
be assumed.
439441
The key(s) must be an instance of :class:`basestring`
440442
(:class:`str` in python 3), and the direction(s) must be one of
441443
(:data:`~pymongo.ASCENDING`, :data:`~pymongo.DESCENDING`,
@@ -477,8 +479,8 @@ def __init__(self, keys: _IndexKeyHint, **kwargs: Any) -> None:
477479
server version.
478480
479481
:Parameters:
480-
- `keys`: a single key or a list of (key, direction)
481-
pairs specifying the index to create
482+
- `keys`: a single key or a list containing (key, direction) pairs
483+
or keys specifying the index to create
482484
- `**kwargs` (optional): any additional index creation
483485
options (see the above list) should be passed as keyword
484486
arguments

test/test_collection.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,10 @@ def test_create_index(self):
315315
with self.write_concern_collection() as coll:
316316
coll.create_index([("hello", DESCENDING)])
317317

318+
db.test.create_index(["hello", "world"])
319+
db.test.create_index(["hello", ("world", DESCENDING)])
320+
db.test.create_index({"hello": 1}.items()) # type:ignore[arg-type]
321+
318322
def test_drop_index(self):
319323
db = self.db
320324
db.test.drop_indexes()
@@ -1680,7 +1684,7 @@ def to_list(things):
16801684

16811685
self.assertRaises(TypeError, db.test.find, sort=5)
16821686
self.assertRaises(TypeError, db.test.find, sort="hello")
1683-
self.assertRaises(ValueError, db.test.find, sort=["hello", 1])
1687+
self.assertRaises(TypeError, db.test.find, sort=["hello", 1])
16841688

16851689
# TODO doesn't actually test functionality, just that it doesn't blow up
16861690
def test_cursor_timeout(self):

test/test_cursor.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
from pymongo.collation import Collation
4141
from pymongo.cursor import Cursor, CursorType
4242
from pymongo.errors import ExecutionTimeout, InvalidOperation, OperationFailure
43+
from pymongo.operations import _IndexList
4344
from pymongo.read_concern import ReadConcern
4445
from pymongo.read_preferences import ReadPreference
4546
from pymongo.write_concern import WriteConcern
@@ -366,6 +367,21 @@ def test_hint(self):
366367
break
367368
self.assertRaises(InvalidOperation, a.hint, spec)
368369

370+
db.test.drop()
371+
db.test.insert_many([{"num": i, "foo": i} for i in range(100)])
372+
spec: _IndexList = ["num", ("foo", DESCENDING)]
373+
db.test.create_index(spec)
374+
first = next(db.test.find().hint(spec))
375+
self.assertEqual(0, first.get("num"))
376+
self.assertEqual(0, first.get("foo"))
377+
378+
db.test.drop()
379+
db.test.insert_many([{"num": i, "foo": i} for i in range(100)])
380+
spec = ["num"]
381+
db.test.create_index(spec)
382+
first = next(db.test.find().hint(spec))
383+
self.assertEqual(0, first.get("num"))
384+
369385
def test_hint_by_name(self):
370386
db = self.db
371387
db.test.drop()
@@ -715,6 +731,8 @@ def test_sort(self):
715731
(i["a"], i["b"]) for i in db.test.find().sort([("b", DESCENDING), ("a", ASCENDING)])
716732
]
717733
self.assertEqual(result, expected)
734+
result = [(i["a"], i["b"]) for i in db.test.find().sort([("b", DESCENDING), "a"])]
735+
self.assertEqual(result, expected)
718736

719737
a = db.test.find()
720738
a.sort("x", ASCENDING)

0 commit comments

Comments
 (0)