Elasticsearch 计算距离,根据距离排序,地理点和地理多边形范围查找
文章目录
总结
Elasticsearch 计算并返回距离一共有两种方法:sort 和 script_fields
环境概述
- CentOS 7.6
- Elasticsearch 7.10
创建索引
PUT/scenic_spot
{"mappings":{"properties":{"id":{"type":"keyword"},"name":{"type":"text"},"pin":{"properties":{"location":{"type":"geo_point"}}}}},"settings":{"number_of_replicas":3,"number_of_shards":3}}
索引数据
PUT/scenic_spot/_doc/1{"id":1,"name":"恭王府景区","pin":{"location":{"lat":31.007925,"lon":103.607572}}}PUT/scenic_spot/_doc/2{"id":2,"name":"故宫博物院","pin":{"location":{"lat":39.917248,"lon":116.397176}}}PUT/scenic_spot/_doc/3{"id":3,"name":"天坛公园","pin":{"location":{"lat":39.881265,"lon":116.410638}}}PUT/scenic_spot/_doc/4{"id":4,"name":"颐和园","pin":{"location":{"lat":39.991664,"lon":116.271966}}}PUT/scenic_spot/_doc/5{"id":5,"name":"八达岭长城","pin":{"location":{"lat":40.361375,"lon":116.019809}}}
Sort 返回距离
GET/scenic_spot/_search
{"query":{"match_all":{}},"sort":[{"_geo_distance":{"pin.location":{"lat":38.912780578039346,"lon":120.18819440815733},"order":"asc","unit":"km"}}],"size":1}
响应结果如下,hits 下的 sort 字段就是距离,单位:km。
{"took":4,"timed_out":false,"_shards":{"total":3,"successful":3,"skipped":0,"failed":0},"hits":{"total":{"value":5,"relation":"eq"},"max_score":null,"hits":[{"_index":"scenic_spot","_type":"_doc","_id":"3","_score":null,"_source":{"id":3,"name":"天坛公园","pin":{"location":{"lat":39.881265,"lon":116.410638}}},"sort":[341.96155623680716]}]}}
Script_fields 返回距离
GET/scenic_spot/_search
{"query":{"match_all":{}},"_source":true,"script_fields":{"distance":{"script":{"lang":"painless","params":{"lat":38.912780578039346,"lon":120.18819440815733},"source":"doc['pin.location'].arcDistance(params.lat, params.lon)/1000"}}},"size":1}
5.x 以前支持:distanceInKm(lat, lon) 函数,后来被废弃。现在只支持 arcDistance(lat, lon) 函数:计算两点距离,单位为:m。响应结果如下,hits 下的 fields.distance 字段就是距离,单位:km。
{"took":3,"timed_out":false,"_shards":{"total":3,"successful":3,"skipped":0,"failed":0},"hits":{"total":{"value":5,"relation":"eq"},"max_score":1.0,"hits":[{"_index":"scenic_spot","_type":"_doc","_id":"5","_score":1.0,"_source":{"id":5,"name":"八达岭长城","pin":{"location":{"lat":40.361375,"lon":116.019809}}},"fields":{"distance":[391.55015001577397]}}]}}
地理点和地理多边形范围查找
Elasticsearch](https://so.csdn.net/so/search?q=Elasticsearch&spm=1001.2101.3001.7020) 是一个功能强大的搜索引擎,支持地理查询,但并不是每个人都习惯于处理空间数据。 如果你对地理处理了解不多,或者想通过 Elasticsearch 了解地理处理,那么本文适合你。在我们的现实生活中,我们经常使用的滴滴打车,美团送餐,美国的 Uber, Lyft 打车,还有一些交友 apps 等等,它们都是使用 Elasticsearch 进行位置搜索的例子。
地理距离查询返回距离点最大距离的所有文档,例如:Dolores 想认识距离她约 300 米的所有人:
红色圆圈的半径为 300 米,我们可以看到只有 William 在圆圈内。
让我们来做一下 Elasticsearch 的实现。 首先,使用属性 name 和 location 创建一个 user_location 索引。
PUT user_location
{"mappings":{"properties":{"name":{"type":"text"},"location":{"type":"geo_point"}}}}
location 的数据类型为 geo_point, 表示地球上的位置。 点具有经度和纬度(坐标)。 你可以在官方文档中检查所有可接受的 geo_point 格式。
现在,让我们为 William,Robert 和 Bernard 的位置创建文档。我们使用 _bulk API 来导入数据:
POST user_location/_bulk
{"index":{"_id":"1"}}{"name":"William","location":"-25.443053, -49.238396"}{"index":{"_id":"2"}}{"name":"Robert","location":"-25.440173, -49.243169"}{"index":{"_id":"3"}}{"name":"Bernard","location":"-25.440262, -49.247720"}
为了说明问题的方便,我特地创建一个新的索引叫做 locations,它包含了 Dolores 的位置信息:
PUT locations
{"mappings":{"properties":{"name":{"type":"text"},"location":{"type":"geo_point"}}}}
POST locations/_bulk
{"index":{"_id":"1"}}{"name":"William","location":"-25.443053, -49.238396"}{"index":{"_id":"2"}}{"name":"Robert","location":"-25.440173, -49.243169"}{"index":{"_id":"3"}}{"name":"Bernard","location":"-25.440262, -49.247720"}{"index":{"_id":"4"}}{"name":"Dolores","location":"-25.442987, -49.239504"}
在上面的 _id 为 4 的文档就是 Doloes 的位置信息。我们来创建一个叫做 locations 的索引模式:
我们打开 Maps 应用:
我们发现这四个位置位于南美的某个地方。我们编辑 location 层的设置,当我们点击该位置的时候,显示名字及 id。我们调整合适的 zoom 大小:
从上面的图中,我们很清楚地看到每个人的相对的位置。离 Dolores 最近的就是 Willam,也就是那个被盖着的那个,接着就是 Robert。最远的就是 Bernard。请注意,我们上面的展示都是以 locations 这个索引来进行展示的。它里面含有 Dolores。我们现在使用 user_location 索引来进行搜索:
GET user_location/_search
{"query":{"bool":{"filter":{"geo_distance":{"distance":"300m","location":"-25.442987, -49.239504"}}}}}
在上面,我们针对 Dolores 来进行搜索。显示的结果是:
{"took":55,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":1,"relation":"eq"},"max_score":0.0,"hits":[{"_index":"user_location","_type":"_doc","_id":"1","_score":0.0,"_source":{"name":"William","location":"-25.443053, -49.238396"}}]}}
也就是说在 Dolores 方圆 300m 之内,只有 William。如果我们把半径增加到 600 m,那么我可以看到 Robert:
GET user_location/_search
{"query":{"bool":{"filter":{"geo_distance":{"distance":"600m","location":"-25.442987, -49.239504"}}}}}
{"took":2,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":2,"relation":"eq"},"max_score":0.0,"hits":[{"_index":"user_location","_type":"_doc","_id":"1","_score":0.0,"_source":{"name":"William","location":"-25.443053, -49.238396"}},{"_index":"user_location","_type":"_doc","_id":"2","_score":0.0,"_source":{"name":"Robert","location":"-25.440173, -49.243169"}}]}}
地理多边形查询可获取多边形内的文档。
“多边形是具有直边的封闭形状。 矩形,三角形,六边形和八边形都是多边形的例子。”
它由点列表表示。 两点之间最接近的路径是一条直线。 多边形的起点和终点均相同。 在下面的图上检查下面的植物园多边形。
请注意,Elasticsearch 上的地理查询会检查文档的 geo_point 属性是否在多边形内。 例:Dolores 想知道植物园内的每个人。
GET user_location/_search
{"query":{"bool":{"filter":{"geo_polygon":{"location":{"points":["-25.44373,-49.24248","-25.44297,-49.24230","-25.44177,-49.23642","-25.43961,-49.23822","-25.43991,-49.23781","-25.44170,-49.23647","-25.44210,-49.23586","-25.44218,-49.23506","-25.44358,-49.23491","-25.44406,-49.24139","-25.44373,-49.24248"]}}}}}}
上面的搜索返回的结果:
{"took":15,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":1,"relation":"eq"},"max_score":0.0,"hits":[{"_index":"user_location","_type":"_doc","_id":"1","_score":0.0,"_source":{"name":"William","location":"-25.443053, -49.238396"}}]}}
版权归原作者 qaz5865026123 所有, 如有侵权,请联系我们删除。