elasticsearch地理位置查询
一、背景
最近有个需求,需要获取某个位置附近的楼盘,**比如:**获取当前用户所在位置,方圆100km千米之内的楼盘信息。经过调研,发现可以使用 redis
、mongodb
、elasticsearch
等实现。经过考虑之后决定使用es
来实现,此处简单记录下es
中geo
方面api的使用。
二、geo数据类型
在es
中存在2种地理位置数据类型,geo_point
和geo_shape
。es无法自动识别这种数据类型,需要在创建mapping的时候,自己手动指定。
1、geo_point
geo_point
使用的是经纬度
的坐标点,可以计算落在某个矩形内的点、以某个点为半径(圆)的点、某个多边形内的点(弃用了)、排序、聚合等操作。
2、geo_shape
geo_shape
表示的是一个复杂的图形,使用的是GeoJSON
的格式来表示复杂的图形。比如:我们要表示一个图书馆的坐标位置,如果图书馆占的位置比较大,用一个点表示可能就不准了,此时就可以使用geo_shape
来表示了。
不过这种数据类型也有缺点:比如不能排序等等(因为是多边形的点)。
三、此处对geo_point类型实战
1、背景
1、图中的 ① ② ③ ④ 表示是需要加入到 es 中的建筑物
建筑物 | 坐标 | 距离地点 | 相隔距离 | 解释 |
---|---|---|---|---|
上海站 | 121.462311,31.256224 | 上海站 | ||
上海静安洲际酒店 | 121.460186,31.251281 | 上海站 | 586.24米 | 上海站和该酒店大概像个586.24米 |
交通公园 | 121.473939,31.253531 | 上海站 | 1146.45米 | |
万业远景大厦 | 121.448215,31.26229 | 上海站 | 1501.74米 |
2、图中的圆形、正方形、多边形表示后期需要使用 es 查询出来里面里面的地点。
3、图中的短小的箭头️表示边界。
2、插入地点数据
1、创建索引
PUT /geo_index
{
"settings": {
"number_of_shards": 2,
"number_of_replicas": 2,
"analysis": {
"analyzer": {
"default": {
"type": "ik_max_word"
}
}
}
},
"mappings": {
"properties": {
"building_name": {
"type": "keyword"
},
"location": {
// 此处手动指定数据类型
"type": "geo_point"
}
}
}
}
注意️:
1、在索引中,我们自己指定来location
字段的类型为geo_point
类型。
2、building_name
的字段类型为keyword
表示不分词,这个字段只是为了测试,没有什么用。
3、不用指定索引的type,在es7中只有一个type。
2、插入地理位置数据
POST _bulk
{"create":{"_index":"geo_index","_id":1}}
{"building_name":"上海站","location":{"lat":31.256224,"lon":121.462311}}
{"create":{"_index":"geo_index","_id":2}}
{"building_name":"上海静安洲际酒店","location":"POINT (121.460186 31.251281)"}
{"create":{"_index":"geo_index","_id":3}}
{"building_name":"交通公园","location":"31.253531,121.473939"}
{"create":{"_index":"geo_index","_id":4}}
{"building_name":"万业远景大厦","location":[121.448215,31.26229]}
注意️:
1、从上面可知:地理位置的插入的格式可以存在4种
方式。
1、 {"lat":"","lon":""}
2、 "lat,lon"
3、 [Well-Known Text](https://docs.opengeospatial.org/is/12-063r5/12-063r5.html) "POINT (lon lat)"
4、 [lon,lat]
5、 还有一种 geohash 的格式
需要注意的是:使用 数组/Well-Known-Text 的格式的时候,经纬度是反过来的。
3、执行检索
1、geo_bounding_box 矩形过滤
从上图可知左上角和右下方的坐标分别为 (121.444075,31.265395)和(121.468417,31.253845)
执行查询,应该可以查询出 上海站
和万业远景大厦
1、es查询语句
GET /geo_index/_search
{
"query": {
"bool": {
"must": {
"match_all": {}
},
"filter": {
"geo_bounding_box": {
"location": {
"top_left": {
"lat": 31.265395,
"lon": 121.444075
},
"bottom_right": {
"lat": 31.253845,
"lon": 121.468417
}
}
}
}
}
}
}
2、查询结果
从图中可以看到,查询出来了 上海站
和万业远景大厦
,结果是正确的。
2、geo_distance 圆形查询
这个是距离查询,是以某个点向周围扩算的距离范围。
在上一步的背景中,我们知道上海站
的坐标(121.462311,31.256224
),同时也知道了上海站距离各个周边的距离有多远,此处我们以上海站为中心,查询方圆600米
的建筑物,可知只有上海静安洲际酒店
和上海站
符合。
1、es查询语句
GET /geo_index/_search
{
"query": {
"bool": {
"must": {
"match_all": {}
},
"filter": {
"geo_distance": {
"distance": "600m",
"distance_type": "arc",
"_name":"optional_name",
"location": {
"lat": 31.256224,
"lon": 121.462311
}
}
}
}
}
}
注意️:
1、distance_type
的值存在2个 arc
和plane
arc
:默认的方式,这种方式计算比较精确,但是比较慢,是把地球当作一个球体计算。plane
:这种方式计算比较快,但是可能不怎么准,越靠近赤道越准,是把地球当成平坦的进行计算。
2、distance
后面可用的单位有km
、m
、cm
、mm
、nmi
、mi
、yd
、ft
、in
2、查询结果
3、geo_distance 查询并排序,返回距离相隔多少米
1、es 查询语句
GET /geo_index/_search
{
"query": {
"bool": {
"must": {
"match_all": {}
},
"filter": {
"geo_distance": {
"distance": "600m",
"distance_type": "arc",
"_name": "optional_name",
"location": {
"lat": 31.256224,
"lon": 121.462311
}
}
}
}
},
"sort": [
{
"_geo_distance": {
"location": {
"lat": 31.256224,
"lon": 121.462311
},
"order": "desc",
"unit": "m",
"distance_type": "arc"
}
}
]
}
注意️:
1、sort
执行排序。
2、查询结果
4、geo_distance聚合
需求:
1. 统计`上海站`500米之内的建筑物有多少。
2. 统计`上海站`500-1000米之内的建筑物有多少。
3. 统计`上海站`大于1000米的建筑物有多少。
1、es查询语句
GET /geo_index/_search
{
"query": {
"bool": {
"must": {
"match_all": {}
}
}
},
"aggs": {
"rings_around_amsterdam": {
"geo_distance": {
"field": "location",
"origin": {
"lat": 31.256224,
"lon": 121.462311
},
"unit": "m",
"distance_type": "arc",
"ranges": [
{"to": 500,"key": "first"},
{"from": 500,"to": 1000,"key": "second" },
{"from": 1000,"key": "third"}
],
"keyed": true
}
}
}
}
2、查询结果
从上图中可以看到:
1、距离上海站在 0-500米之间的建筑物只有1个。
2、距离上海站在 500-1000之间的建筑物有1个。
3、距离上海站在 1000以上的有2个。
5、geo-polygon-多边形查询(过时)
在 es7.12 中已经过时了,推荐使用 geo_shape来实现
6、一个综合案例
1、需求:
1、查询语句query
,查询出所有的数据,并过滤出以上海站为中心的3km内的所有的建筑物。
2、aggs
,用于统计出上海在 500米以内、500-1000米、1000米之外的建筑物数量。
3、sort
用于排序。
4、post_filter
用于将结果缩小到上海站1000米以内。
2、查询结果
GET /geo_index/_search
{
"query": {
"bool": {
"must": {
"match_all": {}
},
// 过滤出上海站周围3km范围内的建筑物
"filter": {
"geo_distance": {
"distance": "3km",
"distance_type": "arc",
"_name":"optional_name",
"location": {
"lat": 31.256224,
"lon": 121.462311
}
}
}
}
},
// 聚合上海站周围的建筑物的数量
"aggs": {
"rings_around_amsterdam": {
"geo_distance": {
"field": "location",
"origin": {
"lat": 31.256224,
"lon": 121.462311
},
"unit": "m",
"distance_type": "arc",
"ranges": [
{"to": 500,"key": "first"},
{"from": 500,"to": 1000,"key": "second" },
{"from": 1000,"key": "third"}
],
"keyed": true
}
}
},
// 对查询到的结果排序,并将距离放到响应数据的 sort 字段中。
"sort": [
{
"_geo_distance": {
"location": {
"lat": 31.256224,
"lon": 121.462311
},
"order": "desc",
"unit": "m",
"distance_type": "arc"
}
}
],
// 将结果缩小到上海站附近1km的范围内。
"post_filter": {
"geo_distance": {
"distance": "1km",
"location": {
"lat": 31.256224,
"lon": 121.462311
}
}
}
}
四、参考文档
2、距离单位
3、排序
4、矩形查询
6、坐标拾取系统