24
22
sys.path[0:0] = [""]
26
from nose.plugins.skip import SkipTest
28
24
from bson.code import Code
25
from bson.py3compat import u, PY3
29
26
from bson.son import SON
30
from pymongo import (ASCENDING,
27
from pymongo import (MongoClient,
34
32
from pymongo.command_cursor import CommandCursor
33
from pymongo.cursor import CursorType
35
34
from pymongo.cursor_manager import CursorManager
36
from pymongo.database import Database
37
35
from pymongo.errors import (InvalidOperation,
40
from test import version
41
from test.test_client import get_client
42
from test.utils import (catch_warnings, is_mongos,
43
get_command_line, server_started_with_auth)
46
class TestCursor(unittest.TestCase):
49
self.client = get_client()
50
self.db = Database(self.client, "pymongo_test")
38
from test import (client_context,
44
from test.utils import server_started_with_auth
50
class TestCursorNoConnect(unittest.TestCase):
54
client = MongoClient(host, port, connect=False)
57
def test_deepcopy_cursor_littered_with_regexes(self):
58
cursor = self.db.test.find({
59
"x": re.compile("^hmmm.*"),
60
"y": [re.compile("^hmm.*")],
61
"z": {"a": [re.compile("^hm.*")]},
62
re.compile("^key.*"): {"a": [re.compile("^hm.*")]}})
64
cursor2 = copy.deepcopy(cursor)
65
self.assertEqual(cursor._Cursor__spec, cursor2._Cursor__spec)
67
def test_add_remove_option(self):
68
cursor = self.db.test.find()
69
self.assertEqual(0, cursor._Cursor__query_flags)
71
cursor2 = self.db.test.find(cursor_type=CursorType.TAILABLE)
72
self.assertEqual(2, cursor2._Cursor__query_flags)
73
self.assertEqual(cursor._Cursor__query_flags,
74
cursor2._Cursor__query_flags)
76
cursor2 = self.db.test.find(cursor_type=CursorType.TAILABLE_AWAIT)
77
self.assertEqual(34, cursor2._Cursor__query_flags)
78
self.assertEqual(cursor._Cursor__query_flags,
79
cursor2._Cursor__query_flags)
80
cursor.add_option(128)
81
cursor2 = self.db.test.find(
82
cursor_type=CursorType.TAILABLE_AWAIT).add_option(128)
83
self.assertEqual(162, cursor2._Cursor__query_flags)
84
self.assertEqual(cursor._Cursor__query_flags,
85
cursor2._Cursor__query_flags)
87
self.assertEqual(162, cursor._Cursor__query_flags)
88
cursor.add_option(128)
89
self.assertEqual(162, cursor._Cursor__query_flags)
91
cursor.remove_option(128)
92
cursor2 = self.db.test.find(cursor_type=CursorType.TAILABLE_AWAIT)
93
self.assertEqual(34, cursor2._Cursor__query_flags)
94
self.assertEqual(cursor._Cursor__query_flags,
95
cursor2._Cursor__query_flags)
96
cursor.remove_option(32)
97
cursor2 = self.db.test.find(cursor_type=CursorType.TAILABLE)
98
self.assertEqual(2, cursor2._Cursor__query_flags)
99
self.assertEqual(cursor._Cursor__query_flags,
100
cursor2._Cursor__query_flags)
102
self.assertEqual(2, cursor._Cursor__query_flags)
103
cursor.remove_option(32)
104
self.assertEqual(2, cursor._Cursor__query_flags)
107
cursor = self.db.test.find(no_cursor_timeout=True)
108
self.assertEqual(16, cursor._Cursor__query_flags)
109
cursor2 = self.db.test.find().add_option(16)
110
self.assertEqual(cursor._Cursor__query_flags,
111
cursor2._Cursor__query_flags)
112
cursor.remove_option(16)
113
self.assertEqual(0, cursor._Cursor__query_flags)
115
# Tailable / Await data
116
cursor = self.db.test.find(cursor_type=CursorType.TAILABLE_AWAIT)
117
self.assertEqual(34, cursor._Cursor__query_flags)
118
cursor2 = self.db.test.find().add_option(34)
119
self.assertEqual(cursor._Cursor__query_flags,
120
cursor2._Cursor__query_flags)
121
cursor.remove_option(32)
122
self.assertEqual(2, cursor._Cursor__query_flags)
124
# Exhaust - which mongos doesn't support
125
cursor = self.db.test.find(cursor_type=CursorType.EXHAUST)
126
self.assertEqual(64, cursor._Cursor__query_flags)
127
cursor2 = self.db.test.find().add_option(64)
128
self.assertEqual(cursor._Cursor__query_flags,
129
cursor2._Cursor__query_flags)
130
self.assertTrue(cursor._Cursor__exhaust)
131
cursor.remove_option(64)
132
self.assertEqual(0, cursor._Cursor__query_flags)
133
self.assertFalse(cursor._Cursor__exhaust)
136
cursor = self.db.test.find(allow_partial_results=True)
137
self.assertEqual(128, cursor._Cursor__query_flags)
138
cursor2 = self.db.test.find().add_option(128)
139
self.assertEqual(cursor._Cursor__query_flags,
140
cursor2._Cursor__query_flags)
141
cursor.remove_option(128)
142
self.assertEqual(0, cursor._Cursor__query_flags)
145
class TestCursor(IntegrationTest):
147
@client_context.require_version_min(2, 5, 3, -1)
55
148
def test_max_time_ms(self):
56
if not version.at_least(self.db.connection, (2, 5, 3, -1)):
57
raise SkipTest("MaxTimeMS requires MongoDB >= 2.5.3")
60
150
db.pymongo_test.drop()
61
151
coll = db.pymongo_test
62
152
self.assertRaises(TypeError, coll.find().max_time_ms, 'foo')
63
coll.insert({"amalia": 1})
64
coll.insert({"amalia": 2})
153
coll.insert_one({"amalia": 1})
154
coll.insert_one({"amalia": 2})
66
156
coll.find().max_time_ms(None)
67
coll.find().max_time_ms(1L)
157
coll.find().max_time_ms(long(1))
69
159
cursor = coll.find().max_time_ms(999)
70
160
self.assertEqual(999, cursor._Cursor__max_time_ms)
95
186
self.assertRaises(ExecutionTimeout,
96
187
coll.find_one, max_time_ms=1)
98
self.client.admin.command("configureFailPoint",
99
"maxTimeAlwaysTimeOut",
189
client.admin.command("configureFailPoint",
190
"maxTimeAlwaysTimeOut",
193
@client_context.require_version_min(2, 5, 3, -1)
194
@client_context.require_test_commands
102
195
def test_max_time_ms_getmore(self):
103
196
# Test that Cursor handles server timeout error in response to getmore.
104
if "enableTestCommands=1" not in get_command_line(self.client)["argv"]:
105
raise SkipTest("Need test commands enabled")
107
if not version.at_least(self.db.connection, (2, 5, 3, -1)):
108
raise SkipTest("MaxTimeMS requires MongoDB >= 2.5.3")
110
197
coll = self.db.pymongo_test
111
coll.insert({} for _ in range(200))
198
coll.insert_many([{} for _ in range(200)])
112
199
cursor = coll.find().max_time_ms(100)
114
201
# Send initial query before turning on failpoint.
116
203
self.client.admin.command("configureFailPoint",
117
204
"maxTimeAlwaysTimeOut",
157
239
db.test.find({"num": 17, "foo": 17})
158
240
.hint([("foo", ASCENDING)]).explain)
160
index = db.test.create_index("num")
242
spec = [("num", DESCENDING)]
243
index = db.test.create_index(spec)
162
spec = [("num", ASCENDING)]
163
self.assertEqual(db.test.find({}).explain()["cursor"], "BasicCursor")
164
self.assertEqual(db.test.find({}).hint(spec).explain()["cursor"],
165
"BtreeCursor %s" % index)
166
self.assertEqual(db.test.find({}).hint(spec).hint(None)
167
.explain()["cursor"],
245
first = next(db.test.find())
246
self.assertEqual(0, first.get('num'))
247
first = next(db.test.find().hint(spec))
248
self.assertEqual(99, first.get('num'))
169
249
self.assertRaises(OperationFailure,
170
250
db.test.find({"num": 17, "foo": 17})
171
251
.hint([("foo", ASCENDING)]).explain)
334
420
def test_limit_and_batch_size(self):
338
db.test.save({"x": x})
423
db.test.insert_many([{"x": x} for x in range(500)])
340
425
curs = db.test.find().limit(0).batch_size(10)
427
self.assertEqual(10, curs._Cursor__retrieved)
429
curs = db.test.find(limit=0, batch_size=10)
342
431
self.assertEqual(10, curs._Cursor__retrieved)
344
433
curs = db.test.find().limit(-2).batch_size(0)
435
self.assertEqual(2, curs._Cursor__retrieved)
437
curs = db.test.find(limit=-2, batch_size=0)
346
439
self.assertEqual(2, curs._Cursor__retrieved)
348
441
curs = db.test.find().limit(-4).batch_size(5)
443
self.assertEqual(4, curs._Cursor__retrieved)
445
curs = db.test.find(limit=-4, batch_size=5)
350
447
self.assertEqual(4, curs._Cursor__retrieved)
352
449
curs = db.test.find().limit(50).batch_size(500)
451
self.assertEqual(50, curs._Cursor__retrieved)
453
curs = db.test.find(limit=50, batch_size=500)
354
455
self.assertEqual(50, curs._Cursor__retrieved)
356
457
curs = db.test.find().batch_size(500)
459
self.assertEqual(500, curs._Cursor__retrieved)
461
curs = db.test.find(batch_size=500)
358
463
self.assertEqual(500, curs._Cursor__retrieved)
360
465
curs = db.test.find().limit(50)
467
self.assertEqual(50, curs._Cursor__retrieved)
469
curs = db.test.find(limit=50)
362
471
self.assertEqual(50, curs._Cursor__retrieved)
364
473
# these two might be shaky, as the default
499
608
self.assertEqual(0, db.test.acollectionthatdoesntexist.find().count())
610
def test_count_with_hint(self):
611
collection = self.db.test
614
collection.insert_many([{'i': 1}, {'i': 2}])
615
self.assertEqual(2, collection.find().count())
617
collection.create_index([('i', 1)])
619
self.assertEqual(1, collection.find({'i': 1}).hint("_id_").count())
620
self.assertEqual(2, collection.find().hint("_id_").count())
622
if client_context.version.at_least(2, 6, 0):
623
# Count supports hint
624
self.assertRaises(OperationFailure,
625
collection.find({'i': 1}).hint("BAD HINT").count)
629
1, collection.find({'i': 1}).hint("BAD HINT").count())
631
# Create a sparse index which should have no entries.
632
collection.create_index([('x', 1)], sparse=True)
634
if client_context.version.at_least(2, 6, 0):
635
# Count supports hint
636
self.assertEqual(0, collection.find({'i': 1}).hint("x_1").count())
638
0, collection.find({'i': 1}).hint([("x", 1)]).count())
641
self.assertEqual(1, collection.find({'i': 1}).hint("x_1").count())
643
1, collection.find({'i': 1}).hint([("x", 1)]).count())
645
self.assertEqual(2, collection.find().hint("x_1").count())
646
self.assertEqual(2, collection.find().hint([("x", 1)]).count())
501
648
def test_where(self):
691
814
self.assertTrue(isinstance(cursor2._Cursor__hint, SON))
692
815
self.assertEqual(cursor._Cursor__hint, cursor2._Cursor__hint)
694
def test_deepcopy_cursor_littered_with_regexes(self):
696
cursor = self.db.test.find({"x": re.compile("^hmmm.*"),
697
"y": [re.compile("^hmm.*")],
698
"z": {"a": [re.compile("^hm.*")]},
699
re.compile("^key.*"): {"a": [re.compile("^hm.*")]}})
701
cursor2 = copy.deepcopy(cursor)
702
self.assertEqual(cursor._Cursor__spec, cursor2._Cursor__spec)
704
def test_add_remove_option(self):
705
cursor = self.db.test.find()
706
self.assertEqual(0, cursor._Cursor__query_options())
708
cursor2 = self.db.test.find(tailable=True)
709
self.assertEqual(2, cursor2._Cursor__query_options())
710
self.assertEqual(cursor._Cursor__query_options(),
711
cursor2._Cursor__query_options())
712
cursor.add_option(32)
713
cursor2 = self.db.test.find(tailable=True, await_data=True)
714
self.assertEqual(34, cursor2._Cursor__query_options())
715
self.assertEqual(cursor._Cursor__query_options(),
716
cursor2._Cursor__query_options())
717
cursor.add_option(128)
718
cursor2 = self.db.test.find(tailable=True,
719
await_data=True).add_option(128)
720
self.assertEqual(162, cursor2._Cursor__query_options())
721
self.assertEqual(cursor._Cursor__query_options(),
722
cursor2._Cursor__query_options())
724
self.assertEqual(162, cursor._Cursor__query_options())
725
cursor.add_option(128)
726
self.assertEqual(162, cursor._Cursor__query_options())
728
cursor.remove_option(128)
729
cursor2 = self.db.test.find(tailable=True, await_data=True)
730
self.assertEqual(34, cursor2._Cursor__query_options())
731
self.assertEqual(cursor._Cursor__query_options(),
732
cursor2._Cursor__query_options())
733
cursor.remove_option(32)
734
cursor2 = self.db.test.find(tailable=True)
735
self.assertEqual(2, cursor2._Cursor__query_options())
736
self.assertEqual(cursor._Cursor__query_options(),
737
cursor2._Cursor__query_options())
739
self.assertEqual(2, cursor._Cursor__query_options())
740
cursor.remove_option(32)
741
self.assertEqual(2, cursor._Cursor__query_options())
744
cursor = self.db.test.find(slave_okay=True)
745
self.assertEqual(4, cursor._Cursor__query_options())
746
cursor2 = self.db.test.find().add_option(4)
747
self.assertEqual(cursor._Cursor__query_options(),
748
cursor2._Cursor__query_options())
749
self.assertTrue(cursor._Cursor__slave_okay)
750
cursor.remove_option(4)
751
self.assertEqual(0, cursor._Cursor__query_options())
752
self.assertFalse(cursor._Cursor__slave_okay)
755
cursor = self.db.test.find(timeout=False)
756
self.assertEqual(16, cursor._Cursor__query_options())
757
cursor2 = self.db.test.find().add_option(16)
758
self.assertEqual(cursor._Cursor__query_options(),
759
cursor2._Cursor__query_options())
760
cursor.remove_option(16)
761
self.assertEqual(0, cursor._Cursor__query_options())
763
# Tailable / Await data
764
cursor = self.db.test.find(tailable=True, await_data=True)
765
self.assertEqual(34, cursor._Cursor__query_options())
766
cursor2 = self.db.test.find().add_option(34)
767
self.assertEqual(cursor._Cursor__query_options(),
768
cursor2._Cursor__query_options())
769
cursor.remove_option(32)
770
self.assertEqual(2, cursor._Cursor__query_options())
772
# Exhaust - which mongos doesn't support
773
if not is_mongos(self.db.connection):
774
cursor = self.db.test.find(exhaust=True)
775
self.assertEqual(64, cursor._Cursor__query_options())
776
cursor2 = self.db.test.find().add_option(64)
777
self.assertEqual(cursor._Cursor__query_options(),
778
cursor2._Cursor__query_options())
779
self.assertTrue(cursor._Cursor__exhaust)
780
cursor.remove_option(64)
781
self.assertEqual(0, cursor._Cursor__query_options())
782
self.assertFalse(cursor._Cursor__exhaust)
785
cursor = self.db.test.find(partial=True)
786
self.assertEqual(128, cursor._Cursor__query_options())
787
cursor2 = self.db.test.find().add_option(128)
788
self.assertEqual(cursor._Cursor__query_options(),
789
cursor2._Cursor__query_options())
790
cursor.remove_option(128)
791
self.assertEqual(0, cursor._Cursor__query_options())
793
817
def test_count_with_fields(self):
794
818
self.db.test.drop()
795
self.db.test.save({"x": 1})
797
if not version.at_least(self.db.connection, (1, 1, 3, -1)):
798
for _ in self.db.test.find({}, ["a"]):
801
self.assertEqual(0, self.db.test.find({}, ["a"]).count())
803
self.assertEqual(1, self.db.test.find({}, ["a"]).count())
819
self.db.test.insert_one({"x": 1})
820
self.assertEqual(1, self.db.test.find({}, ["a"]).count())
805
822
def test_bad_getitem(self):
806
823
self.assertRaises(TypeError, lambda x: self.db.test.find()[x], "hello")
930
942
def test_get_more(self):
932
944
db.drop_collection("test")
933
db.test.insert([{'i': i} for i in range(10)])
945
db.test.insert_many([{'i': i} for i in range(10)])
934
946
self.assertEqual(10, len(list(db.test.find().batch_size(5))))
936
948
def test_tailable(self):
938
950
db.drop_collection("test")
939
951
db.create_collection("test", capped=True, size=1000, max=3)
942
cursor = db.test.find(tailable=True)
944
db.test.insert({"x": 1})
948
self.assertEqual(1, doc["x"])
949
self.assertEqual(1, count)
951
db.test.insert({"x": 2})
955
self.assertEqual(2, doc["x"])
956
self.assertEqual(1, count)
958
db.test.insert({"x": 3})
962
self.assertEqual(3, doc["x"])
963
self.assertEqual(1, count)
965
# Capped rollover - the collection can never
966
# have more than 3 documents. Just make sure
967
# this doesn't raise...
968
db.test.insert(({"x": i} for i in xrange(4, 7)))
969
self.assertEqual(0, len(list(cursor)))
971
# and that the cursor doesn't think it's still alive.
972
self.assertFalse(cursor.alive)
974
self.assertEqual(3, db.test.count())
976
db.drop_collection("test")
952
self.addCleanup(db.drop_collection, "test")
953
cursor = db.test.find(cursor_type=CursorType.TAILABLE)
955
db.test.insert_one({"x": 1})
959
self.assertEqual(1, doc["x"])
960
self.assertEqual(1, count)
962
db.test.insert_one({"x": 2})
966
self.assertEqual(2, doc["x"])
967
self.assertEqual(1, count)
969
db.test.insert_one({"x": 3})
973
self.assertEqual(3, doc["x"])
974
self.assertEqual(1, count)
976
# Capped rollover - the collection can never
977
# have more than 3 documents. Just make sure
978
# this doesn't raise...
979
db.test.insert_many([{"x": i} for i in range(4, 7)])
980
self.assertEqual(0, len(list(cursor)))
982
# and that the cursor doesn't think it's still alive.
983
self.assertFalse(cursor.alive)
985
self.assertEqual(3, db.test.count())
978
988
def test_distinct(self):
979
if not version.at_least(self.db.connection, (1, 1, 3, 1)):
980
raise SkipTest("distinct with query requires MongoDB >= 1.1.3")
982
989
self.db.drop_collection("test")
984
self.db.test.save({"a": 1})
985
self.db.test.save({"a": 2})
986
self.db.test.save({"a": 2})
987
self.db.test.save({"a": 2})
988
self.db.test.save({"a": 3})
991
self.db.test.insert_many(
992
[{"a": 1}, {"a": 2}, {"a": 2}, {"a": 2}, {"a": 3}])
990
994
distinct = self.db.test.find({"a": {"$lt": 3}}).distinct("a")
1005
1009
self.assertEqual(["b", "c"], distinct)
1007
1011
def test_max_scan(self):
1008
if not version.at_least(self.db.connection, (1, 5, 1)):
1009
raise SkipTest("maxScan requires MongoDB >= 1.5.1")
1011
1012
self.db.drop_collection("test")
1012
for _ in range(100):
1013
self.db.test.insert({})
1013
self.db.test.insert_many([{} for _ in range(100)])
1015
1015
self.assertEqual(100, len(list(self.db.test.find())))
1016
self.assertEqual(50, len(list(self.db.test.find(max_scan=50))))
1016
self.assertEqual(50, len(list(self.db.test.find().max_scan(50))))
1017
1017
self.assertEqual(50, len(list(self.db.test.find()
1018
1018
.max_scan(90).max_scan(50))))
1020
1020
def test_with_statement(self):
1021
if sys.version_info < (2, 6):
1022
raise SkipTest("With statement requires Python >= 2.6")
1024
1021
self.db.drop_collection("test")
1025
for _ in range(100):
1026
self.db.test.insert({})
1022
self.db.test.insert_many([{} for _ in range(100)])
1028
1024
c1 = self.db.test.find()
1030
with self.db.test.find() as c2:
1031
self.assertTrue(c2.alive)
1032
self.assertFalse(c2.alive)
1025
with self.db.test.find() as c2:
1026
self.assertTrue(c2.alive)
1027
self.assertFalse(c2.alive)
1034
with self.db.test.find() as c2:
1035
self.assertEqual(100, len(list(c2)))
1036
self.assertFalse(c2.alive)
1029
with self.db.test.find() as c2:
1030
self.assertEqual(100, len(list(c2)))
1031
self.assertFalse(c2.alive)
1038
1032
self.assertTrue(c1.alive)
1034
@client_context.require_no_mongos
1040
1035
def test_comment(self):
1041
if is_mongos(self.client):
1042
raise SkipTest("profile is not supported by mongos")
1043
if not version.at_least(self.db.connection, (2, 0)):
1044
raise SkipTest("Requires server >= 2.0")
1045
if server_started_with_auth(self.db.connection):
1036
if server_started_with_auth(self.db.client):
1046
1037
raise SkipTest("SERVER-4754 - This test uses profiling.")
1048
1039
def run_with_profiling(func):
1091
1082
def test_cursor_transfer(self):
1093
1084
# This is just a test, don't try this at home...
1094
self.db.test.remove({})
1095
self.db.test.insert({'_id': i} for i in xrange(200))
1086
client = client_context.rs_or_standalone_client
1087
db = client.pymongo_test
1089
db.test.delete_many({})
1090
db.test.insert_many([{'_id': i} for i in range(200)])
1097
1092
class CManager(CursorManager):
1098
def __init__(self, connection):
1099
super(CManager, self).__init__(connection)
1093
def __init__(self, client):
1094
super(CManager, self).__init__(client)
1101
def close(self, dummy):
1096
def close(self, dummy, dummy2):
1102
1097
# Do absolutely nothing...
1105
client = self.db.connection
1106
ctx = catch_warnings()
1108
warnings.simplefilter("ignore", DeprecationWarning)
1109
client.set_cursor_manager(CManager)
1112
cursor = self.db.test.find().batch_size(10)
1113
docs.append(cursor.next())
1116
self.assertEqual(len(docs), 10)
1117
cmd_cursor = {'id': cursor.cursor_id, 'firstBatch': []}
1118
ccursor = CommandCursor(cursor.collection, cmd_cursor,
1119
cursor.conn_id, retrieved=cursor.retrieved)
1120
docs.extend(ccursor)
1121
self.assertEqual(len(docs), 200)
1123
client.set_cursor_manager(CursorManager)
1100
client.set_cursor_manager(CManager)
1101
self.addCleanup(client.set_cursor_manager, CursorManager)
1103
cursor = db.test.find().batch_size(10)
1104
docs.append(next(cursor))
1107
self.assertEqual(len(docs), 10)
1108
cmd_cursor = {'id': cursor.cursor_id, 'firstBatch': []}
1109
ccursor = CommandCursor(cursor.collection, cmd_cursor,
1110
cursor.address, retrieved=cursor.retrieved)
1111
docs.extend(ccursor)
1112
self.assertEqual(len(docs), 200)
1114
def test_modifiers(self):
1115
cur = self.db.test.find()
1116
self.assertTrue('$query' not in cur._Cursor__query_spec())
1117
cur = self.db.test.find().comment("testing").max_time_ms(500)
1118
self.assertTrue('$query' in cur._Cursor__query_spec())
1119
self.assertEqual(cur._Cursor__query_spec()["$comment"], "testing")
1120
self.assertEqual(cur._Cursor__query_spec()["$maxTimeMS"], 500)
1121
cur = self.db.test.find(
1122
modifiers={"$maxTimeMS": 500, "$comment": "testing"})
1123
self.assertTrue('$query' in cur._Cursor__query_spec())
1124
self.assertEqual(cur._Cursor__query_spec()["$comment"], "testing")
1125
self.assertEqual(cur._Cursor__query_spec()["$maxTimeMS"], 500)
1127
def test_alive(self):
1128
self.db.test.delete_many({})
1129
self.db.test.insert_many([{} for _ in range(3)])
1130
self.addCleanup(self.db.test.delete_many, {})
1131
cursor = self.db.test.find().batch_size(2)
1137
self.assertFalse(cursor.alive)
1140
self.assertTrue(cursor.alive)
1126
1143
if __name__ == "__main__":
1127
1144
unittest.main()