python-3.xdjangoelasticsearchelasticsearch-dslelasticsearch-dsl-py

A list of locations that contain a GeoPoint - (geo_spatial_filter_fields, geo_distance)


I'm using elasticsearch-dsl-drf, and I just converted a single location field on my document, to a NestedField with this definition:

location = fields.NestedField(properties={"point": fields.GeoPointField()})

Then on my view I have (I added path and changed the field value to try and make it work):

    geo_spatial_filter_fields = {
        'location': {
            'path': 'location',
            'field': 'point',
            'lookups': [constants.LOOKUP_FILTER_GEO_DISTANCE]
        }
    }

    geo_spatial_ordering_fields = {'location': None}

I'm wondering how this can be achieved? I want to order all the documents based on the closest location from a list of locations for each document.

Edit

Currently experimenting with (changed elasticsearch dsl drf to use this):

{
   "query":{
      "nested":{
         "path":"location",
         "query":{
            "geo_distance":{
               "distance":"16090km",
               "location.point":{
                  "lat":"52.240995",
                  "lon":"0.751156"
               },
               "distance_type":"arc"
            }
         }
      }
   },
   "sort":[
      {
         "_geo_distance":{
            "location.point":{
               "lat":"52.240995",
               "lon":"0.751156"
            },
            "unit":"km",
            "distance_type":"plane",
            "order":"asc"
         }
      },
      {
         "date_first_registered":{
            "order":"desc"
         }
      }
   ]
}

This seems to execute but the sorting is off.

I appreciate your time, Thanks

Solution for elasticsearch-dsl-drf

def attach_nested_path_to_queryset(queryset: Search):
    """
    Attach nested path to the query and sort keys in the queryset
    and update the queryset using `update_from_dict`

    **The updating is done by reference**

    :param queryset: the original queryset
    :type queryset: Search

    :return:
    """
    queryset_dict = queryset.to_dict()

    attach_nested_path_to_query(queryset_dict)
    attach_nested_path_to_sort(queryset_dict)

    # update queryset
    queryset.update_from_dict(queryset_dict)


def attach_nested_path_to_query(queryset_dict: dict):
    """
    Looks for geo_distance in the queryset dict, if it's at the top level
    we modify the top level query, meaning that there's only one query, otherwise
    we loop over the list of `must` queries and try to find `geo_distance`

    **The updating is done by reference**

    :param queryset_dict: the queryset in dict format
    :type queryset_dict: dict

    :return:
    """
    query = queryset_dict["query"]

    if "geo_distance" in query:
        queryset_dict["query"] = {"nested": {"path": "location", "query": query}}
    elif "bool" in query and "must" in query["bool"]:
        for index, must_query in enumerate(query["bool"]["must"]):
            if "geo_distance" in must_query:
                queryset_dict["query"]["bool"]["must"][index] = {"nested": {"path": "location", "query": must_query}}

                break


def attach_nested_path_to_sort(queryset_dict: dict):
    """
    This function loops over the `sort` queries, and
    looks for `_geo_distance` in order to add the `nested_path` key/value

    **The updating is done by reference**

    :param queryset_dict: the queryset in dict format
    :type queryset_dict: dict

    :return:
    """
    sort = queryset_dict["sort"]

    if isinstance(sort, list):
        for index, sorting in enumerate(sort):
            if "_geo_distance" in sorting:
                queryset_dict["sort"][index]["_geo_distance"]["nested_path"] = "location"

Solution

  • Include "nested_path": "location" in the geo distance sort:

    {
       "query":{
          "nested":{
             "path":"location",
             "query":{
                "geo_distance":{
                   "distance":"16090km",
                   "location.point":{
                      "lat":52.240995,
                      "lon":0.751156
                   },
                   "distance_type":"arc"
                }
             }
          }
       },
       "sort":[
          {
             "_geo_distance":{
                "nested_path": "location",
                "location.point":{
                   "lat":52.240995,
                   "lon":0.751156
                },
                "unit":"km",
                "distance_type":"plane",
                "order":"asc"
             }
          },
          {
             "date_first_registered":{
                "order":"desc"
             }
          }
       ]
    }