Elasticsearch 自定义地理距离过滤器
Elasticsearch custom geo distance filter
我想从 Elasticsearch 查询中检索可变距离内的所有点。
假设我有2家商店,一家愿意送货最多3公里,另一家最多送货5公里:
PUT /my_shops/_doc/1
{
"location": {
"lat": 40.12,
"lon": -71.34
},
"max_delivery_distance": 3000
}
PUT /my_shops/_doc/2
{
"location": {
"lat": 41.12,
"lon": -72.34
},
"max_delivery_distance": 5000
}
对于给定的地点,我想知道哪些商店可以送货。如果给定位置在 3 公里以内,IE 查询应该 return shop1 如果给定位置在 5 公里以内,IE 查询应该是 shop2
GET /my_shops/_search
{
"query": {
"bool": {
"must": {
"match_all": {}
},
"filter": {
"geo_distance": {
"distance": max_delivery_distance,
"location": {
"lat": 40,
"lon": -70
}
}
}
}
}
}
我认为您需要使用脚本才能将另一个字段用作参数。经过一些研究,我得出了这个答案:
GET my_shops/_search
{
"query": {
"script": {
"script": {
"params": {
"location": {
"lat": 40,
"lon": -70
}
},
"source": """
return doc['location'].arcDistance(params.location.lat, params.location.lon)/1000 <= doc['max_delivery_distance'].value"""
}
}
}
}
基本上,我们利用了这样一个事实,即与 GEO 点相关的 类 被无痛地列入白名单 https://github.com/elastic/elasticsearch/pull/40180/ 并且脚本接受额外的参数(您的固定位置)。
根据 arcDistance 的文档,我们检索以米为单位的大小,因此您需要将此值除以 1000 转换为公里。
补充说明
我假设 location 和 max_delivery_distance 总是(针对每个文档)定义的。如果不是这种情况,你需要覆盖这种情况。
参考
- Another related question
- https://github.com/elastic/elasticsearch/pull/40180/
还有另一种无需脚本即可解决此问题的方法(性能消耗巨大!!)并让 ES 使用原生地理形状对其进行排序。
我会将每个文档建模为 circle,具有中心位置和(交付)半径。首先,您的索引映射应如下所示:
PUT /my_shops
{
"mappings": {
"properties": {
"delivery_area": {
"type": "geo_shape",
"strategy": "recursive"
}
}
}
}
然后,您的文件需要具有以下格式:
PUT /my_shops/_doc/1
{
"delivery_area" : {
"type" : "circle",
"coordinates" : [-71.34, 40.12],
"radius" : "3000m"
}
}
PUT /my_shops/_doc/2
{
"delivery_area" : {
"type" : "circle",
"coordinates" : [-72.34, 41.12],
"radius" : "5000m"
}
}
最后,查询简单地变成了 geo_shape
查询,查看每个商店的送货点和送货区域之间的交叉点。
GET /my_shops/_search
{
"query": {
"bool": {
"filter": {
"geo_shape": {
"delivery_area": {
"shape": {
"type": "point",
"coordinates": [ -70, 40 ]
},
"relation": "contains"
}
}
}
}
}
}
就是这样!没有脚本,只有地理操作。
我想从 Elasticsearch 查询中检索可变距离内的所有点。 假设我有2家商店,一家愿意送货最多3公里,另一家最多送货5公里:
PUT /my_shops/_doc/1
{
"location": {
"lat": 40.12,
"lon": -71.34
},
"max_delivery_distance": 3000
}
PUT /my_shops/_doc/2
{
"location": {
"lat": 41.12,
"lon": -72.34
},
"max_delivery_distance": 5000
}
对于给定的地点,我想知道哪些商店可以送货。如果给定位置在 3 公里以内,IE 查询应该 return shop1 如果给定位置在 5 公里以内,IE 查询应该是 shop2
GET /my_shops/_search
{
"query": {
"bool": {
"must": {
"match_all": {}
},
"filter": {
"geo_distance": {
"distance": max_delivery_distance,
"location": {
"lat": 40,
"lon": -70
}
}
}
}
}
}
我认为您需要使用脚本才能将另一个字段用作参数。经过一些研究,我得出了这个答案:
GET my_shops/_search
{
"query": {
"script": {
"script": {
"params": {
"location": {
"lat": 40,
"lon": -70
}
},
"source": """
return doc['location'].arcDistance(params.location.lat, params.location.lon)/1000 <= doc['max_delivery_distance'].value"""
}
}
}
}
基本上,我们利用了这样一个事实,即与 GEO 点相关的 类 被无痛地列入白名单 https://github.com/elastic/elasticsearch/pull/40180/ 并且脚本接受额外的参数(您的固定位置)。
根据 arcDistance 的文档,我们检索以米为单位的大小,因此您需要将此值除以 1000 转换为公里。
补充说明
我假设 location 和 max_delivery_distance 总是(针对每个文档)定义的。如果不是这种情况,你需要覆盖这种情况。
参考
- Another related question
- https://github.com/elastic/elasticsearch/pull/40180/
还有另一种无需脚本即可解决此问题的方法(性能消耗巨大!!)并让 ES 使用原生地理形状对其进行排序。
我会将每个文档建模为 circle,具有中心位置和(交付)半径。首先,您的索引映射应如下所示:
PUT /my_shops
{
"mappings": {
"properties": {
"delivery_area": {
"type": "geo_shape",
"strategy": "recursive"
}
}
}
}
然后,您的文件需要具有以下格式:
PUT /my_shops/_doc/1
{
"delivery_area" : {
"type" : "circle",
"coordinates" : [-71.34, 40.12],
"radius" : "3000m"
}
}
PUT /my_shops/_doc/2
{
"delivery_area" : {
"type" : "circle",
"coordinates" : [-72.34, 41.12],
"radius" : "5000m"
}
}
最后,查询简单地变成了 geo_shape
查询,查看每个商店的送货点和送货区域之间的交叉点。
GET /my_shops/_search
{
"query": {
"bool": {
"filter": {
"geo_shape": {
"delivery_area": {
"shape": {
"type": "point",
"coordinates": [ -70, 40 ]
},
"relation": "contains"
}
}
}
}
}
}
就是这样!没有脚本,只有地理操作。