parent
0338ac17b1
commit
94cad89e32
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,3 +12,4 @@ env/
|
|||||||
.settings
|
.settings
|
||||||
.project
|
.project
|
||||||
.pydevproject
|
.pydevproject
|
||||||
|
tests/bugfix.py
|
||||||
|
@ -1303,7 +1303,16 @@ class QuerySet(object):
|
|||||||
# Substitute the correct name for the field into the javascript
|
# Substitute the correct name for the field into the javascript
|
||||||
return u'["%s"]' % fields[-1].db_field
|
return u'["%s"]' % fields[-1].db_field
|
||||||
|
|
||||||
return re.sub(u'\[\s*~([A-z_][A-z_0-9.]+?)\s*\]', field_sub, code)
|
def field_path_sub(match):
|
||||||
|
# Extract just the field name, and look up the field objects
|
||||||
|
field_name = match.group(1).split('.')
|
||||||
|
fields = QuerySet._lookup_field(self._document, field_name)
|
||||||
|
# Substitute the correct name for the field into the javascript
|
||||||
|
return ".".join([f.db_field for f in fields])
|
||||||
|
|
||||||
|
code = re.sub(u'\[\s*~([A-z_][A-z_0-9.]+?)\s*\]', field_sub, code)
|
||||||
|
code = re.sub(u'\{\{\s*~([A-z_][A-z_0-9.]+?)\s*\}\}', field_path_sub, code)
|
||||||
|
return code
|
||||||
|
|
||||||
def exec_js(self, code, *fields, **options):
|
def exec_js(self, code, *fields, **options):
|
||||||
"""Execute a Javascript function on the server. A list of fields may be
|
"""Execute a Javascript function on the server. A list of fields may be
|
||||||
@ -1405,12 +1414,15 @@ class QuerySet(object):
|
|||||||
def _item_frequencies_map_reduce(self, field, normalize=False):
|
def _item_frequencies_map_reduce(self, field, normalize=False):
|
||||||
map_func = """
|
map_func = """
|
||||||
function() {
|
function() {
|
||||||
if (this[~%(field)s].constructor == Array) {
|
path = '{{~%(field)s}}'.split('.');
|
||||||
this[~%(field)s].forEach(function(item) {
|
field = this;
|
||||||
|
for (p in path) { field = field[path[p]]; }
|
||||||
|
if (field.constructor == Array) {
|
||||||
|
field.forEach(function(item) {
|
||||||
emit(item, 1);
|
emit(item, 1);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
emit(this[~%(field)s], 1);
|
emit(field, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""" % dict(field=field)
|
""" % dict(field=field)
|
||||||
@ -1443,12 +1455,16 @@ class QuerySet(object):
|
|||||||
def _item_frequencies_exec_js(self, field, normalize=False):
|
def _item_frequencies_exec_js(self, field, normalize=False):
|
||||||
"""Uses exec_js to execute"""
|
"""Uses exec_js to execute"""
|
||||||
freq_func = """
|
freq_func = """
|
||||||
function(field) {
|
function(path) {
|
||||||
|
path = path.split('.');
|
||||||
|
|
||||||
if (options.normalize) {
|
if (options.normalize) {
|
||||||
var total = 0.0;
|
var total = 0.0;
|
||||||
db[collection].find(query).forEach(function(doc) {
|
db[collection].find(query).forEach(function(doc) {
|
||||||
if (doc[field].constructor == Array) {
|
field = doc;
|
||||||
total += doc[field].length;
|
for (p in path) { field = field[path[p]]; }
|
||||||
|
if (field.constructor == Array) {
|
||||||
|
total += field.length;
|
||||||
} else {
|
} else {
|
||||||
total++;
|
total++;
|
||||||
}
|
}
|
||||||
@ -1461,18 +1477,21 @@ class QuerySet(object):
|
|||||||
inc /= total;
|
inc /= total;
|
||||||
}
|
}
|
||||||
db[collection].find(query).forEach(function(doc) {
|
db[collection].find(query).forEach(function(doc) {
|
||||||
if (doc[field].constructor == Array) {
|
field = doc;
|
||||||
doc[field].forEach(function(item) {
|
for (p in path) { field = field[path[p]]; }
|
||||||
|
if (field.constructor == Array) {
|
||||||
|
field.forEach(function(item) {
|
||||||
frequencies[item] = inc + (isNaN(frequencies[item]) ? 0: frequencies[item]);
|
frequencies[item] = inc + (isNaN(frequencies[item]) ? 0: frequencies[item]);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
var item = doc[field];
|
var item = field;
|
||||||
frequencies[item] = inc + (isNaN(frequencies[item]) ? 0: frequencies[item]);
|
frequencies[item] = inc + (isNaN(frequencies[item]) ? 0: frequencies[item]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return frequencies;
|
return frequencies;
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.exec_js(freq_func, field, normalize=normalize)
|
return self.exec_js(freq_func, field, normalize=normalize)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -1116,6 +1116,11 @@ class QuerySetTest(unittest.TestCase):
|
|||||||
]
|
]
|
||||||
self.assertEqual(results, expected_results)
|
self.assertEqual(results, expected_results)
|
||||||
|
|
||||||
|
# Test template style
|
||||||
|
code = "{{~comments.content}}"
|
||||||
|
sub_code = BlogPost.objects._sub_js_fields(code)
|
||||||
|
self.assertEquals("cmnts.body", sub_code)
|
||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
@ -1637,6 +1642,64 @@ class QuerySetTest(unittest.TestCase):
|
|||||||
|
|
||||||
BlogPost.drop_collection()
|
BlogPost.drop_collection()
|
||||||
|
|
||||||
|
def test_item_frequencies_on_embedded(self):
|
||||||
|
"""Ensure that item frequencies are properly generated from lists.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Phone(EmbeddedDocument):
|
||||||
|
number = StringField()
|
||||||
|
|
||||||
|
class Person(Document):
|
||||||
|
name = StringField()
|
||||||
|
phone = EmbeddedDocumentField(Phone)
|
||||||
|
|
||||||
|
Person.drop_collection()
|
||||||
|
|
||||||
|
doc = Person(name="Guido")
|
||||||
|
doc.phone = Phone(number='62-3331-1656')
|
||||||
|
doc.save()
|
||||||
|
|
||||||
|
doc = Person(name="Marr")
|
||||||
|
doc.phone = Phone(number='62-3331-1656')
|
||||||
|
doc.save()
|
||||||
|
|
||||||
|
doc = Person(name="WP Junior")
|
||||||
|
doc.phone = Phone(number='62-3332-1656')
|
||||||
|
doc.save()
|
||||||
|
|
||||||
|
|
||||||
|
def test_assertions(f):
|
||||||
|
f = dict((key, int(val)) for key, val in f.items())
|
||||||
|
self.assertEqual(set(['62-3331-1656', '62-3332-1656']), set(f.keys()))
|
||||||
|
self.assertEqual(f['62-3331-1656'], 2)
|
||||||
|
self.assertEqual(f['62-3332-1656'], 1)
|
||||||
|
|
||||||
|
exec_js = Person.objects.item_frequencies('phone.number')
|
||||||
|
map_reduce = Person.objects.item_frequencies('phone.number', map_reduce=True)
|
||||||
|
test_assertions(exec_js)
|
||||||
|
test_assertions(map_reduce)
|
||||||
|
|
||||||
|
# Ensure query is taken into account
|
||||||
|
def test_assertions(f):
|
||||||
|
f = dict((key, int(val)) for key, val in f.items())
|
||||||
|
self.assertEqual(set(['62-3331-1656']), set(f.keys()))
|
||||||
|
self.assertEqual(f['62-3331-1656'], 2)
|
||||||
|
|
||||||
|
exec_js = Person.objects(phone__number='62-3331-1656').item_frequencies('phone.number')
|
||||||
|
map_reduce = Person.objects(phone__number='62-3331-1656').item_frequencies('phone.number', map_reduce=True)
|
||||||
|
test_assertions(exec_js)
|
||||||
|
test_assertions(map_reduce)
|
||||||
|
|
||||||
|
# Check that normalization works
|
||||||
|
def test_assertions(f):
|
||||||
|
self.assertEqual(f['62-3331-1656'], 2.0/3.0)
|
||||||
|
self.assertEqual(f['62-3332-1656'], 1.0/3.0)
|
||||||
|
|
||||||
|
exec_js = Person.objects.item_frequencies('phone.number', normalize=True)
|
||||||
|
map_reduce = Person.objects.item_frequencies('phone.number', normalize=True, map_reduce=True)
|
||||||
|
test_assertions(exec_js)
|
||||||
|
test_assertions(map_reduce)
|
||||||
|
|
||||||
def test_average(self):
|
def test_average(self):
|
||||||
"""Ensure that field can be averaged correctly.
|
"""Ensure that field can be averaged correctly.
|
||||||
"""
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user