Source code for django_elasticsearch_dsl_drf.filter_backends.ordering.geo_spatial

"""
Geo-spatial ordering backend.
"""

from rest_framework.filters import BaseFilterBackend

from ..mixins import FilterBackendMixin
from ...constants import (
    GEO_DISTANCE_ORDERING_PARAM,
)

__title__ = 'django_elasticsearch_dsl_drf.filter_backends.ordering.common'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2017-2019 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('GeoSpatialOrderingFilterBackend',)


[docs]class GeoSpatialOrderingFilterBackend(BaseFilterBackend, FilterBackendMixin): """Geo-spatial ordering filter backend for Elasticsearch. Example: >>> from django_elasticsearch_dsl_drf.filter_backends import ( >>> GeoSpatialOrderingFilterBackend >>> ) >>> from django_elasticsearch_dsl_drf.viewsets import ( >>> BaseDocumentViewSet, >>> ) >>> >>> # Local article document definition >>> from .documents import ArticleDocument >>> >>> # Local article document serializer >>> from .serializers import ArticleDocumentSerializer >>> >>> class ArticleDocumentView(BaseDocumentViewSet): >>> >>> document = ArticleDocument >>> serializer_class = ArticleDocumentSerializer >>> filter_backends = [GeoSpatialOrderingFilterBackend,] >>> geo_spatial_ordering_fields = { >>> 'location': { >>> 'field': 'location', >>> } >>> } """ ordering_param = GEO_DISTANCE_ORDERING_PARAM
[docs] @classmethod def get_geo_distance_params(cls, value, field): """Get params for `geo_distance` ordering. Example: /api/articles/?ordering=-location__45.3214__-34.3421__km__planes :param value: :param field: :type value: str :type field: :return: Params to be used in `geo_distance` query. :rtype: dict """ __values = cls.split_lookup_complex_value(value, maxsplit=3) __len_values = len(__values) if __len_values < 2: return {} params = { '_geo_distance': { field: { 'lat': __values[0], 'lon': __values[1], } } } if __len_values > 2: params['_geo_distance']['unit'] = __values[2] else: params['_geo_distance']['unit'] = 'm' if __len_values > 3: params['_geo_distance']['distance_type'] = __values[3] else: params['_geo_distance']['distance_type'] = 'arc' return params
[docs] def get_geo_spatial_field_name(self, request, view, name): """Get geo-spatial field name. We have to deal with a couple of situations here: Example 1: >>> geo_spatial_ordering_fields = { >>> 'location': None, >>> } Example 2: >>> geo_spatial_ordering_fields = { >>> 'location': 'location', >>> } Example 3: >>> geo_spatial_ordering_fields = { >>> 'location': { >>> 'field': 'location' >>> }, >>> } :param request: :param view: :param name: :return: """ options = view.geo_spatial_ordering_fields[name] if options is None: return name elif isinstance(options, dict): if 'field' in options: return options['field'] else: return options
[docs] def get_ordering_query_params(self, request, view): """Get ordering query params. :param request: Django REST framework request. :param view: View. :type request: rest_framework.request.Request :type view: rest_framework.viewsets.ReadOnlyModelViewSet :return: Ordering params to be used for ordering. :rtype: list """ # TODO: Support `mode` argument. query_params = request.query_params.copy() ordering_query_params = query_params.getlist(self.ordering_param, []) __ordering_params = [] # Remove invalid ordering query params for query_param in ordering_query_params: try: __key, __value = FilterBackendMixin.split_lookup_complex_value( query_param.lstrip('-'), maxsplit=1, ) # Probably using both # OrderingFilterBackend/DefaultOrderingFilterBackend # and GeoSpatialOrderingFilterBackend except ValueError: break __direction = 'desc' if query_param.startswith('-') else 'asc' if __key in view.geo_spatial_ordering_fields: __field_name = self.get_geo_spatial_field_name( request, view, __key ) __params = self.get_geo_distance_params(__value, __field_name) __params['_geo_distance']['order'] = __direction __ordering_params.append(__params) return __ordering_params
[docs] def filter_queryset(self, request, queryset, view): """Filter the queryset. :param request: Django REST framework request. :param queryset: Base queryset. :param view: View. :type request: rest_framework.request.Request :type queryset: elasticsearch_dsl.search.Search :type view: rest_framework.viewsets.ReadOnlyModelViewSet :return: Updated queryset. :rtype: elasticsearch_dsl.search.Search """ ordering_query_params = self.get_ordering_query_params(request, view) if ordering_query_params: return queryset.sort(*ordering_query_params) return queryset