How to use elasticsearch distance query with more than one geopoint - elasticsearch

Say, I want to search for a document which is within 5kms of any of the three geo points A,B or C. Is it possible to do it within a single query or how to do it?

Yes, you can use a bool/should query with three geo_distance queries.
POST /your_index/_yearch
{
"query": {
"bool": {
"should": [
{
"geo_distance": {
"distance": "5km",
"pin.location": {
"lat": 40,
"lon": -70
}
}
},
{
"geo_distance": {
"distance": "5km",
"pin.location": {
"lat": 41,
"lon": -71
}
}
},
{
"geo_distance": {
"distance": "5km",
"pin.location": {
"lat": 42,
"lon": -72
}
}
}
]
}
}
}

Related

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

Elastic Search Geo Spatial search implementation

I am trying to understand how elastic search supports Geo Spatial search internally.
For the basic search, it uses the inverted index; but how does it combine with the additional search criteria like searching for a particular text within a certain radius.
I would like to understand the internals of how the index would be stored and queried to support these queries
Text & geo queries are executed separately of one another. Let's take a concrete example:
PUT restaurants
{
"mappings": {
"properties": {
"location": {
"type": "geo_point"
},
"menu": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
POST restaurants/_doc
{
"name": "rest1",
"location": {
"lat": 40.739812,
"lon": -74.006201
},
"menu": [
"european",
"french",
"pizza"
]
}
POST restaurants/_doc
{
"name": "rest2",
"location": {
"lat": 40.7403963,
"lon": -73.9950026
},
"menu": [
"pizza",
"kebab"
]
}
You'd then match a text field and apply a geo_distance filter:
GET restaurants/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"menu": "pizza"
}
},
{
"geo_distance": {
"distance": "0.5mi",
"location": {
"lat": 40.7388,
"lon": -73.9982
}
}
},
{
"function_score": {
"query": {
"match_all": {}
},
"boost_mode": "avg",
"functions": [
{
"gauss": {
"location": {
"origin": {
"lat": 40.7388,
"lon": -73.9982
},
"scale": "0.5mi"
}
}
}
]
}
}
]
}
}
}
Since the geo_distance query only assigns a boolean value (--> score=1; only checking if the location is within a given radius), you may want to apply a gaussian function_score to boost the locations that are closer to a given origin.
Finally, these scores are overridable by using a _geo_distance sort where you'd order by the proximity (while of course keeping the match query intact):
...
"query: {...},
"sort": [
{
"_geo_distance": {
"location": {
"lat": 40.7388,
"lon": -73.9982
},
"order": "asc"
}
}
]
}

How to output in ElasticSearch distance for same location that chosen by geo_distance from multiple locations

I have multiple locations:
Document 1 -
"contact": [
{
"address": {
"geolocation": {
"lon": -73.5409,
"lat": 41.2512
}
}
}
]
Document 2 -
{ "contact": [
{
"address": {
"geolocation": {
"lon": -73.7055,
"lat": 40.6744
}
}
},
{
"address": [
{
"geolocation": {
"lon": -73.9325,
"lat": 40.7482
}
},
{
"geolocation": {
"lon": -87.9921,
"lat": 42.9959
}
},
{
"geolocation": {
"lon": -95.4563,
"lat": 29.8775
}
}
]
}
]
}
geo_distance finds both documents by closest location.
"geo_distance": {
"distance": "275mi",
"distance_type": "plane",
"contact.address.geolocation": {
"lat": 42,
"lon": -71
},
"unit": "mi"
}
}
But when I add script field to output lat, lon, and distance
"script_fields": {
"distance_value": {
"script": "doc.containsKey('contact.address.geolocation') ? doc['contact.address.geolocation'].value ? doc['contact.address.geolocation'].arcDistanceInMiles(42.2882,-71.0474) : null : null"
},
"geolocation": {
"script": "doc.containsKey('contact.address.geolocation') ? doc['contact.address.geolocation'].value : null"
}
}
it output random geolocation element from Document 2.
For document 1 it is 147 miles
But for document 2 it is 1601 miles because it takes different location than in geo_distance filter.
How can I print same value as in geo_distance? I want to show distance to my point.
I've tried this script:
"script_fields": {
"distance_value": {
"script": "if (doc.containsKey('contact.address.geolocation')==false) return null; min = 40000; for(e in doc['contact.address.geolocation']){ c=0; if(e!=null) c = e.arcDistanceInMiles(42.2882,-71.0474); if(c<min) min=c;}; return min;"
}
}
It gives error
No signature of method: org.elasticsearch.common.geo.GeoPoint.arcDistanceInMiles() is applicable for argument types: (java.lang.Double, java.lang.Double)
Also I don't think it will iterate over all gelocation fields.
I found only one way to output same distance as in the filter - add "sort" element:
"sort": [
"_score",
{
"_geo_distance": {
"contact.address.geolocation": [
-71,
42
],
"order": "asc",
"unit": "mi"
}
}
]

ElasticSearch 2 bucket level sorting

The mapping of database is this:
{
"users": {
"mappings": {
"user": {
"properties": {
credentials": {
"type": "nested",
"properties": {
"achievement_id": {
"type": "string"
},
"percentage_completion": {
"type": "integer"
}
}
},
"current_location": {
"type": "geo_point"
},
"locations": {
"type": "geo_point"
}
}
}
}
}
Now In the mapping, You can see there are two geo-distance fields one is current_location and other is locations. Now I want to sort user based on credentials.percentage_completion which is a nested field. This work fine for example this query,
Example Query:
GET /users/user/_search?size=23
{
"sort": [
{
"credentials.percentage_completion": {
"order": "desc",
"missing": "_last"
}
},
"_score"
],
"query": {
"filtered": {
"query": {
"match_all": {}
},
"filter": {
"geo_distance": {
"distance": "100000000km",
"user.locations": {
"lat": 19.77,
"lon": 73
}
}
}
}
}
}
I want to change sorting order made into buckets, the desired order is first show all the people who are at 100KM radius of user.current_location and sort them according to credentials.percentage_completion and then rest of users sorted again by credentials.percentage_completion.
I tried putting conditional in sorting and made it multilevel but that will not work because only nested can have filters and that on nested fields child only.
I thought I can use _score for sorting and give more relevance to people who are under 1000 km but geo-distance is a filter, I don't seem to find any way to give relevance in filter.
Is there anything I am missing here , any help would be great.
Thanks
Finally solved it, posting it here so other can also take some lead if they get here. The way to solve this is to give constant relevance score to particular query but as here it was Geo distance so was not able to use that in query, then I found Constant Score query: It allows to wrap a filter inside a query.
This is how query looks:
GET /users/user/_search?size=23
{
"sort": [
"_score",
{
"credentials.udacity_percentage_completion": {
"order": "desc",
"missing": "_last"
}
}
],
"explain": true,
"query": {
"filtered": {
"query": {
"bool": {
"should": [
{
"constant_score": {
"filter": {
"geo_distance": {
"distance": "100km",
"user.current_location": {
"lat": 19.77,
"lon": 73
}
}
},
"boost": 50
}
},
{
"constant_score": {
"filter": {
"geo_distance": {
"distance": "1000000km",
"user.locations": {
"lat": 19.77,
"lon": 73
}
}
},
"boost": 1
}
}
]
}
},
"filter": {
"geo_distance": {
"distance": "10000km",
"user.locations": {
"lat": 19.77,
"lon": 73
}
}
}
}
}
}

Array of locations in elasticserach spatial query

I am new to this elastic search concept i can't find a solution for my problem. suppose consider the following query.
GET banknew/_search/
{
"query": {
"match_all": {}
},
"filter": {
"geo_distance": {
"location": {
"lat": 8.722479,
"lon": 78.13047
},
"distance": "5km"
}
}
}
This will gave me the result. The above query is for 1 location(means 1 lat, lng). But i have to get the result for multiple locations(means for 2 or more lat, lng). What i tried is
GET banknew/_search/
{
"query": {
"match_all": {}
},
"filter": {
"geo_distance": {
"location": [{
"lat": 8.722479,
"lon": 78.13047
},{
"lat": 8.722479,
"lon": 78.13047
} ],
"distance": "5km"
}
}
}
I have to get the result of points within 5km for 1st location and also 2nd location.
But i am receiving error `"error": "SearchPhaseExecutionException[Failed to execute phase [query], all shards failed". Whether its possible. Please guide me. Thanks in advance
You could use another geo_distance filter and wrap it up in a bool filter.
If you are searching result at 5km from first location OR second location, add it in the should clause.
Try something like this :
GET banknew/_search/
{
"query": {
"match_all": {}
},
"filter": {
"bool": {
"should": [
{
"geo_distance": {
"distance": "5km",
"location": {
"lat": lat1,
"lon": lon1
}
}
},
{
"geo_distance": {
"distance": "5km",
"location": {
"lat": lat2,
"lon": lon2
}
}
}
],
"minimum_should_match": 1
}
}
}

Resources