2

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
            }
          }
        }
      ]
    }
  }
}
2
  • What does your query look like? Commented Dec 28, 2020 at 12:14
  • As I am new to Es, I can't think of how to create a query for this, normally I follow this as filter "geo_distance": { "distance": "200km", "_geoloc": { "lat": lat, "lon": lon } } But as in this case radius is dynamic, can't find any way to do this in the documentation as well. Commented Dec 28, 2020 at 12:23

2 Answers 2

1

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"
      }
    }
  ]
}
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks, Joe. This is great, it does seem complicated but solves my problem. On your splitting point, I have updated the question with the approach I was thinking, please have a look.
You're welcome! As to your edit: yea that'd work too but if and only if you make those fields nested too. If you have arrays of objects, their properties get flattened and you lose the connections between them.
I was going through this link, it says in case of an array of locations, the query I added above, iterates and checks all locations in the array and returns true if any matches. In my case, I only need one condition to be satisfied, either location of type A, B, or C should satisfy the criteria. Am I interpreting it wrong? It would be great if you can point to a link, about flattening of objects in an array,
My bad -- the geo_points don't need to be nested. I was confusing them with regular key-value objects inside of arrays as described in the official docs. I'll edit my answer.
0

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"
                    }
                }
            ],

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.