Skip to content

Commit bde8f1b

Browse files
geo spatial geo distance range queries - yet untested code
1 parent 88e8da2 commit bde8f1b

File tree

1 file changed

+255
-10
lines changed
  • src/django_elasticsearch_dsl_drf/filter_backends/filtering

1 file changed

+255
-10
lines changed

src/django_elasticsearch_dsl_drf/filter_backends/filtering/geo_spatial.py

Lines changed: 255 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@
2929
from ...constants import (
3030
ALL_GEO_SPATIAL_LOOKUP_FILTERS_AND_QUERIES,
3131
LOOKUP_FILTER_GEO_DISTANCE,
32+
LOOKUP_FILTER_GEO_DISTANCE_RANGE,
33+
LOOKUP_FILTER_GEO_DISTANCE_GT,
34+
LOOKUP_FILTER_GEO_DISTANCE_GTE,
35+
LOOKUP_FILTER_GEO_DISTANCE_LT,
36+
LOOKUP_FILTER_GEO_DISTANCE_LTE,
37+
LOOKUP_FILTER_GEO_DISTANCE_FROM,
38+
LOOKUP_FILTER_GEO_DISTANCE_TO,
39+
LOOKUP_FILTER_GEO_DISTANCE_INCLUDE_UPPER,
40+
LOOKUP_FILTER_GEO_DISTANCE_INCLUDE_LOWER,
3241
)
3342
from ..mixins import FilterBackendMixin
3443

@@ -102,10 +111,16 @@ def prepare_filter_fields(cls, view):
102111

103112
@classmethod
104113
def get_geo_distance_params(cls, value, field):
105-
"""Get params for `geo_distance` query
114+
"""Get params for `geo_distance` query.
115+
116+
Example:
117+
118+
/api/articles/?location__geo_distance=2km|43.53|-12.23
106119
107120
:param value:
121+
:param field:
108122
:type value: str
123+
:type field:
109124
:return: Params to be used in `geo_distance` query.
110125
:rtype: dict
111126
"""
@@ -130,6 +145,96 @@ def get_geo_distance_params(cls, value, field):
130145

131146
return params
132147

148+
@classmethod
149+
def get_range_params(cls, value, field):
150+
"""Get params for `range` query.
151+
152+
Example:
153+
154+
/api/articles/?location__geo_distance_range=2km|10km|43.53|-12.23
155+
156+
Elasticsearch query:
157+
158+
{
159+
"query": {
160+
"bool" : {
161+
"must" : {
162+
"match_all" : {}
163+
},
164+
"filter" : {
165+
"geo_distance_range" : {
166+
"from" : "200km",
167+
"to" : "400km",
168+
"pin.location" : {
169+
"lat" : 40,
170+
"lon" : -70
171+
}
172+
}
173+
}
174+
}
175+
}
176+
}
177+
178+
:param value:
179+
:param field:
180+
:type: str
181+
:type: str
182+
:return: Params to be used in `range` query.
183+
:rtype: dict
184+
"""
185+
__values = cls.split_lookup_value(value, maxsplit=4)
186+
__len_values = len(__values)
187+
188+
if __len_values < 4:
189+
return {}
190+
191+
params = {
192+
'from': __values[0],
193+
'to': __values[1],
194+
field: {
195+
'lat': __values[2],
196+
'lon': __values[3]
197+
}
198+
}
199+
200+
return params
201+
202+
@classmethod
203+
def get_gte_lte_params(cls, value, lookup, field):
204+
"""Get params for `gte`, `gt`, `lte` and `lt` query.
205+
206+
Examples:
207+
208+
/api/articles/?location__geo_distance_gt=2km|43.53|-12.23
209+
/api/articles/?location__geo_distance_gte=2km|43.53|-12.23
210+
/api/articles/?location__geo_distance_lt=2km|43.53|-12.23
211+
/api/articles/?location__geo_distance_lte=2km|43.53|-12.23
212+
213+
:param value:
214+
:param lookup:
215+
:param field:
216+
:type value: str
217+
:type lookup: str
218+
:type field: str
219+
:return: Params to be used in `range` query.
220+
:rtype: dict
221+
"""
222+
__values = cls.split_lookup_value(value, maxsplit=3)
223+
__len_values = len(__values)
224+
225+
if __len_values < 3:
226+
return {}
227+
228+
params = {
229+
lookup: __values[0],
230+
field: {
231+
'lat': __values[2],
232+
'lon': __values[3]
233+
}
234+
}
235+
236+
return params
237+
133238
@classmethod
134239
def apply_query_geo_distance(cls, queryset, options, value):
135240
"""Apply `wildcard` filter.
@@ -150,6 +255,96 @@ def apply_query_geo_distance(cls, queryset, options, value):
150255
)
151256
)
152257

258+
@classmethod
259+
def apply_query_geo_distance_range(cls, queryset, options, value):
260+
"""Apply `range` filter.
261+
262+
:param queryset: Original queryset.
263+
:param options: Filter options.
264+
:param value: value to filter on.
265+
:type queryset: elasticsearch_dsl.search.Search
266+
:type options: dict
267+
:type value: str
268+
:return: Modified queryset.
269+
:rtype: elasticsearch_dsl.search.Search
270+
"""
271+
return queryset.filter(
272+
'geo_distance_range',
273+
**cls.get_range_params(value, options['field'])
274+
)
275+
276+
@classmethod
277+
def apply_query_geo_distance_gt(cls, queryset, options, value):
278+
"""Apply `gt` filter.
279+
280+
:param queryset: Original queryset.
281+
:param options: Filter options.
282+
:param value: value to filter on.
283+
:type queryset: elasticsearch_dsl.search.Search
284+
:type options: dict
285+
:type value: str
286+
:return: Modified queryset.
287+
:rtype: elasticsearch_dsl.search.Search
288+
"""
289+
return queryset.filter(
290+
'geo_distance_range',
291+
**cls.get_gte_lte_params(value, 'gt', options['field'])
292+
)
293+
294+
@classmethod
295+
def apply_query_geo_distance_gte(cls, queryset, options, value):
296+
"""Apply `gte` filter.
297+
298+
:param queryset: Original queryset.
299+
:param options: Filter options.
300+
:param value: value to filter on.
301+
:type queryset: elasticsearch_dsl.search.Search
302+
:type options: dict
303+
:type value: str
304+
:return: Modified queryset.
305+
:rtype: elasticsearch_dsl.search.Search
306+
"""
307+
return queryset.filter(
308+
'geo_distance_range',
309+
**cls.get_gte_lte_params(value, 'gte', options['field'])
310+
)
311+
312+
@classmethod
313+
def apply_query_geo_distance_lt(cls, queryset, options, value):
314+
"""Apply `lt` filter.
315+
316+
:param queryset: Original queryset.
317+
:param options: Filter options.
318+
:param value: value to filter on.
319+
:type queryset: elasticsearch_dsl.search.Search
320+
:type options: dict
321+
:type value: str
322+
:return: Modified queryset.
323+
:rtype: elasticsearch_dsl.search.Search
324+
"""
325+
return queryset.filter(
326+
'geo_distance_range',
327+
**cls.get_gte_lte_params(value, 'lt', options['field'])
328+
)
329+
330+
@classmethod
331+
def apply_query_geo_distance_lte(cls, queryset, options, value):
332+
"""Apply `lte` filter.
333+
334+
:param queryset: Original queryset.
335+
:param options: Filter options.
336+
:param value: value to filter on.
337+
:type queryset: elasticsearch_dsl.search.Search
338+
:type options: dict
339+
:type value: str
340+
:return: Modified queryset.
341+
:rtype: elasticsearch_dsl.search.Search
342+
"""
343+
return queryset.filter(
344+
'geo_distance_range',
345+
**cls.get_gte_lte_params(value, 'lte', options['field'])
346+
)
347+
153348
def get_filter_query_params(self, request, view):
154349
"""Get query params to be filtered on.
155350
@@ -216,15 +411,65 @@ def filter_queryset(self, request, queryset, view):
216411
# For all other cases, when we don't have multiple values,
217412
# we follow the normal flow.
218413
for value in options['values']:
414+
219415
# `geo_distance` filter lookup
220416
if options['lookup'] == LOOKUP_FILTER_GEO_DISTANCE:
221-
queryset = self.apply_query_geo_distance(queryset,
222-
options,
223-
value)
224-
225-
# # `geo_distance` filter lookup
226-
# else:
227-
# queryset = self.apply_query_geo_distance(queryset,
228-
# options,
229-
# value)
417+
queryset = self.apply_query_geo_distance(
418+
queryset,
419+
options,
420+
value
421+
)
422+
423+
# `geo_distance_range` `range` filter lookup
424+
elif options['lookup'] == LOOKUP_FILTER_GEO_DISTANCE_RANGE:
425+
queryset = self.apply_query_geo_distance_range(
426+
queryset,
427+
options,
428+
value
429+
)
430+
431+
# `geo_distance_range` `gt` filter lookup
432+
elif options['lookup'] in (
433+
LOOKUP_FILTER_GEO_DISTANCE_GT,
434+
LOOKUP_FILTER_GEO_DISTANCE_FROM
435+
):
436+
queryset = self.apply_query_geo_distance_gt(
437+
queryset,
438+
options,
439+
value
440+
)
441+
442+
# `geo_distance_range` `gte` filter lookup
443+
elif options['lookup'] in (
444+
LOOKUP_FILTER_GEO_DISTANCE_GTE,
445+
LOOKUP_FILTER_GEO_DISTANCE_INCLUDE_LOWER
446+
):
447+
queryset = self.apply_query_geo_distance_gte(
448+
queryset,
449+
options,
450+
value
451+
)
452+
453+
# `geo_distance_range` `lt` filter lookup
454+
elif options['lookup'] in (
455+
LOOKUP_FILTER_GEO_DISTANCE_LT,
456+
LOOKUP_FILTER_GEO_DISTANCE_TO
457+
):
458+
queryset = self.apply_query_geo_distance_lt(
459+
queryset,
460+
options,
461+
value
462+
)
463+
464+
# `geo_distance_range` `lte` lookup
465+
elif options['lookup'] == (
466+
LOOKUP_FILTER_GEO_DISTANCE_LTE,
467+
LOOKUP_FILTER_GEO_DISTANCE_INCLUDE_UPPER
468+
):
469+
queryset = self.apply_query_geo_distance_lte(
470+
queryset,
471+
options,
472+
value
473+
)
474+
230475
return queryset

0 commit comments

Comments
 (0)