Skip to content
2 changes: 2 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,5 @@ that much better:
* Tom (https://github.com/tomprimozic)
* j0hnsmith (https://github.com/j0hnsmith)
* Damien Churchill (https://github.com/damoxc)
* Jonathan Simon Prates (https://github.com/jonathansp)
* Thiago Papageorgiou (https://github.com/tmpapageorgiou)
25 changes: 21 additions & 4 deletions mongoengine/queryset/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def __init__(self, document, collection):
self._initial_query = {}
self._where_clause = None
self._loaded_fields = QueryFieldList()
self._ordering = []
self._ordering = None
self._snapshot = False
self._timeout = True
self._class_check = True
Expand Down Expand Up @@ -154,6 +154,22 @@ def __getitem__(self, key):
def __iter__(self):
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

def all(self):
Expand Down Expand Up @@ -1189,8 +1205,9 @@ def _cursor(self):
if self._ordering:
# Apply query ordering
self._cursor_obj.sort(self._ordering)
elif self._document._meta['ordering']:
# Otherwise, apply the ordering from the document model
elif self._ordering is None and self._document._meta['ordering']:
# 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'])
self._cursor_obj.sort(order)

Expand Down Expand Up @@ -1392,7 +1409,7 @@ def _get_order_by(self, keys):
pass
key_list.append((key, direction))

if self._cursor_obj:
if self._cursor_obj and key_list:
self._cursor_obj.sort(key_list)
return key_list

Expand Down
175 changes: 175 additions & 0 deletions tests/queryset/queryset.py
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,76 @@ class BlogPost(Document):
expected = [blog_post_1, blog_post_2, blog_post_3]
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):
"""Ensure that an embedded document is properly returned from a query.
"""
Expand Down Expand Up @@ -3814,6 +3884,111 @@ class Example(Document):
self.assertEqual(Example.objects(size=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__':
unittest.main()