PYTHON-314, PYTHON-744 - Hint by index name, count with hint.

This change introduces two closely related features. Cursor.hint
now accepts the name of an index as an alternative to passing
the index spec. Cursor.count will now pass the hint, if one was
specified, to the count command. Count with hint is only supported
by MongoDB 2.6 and newer.
This commit is contained in:
Bernie Hackett 2014-09-12 13:50:04 -07:00
parent 91622e62d5
commit 1ac607c447
2 changed files with 69 additions and 6 deletions

View File

@ -700,6 +700,12 @@ class Cursor(object):
`with_limit_and_skip` to ``True`` if that is the desired behavior.
Raises :class:`~pymongo.errors.OperationFailure` on a database error.
When used with MongoDB >= 2.6, :meth:`~count` uses any :meth:`~hint`
applied to the query. In the following example the hint is passed to
the count command:
collection.find({'field': 'value'}).hint('field_1').count()
With :class:`~pymongo.mongo_replica_set_client.MongoReplicaSetClient`
or :class:`~pymongo.master_slave_connection.MasterSlaveConnection`,
if `read_preference` is not
@ -721,6 +727,9 @@ class Cursor(object):
collection.find({}, network_timeout=1).count()
.. versionchanged:: 2.8
The :meth:`~count` method now supports :meth:`~hint`.
.. versionadded:: 1.1.1
The `with_limit_and_skip` parameter.
:meth:`~pymongo.cursor.Cursor.__len__` was deprecated in favor of
@ -742,6 +751,9 @@ class Cursor(object):
if self.__comment:
command['$comment'] = self.__comment
if self.__hint is not None:
command['hint'] = self.__hint
if with_limit_and_skip:
if self.__limit:
command["limit"] = self.__limit
@ -834,20 +846,26 @@ class Cursor(object):
`index` should be an index as passed to
:meth:`~pymongo.collection.Collection.create_index`
(e.g. ``[('field', ASCENDING)]``). If `index`
is ``None`` any existing hints for this query are cleared. The
last hint applied to this cursor takes precedence over all
others.
(e.g. ``[('field', ASCENDING)]``) or the name of the index.
If `index` is ``None`` any existing hint for this query is
cleared. The last hint applied to this cursor takes precedence
over all others.
:Parameters:
- `index`: index to hint on (as an index specifier)
.. versionchanged:: 2.8
The :meth:`~hint` method accepts the name of the index.
"""
self.__check_okay_to_chain()
if index is None:
self.__hint = None
return self
self.__hint = helpers._index_document(index)
if isinstance(index, basestring):
self.__hint = index
else:
self.__hint = helpers._index_document(index)
return self
def comment(self, comment):

View File

@ -173,7 +173,18 @@ class TestCursor(unittest.TestCase):
break
self.assertRaises(InvalidOperation, a.hint, spec)
self.assertRaises(TypeError, db.test.find().hint, index)
def test_hint_by_name(self):
db = self.db
db.test.drop()
for i in range(100):
db.test.insert({'i': i})
db.test.create_index([('i', DESCENDING)], name='fooindex')
first = db.test.find().next()
self.assertEqual(0, first.get('i'))
first = db.test.find().hint('fooindex').next()
self.assertEqual(99, first.get('i'))
def test_limit(self):
db = self.db
@ -495,6 +506,40 @@ class TestCursor(unittest.TestCase):
self.assertEqual(0, db.test.acollectionthatdoesntexist.find().count())
def test_count_with_hint(self):
collection = self.db.test
collection.drop()
collection.save({'i': 1})
collection.save({'i': 2})
self.assertEqual(2, collection.find().count())
collection.create_index([('i', 1)])
self.assertEqual(1, collection.find({'i': 1}).hint("_id_").count())
self.assertEqual(2, collection.find().hint("_id_").count())
if version.at_least(self.client, (2, 6, 0)):
# Count supports hint
self.assertRaises(OperationFailure,
collection.find({'i': 1}).hint("BAD HINT").count)
else:
# Hint is ignored
self.assertEqual(
1, collection.find({'i': 1}).hint("BAD HINT").count())
# Create a sparse index which should have no entries.
collection.create_index([('x', 1)], sparse=True)
if version.at_least(self.client, (2, 6, 0)):
# Count supports hint
self.assertEqual(0, collection.find({'i': 1}).hint("x_1").count())
else:
# Hint is ignored
self.assertEqual(1, collection.find({'i': 1}).hint("x_1").count())
self.assertEqual(2, collection.find().hint("x_1").count())
def test_where(self):
db = self.db
db.test.drop()