diff --git a/pymongo/cursor.py b/pymongo/cursor.py index 7ef62f779..b42314e08 100644 --- a/pymongo/cursor.py +++ b/pymongo/cursor.py @@ -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): diff --git a/test/test_cursor.py b/test/test_cursor.py index b176bdd8b..dccf3f11d 100644 --- a/test/test_cursor.py +++ b/test/test_cursor.py @@ -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()