Merge pull request #655 from jonathansp/master

Avoid to open all documents from cursors in an if stmt
This commit is contained in:
Yohan Graterol 2014-06-25 09:45:51 -05:00
commit 2d5280fc95
3 changed files with 198 additions and 4 deletions

View File

@ -189,3 +189,5 @@ that much better:
* Tom (https://github.com/tomprimozic) * Tom (https://github.com/tomprimozic)
* j0hnsmith (https://github.com/j0hnsmith) * j0hnsmith (https://github.com/j0hnsmith)
* Damien Churchill (https://github.com/damoxc) * Damien Churchill (https://github.com/damoxc)
* Jonathan Simon Prates (https://github.com/jonathansp)
* Thiago Papageorgiou (https://github.com/tmpapageorgiou)

View File

@ -50,7 +50,7 @@ class BaseQuerySet(object):
self._initial_query = {} self._initial_query = {}
self._where_clause = None self._where_clause = None
self._loaded_fields = QueryFieldList() self._loaded_fields = QueryFieldList()
self._ordering = [] self._ordering = None
self._snapshot = False self._snapshot = False
self._timeout = True self._timeout = True
self._class_check = True self._class_check = True
@ -154,6 +154,22 @@ class BaseQuerySet(object):
def __iter__(self): def __iter__(self):
raise NotImplementedError raise NotImplementedError
def _has_data(self):
""" Retrieves whether cursor has any data. """
queryset = self.order_by()
return False if queryset.first() is None else True
def __nonzero__(self):
""" Avoid to open all records in an if stmt in Py2. """
return self._has_data()
def __bool__(self):
""" Avoid to open all records in an if stmt in Py3. """
return self._has_data()
# Core functions # Core functions
def all(self): def all(self):
@ -1189,8 +1205,9 @@ class BaseQuerySet(object):
if self._ordering: if self._ordering:
# Apply query ordering # Apply query ordering
self._cursor_obj.sort(self._ordering) self._cursor_obj.sort(self._ordering)
elif self._document._meta['ordering']: elif self._ordering is None and self._document._meta['ordering']:
# Otherwise, apply the ordering from the document model # Otherwise, apply the ordering from the document model, unless
# it's been explicitly cleared via order_by with no arguments
order = self._get_order_by(self._document._meta['ordering']) order = self._get_order_by(self._document._meta['ordering'])
self._cursor_obj.sort(order) self._cursor_obj.sort(order)
@ -1392,7 +1409,7 @@ class BaseQuerySet(object):
pass pass
key_list.append((key, direction)) key_list.append((key, direction))
if self._cursor_obj: if self._cursor_obj and key_list:
self._cursor_obj.sort(key_list) self._cursor_obj.sort(key_list)
return key_list return key_list

View File

@ -1040,6 +1040,76 @@ class QuerySetTest(unittest.TestCase):
expected = [blog_post_1, blog_post_2, blog_post_3] expected = [blog_post_1, blog_post_2, blog_post_3]
self.assertSequence(qs, expected) self.assertSequence(qs, expected)
def test_clear_ordering(self):
""" Make sure one can clear the query set ordering by applying a
consecutive order_by()
"""
class Person(Document):
name = StringField()
Person.drop_collection()
Person(name="A").save()
Person(name="B").save()
qs = Person.objects.order_by('-name')
# Make sure we can clear a previously specified ordering
with query_counter() as q:
lst = list(qs.order_by())
op = q.db.system.profile.find({"ns":
{"$ne": "%s.system.indexes" % q.db.name}})[0]
self.assertTrue('$orderby' not in op['query'])
self.assertEqual(lst[0].name, 'A')
# Make sure previously specified ordering is preserved during
# consecutive calls to the same query set
with query_counter() as q:
lst = list(qs)
op = q.db.system.profile.find({"ns":
{"$ne": "%s.system.indexes" % q.db.name}})[0]
self.assertTrue('$orderby' in op['query'])
self.assertEqual(lst[0].name, 'B')
def test_clear_default_ordering(self):
class Person(Document):
name = StringField()
meta = {
'ordering': ['-name']
}
Person.drop_collection()
Person(name="A").save()
Person(name="B").save()
qs = Person.objects
# Make sure clearing default ordering works
with query_counter() as q:
lst = list(qs.order_by())
op = q.db.system.profile.find({"ns":
{"$ne": "%s.system.indexes" % q.db.name}})[0]
self.assertTrue('$orderby' not in op['query'])
self.assertEqual(lst[0].name, 'A')
# Make sure default ordering is preserved during consecutive calls
# to the same query set
with query_counter() as q:
lst = list(qs)
op = q.db.system.profile.find({"ns":
{"$ne": "%s.system.indexes" % q.db.name}})[0]
self.assertTrue('$orderby' in op['query'])
self.assertEqual(lst[0].name, 'B')
def test_find_embedded(self): def test_find_embedded(self):
"""Ensure that an embedded document is properly returned from a query. """Ensure that an embedded document is properly returned from a query.
""" """
@ -3814,6 +3884,111 @@ class QuerySetTest(unittest.TestCase):
self.assertEqual(Example.objects(size=instance_size).count(), 1) self.assertEqual(Example.objects(size=instance_size).count(), 1)
self.assertEqual(Example.objects(size__in=[instance_size]).count(), 1) self.assertEqual(Example.objects(size__in=[instance_size]).count(), 1)
def test_cursor_in_an_if_stmt(self):
class Test(Document):
test_field = StringField()
Test.drop_collection()
queryset = Test.objects
if queryset:
raise AssertionError('Empty cursor returns True')
test = Test()
test.test_field = 'test'
test.save()
queryset = Test.objects
if not test:
raise AssertionError('Cursor has data and returned False')
queryset.next()
if not queryset:
raise AssertionError('Cursor has data and it must returns True,'
' even in the last item.')
def test_bool_performance(self):
class Person(Document):
name = StringField()
Person.drop_collection()
for i in xrange(100):
Person(name="No: %s" % i).save()
with query_counter() as q:
if Person.objects:
pass
self.assertEqual(q, 1)
op = q.db.system.profile.find({"ns":
{"$ne": "%s.system.indexes" % q.db.name}})[0]
self.assertEqual(op['nreturned'], 1)
def test_bool_with_ordering(self):
class Person(Document):
name = StringField()
Person.drop_collection()
Person(name="Test").save()
qs = Person.objects.order_by('name')
with query_counter() as q:
if qs:
pass
op = q.db.system.profile.find({"ns":
{"$ne": "%s.system.indexes" % q.db.name}})[0]
self.assertFalse('$orderby' in op['query'],
'BaseQuerySet cannot use orderby in if stmt')
with query_counter() as p:
for x in qs:
pass
op = p.db.system.profile.find({"ns":
{"$ne": "%s.system.indexes" % q.db.name}})[0]
self.assertTrue('$orderby' in op['query'],
'BaseQuerySet cannot remove orderby in for loop')
def test_bool_with_ordering_from_meta_dict(self):
class Person(Document):
name = StringField()
meta = {
'ordering': ['name']
}
Person.drop_collection()
Person(name="B").save()
Person(name="C").save()
Person(name="A").save()
with query_counter() as q:
if Person.objects:
pass
op = q.db.system.profile.find({"ns":
{"$ne": "%s.system.indexes" % q.db.name}})[0]
self.assertFalse('$orderby' in op['query'],
'BaseQuerySet must remove orderby from meta in boolen test')
self.assertEqual(Person.objects.first().name, 'A')
self.assertTrue(Person.objects._has_data(),
'Cursor has data and returned False')
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()