77from mongoengine .connection import get_connection
88from mongoengine .common import _import_class
99from mongoengine .errors import InvalidQueryError
10+ from mongoengine .python_support import IS_PYMONGO_3
1011
1112__all__ = ('query' , 'update' )
1213
1516 'all' , 'size' , 'exists' , 'not' , 'elemMatch' , 'type' )
1617GEO_OPERATORS = ('within_distance' , 'within_spherical_distance' ,
1718 'within_box' , 'within_polygon' , 'near' , 'near_sphere' ,
18- 'max_distance' , 'geo_within' , 'geo_within_box' ,
19+ 'max_distance' , 'min_distance' , ' geo_within' , 'geo_within_box' ,
1920 'geo_within_polygon' , 'geo_within_center' ,
2021 'geo_within_sphere' , 'geo_intersects' )
2122STRING_OPERATORS = ('contains' , 'icontains' , 'startswith' ,
@@ -44,8 +45,8 @@ def query(_doc_cls=None, _field_operation=False, **query):
4445 if len (parts ) > 1 and parts [- 1 ] in MATCH_OPERATORS :
4546 op = parts .pop ()
4647
47- # if user escape field name by __
48- if len (parts ) > 1 and parts [- 1 ] == "" :
48+ #if user escape field name by __
49+ if len (parts ) > 1 and parts [- 1 ]== "" :
4950 parts .pop ()
5051
5152 negate = False
@@ -126,24 +127,34 @@ def query(_doc_cls=None, _field_operation=False, **query):
126127 elif key in mongo_query :
127128 if key in mongo_query and isinstance (mongo_query [key ], dict ):
128129 mongo_query [key ].update (value )
129- # $maxDistance needs to come last - convert to SON
130+ # $max/minDistance needs to come last - convert to SON
130131 value_dict = mongo_query [key ]
131- if '$maxDistance' in value_dict and '$near' in value_dict :
132+ if ('$maxDistance' in value_dict or '$minDistance' in value_dict ) and \
133+ ('$near' in value_dict or '$nearSphere' in value_dict ):
132134 value_son = SON ()
133- if isinstance (value_dict ['$near' ], dict ):
134- for k , v in value_dict .iteritems ():
135- if k == '$maxDistance' :
136- continue
137- value_son [k ] = v
138- value_son ['$near' ] = SON (value_son ['$near' ])
139- value_son ['$near' ]['$maxDistance' ] = value_dict ['$maxDistance' ]
140- else :
141- for k , v in value_dict .iteritems ():
142- if k == '$maxDistance' :
143- continue
144- value_son [k ] = v
145- value_son ['$maxDistance' ] = value_dict ['$maxDistance' ]
146-
135+ for k , v in value_dict .iteritems ():
136+ if k == '$maxDistance' or k == '$minDistance' :
137+ continue
138+ value_son [k ] = v
139+ # Required for MongoDB >= 2.6, may fail when combining
140+ # PyMongo 3+ and MongoDB < 2.6
141+ near_embedded = False
142+ for near_op in ('$near' , '$nearSphere' ):
143+ if isinstance (value_dict .get (near_op ), dict ) and (
144+ IS_PYMONGO_3 or get_connection ().max_wire_version > 1 ):
145+ value_son [near_op ] = SON (value_son [near_op ])
146+ if '$maxDistance' in value_dict :
147+ value_son [near_op ][
148+ '$maxDistance' ] = value_dict ['$maxDistance' ]
149+ if '$minDistance' in value_dict :
150+ value_son [near_op ][
151+ '$minDistance' ] = value_dict ['$minDistance' ]
152+ near_embedded = True
153+ if not near_embedded :
154+ if '$maxDistance' in value_dict :
155+ value_son ['$maxDistance' ] = value_dict ['$maxDistance' ]
156+ if '$minDistance' in value_dict :
157+ value_son ['$minDistance' ] = value_dict ['$minDistance' ]
147158 mongo_query [key ] = value_son
148159 else :
149160 # Store for manually merging later
@@ -297,7 +308,11 @@ def update(_doc_cls=None, **update):
297308
298309def _geo_operator (field , op , value ):
299310 """Helper to return the query for a given geo query"""
300- if field ._geo_index == pymongo .GEO2D :
311+ if op == "max_distance" :
312+ value = {'$maxDistance' : value }
313+ elif op == "min_distance" :
314+ value = {'$minDistance' : value }
315+ elif field ._geo_index == pymongo .GEO2D :
301316 if op == "within_distance" :
302317 value = {'$within' : {'$center' : value }}
303318 elif op == "within_spherical_distance" :
@@ -310,8 +325,6 @@ def _geo_operator(field, op, value):
310325 value = {'$nearSphere' : value }
311326 elif op == 'within_box' :
312327 value = {'$within' : {'$box' : value }}
313- elif op == "max_distance" :
314- value = {'$maxDistance' : value }
315328 else :
316329 raise NotImplementedError ("Geo method '%s' has not "
317330 "been implemented for a GeoPointField" % op )
@@ -330,8 +343,6 @@ def _geo_operator(field, op, value):
330343 value = {"$geoIntersects" : _infer_geometry (value )}
331344 elif op == "near" :
332345 value = {'$near' : _infer_geometry (value )}
333- elif op == "max_distance" :
334- value = {'$maxDistance' : value }
335346 else :
336347 raise NotImplementedError ("Geo method '%s' has not "
337348 "been implemented for a %s " % (op , field ._name ))
0 commit comments