00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 """Test the cursor module."""
00016 import unittest
00017 import random
00018 import warnings
00019 import sys
00020 import itertools
00021 sys.path[0:0] = [""]
00022
00023 from nose.plugins.skip import SkipTest
00024
00025 from bson.code import Code
00026 from pymongo import (ASCENDING,
00027 DESCENDING)
00028 from pymongo.cursor import Cursor
00029 from pymongo.database import Database
00030 from pymongo.errors import (InvalidOperation,
00031 OperationFailure)
00032 from test_connection import get_connection
00033 import version
00034
00035
00036 class TestCursor(unittest.TestCase):
00037
00038 def setUp(self):
00039 self.db = Database(get_connection(), "pymongo_test")
00040
00041 def test_explain(self):
00042 a = self.db.test.find()
00043 b = a.explain()
00044 for _ in a:
00045 break
00046 c = a.explain()
00047 del b["millis"]
00048 b.pop("oldPlan", None)
00049 del c["millis"]
00050 c.pop("oldPlan", None)
00051 self.assertEqual(b, c)
00052 self.assert_("cursor" in b)
00053
00054 def test_hint(self):
00055 db = self.db
00056 self.assertRaises(TypeError, db.test.find().hint, 5.5)
00057 db.test.drop()
00058
00059 for i in range(100):
00060 db.test.insert({"num": i, "foo": i})
00061
00062 self.assertRaises(OperationFailure,
00063 db.test.find({"num": 17, "foo": 17})
00064 .hint([("num", ASCENDING)]).explain)
00065 self.assertRaises(OperationFailure,
00066 db.test.find({"num": 17, "foo": 17})
00067 .hint([("foo", ASCENDING)]).explain)
00068
00069 index = db.test.create_index("num")
00070
00071 spec = [("num", ASCENDING)]
00072 self.assertEqual(db.test.find({}).explain()["cursor"], "BasicCursor")
00073 self.assertEqual(db.test.find({}).hint(spec).explain()["cursor"],
00074 "BtreeCursor %s" % index)
00075 self.assertEqual(db.test.find({}).hint(spec).hint(None)
00076 .explain()["cursor"],
00077 "BasicCursor")
00078 self.assertRaises(OperationFailure,
00079 db.test.find({"num": 17, "foo": 17})
00080 .hint([("foo", ASCENDING)]).explain)
00081
00082 a = db.test.find({"num": 17})
00083 a.hint(spec)
00084 for _ in a:
00085 break
00086 self.assertRaises(InvalidOperation, a.hint, spec)
00087
00088 self.assertRaises(TypeError, db.test.find().hint, index)
00089
00090
00091 def test_limit(self):
00092 db = self.db
00093
00094 self.assertRaises(TypeError, db.test.find().limit, None)
00095 self.assertRaises(TypeError, db.test.find().limit, "hello")
00096 self.assertRaises(TypeError, db.test.find().limit, 5.5)
00097
00098 db.test.drop()
00099 for i in range(100):
00100 db.test.save({"x": i})
00101
00102 count = 0
00103 for _ in db.test.find():
00104 count += 1
00105 self.assertEqual(count, 100)
00106
00107 count = 0
00108 for _ in db.test.find().limit(20):
00109 count += 1
00110 self.assertEqual(count, 20)
00111
00112 count = 0
00113 for _ in db.test.find().limit(99):
00114 count += 1
00115 self.assertEqual(count, 99)
00116
00117 count = 0
00118 for _ in db.test.find().limit(1):
00119 count += 1
00120 self.assertEqual(count, 1)
00121
00122 count = 0
00123 for _ in db.test.find().limit(0):
00124 count += 1
00125 self.assertEqual(count, 100)
00126
00127 count = 0
00128 for _ in db.test.find().limit(0).limit(50).limit(10):
00129 count += 1
00130 self.assertEqual(count, 10)
00131
00132 a = db.test.find()
00133 a.limit(10)
00134 for _ in a:
00135 break
00136 self.assertRaises(InvalidOperation, a.limit, 5)
00137
00138
00139 def test_batch_size(self):
00140 db = self.db
00141 db.test.drop()
00142 for x in range(200):
00143 db.test.save({"x": x})
00144
00145 self.assertRaises(TypeError, db.test.find().batch_size, None)
00146 self.assertRaises(TypeError, db.test.find().batch_size, "hello")
00147 self.assertRaises(TypeError, db.test.find().batch_size, 5.5)
00148 self.assertRaises(ValueError, db.test.find().batch_size, -1)
00149 a = db.test.find()
00150 for _ in a:
00151 break
00152 self.assertRaises(InvalidOperation, a.batch_size, 5)
00153
00154 def cursor_count(cursor, expected_count):
00155 count = 0
00156 for _ in cursor:
00157 count += 1
00158 self.assertEqual(expected_count, count)
00159
00160 cursor_count(db.test.find().batch_size(0), 200)
00161 cursor_count(db.test.find().batch_size(1), 200)
00162 cursor_count(db.test.find().batch_size(2), 200)
00163 cursor_count(db.test.find().batch_size(5), 200)
00164 cursor_count(db.test.find().batch_size(100), 200)
00165 cursor_count(db.test.find().batch_size(500), 200)
00166
00167 cursor_count(db.test.find().batch_size(0).limit(1), 1)
00168 cursor_count(db.test.find().batch_size(1).limit(1), 1)
00169 cursor_count(db.test.find().batch_size(2).limit(1), 1)
00170 cursor_count(db.test.find().batch_size(5).limit(1), 1)
00171 cursor_count(db.test.find().batch_size(100).limit(1), 1)
00172 cursor_count(db.test.find().batch_size(500).limit(1), 1)
00173
00174 cursor_count(db.test.find().batch_size(0).limit(10), 10)
00175 cursor_count(db.test.find().batch_size(1).limit(10), 10)
00176 cursor_count(db.test.find().batch_size(2).limit(10), 10)
00177 cursor_count(db.test.find().batch_size(5).limit(10), 10)
00178 cursor_count(db.test.find().batch_size(100).limit(10), 10)
00179 cursor_count(db.test.find().batch_size(500).limit(10), 10)
00180
00181
00182 def test_skip(self):
00183 db = self.db
00184
00185 self.assertRaises(TypeError, db.test.find().skip, None)
00186 self.assertRaises(TypeError, db.test.find().skip, "hello")
00187 self.assertRaises(TypeError, db.test.find().skip, 5.5)
00188
00189 db.drop_collection("test")
00190
00191 for i in range(100):
00192 db.test.save({"x": i})
00193
00194 for i in db.test.find():
00195 self.assertEqual(i["x"], 0)
00196 break
00197
00198 for i in db.test.find().skip(20):
00199 self.assertEqual(i["x"], 20)
00200 break
00201
00202 for i in db.test.find().skip(99):
00203 self.assertEqual(i["x"], 99)
00204 break
00205
00206 for i in db.test.find().skip(1):
00207 self.assertEqual(i["x"], 1)
00208 break
00209
00210 for i in db.test.find().skip(0):
00211 self.assertEqual(i["x"], 0)
00212 break
00213
00214 for i in db.test.find().skip(0).skip(50).skip(10):
00215 self.assertEqual(i["x"], 10)
00216 break
00217
00218 for i in db.test.find().skip(1000):
00219 self.fail()
00220
00221 a = db.test.find()
00222 a.skip(10)
00223 for _ in a:
00224 break
00225 self.assertRaises(InvalidOperation, a.skip, 5)
00226
00227 def test_sort(self):
00228 db = self.db
00229
00230 self.assertRaises(TypeError, db.test.find().sort, 5)
00231 self.assertRaises(ValueError, db.test.find().sort, [])
00232 self.assertRaises(TypeError, db.test.find().sort, [], ASCENDING)
00233 self.assertRaises(TypeError, db.test.find().sort,
00234 [("hello", DESCENDING)], DESCENDING)
00235 self.assertRaises(TypeError, db.test.find().sort, "hello", "world")
00236
00237 db.test.drop()
00238
00239 unsort = range(10)
00240 random.shuffle(unsort)
00241
00242 for i in unsort:
00243 db.test.save({"x": i})
00244
00245 asc = [i["x"] for i in db.test.find().sort("x", ASCENDING)]
00246 self.assertEqual(asc, range(10))
00247 asc = [i["x"] for i in db.test.find().sort("x")]
00248 self.assertEqual(asc, range(10))
00249 asc = [i["x"] for i in db.test.find().sort([("x", ASCENDING)])]
00250 self.assertEqual(asc, range(10))
00251
00252 expect = range(10)
00253 expect.reverse()
00254 desc = [i["x"] for i in db.test.find().sort("x", DESCENDING)]
00255 self.assertEqual(desc, expect)
00256 desc = [i["x"] for i in db.test.find().sort([("x", DESCENDING)])]
00257 self.assertEqual(desc, expect)
00258 desc = [i["x"] for i in
00259 db.test.find().sort("x", ASCENDING).sort("x", DESCENDING)]
00260 self.assertEqual(desc, expect)
00261
00262 expected = [(1, 5), (2, 5), (0, 3), (7, 3), (9, 2), (2, 1), (3, 1)]
00263 shuffled = list(expected)
00264 random.shuffle(shuffled)
00265
00266 db.test.drop()
00267 for (a, b) in shuffled:
00268 db.test.save({"a": a, "b": b})
00269
00270 result = [(i["a"], i["b"]) for i in
00271 db.test.find().sort([("b", DESCENDING),
00272 ("a", ASCENDING)])]
00273 self.assertEqual(result, expected)
00274
00275 a = db.test.find()
00276 a.sort("x", ASCENDING)
00277 for _ in a:
00278 break
00279 self.assertRaises(InvalidOperation, a.sort, "x", ASCENDING)
00280
00281 def test_count(self):
00282 db = self.db
00283 db.test.drop()
00284
00285 self.assertEqual(0, db.test.find().count())
00286
00287 for i in range(10):
00288 db.test.save({"x": i})
00289
00290 self.assertEqual(10, db.test.find().count())
00291 self.assert_(isinstance(db.test.find().count(), int))
00292 self.assertEqual(10, db.test.find().limit(5).count())
00293 self.assertEqual(10, db.test.find().skip(5).count())
00294
00295 self.assertEqual(1, db.test.find({"x": 1}).count())
00296 self.assertEqual(5, db.test.find({"x": {"$lt": 5}}).count())
00297
00298 a = db.test.find()
00299 b = a.count()
00300 for _ in a:
00301 break
00302 self.assertEqual(b, a.count())
00303
00304 self.assertEqual(0, db.test.acollectionthatdoesntexist.find().count())
00305
00306 def test_where(self):
00307 db = self.db
00308 db.test.drop()
00309
00310 a = db.test.find()
00311 self.assertRaises(TypeError, a.where, 5)
00312 self.assertRaises(TypeError, a.where, None)
00313 self.assertRaises(TypeError, a.where, {})
00314
00315 for i in range(10):
00316 db.test.save({"x": i})
00317
00318 self.assertEqual(3, len(list(db.test.find().where('this.x < 3'))))
00319 self.assertEqual(3,
00320 len(list(db.test.find().where(Code('this.x < 3')))))
00321 self.assertEqual(3, len(list(db.test.find().where(Code('this.x < i',
00322 {"i": 3})))))
00323 self.assertEqual(10, len(list(db.test.find())))
00324
00325 self.assertEqual(3, db.test.find().where('this.x < 3').count())
00326 self.assertEqual(10, db.test.find().count())
00327 self.assertEqual(3, db.test.find().where(u'this.x < 3').count())
00328 self.assertEqual([0, 1, 2],
00329 [a["x"] for a in
00330 db.test.find().where('this.x < 3')])
00331 self.assertEqual([],
00332 [a["x"] for a in
00333 db.test.find({"x": 5}).where('this.x < 3')])
00334 self.assertEqual([5],
00335 [a["x"] for a in
00336 db.test.find({"x": 5}).where('this.x > 3')])
00337
00338 cursor = db.test.find().where('this.x < 3').where('this.x > 7')
00339 self.assertEqual([8, 9], [a["x"] for a in cursor])
00340
00341 a = db.test.find()
00342 b = a.where('this.x > 3')
00343 for _ in a:
00344 break
00345 self.assertRaises(InvalidOperation, a.where, 'this.x < 3')
00346
00347 def test_kill_cursors(self):
00348 db = self.db
00349 db.drop_collection("test")
00350
00351 c = db.command("cursorInfo")["clientCursors_size"]
00352
00353 test = db.test
00354 for i in range(10000):
00355 test.insert({"i": i})
00356 self.assertEqual(c, db.command("cursorInfo")["clientCursors_size"])
00357
00358 for _ in range(10):
00359 db.test.find_one()
00360 self.assertEqual(c, db.command("cursorInfo")["clientCursors_size"])
00361
00362 for _ in range(10):
00363 for x in db.test.find():
00364 break
00365 self.assertEqual(c, db.command("cursorInfo")["clientCursors_size"])
00366
00367 a = db.test.find()
00368 for x in a:
00369 break
00370 self.assertNotEqual(c, db.command("cursorInfo")["clientCursors_size"])
00371
00372 del a
00373 self.assertEqual(c, db.command("cursorInfo")["clientCursors_size"])
00374
00375 a = db.test.find().limit(10)
00376 for x in a:
00377 break
00378 self.assertEqual(c, db.command("cursorInfo")["clientCursors_size"])
00379
00380 def test_rewind(self):
00381 self.db.test.save({"x": 1})
00382 self.db.test.save({"x": 2})
00383 self.db.test.save({"x": 3})
00384
00385 cursor = self.db.test.find().limit(2)
00386
00387 count = 0
00388 for _ in cursor:
00389 count += 1
00390 self.assertEqual(2, count)
00391
00392 count = 0
00393 for _ in cursor:
00394 count += 1
00395 self.assertEqual(0, count)
00396
00397 cursor.rewind()
00398 count = 0
00399 for _ in cursor:
00400 count += 1
00401 self.assertEqual(2, count)
00402
00403 cursor.rewind()
00404 count = 0
00405 for _ in cursor:
00406 break
00407 cursor.rewind()
00408 for _ in cursor:
00409 count += 1
00410 self.assertEqual(2, count)
00411
00412 self.assertEqual(cursor, cursor.rewind())
00413
00414 def test_clone(self):
00415 self.db.test.save({"x": 1})
00416 self.db.test.save({"x": 2})
00417 self.db.test.save({"x": 3})
00418
00419 cursor = self.db.test.find().limit(2)
00420
00421 count = 0
00422 for _ in cursor:
00423 count += 1
00424 self.assertEqual(2, count)
00425
00426 count = 0
00427 for _ in cursor:
00428 count += 1
00429 self.assertEqual(0, count)
00430
00431 cursor = cursor.clone()
00432 cursor2 = cursor.clone()
00433 count = 0
00434 for _ in cursor:
00435 count += 1
00436 self.assertEqual(2, count)
00437 for _ in cursor2:
00438 count += 1
00439 self.assertEqual(4, count)
00440
00441 cursor.rewind()
00442 count = 0
00443 for _ in cursor:
00444 break
00445 cursor = cursor.clone()
00446 for _ in cursor:
00447 count += 1
00448 self.assertEqual(2, count)
00449
00450 self.assertNotEqual(cursor, cursor.clone())
00451
00452 def test_count_with_fields(self):
00453 self.db.test.drop()
00454 self.db.test.save({"x": 1})
00455
00456 if not version.at_least(self.db.connection, (1, 1, 3, -1)):
00457 for _ in self.db.test.find({}, ["a"]):
00458 self.fail()
00459
00460 self.assertEqual(0, self.db.test.find({}, ["a"]).count())
00461 else:
00462 self.assertEqual(1, self.db.test.find({}, ["a"]).count())
00463
00464 def test_bad_getitem(self):
00465 self.assertRaises(TypeError, lambda x: self.db.test.find()[x], "hello")
00466 self.assertRaises(TypeError, lambda x: self.db.test.find()[x], 5.5)
00467 self.assertRaises(TypeError, lambda x: self.db.test.find()[x], None)
00468
00469 def test_getitem_slice_index(self):
00470 self.db.drop_collection("test")
00471 for i in range(100):
00472 self.db.test.save({"i": i})
00473
00474 izip = itertools.izip
00475 count = itertools.count
00476
00477 self.assertRaises(IndexError, lambda: self.db.test.find()[-1:])
00478 self.assertRaises(IndexError, lambda: self.db.test.find()[1:2:2])
00479
00480 for a, b in izip(count(0), self.db.test.find()):
00481 self.assertEqual(a, b['i'])
00482
00483 self.assertEqual(100, len(list(self.db.test.find()[0:])))
00484 for a, b in izip(count(0), self.db.test.find()[0:]):
00485 self.assertEqual(a, b['i'])
00486
00487 self.assertEqual(80, len(list(self.db.test.find()[20:])))
00488 for a, b in izip(count(20), self.db.test.find()[20:]):
00489 self.assertEqual(a, b['i'])
00490
00491 for a, b in izip(count(99), self.db.test.find()[99:]):
00492 self.assertEqual(a, b['i'])
00493
00494 for i in self.db.test.find()[1000:]:
00495 self.fail()
00496
00497 self.assertEqual(5, len(list(self.db.test.find()[20:25])))
00498 self.assertEqual(5, len(list(self.db.test.find()[20L:25L])))
00499 for a, b in izip(count(20), self.db.test.find()[20:25]):
00500 self.assertEqual(a, b['i'])
00501
00502 self.assertEqual(80, len(list(self.db.test.find()[40:45][20:])))
00503 for a, b in izip(count(20), self.db.test.find()[40:45][20:]):
00504 self.assertEqual(a, b['i'])
00505
00506 self.assertEqual(80, len(list(self.db.test.find()[40:45].limit(0).skip(20))))
00507 for a, b in izip(count(20), self.db.test.find()[40:45].limit(0).skip(20)):
00508 self.assertEqual(a, b['i'])
00509
00510 self.assertEqual(80, len(list(self.db.test.find().limit(10).skip(40)[20:])))
00511 for a, b in izip(count(20), self.db.test.find().limit(10).skip(40)[20:]):
00512 self.assertEqual(a, b['i'])
00513
00514 self.assertEqual(1, len(list(self.db.test.find()[:1])))
00515 self.assertEqual(5, len(list(self.db.test.find()[:5])))
00516
00517 self.assertEqual(1, len(list(self.db.test.find()[99:100])))
00518 self.assertEqual(1, len(list(self.db.test.find()[99:1000])))
00519 self.assertEqual(0, len(list(self.db.test.find()[10:10])))
00520 self.assertEqual(0, len(list(self.db.test.find()[:0])))
00521 self.assertEqual(80, len(list(self.db.test.find()[10:10].limit(0).skip(20))))
00522
00523 self.assertRaises(IndexError, lambda: self.db.test.find()[10:8])
00524
00525 def test_getitem_numeric_index(self):
00526 self.db.drop_collection("test")
00527 for i in range(100):
00528 self.db.test.save({"i": i})
00529
00530 self.assertEqual(0, self.db.test.find()[0]['i'])
00531 self.assertEqual(50, self.db.test.find()[50]['i'])
00532 self.assertEqual(50, self.db.test.find().skip(50)[0]['i'])
00533 self.assertEqual(50, self.db.test.find().skip(49)[1]['i'])
00534 self.assertEqual(50, self.db.test.find()[50L]['i'])
00535 self.assertEqual(99, self.db.test.find()[99]['i'])
00536
00537 self.assertRaises(IndexError, lambda x: self.db.test.find()[x], -1)
00538 self.assertRaises(IndexError, lambda x: self.db.test.find()[x], 100)
00539 self.assertRaises(IndexError, lambda x: self.db.test.find().skip(50)[x], 50)
00540
00541 def test_count_with_limit_and_skip(self):
00542 if not version.at_least(self.db.connection, (1, 1, 4, -1)):
00543 raise SkipTest()
00544
00545 def check_len(cursor, length):
00546 self.assertEqual(len(list(cursor)), cursor.count(True))
00547 self.assertEqual(length, cursor.count(True))
00548
00549 self.db.drop_collection("test")
00550 for i in range(100):
00551 self.db.test.save({"i": i})
00552
00553 check_len(self.db.test.find(), 100)
00554
00555 check_len(self.db.test.find().limit(10), 10)
00556 check_len(self.db.test.find().limit(110), 100)
00557
00558 check_len(self.db.test.find().skip(10), 90)
00559 check_len(self.db.test.find().skip(110), 0)
00560
00561 check_len(self.db.test.find().limit(10).skip(10), 10)
00562 check_len(self.db.test.find()[10:20], 10)
00563 check_len(self.db.test.find().limit(10).skip(95), 5)
00564 check_len(self.db.test.find()[95:105], 5)
00565
00566 def test_len(self):
00567 self.assertRaises(TypeError, len, self.db.test.find())
00568
00569 def test_properties(self):
00570 self.assertEqual(self.db.test, self.db.test.find().collection)
00571
00572 def set_coll():
00573 self.db.test.find().collection = "hello"
00574
00575 self.assertRaises(AttributeError, set_coll)
00576
00577 def test_tailable(self):
00578 db = self.db
00579 db.drop_collection("test")
00580 db.create_collection("test", capped=True, size=1000)
00581
00582 cursor = db.test.find(tailable=True)
00583
00584 db.test.insert({"x": 1})
00585 count = 0
00586 for doc in cursor:
00587 count += 1
00588 self.assertEqual(1, doc["x"])
00589 self.assertEqual(1, count)
00590
00591 db.test.insert({"x": 2})
00592 count = 0
00593 for doc in cursor:
00594 count += 1
00595 self.assertEqual(2, doc["x"])
00596 self.assertEqual(1, count)
00597
00598 db.test.insert({"x": 3})
00599 count = 0
00600 for doc in cursor:
00601 count += 1
00602 self.assertEqual(3, doc["x"])
00603 self.assertEqual(1, count)
00604
00605 self.assertEqual(3, db.test.count())
00606 db.drop_collection("test")
00607
00608 def test_distinct(self):
00609 if not version.at_least(self.db.connection, (1, 1, 3, 1)):
00610 raise SkipTest()
00611
00612 self.db.drop_collection("test")
00613
00614 self.db.test.save({"a": 1})
00615 self.db.test.save({"a": 2})
00616 self.db.test.save({"a": 2})
00617 self.db.test.save({"a": 2})
00618 self.db.test.save({"a": 3})
00619
00620 distinct = self.db.test.find({"a": {"$lt": 3}}).distinct("a")
00621 distinct.sort()
00622
00623 self.assertEqual([1, 2], distinct)
00624
00625 self.db.drop_collection("test")
00626
00627 self.db.test.save({"a": {"b": "a"}, "c": 12})
00628 self.db.test.save({"a": {"b": "b"}, "c": 8})
00629 self.db.test.save({"a": {"b": "c"}, "c": 12})
00630 self.db.test.save({"a": {"b": "c"}, "c": 8})
00631
00632 distinct = self.db.test.find({"c": 8}).distinct("a.b")
00633 distinct.sort()
00634
00635 self.assertEqual(["b", "c"], distinct)
00636
00637 def test_max_scan(self):
00638 if not version.at_least(self.db.connection, (1, 5, 1)):
00639 raise SkipTest()
00640
00641 self.db.drop_collection("test")
00642 for _ in range(100):
00643 self.db.test.insert({})
00644
00645 self.assertEqual(100, len(list(self.db.test.find())))
00646 self.assertEqual(50, len(list(self.db.test.find(max_scan=50))))
00647 self.assertEqual(50, len(list(self.db.test.find()
00648 .max_scan(90).max_scan(50))))
00649
00650
00651 if __name__ == "__main__":
00652 unittest.main()