what is the best way to find available points(merchants) around a point(user) if these points(merchants) use different radius in Elasticsearch - elasticsearch

I used geo_shape for this problem, want to know if there are some other better ways(faster) to solve this problem, ES version is 6.5.
mapping:
{
"index": {
"mappings": {
"merchant": {
"_all": {
"enabled": false
},
"properties": {
"delivery_circle": {
"type": "geo_shape",
"tree": "quadtree",
"precision": "50.0m",
"distance_error_pct": 0.025
}
}
}
}
}
}
document example:
{
"_source": {
"id": 1,
"delivery_circle": {
"coordinates": [ // merchant location, its radius is 4km
123.456,
1.2345
],
"radius": "4000m",
"type": "circle"
}
}
}
{
"_source": {
"id": 2,
"delivery_circle": {
"coordinates": [ // merchant location, its radius is 5km
123.567,
1.3456
],
"radius": "5000m",
"type": "circle"
}
}
}
search query example:
{
"query": {
"bool": {
"filter": [
{
"geo_shape": {
"delivery_circle": {
"relation": "contains",
"shape": {
"coordinates": [ // user location
123,
1
],
"type": "point"
}
}
}
}
]
}
}
}

This is off topic suggestion, but you can consider using geohash for the same. This can reduce your search time complexity.
https://en.wikipedia.org/wiki/Geohash

Related

Find coordinates in a polygon

How can I find polygons that stored in elastic index.
Simple mapping:
PUT /regions
{
"mappings": {
"properties": {
"location": {
"type": "geo_shape"
}
}
}
}
And simple polygon:
/regions/_doc/1
{
"location" : {
"type" : "polygon",
"coordinates" : [
[
[53.847332102970626,27.485155519098047],
[53.84626875748117,27.487134989351038],
[53.8449047241684,27.48501067981124],
[53.84612634308789,27.482945378869765],
[53.847411219859,27.48502677306532],
[53.847332102970626,27.485155519098047]
]
]
}
}
According to documentation I can only search coordinates within polygon only if the polygon is contained in the request Geo-polygon query, but I need to find polygons by coordinates in query. Elasticsearch 7.6 version.
Query:
{
"query": {
"match_all": {}
},
"filter": {
"geo_shape": {
"geometry": {
"shape": {
"coordinates": [
53.846415,
27.485756
],
"type": "point"
},
"relation": "whithin"
}
}
}
}
You were on the right path but your query was heavily malformed. Here's the fix:
{
"query": {
"bool": {
"filter": {
"geo_shape": {
"location": {
"shape": {
"coordinates": [
53.846415,
27.485756
],
"type": "point"
},
"relation": "intersects"
}
}
}
}
}
}
Notice how I used intersects instead of within. The reason is explained in this GIS StackExchange answer.

Elasticsearch - Query to Determine All Unique IDs that are distance X away from a particular ID?

I have data in this format generated from a random walk (to simulate people walking around). It is set up in this manner { location : { lat: someLat, lon: someLong }, id: uniqueId, date:date }. I am trying to write a query given a users unique ID, find how many other unique IDs came within X distance of the given ID between a certain time range. Any hints on how to accomplish this?
My idea is to have a top level filter aggregration, with a nested geo-query of some sort. I think the geo-distance query is the way to go, but I am not sure how to include it into the below query to get all of unique IDs that come within X distance of the ID I am filtering on. The query below is where I am starting from, I am filtering all documents from now - 1 day to now, where the documents user Id is the provided value. How would I check all other documents for their distances against documents that match this query?
{
"aggs" : {
"range": {
"date_range": {
"field": "date",
"format": "MM-yyyy",
"ranges": [
{ "to": "now" },
{ "from": "now-1d" }
]
}
},
"locations" : {
"filter" : {
"term": { "id.keyword": "7a50ab18-886b-42a2-80ad-3d45112e3cfd" }
}
}
}
}
Your hunch is correct. All of this can be done using range & geo_distance filtering and _geo_distance sorting. You wanna filter on the query-level, not in the aggs though:
GET walking/_search
{
"size": 0,
"query": {
"bool": {
"must": [
{
"range": {
"date": {
"gte": "now-1d"
}
}
}
],
"filter": [
{
"geo_distance": {
"distance": "20m",
"location": {
"lat": 48.20150179951008,
"lon": 16.39111876487732
}
}
}
]
}
},
"aggs": {
"rings_around_loc": {
"geo_distance": {
"field": "location",
"origin": {
"lat": 48.20150179951008,
"lon": 16.39111876487732
},
"unit": "m",
"keyed": true,
"ranges": [
{
"to": 10
},
{
"from": 10,
"to": 50
},
{
"from": 50
}
]
}
},
"locations": {
"value_count": {
"field": "id.keyword"
}
}
},
"sort": [
{
"_geo_distance": {
"location": {
"lat": 48.20150179951008,
"lon": 16.39111876487732
},
"order": "asc",
"unit": "m",
"mode": "min",
"distance_type": "arc",
"ignore_unmapped": true
}
}
]
}
Not sure what you need the range buckets for so I left them out.
Full steps to replicate:
PUT walking
{
"mappings": {
"properties": {
"date": {
"type": "date"
},
"id": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"location": {
"type": "geo_point"
}
}
}
}
And then POST _bulk this random walk data

Elasticsearch - return polygons in a given point (relation": "intersects returns union instead)

I have multiple polygons in the index and I'm trying to return all the polygons in a given point.
But my query returns all the docs in the index even if it doesn't falls in polygon 1.
It gives me union instead of intersect.
Query is given below
Map Ref. http://geojson.io/#map=7/-31.775/144.382
PUT /example3
{
"mappings": {
"_doc": {
"properties": {
"features": {
"properties": {
"geometry": {
"type": "geo_shape"
}
}
}
}
}
}
}
Sample data:
{
"type": "Feature",
"geometry": {
"name": "Poly - 04",
"type": "Polygon",
"coordinates": [
[
[
144.91670608520508,
-37.82524314302977
],
[
144.96846199035645,
-37.82524314302977
],
[
144.96846199035645,
-37.78787789236923
],
[
144.91670608520508,
-37.78787789236923
],
[
144.91670608520508,
-37.82524314302977
]
]
]
}
}
Query:
GET /example3/_search
{
"query":{
"bool": {
"must": {
"match_all": {}
},
"filter": {
"geo_shape": {
"features.geometry": {
"relation": "intersects",
"shape": {
"type": "point",
"coordinates" :
[
146.0138,
-37.1734
]
}
}
}
}
}
}
}

Distinct values from array-field matching filter in Elasticsearch 2.4

In short: I want to lookup for distinct values in some field of the document BUT only matching some filter. The problem is in array-fields.
Imagine there are following documents in ES 2.4:
[
{
"states": [
"Washington (US-WA)",
"California (US-CA)"
]
},
{
"states": [
"Washington (US-WA)"
]
}
]
I'd like my users to be able to lookup all possible states via typeahead, so I have the following query for the "wa" user request:
{
"query": {
"wildcard": {
"states.raw": "*wa*"
}
},
"aggregations": {
"typed": {
"terms": {
"field": "states.raw"
},
"aggregations": {
"typed_hits": {
"top_hits": {
"_source": { "includes": ["states"] }
}
}
}
}
}
}
states.raw is a sub-field with not_analyzed option
This query works pretty well unless I have an array of values like in the example - it returns both Washington and California. I do understand why it happens (query and aggregations are working on top of the document and the document contains both, even though only one option matched the filter), but I really want to only see Washington and don't want to add another layer of filtering on the application side for the ES results.
Is there a way to do so via single ES 2.4 request?
You could use the "Filtering Values" feature (see https://www.elastic.co/guide/en/elasticsearch/reference/2.4/search-aggregations-bucket-terms-aggregation.html#_filtering_values_2).
So, your request could look like:
POST /index/collection/_search?size=0
{
"aggregations": {
"typed": {
"terms": {
"field": "states.raw",
"include": ".*wa.*" // You need to carefully quote the "wa" string because it'll be used as part of RegExp
},
"aggregations": {
"typed_hits": {
"top_hits": {
"_source": { "includes": ["states"] }
}
}
}
}
}
}
I can't hold myself back, though, and not tell you that using wildcard with leading wildcard is not the best solution. Do, please please, consider using ngrams for this:
PUT states
{
"settings": {
"analysis": {
"filter": {
"ngrams": {
"type": "nGram",
"min_gram": "2",
"max_gram": "20"
}
},
"analyzer": {
"ngram_analyzer": {
"type": "custom",
"filter": [
"standard",
"lowercase",
"ngrams"
],
"tokenizer": "standard"
}
}
}
},
"mappings": {
"doc": {
"properties": {
"location": {
"properties": {
"states": {
"type": "string",
"fields": {
"raw": {
"type": "string",
"index": "not_analyzed"
},
"ngrams": {
"type": "string",
"analyzer": "ngram_analyzer"
}
}
}
}
}
}
}
}
}
POST states/doc/1
{
"text":"bla1",
"location": [
{
"states": [
"Washington (US-WA)",
"California (US-CA)"
]
},
{
"states": [
"Washington (US-WA)"
]
}
]
}
POST states/doc/2
{
"text":"bla2",
"location": [
{
"states": [
"Washington (US-WA)",
"California (US-CA)"
]
}
]
}
POST states/doc/3
{
"text":"bla3",
"location": [
{
"states": [
"California (US-CA)"
]
},
{
"states": [
"Illinois (US-IL)"
]
}
]
}
And the final query:
GET states/_search
{
"query": {
"term": {
"location.states.ngrams": {
"value": "sh"
}
}
},
"aggregations": {
"filtering_states": {
"terms": {
"field": "location.states.raw",
"include": ".*sh.*"
},
"aggs": {
"typed_hits": {
"top_hits": {
"_source": {
"includes": [
"location.states"
]
}
}
}
}
}
}
}

Elasticsearch geosearch with distance preference

Sorry for the noob question... I have Restaurant objects in Elasticsearch 2.3, each has a GeoPoint and a home delivery distance preference. In pseudocode
restaurant: {
location: (x, y)
deliveryPreference: 10km
}
and a user:
user {
location: (a,b)
}
How would I issue a search for a user looking for all restaurants that can deliver in his area?
The solution involves using geo_shapes. You need to model the restaurant documents as follows:
PUT restaurants
{
"mappings": {
"restaurant": {
"properties": {
"name": {
"type": "text"
},
"location": {
"type": "geo_point"
},
"delivery_area": {
"type": "geo_shape",
"tree": "quadtree",
"precision": "1m"
}
}
}
}
}
You can then index your restaurants as follows:
POST /restaurants/restaurant/1
{
"name": "My Food place",
"location": [-45.0, 45.0], <-- lon, lat !!!
"delivery_area": {
"type": "circle",
"coordinates" : [-45.0, 45.0], <-- lon, lat !!!
"radius" : "10km"
}
}
Each restaurant will thus be associated with a circle shape centered on its location and with a proper radius.
Finally, when a user wants to know which restaurant can deliver at the location she is currently at, you can issue the following geo_shape query:
POST /restaurants/_search
{
"query":{
"bool": {
"filter": {
"geo_shape": {
"delivery_area": {
"shape": {
"type": "point",
"coordinates" : [<user_lon>, <user_lat>]
},
"relation": "contains"
}
}
}
}
}
}
In this query, we are retrieving restaurants whose delivery_area shape contains the point the user is currently located at.
Index the restraunts documents with following mappings
{
"mappings": {
"type_name": {
"properties": {
"name": {
"type": "text"
},
"location": {
"type": "text"
},
"location_geo": {
"type": "geo_point"
}
}
}
}
}
Use geo_filter query
{
"query": {
"bool": {
"filter": {
"geo_distance": {
"distance": "10km",
"location_geo": {
"lat": 40,
"lon": -70
}
}
}
}
}
}

Resources