Merge branch 'dev' of git://github.com/hmarr/mongoengine into dev

This commit is contained in:
Wilson Júnior 2011-08-17 09:24:17 -03:00
commit 4e462ffdb5
7 changed files with 222 additions and 27 deletions

61
AUTHORS
View File

@ -1,3 +1,5 @@
The PRIMARY AUTHORS are (and/or have been):
Harry Marr <harry@hmarr.com>
Matt Dennewitz <mattdennewitz@gmail.com>
Deepak Thukral <iapain@yahoo.com>
@ -5,3 +7,62 @@ Florian Schlachter <flori@n-schlachter.de>
Steve Challis <steve@stevechallis.com>
Ross Lawley <ross.lawley@gmail.com>
Wilson Júnior <wilsonpjunior@gmail.com>
Dan Crosta https://github.com/dcrosta
CONTRIBUTORS
Dervived from the git logs, inevitably incomplete but all of whom and others
have submitted patches, reported bugs and generally helped make MongoEngine
that much better:
* Harry Marr
* Ross Lawley
* blackbrrr
* Florian Schlachter
* Vincent Driessen
* Steve Challis
* flosch
* Deepak Thukral
* Colin Howe
* Wilson Júnior
* Alistair Roche
* Dan Crosta
* Viktor Kerkez
* Stephan Jaekel
* Rached Ben Mustapha
* Greg Turner
* Daniel Hasselrot
* Mircea Pasoi
* Matt Chisholm
* James Punteney
* TimothéePeignier
* Stuart Rackham
* Serge Matveenko
* Matt Dennewitz
* Don Spaulding
* Ales Zoulek
* sshwsfc
* sib
* Samuel Clay
* Nick Vlku
* martin
* Flavio Amieiro
* Анхбаяр Лхагвадорж
* Zak Johnson
* Victor Farazdagi
* vandersonmota
* Theo Julienne
* sp
* Slavi Pantaleev
* Richard Henry
* Nicolas Perriault
* Nick Vlku Jr
* Michael Henson
* Leo Honkanen
* kuno
* Josh Ourisman
* Jaime
* Igor Ivanov
* Gregg Lind
* Gareth Lloyd
* Albert Choi

View File

@ -5,6 +5,8 @@ Changelog
Changes in dev
==============
- Updated sum / average to use map_reduce as db.eval doesn't work in sharded environments
- Added where() - filter to allowing users to specify query expressions as Javascript
- Added SequenceField - for creating sequential counters
- Added update() convenience method to a document
- Added cascading saves - so changes to Referenced documents are saved on .save()
@ -28,7 +30,7 @@ Changes in dev
- Added insert method for bulk inserts
- Added blinker signal support
- Added query_counter context manager for tests
- Added optional map_reduce method item_frequencies
- Added map_reduce method item_frequencies and set as default (as db.eval doesn't work in sharded environments)
- Added inline_map_reduce option to map_reduce
- Updated connection exception so it provides more info on the cause.
- Added searching multiple levels deep in ``DictField``

View File

@ -812,7 +812,8 @@ class BaseDocument(object):
field_cls = field.document_type
if field_cls in inspected_classes:
continue
geo_indices += field_cls._geo_indices(inspected_classes)
if hasattr(field_cls, '_geo_indices'):
geo_indices += field_cls._geo_indices(inspected_classes)
elif field._geo_index:
geo_indices.append(field)
return geo_indices

View File

@ -620,7 +620,7 @@ class GenericReferenceField(BaseField):
"""A reference to *any* :class:`~mongoengine.document.Document` subclass
that will be automatically dereferenced on access (lazily).
note: Any documents used as a generic reference must be registered in the
..note :: Any documents used as a generic reference must be registered in the
document registry. Importing the model will automatically register it.
.. versionadded:: 0.3
@ -925,7 +925,7 @@ class SequenceField(IntField):
return value
def __set__(self, instance, value):
if value is None and instance._initialised:
value = self.generate_new_value()

View File

@ -1397,22 +1397,45 @@ class QuerySet(object):
db = _get_db()
return db.eval(code, *fields)
def where(self, where_clause):
"""Filter ``QuerySet`` results with a ``$where`` clause (a Javascript
expression). Performs automatic field name substitution like
:meth:`mongoengine.queryset.Queryset.exec_js`.
.. note:: When using this mode of query, the database will call your
function, or evaluate your predicate clause, for each object
in the collection.
"""
where_clause = self._sub_js_fields(where_clause)
self._where_clause = where_clause
return self
def sum(self, field):
"""Sum over the values of the specified field.
:param field: the field to sum over; use dot-notation to refer to
embedded document fields
"""
sum_func = """
function(sumField) {
var total = 0.0;
db[collection].find(query).forEach(function(doc) {
total += (doc[sumField] || 0.0);
});
return total;
map_func = pymongo.code.Code("""
function() {
emit(1, this[field] || 0);
}
"""
return self.exec_js(sum_func, field)
""", scope={'field': field})
reduce_func = pymongo.code.Code("""
function(key, values) {
var sum = 0;
for (var i in values) {
sum += values[i];
}
return sum;
}
""")
for result in self.map_reduce(map_func, reduce_func, output='inline'):
return result.value
else:
return 0
def average(self, field):
"""Average over the values of the specified field.
@ -1420,22 +1443,38 @@ class QuerySet(object):
:param field: the field to average over; use dot-notation to refer to
embedded document fields
"""
average_func = """
function(averageField) {
var total = 0.0;
var num = 0;
db[collection].find(query).forEach(function(doc) {
if (doc[averageField] !== undefined) {
total += doc[averageField];
num += 1;
}
});
return total / num;
map_func = pymongo.code.Code("""
function() {
if (this.hasOwnProperty(field))
emit(1, {t: this[field] || 0, c: 1});
}
"""
return self.exec_js(average_func, field)
""", scope={'field': field})
def item_frequencies(self, field, normalize=False, map_reduce=False):
reduce_func = pymongo.code.Code("""
function(key, values) {
var out = {t: 0, c: 0};
for (var i in values) {
var value = values[i];
out.t += value.t;
out.c += value.c;
}
return out;
}
""")
finalize_func = pymongo.code.Code("""
function(key, value) {
return value.t / value.c;
}
""")
for result in self.map_reduce(map_func, reduce_func, finalize_f=finalize_func, output='inline'):
return result.value
else:
return 0
def item_frequencies(self, field, normalize=False, map_reduce=True):
"""Returns a dictionary of all items present in a field across
the whole queried set of documents, and their corresponding frequency.
This is useful for generating tag clouds, or searching documents.

View File

@ -690,6 +690,57 @@ class DocumentTest(unittest.TestCase):
BlogPost.drop_collection()
def test_embedded_document_index(self):
"""Tests settings an index on an embedded document
"""
class Date(EmbeddedDocument):
year = IntField(db_field='yr')
class BlogPost(Document):
title = StringField()
date = EmbeddedDocumentField(Date)
meta = {
'indexes': [
'-date.year'
],
}
BlogPost.drop_collection()
info = BlogPost.objects._collection.index_information()
self.assertEqual(info.keys(), ['_types_1_date.yr_-1', '_id_'])
BlogPost.drop_collection()
def test_list_embedded_document_index(self):
"""Ensure list embedded documents can be indexed
"""
class Tag(EmbeddedDocument):
name = StringField(db_field='tag')
class BlogPost(Document):
title = StringField()
tags = ListField(EmbeddedDocumentField(Tag))
meta = {
'indexes': [
'tags.name'
],
}
BlogPost.drop_collection()
info = BlogPost.objects._collection.index_information()
# we don't use _types in with list fields by default
self.assertEqual(info.keys(), ['_id_', '_types_1', 'tags.tag_1'])
post1 = BlogPost(title="Embedded Indexes tests in place",
tags=[Tag(name="about"), Tag(name="time")]
)
post1.save()
BlogPost.drop_collection()
def test_geo_indexes_recursion(self):
class User(Document):

View File

@ -2502,6 +2502,47 @@ class QuerySetTest(unittest.TestCase):
for key, value in info.iteritems()]
self.assertTrue(([('_types', 1), ('message', 1)], False, False) in info)
def test_where(self):
"""Ensure that where clauses work.
"""
class IntPair(Document):
fielda = IntField()
fieldb = IntField()
IntPair.objects._collection.remove()
a = IntPair(fielda=1, fieldb=1)
b = IntPair(fielda=1, fieldb=2)
c = IntPair(fielda=2, fieldb=1)
a.save()
b.save()
c.save()
query = IntPair.objects.where('this[~fielda] >= this[~fieldb]')
self.assertEqual('this["fielda"] >= this["fieldb"]', query._where_clause)
results = list(query)
self.assertEqual(2, len(results))
self.assertTrue(a in results)
self.assertTrue(c in results)
query = IntPair.objects.where('this[~fielda] == this[~fieldb]')
results = list(query)
self.assertEqual(1, len(results))
self.assertTrue(a in results)
query = IntPair.objects.where('function() { return this[~fielda] >= this[~fieldb] }')
self.assertEqual('function() { return this["fielda"] >= this["fieldb"] }', query._where_clause)
results = list(query)
self.assertEqual(2, len(results))
self.assertTrue(a in results)
self.assertTrue(c in results)
def invalid_where():
list(IntPair.objects.where(fielda__gte=3))
self.assertRaises(TypeError, invalid_where)
class QTest(unittest.TestCase):