umang
umang

Reputation: 127

Elasticsearch, Filter documents based on different radius for different geopoint field

I have ES documents similar to this, I have a location array with a type field.

{
  "type": "A/B/C",
  "locations1": [
    {
      "lat": 19.0179332,
      "lon": 72.868069
    },
    {
      "lat": 18.4421771,
      "lon": 73.8585108
    }
  ]
}

Type value determines the distance applicable for that location.

Let's say, the allowed distance of query for type A is 10km, for type B is 100km, for type C is 1000km. Given location L, I want to find all documents which satisfy the distance criteria for that document for the given location and the final result should be sorted by distance.

I am not able to figure out how to use dynamic radius for this. Is it possible or I need to change my document structure similar to this?

EDIT:

I was also thinking of destructing the document locations like this

  "locationsTypeA": [
    {
      "lat": 19.0179332,
      "lon": 72.868069
    },
    {
      "lat": 18.4421771,
      "lon": 73.8585108
    }
  ],
  "locationsTypeB": [
    {
      "lat": 19.0179332,
      "lon": 72.868069
    },
    {
      "lat": 18.4421771,
      "lon": 73.8585108
    }
  ],
  "locationsTypeC": [
    {
      "lat": 19.0179332,
      "lon": 72.868069
    },
    {
      "lat": 18.4421771,
      "lon": 73.8585108
    }
  ]
}

And then I can use the query

  "query": {
    "bool": {
      "should": [
        {
          "geo_distance": {
            "distance": "10km",
            "locationsTypeA": {
              "lat": 12.5,
              "lon": 18.2
            }
          }
        },
        {
          "geo_distance": {
            "distance": "100km",
            "locationsTypeB": {
              "lat": 12.5,
              "lon": 18.2
            }
          }
        },
        {
          "geo_distance": {
            "distance": "1000km",
            "locationsTypeC": {
              "lat": 12.5,
              "lon": 18.2
            }
          }
        }
      ]
    }
  }
}

Upvotes: 2

Views: 633

Answers (2)

zulqarnain
zulqarnain

Reputation: 1626

I did the similar but less complex approach

Here's the code:

{
            query: {
              bool: {
                must: [
                  {
                    match: {
                      companyName: {
                        query: req.text
                      }
                    }
                  },
                  {
                    script: {
                      script: {
                        params: {
                          lat: parseFloat(req.lat),
                          lon: parseFloat(req.lon)
                        },
                        source: "doc['location'].arcDistance(params.lat, params.lon) / 1000 < doc['searchRadius'].value",
                        lang: "painless"
                      }
                    }
                  }
                ]
              }
            },
            sort: [
                {
                    _geo_distance: {
                        location: {
                            lat: parseFloat(req.lat),
                            lon: parseFloat(req.lon)
                        },
                        order: "asc",
                        unit:"km"
                    }
                }
            ],

Upvotes: 0

Joe - Check out my books
Joe - Check out my books

Reputation: 16933

Using the 1st doc structure and the mapping looking like:

PUT geoindex
{
  "mappings": {
    "properties": {
      "locations": {
        "type": "geo_point"
      }
    }
  }
}

Let's take a random point between Pune and Mumbai to be the origin relative to which we'll perform a scripted geo query using the arcDistance function:

GET geoindex/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "script": {
            "script": {
              "source": """
                def type = doc['type.keyword'].value;
                def dynamic_distance;
                if (type == "A") {
                  dynamic_distance = 10e3;
                } else if (type == "B") {
                  dynamic_distance = 100e3;
                } else if (type == "C") {
                  dynamic_distance = 1000e3;
                }

                def distance_in_m = doc['locations'].arcDistance(
                  params.origin.lat,
                  params.origin.lon
                );
                
                return distance_in_m < dynamic_distance
              """,
              "params": {
                "origin": {
                  "lat": 18.81531,
                  "lon": 73.49029
                }
              }
            }
          }
        }
      ]
    }
  },
  "sort": [
    {
      "_geo_distance": {
        "locations": {
          "lat": 18.81531,
          "lon": 73.49029
        },
        "order": "asc"
      }
    }
  ]
}

Upvotes: 1

Related Questions