@@ 7,6 7,7 @@ import warnings
from copy import deepcopy
from functools import partial
from itertools import starmap
+from operator import itemgetter
try:
from itertools import ifilter as filter, imap as map
@@ 14,6 15,9 @@ except ImportError: # pragma: no cover
pass
+ASCENDING = False
+DESCENDING = True
+
class MalformedQueryException(Exception):
pass
@@ 217,7 221,7 @@ class Collection(object):
document['_id'] = id
return document
- def find(self, query=None, skip=None, limit=None, hint=None):
+ def find(self, query=None, skip=None, limit=None, hint=None, sort=None):
"""
Returns a list of documents in this collection that match a given query
"""
@@ 255,10 259,15 @@ class Collection(object):
results.append(match)
# Just return if we already reached the limit
- if limit and len(results) == limit:
- return results
+ if limit and len(results) == limit and sort is None:
+ break
+ if sort: # sort={key1:direction1, key2:direction2, ...}
+ sort_keys = list(sort.keys())
+ sort_keys.reverse() # sort from right to left
+ for key in sort_keys:
+ results = sorted(results, key=itemgetter(key), reverse=sort[key])
- return results
+ return results[:limit] if isinstance(limit, int) else results
def _apply_query(self, query, document):
"""
@@ 345,6 345,70 @@ class TestCollection(object):
"WHERE type='table' and name like '{name}{{%}}'")
assert self.collection.db.execute(cmd.format(name=self.collection.name)).fetchone() is None
+ def test_find_with_sort(self):
+ self.collection.create()
+ self.collection.save({'a':1, 'b':'c'})
+ self.collection.save({'a':1, 'b':'a'})
+ self.collection.save({'a':5, 'b':'x'})
+ self.collection.save({'a':3, 'b':'x'})
+ self.collection.save({'a':4, 'b':'z'})
+ assert [
+ {'a':1, 'b':'c', '_id':1},
+ {'a':1, 'b':'a', '_id':2},
+ {'a':5, 'b':'x', '_id':3},
+ {'a':3, 'b':'x', '_id':4},
+ {'a':4, 'b':'z', '_id':5},
+ ] == self.collection.find()
+ assert [
+ {'a':1, 'b':'c', '_id':1},
+ {'a':1, 'b':'a', '_id':2},
+ {'a':3, 'b':'x', '_id':4},
+ {'a':4, 'b':'z', '_id':5},
+ {'a':5, 'b':'x', '_id':3},
+ ] == self.collection.find(sort={'a':nosqlite.ASCENDING})
+ assert [
+ {'a':1, 'b':'a', '_id':2},
+ {'a':1, 'b':'c', '_id':1},
+ {'a':5, 'b':'x', '_id':3},
+ {'a':3, 'b':'x', '_id':4},
+ {'a':4, 'b':'z', '_id':5},
+ ] == self.collection.find(sort={'b':nosqlite.ASCENDING})
+ assert [
+ {'a':5, 'b':'x', '_id':3},
+ {'a':4, 'b':'z', '_id':5},
+ {'a':3, 'b':'x', '_id':4},
+ {'a':1, 'b':'c', '_id':1},
+ {'a':1, 'b':'a', '_id':2},
+ ] == self.collection.find(sort={'a':nosqlite.DESCENDING})
+ assert [
+ {'a':4, 'b':'z', '_id':5},
+ {'a':5, 'b':'x', '_id':3},
+ {'a':3, 'b':'x', '_id':4},
+ {'a':1, 'b':'c', '_id':1},
+ {'a':1, 'b':'a', '_id':2},
+ ] == self.collection.find(sort={'b':nosqlite.DESCENDING})
+ assert [
+ {'a':1, 'b':'a', '_id':2},
+ {'a':1, 'b':'c', '_id':1},
+ {'a':3, 'b':'x', '_id':4},
+ {'a':4, 'b':'z', '_id':5},
+ {'a':5, 'b':'x', '_id':3},
+ ] == self.collection.find(sort={'a':nosqlite.ASCENDING, 'b':nosqlite.ASCENDING})
+ assert [
+ {'a':5, 'b':'x', '_id':3},
+ {'a':4, 'b':'z', '_id':5},
+ {'a':3, 'b':'x', '_id':4},
+ {'a':1, 'b':'a', '_id':2},
+ {'a':1, 'b':'c', '_id':1},
+ ] == self.collection.find(sort={'a':nosqlite.DESCENDING, 'b':nosqlite.ASCENDING})
+ assert [
+ {'a':5, 'b':'x', '_id':3},
+ {'a':4, 'b':'z', '_id':5},
+ {'a':3, 'b':'x', '_id':4},
+ {'a':1, 'b':'c', '_id':1},
+ {'a':1, 'b':'a', '_id':2},
+ ] == self.collection.find(sort={'a':nosqlite.DESCENDING, 'b':nosqlite.DESCENDING})
+
@mark.parametrize('strdoc,doc', [
('{"foo": "bar"}', {'_id': 1, 'foo': 'bar'}),
(u'{"foo": "☃"}', {'_id': 1, 'foo': u'☃'}),