updated advanced map/reduce test to include scope; misc cleanup in queryset
This commit is contained in:
@@ -5,13 +5,13 @@ import unittest
|
||||
import pymongo
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from mongoengine.queryset import (QuerySet, MultipleObjectsReturned,
|
||||
from mongoengine.queryset import (QuerySet, MultipleObjectsReturned,
|
||||
DoesNotExist)
|
||||
from mongoengine import *
|
||||
|
||||
|
||||
class QuerySetTest(unittest.TestCase):
|
||||
|
||||
|
||||
def setUp(self):
|
||||
connect(db='mongoenginetest')
|
||||
|
||||
@@ -19,12 +19,12 @@ class QuerySetTest(unittest.TestCase):
|
||||
name = StringField()
|
||||
age = IntField()
|
||||
self.Person = Person
|
||||
|
||||
|
||||
def test_initialisation(self):
|
||||
"""Ensure that a QuerySet is correctly initialised by QuerySetManager.
|
||||
"""
|
||||
self.assertTrue(isinstance(self.Person.objects, QuerySet))
|
||||
self.assertEqual(self.Person.objects._collection.name,
|
||||
self.assertEqual(self.Person.objects._collection.name,
|
||||
self.Person._meta['collection'])
|
||||
self.assertTrue(isinstance(self.Person.objects._collection,
|
||||
pymongo.collection.Collection))
|
||||
@@ -34,15 +34,15 @@ class QuerySetTest(unittest.TestCase):
|
||||
"""
|
||||
self.assertEqual(QuerySet._transform_query(name='test', age=30),
|
||||
{'name': 'test', 'age': 30})
|
||||
self.assertEqual(QuerySet._transform_query(age__lt=30),
|
||||
self.assertEqual(QuerySet._transform_query(age__lt=30),
|
||||
{'age': {'$lt': 30}})
|
||||
self.assertEqual(QuerySet._transform_query(age__gt=20, age__lt=50),
|
||||
{'age': {'$gt': 20, '$lt': 50}})
|
||||
self.assertEqual(QuerySet._transform_query(age=20, age__gt=50),
|
||||
{'age': 20})
|
||||
self.assertEqual(QuerySet._transform_query(friend__age__gte=30),
|
||||
self.assertEqual(QuerySet._transform_query(friend__age__gte=30),
|
||||
{'friend.age': {'$gte': 30}})
|
||||
self.assertEqual(QuerySet._transform_query(name__exists=True),
|
||||
self.assertEqual(QuerySet._transform_query(name__exists=True),
|
||||
{'name': {'$exists': True}})
|
||||
|
||||
def test_find(self):
|
||||
@@ -134,7 +134,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
self.assertEqual(person.name, "User B")
|
||||
|
||||
self.assertRaises(IndexError, self.Person.objects.__getitem__, 2)
|
||||
|
||||
|
||||
# Find a document using just the object id
|
||||
person = self.Person.objects.with_id(person1.id)
|
||||
self.assertEqual(person.name, "User A")
|
||||
@@ -170,7 +170,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
person2.save()
|
||||
|
||||
# Retrieve the first person from the database
|
||||
self.assertRaises(MultipleObjectsReturned,
|
||||
self.assertRaises(MultipleObjectsReturned,
|
||||
self.Person.objects.get_or_create)
|
||||
|
||||
# Use a query to filter the people found to just person2
|
||||
@@ -191,36 +191,36 @@ class QuerySetTest(unittest.TestCase):
|
||||
"""Ensure filters can be chained together.
|
||||
"""
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class BlogPost(Document):
|
||||
title = StringField()
|
||||
is_published = BooleanField()
|
||||
published_date = DateTimeField()
|
||||
|
||||
|
||||
@queryset_manager
|
||||
def published(doc_cls, queryset):
|
||||
return queryset(is_published=True)
|
||||
|
||||
blog_post_1 = BlogPost(title="Blog Post #1",
|
||||
|
||||
blog_post_1 = BlogPost(title="Blog Post #1",
|
||||
is_published = True,
|
||||
published_date=datetime(2010, 1, 5, 0, 0 ,0))
|
||||
blog_post_2 = BlogPost(title="Blog Post #2",
|
||||
blog_post_2 = BlogPost(title="Blog Post #2",
|
||||
is_published = True,
|
||||
published_date=datetime(2010, 1, 6, 0, 0 ,0))
|
||||
blog_post_3 = BlogPost(title="Blog Post #3",
|
||||
blog_post_3 = BlogPost(title="Blog Post #3",
|
||||
is_published = True,
|
||||
published_date=datetime(2010, 1, 7, 0, 0 ,0))
|
||||
|
||||
blog_post_1.save()
|
||||
blog_post_2.save()
|
||||
blog_post_3.save()
|
||||
|
||||
|
||||
# find all published blog posts before 2010-01-07
|
||||
published_posts = BlogPost.published()
|
||||
published_posts = published_posts.filter(
|
||||
published_date__lt=datetime(2010, 1, 7, 0, 0 ,0))
|
||||
self.assertEqual(published_posts.count(), 2)
|
||||
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_ordering(self):
|
||||
@@ -236,22 +236,22 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
blog_post_1 = BlogPost(title="Blog Post #1",
|
||||
blog_post_1 = BlogPost(title="Blog Post #1",
|
||||
published_date=datetime(2010, 1, 5, 0, 0 ,0))
|
||||
blog_post_2 = BlogPost(title="Blog Post #2",
|
||||
blog_post_2 = BlogPost(title="Blog Post #2",
|
||||
published_date=datetime(2010, 1, 6, 0, 0 ,0))
|
||||
blog_post_3 = BlogPost(title="Blog Post #3",
|
||||
blog_post_3 = BlogPost(title="Blog Post #3",
|
||||
published_date=datetime(2010, 1, 7, 0, 0 ,0))
|
||||
|
||||
blog_post_1.save()
|
||||
blog_post_2.save()
|
||||
blog_post_3.save()
|
||||
|
||||
|
||||
# get the "first" BlogPost using default ordering
|
||||
# from BlogPost.meta.ordering
|
||||
latest_post = BlogPost.objects.first()
|
||||
latest_post = BlogPost.objects.first()
|
||||
self.assertEqual(latest_post.title, "Blog Post #3")
|
||||
|
||||
|
||||
# override default ordering, order BlogPosts by "published_date"
|
||||
first_post = BlogPost.objects.order_by("+published_date").first()
|
||||
self.assertEqual(first_post.title, "Blog Post #1")
|
||||
@@ -277,7 +277,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
result = BlogPost.objects.first()
|
||||
self.assertTrue(isinstance(result.author, User))
|
||||
self.assertEqual(result.author.name, 'Test User')
|
||||
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
def test_find_dict_item(self):
|
||||
@@ -339,7 +339,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
self.Person(name='user2', age=20).save()
|
||||
self.Person(name='user3', age=30).save()
|
||||
self.Person(name='user4', age=40).save()
|
||||
|
||||
|
||||
self.assertEqual(len(self.Person.objects(Q(age__in=[20]))), 2)
|
||||
self.assertEqual(len(self.Person.objects(Q(age__in=[20, 30]))), 3)
|
||||
|
||||
@@ -452,10 +452,10 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
names = [p.name for p in self.Person.objects.order_by('age')]
|
||||
self.assertEqual(names, ['User A', 'User C', 'User B'])
|
||||
|
||||
|
||||
ages = [p.age for p in self.Person.objects.order_by('-name')]
|
||||
self.assertEqual(ages, [30, 40, 20])
|
||||
|
||||
|
||||
def test_map_reduce(self):
|
||||
"""Ensure map/reduce is both mapping and reducing.
|
||||
"""
|
||||
@@ -463,15 +463,15 @@ class QuerySetTest(unittest.TestCase):
|
||||
artists = ListField(StringField())
|
||||
title = StringField()
|
||||
is_cover = BooleanField()
|
||||
|
||||
|
||||
Song.drop_collection()
|
||||
|
||||
|
||||
Song(title="Gloria", is_cover=True, artists=['Patti Smith']).save()
|
||||
Song(title="Redondo beach", is_cover=False,
|
||||
Song(title="Redondo beach", is_cover=False,
|
||||
artists=['Patti Smith']).save()
|
||||
Song(title="My Generation", is_cover=True,
|
||||
artists=['Patti Smith', 'John Cale']).save()
|
||||
|
||||
|
||||
map_f = """
|
||||
function() {
|
||||
this.artists.forEach(function(artist) {
|
||||
@@ -479,7 +479,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
});
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
reduce_f = """
|
||||
function(key, values) {
|
||||
var total = 0;
|
||||
@@ -489,27 +489,27 @@ class QuerySetTest(unittest.TestCase):
|
||||
return total;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
# ensure both artists are found
|
||||
results = Song.objects.map_reduce(map_f, reduce_f)
|
||||
results = list(results)
|
||||
self.assertEqual(len(results), 2)
|
||||
|
||||
|
||||
# query for a count of Songs per artist, ordered by -count.
|
||||
# Patti Smith has 3 song credits, and should therefore be first.
|
||||
results = Song.objects.order_by("-value").map_reduce(map_f, reduce_f)
|
||||
results = list(results)
|
||||
self.assertEqual(results[0].key, "Patti Smith")
|
||||
self.assertEqual(results[0].value, 3.0)
|
||||
|
||||
|
||||
Song.drop_collection()
|
||||
|
||||
|
||||
def test_map_reduce_finalize(self):
|
||||
"""Ensure finalize is running by simulating "hotness"
|
||||
ranking with Reddit algorithm.
|
||||
"""Ensure that map, reduce, and finalize run and introduce "scope"
|
||||
by simulating "hotness" ranking with Reddit algorithm.
|
||||
"""
|
||||
from time import mktime
|
||||
|
||||
|
||||
class Link(Document):
|
||||
title = StringField()
|
||||
up_votes = IntField()
|
||||
@@ -521,10 +521,10 @@ class QuerySetTest(unittest.TestCase):
|
||||
now = datetime.utcnow()
|
||||
|
||||
# Note: Test data taken from a custom Reddit homepage on
|
||||
# Fri, 12 Feb 2010 14:36:00 -0600. Link ordering should
|
||||
# Fri, 12 Feb 2010 14:36:00 -0600. Link ordering should
|
||||
# reflect order of insertion below.
|
||||
|
||||
Link(title = "Google Buzz auto-followed a woman's abusive ex ...",
|
||||
|
||||
Link(title = "Google Buzz auto-followed a woman's abusive ex ...",
|
||||
up_votes = 1079,
|
||||
down_votes = 553,
|
||||
submitted = now-timedelta(hours=4)).save()
|
||||
@@ -532,12 +532,12 @@ class QuerySetTest(unittest.TestCase):
|
||||
up_votes = 481,
|
||||
down_votes = 124,
|
||||
submitted = now-timedelta(hours=2)).save()
|
||||
Link(title = "This Is A Mosquito Getting Killed By A Laser",
|
||||
Link(title = "This Is A Mosquito Getting Killed By A Laser",
|
||||
up_votes = 1446,
|
||||
down_votes = 530,
|
||||
submitted=now-timedelta(hours=13)).save()
|
||||
Link(title = "Arabic flashcards land physics student in jail.",
|
||||
up_votes = 215,
|
||||
up_votes = 215,
|
||||
down_votes = 105,
|
||||
submitted = now-timedelta(hours=6)).save()
|
||||
Link(title = "The Burger Lab: Presenting, the Flood Burger",
|
||||
@@ -552,21 +552,19 @@ class QuerySetTest(unittest.TestCase):
|
||||
map_f = """
|
||||
function() {
|
||||
emit(this._id, {up_delta: this.up_votes - this.down_votes,
|
||||
reddit_epoch: new Date(2005, 12, 8, 7, 46, 43, 0).getTime(),
|
||||
sub_date: this.submitted.getTime()})
|
||||
sub_date: this.submitted.getTime() / 1000})
|
||||
}
|
||||
"""
|
||||
|
||||
reduce_f = """
|
||||
function(key, values) {
|
||||
data = values[0];
|
||||
|
||||
|
||||
x = data.up_delta;
|
||||
|
||||
|
||||
// calculate time diff between reddit epoch and submission
|
||||
sec_since_epoch = data.sub_date - data.reddit_epoch;
|
||||
sec_since_epoch /= 1000;
|
||||
|
||||
sec_since_epoch = data.sub_date - reddit_epoch;
|
||||
|
||||
// calculate 'Y'
|
||||
if(x > 0) {
|
||||
y = 1;
|
||||
@@ -575,18 +573,18 @@ class QuerySetTest(unittest.TestCase):
|
||||
} else {
|
||||
y = -1;
|
||||
}
|
||||
|
||||
|
||||
// calculate 'Z', the maximal value
|
||||
if(Math.abs(x) >= 1) {
|
||||
z = Math.abs(x);
|
||||
} else {
|
||||
z = 1;
|
||||
}
|
||||
|
||||
|
||||
return {x: x, y: y, z: z, t_s: sec_since_epoch};
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
finalize_f = """
|
||||
function(key, value) {
|
||||
// f(sec_since_epoch,y,z) = log10(z) + ((y*sec_since_epoch) / 45000)
|
||||
@@ -596,19 +594,25 @@ class QuerySetTest(unittest.TestCase):
|
||||
}
|
||||
"""
|
||||
|
||||
reddit_epoch = mktime(datetime(2005, 12, 8, 7, 46, 43).timetuple())
|
||||
scope = {'reddit_epoch': reddit_epoch}
|
||||
|
||||
# ensure both artists are found
|
||||
results = Link.objects.order_by("-value")
|
||||
results = results.map_reduce(map_f, reduce_f, finalize_f=finalize_f)
|
||||
results = results.map_reduce(map_f,
|
||||
reduce_f,
|
||||
finalize_f=finalize_f,
|
||||
scope=scope)
|
||||
results = list(results)
|
||||
|
||||
|
||||
# assert troublesome Buzz article is ranked 1st
|
||||
self.assertTrue(results[0].object.title.startswith("Google Buzz"))
|
||||
|
||||
|
||||
# assert laser vision is ranked last
|
||||
self.assertTrue(results[-1].object.title.startswith("How to see"))
|
||||
|
||||
Link.drop_collection()
|
||||
|
||||
|
||||
def test_item_frequencies(self):
|
||||
"""Ensure that item frequencies are properly generated from lists.
|
||||
"""
|
||||
@@ -705,20 +709,20 @@ class QuerySetTest(unittest.TestCase):
|
||||
title = StringField(name='postTitle')
|
||||
comments = ListField(EmbeddedDocumentField(Comment),
|
||||
name='postComments')
|
||||
|
||||
|
||||
|
||||
BlogPost.drop_collection()
|
||||
|
||||
data = {'title': 'Post 1', 'comments': [Comment(content='test')]}
|
||||
BlogPost(**data).save()
|
||||
|
||||
self.assertTrue('postTitle' in
|
||||
self.assertTrue('postTitle' in
|
||||
BlogPost.objects(title=data['title'])._query)
|
||||
self.assertFalse('title' in
|
||||
self.assertFalse('title' in
|
||||
BlogPost.objects(title=data['title'])._query)
|
||||
self.assertEqual(len(BlogPost.objects(title=data['title'])), 1)
|
||||
|
||||
self.assertTrue('postComments.commentContent' in
|
||||
self.assertTrue('postComments.commentContent' in
|
||||
BlogPost.objects(comments__content='test')._query)
|
||||
self.assertEqual(len(BlogPost.objects(comments__content='test')), 1)
|
||||
|
||||
@@ -739,7 +743,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
post.save()
|
||||
|
||||
# Test that query may be performed by providing a document as a value
|
||||
# while using a ReferenceField's name - the document should be
|
||||
# while using a ReferenceField's name - the document should be
|
||||
# converted to an DBRef, which is legal, unlike a Document object
|
||||
post_obj = BlogPost.objects(author=person).first()
|
||||
self.assertEqual(post.id, post_obj.id)
|
||||
@@ -783,7 +787,7 @@ class QuerySetTest(unittest.TestCase):
|
||||
|
||||
|
||||
class QTest(unittest.TestCase):
|
||||
|
||||
|
||||
def test_or_and(self):
|
||||
"""Ensure that Q objects may be combined correctly.
|
||||
"""
|
||||
@@ -807,8 +811,8 @@ class QTest(unittest.TestCase):
|
||||
examples = [
|
||||
({'name': 'test'}, 'this.name == i0f0', {'i0f0': 'test'}),
|
||||
({'age': {'$gt': 18}}, 'this.age > i0f0o0', {'i0f0o0': 18}),
|
||||
({'name': 'test', 'age': {'$gt': 18, '$lte': 65}},
|
||||
'this.age <= i0f0o0 && this.age > i0f0o1 && this.name == i0f1',
|
||||
({'name': 'test', 'age': {'$gt': 18, '$lte': 65}},
|
||||
'this.age <= i0f0o0 && this.age > i0f0o1 && this.name == i0f1',
|
||||
{'i0f0o0': 65, 'i0f0o1': 18, 'i0f1': 'test'}),
|
||||
]
|
||||
for item, js, scope in examples:
|
||||
|
||||
Reference in New Issue
Block a user