From 96dbeea171cc7d081ccd269705752722bad146bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D1=85=D0=B1=D0=B0=D1=8F=D1=80=20=D0=9B=D1=85?= =?UTF-8?q?=D0=B0=D0=B3=D0=B2=D0=B0=D0=B4=D0=BE=D1=80=D0=B6?= Date: Tue, 12 Apr 2011 20:23:16 +0800 Subject: [PATCH 1/4] Added __hash__, __ne__ with test. --- mongoengine/base.py | 11 +++++++++++ tests/document.py | 48 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/mongoengine/base.py b/mongoengine/base.py index 6b74cb07..6340e319 100644 --- a/mongoengine/base.py +++ b/mongoengine/base.py @@ -489,6 +489,17 @@ class BaseDocument(object): return True return False + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + """ For list, dic key """ + if self.pk is None: + # For new object + return super(BaseDocument,self).__hash__() + else: + return hash(self.pk) + if sys.version_info < (2, 5): # Prior to Python 2.5, Exception was an old-style class def subclass_exception(name, parents, unused): diff --git a/tests/document.py b/tests/document.py index c0567632..280b671e 100644 --- a/tests/document.py +++ b/tests/document.py @@ -627,6 +627,54 @@ class DocumentTest(unittest.TestCase): def tearDown(self): self.Person.drop_collection() + def test_document_hash(self): + """Test document in list, dict, set + """ + class User(Document): + pass + + class BlogPost(Document): + pass + + # Clear old datas + User.drop_collection() + BlogPost.drop_collection() + + u1 = User.objects.create() + u2 = User.objects.create() + u3 = User.objects.create() + u4 = User() # New object + + b1 = BlogPost.objects.create() + b2 = BlogPost.objects.create() + + # in List + all_user_list = list(User.objects.all()) + + self.assertTrue(u1 in all_user_list) + self.assertTrue(u2 in all_user_list) + self.assertTrue(u3 in all_user_list) + self.assertFalse(u4 in all_user_list) # New object + self.assertFalse(b1 in all_user_list) # Other object + self.assertFalse(b2 in all_user_list) # Other object + + # in Dict + all_user_dic = {} + for u in User.objects.all(): + all_user_dic[u] = "OK" + + self.assertEqual(all_user_dic.get(u1, False), "OK" ) + self.assertEqual(all_user_dic.get(u2, False), "OK" ) + self.assertEqual(all_user_dic.get(u3, False), "OK" ) + self.assertEqual(all_user_dic.get(u4, False), False ) # New object + self.assertEqual(all_user_dic.get(b1, False), False ) # Other object + self.assertEqual(all_user_dic.get(b2, False), False ) # Other object + + # in Set + all_user_set = set(User.objects.all()) + + self.assertTrue(u1 in all_user_set ) + if __name__ == '__main__': unittest.main() From 3b7a8ce449ef6ca4f4c09775a2a5cdbed248a2aa Mon Sep 17 00:00:00 2001 From: Matt Chisholm Date: Mon, 9 May 2011 22:29:27 +0200 Subject: [PATCH 2/4] change comments to reflect that the geospatial queries use degrees, not miles --- tests/queryset.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/queryset.py b/tests/queryset.py index 6ca4174d..3592cc69 100644 --- a/tests/queryset.py +++ b/tests/queryset.py @@ -1316,7 +1316,7 @@ class QuerySetTest(unittest.TestCase): self.assertEqual(events.count(), 3) self.assertEqual(list(events), [event1, event3, event2]) - # find events within 5 miles of pitchfork office, chicago + # find events within 5 degrees of pitchfork office, chicago point_and_distance = [[41.9120459, -87.67892], 5] events = Event.objects(location__within_distance=point_and_distance) self.assertEqual(events.count(), 2) @@ -1331,13 +1331,13 @@ class QuerySetTest(unittest.TestCase): self.assertEqual(events.count(), 3) self.assertEqual(list(events), [event3, event1, event2]) - # find events around san francisco + # find events within 10 degrees of san francisco point_and_distance = [[37.7566023, -122.415579], 10] events = Event.objects(location__within_distance=point_and_distance) self.assertEqual(events.count(), 1) self.assertEqual(events[0], event2) - # find events within 1 mile of greenpoint, broolyn, nyc, ny + # find events within 1 degree of greenpoint, broolyn, nyc, ny point_and_distance = [[40.7237134, -73.9509714], 1] events = Event.objects(location__within_distance=point_and_distance) self.assertEqual(events.count(), 0) From 608f08c267aff857eba5bc28ef9fc70fd11abe71 Mon Sep 17 00:00:00 2001 From: Matt Chisholm Date: Tue, 10 May 2011 12:28:56 +0200 Subject: [PATCH 3/4] Implement spherical geospatial query operators & unit tests fixes https://github.com/hmarr/mongoengine/issues/163 --- mongoengine/queryset.py | 6 ++++- tests/queryset.py | 52 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/mongoengine/queryset.py b/mongoengine/queryset.py index 519dda03..008d2406 100644 --- a/mongoengine/queryset.py +++ b/mongoengine/queryset.py @@ -475,7 +475,7 @@ class QuerySet(object): """ operators = ['ne', 'gt', 'gte', 'lt', 'lte', 'in', 'nin', 'mod', 'all', 'size', 'exists', 'not'] - geo_operators = ['within_distance', 'within_box', 'near'] + geo_operators = ['within_distance', 'within_spherical_distance', 'within_box', 'near', 'near_sphere'] match_operators = ['contains', 'icontains', 'startswith', 'istartswith', 'endswith', 'iendswith', 'exact', 'iexact'] @@ -519,8 +519,12 @@ class QuerySet(object): if op in geo_operators: if op == "within_distance": value = {'$within': {'$center': value}} + elif op == "within_spherical_distance": + value = {'$within': {'$centerSphere': value}} elif op == "near": value = {'$near': value} + elif op == "near_sphere": + value = {'$nearSphere': value} elif op == 'within_box': value = {'$within': {'$box': value}} else: diff --git a/tests/queryset.py b/tests/queryset.py index 3592cc69..03256d50 100644 --- a/tests/queryset.py +++ b/tests/queryset.py @@ -1357,6 +1357,58 @@ class QuerySetTest(unittest.TestCase): Event.drop_collection() + def test_spherical_geospatial_operators(self): + """Ensure that spherical geospatial queries are working + """ + class Point(Document): + location = GeoPointField() + + Point.drop_collection() + + # These points are one degree apart, which (according to Google Maps) + # is about 110 km apart at this place on the Earth. + north_point = Point(location=[-122, 38]) # Near Concord, CA + south_point = Point(location=[-122, 37]) # Near Santa Cruz, CA + north_point.save() + south_point.save() + + earth_radius = 6378.009; # in km (needs to be a float for dividing by) + + # Finds both points because they are within 60 km of the reference + # point equidistant between them. + points = Point.objects(location__near_sphere=[-122, 37.5]) + self.assertEqual(points.count(), 2) + + # Finds both points, but orders the north point first because it's + # closer to the reference point to the north. + points = Point.objects(location__near_sphere=[-122, 38.5]) + self.assertEqual(points.count(), 2) + self.assertEqual(points[0].id, north_point.id) + self.assertEqual(points[1].id, south_point.id) + + # Finds both points, but orders the south point first because it's + # closer to the reference point to the south. + points = Point.objects(location__near_sphere=[-122, 36.5]) + self.assertEqual(points.count(), 2) + self.assertEqual(points[0].id, south_point.id) + self.assertEqual(points[1].id, north_point.id) + + # Same behavior for _within_spherical_distance + points = Point.objects( + location__within_spherical_distance=[[-122, 37.5], 60/earth_radius] + ); + self.assertEqual(points.count(), 2) + + # Finds only one point because only the first point is within 60km of + # the reference point to the south. + points = Point.objects( + location__within_spherical_distance=[[-122, 36.5], 60/earth_radius] + ); + self.assertEqual(points.count(), 1) + self.assertEqual(points[0].id, south_point.id) + + Point.drop_collection() + def test_custom_querysets(self): """Ensure that custom QuerySet classes may be used. """ From 6cf0cf9e7d8076773ebfebb60d4e56faa9cd49f8 Mon Sep 17 00:00:00 2001 From: Matt Chisholm Date: Tue, 10 May 2011 12:31:31 +0200 Subject: [PATCH 4/4] slight reordering of test code for clarity --- tests/queryset.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/queryset.py b/tests/queryset.py index 03256d50..6d4f1619 100644 --- a/tests/queryset.py +++ b/tests/queryset.py @@ -1379,6 +1379,12 @@ class QuerySetTest(unittest.TestCase): points = Point.objects(location__near_sphere=[-122, 37.5]) self.assertEqual(points.count(), 2) + # Same behavior for _within_spherical_distance + points = Point.objects( + location__within_spherical_distance=[[-122, 37.5], 60/earth_radius] + ); + self.assertEqual(points.count(), 2) + # Finds both points, but orders the north point first because it's # closer to the reference point to the north. points = Point.objects(location__near_sphere=[-122, 38.5]) @@ -1393,12 +1399,6 @@ class QuerySetTest(unittest.TestCase): self.assertEqual(points[0].id, south_point.id) self.assertEqual(points[1].id, north_point.id) - # Same behavior for _within_spherical_distance - points = Point.objects( - location__within_spherical_distance=[[-122, 37.5], 60/earth_radius] - ); - self.assertEqual(points.count(), 2) - # Finds only one point because only the first point is within 60km of # the reference point to the south. points = Point.objects(