Geo Points
Geo-points 就是我们地球的经纬度,它能够用于计算地球两点之间的距离。
Geo-points 不能使用动态mapping,必须提前制定好geo_point类型
PUT /attractions
{
"mappings": {
"restaurant": {
"properties": {
"name": {
"type": "string"
},
"location": {
"type": "geo_point"
}
}
}
}
}
Lat/Lon Formats
假设我们把 location 字段定义为geo_point 类型,我们创建文档的时候应该包含一对 latitude/longitude ,可以是 string ,arrays 或者 objects
PUT /attractions/restaurant/1
{
"name": "Chipotle Mexican Grill",
"location": "40.715, -74.011"
}
PUT /attractions/restaurant/2
{
"name": "Pala Pizza",
"location": {
"lat": 40.722,
"lon": -73.989
}
}
PUT /attractions/restaurant/3
{
"name": "Mini Munchies Pizza",
"location": [ -73.983, 40.719 ]
}
注意:
latitude/longitude 在每种类型的顺序是不一样的,在字符串中是"latitude,longitude",但是在数组中应为[longitude,latitude],
以前,字符串和数组中的经纬度顺序是一致的,但是为了和GeoJSON保持一致,所以讲数组的经纬度顺序颠倒了。
Filtering by Geo Point
四种geo-point filters 可以帮助我们去过滤数据,当我们使用geolocation时。
geo_bounding_box:
找出落在一个指定的长方形中的geo-points
geo_distance:
找出以一个点为中心,一个指定半径范围内的geo-points
geo_distance_range:
找出以一个点为中心,设置一个最小半径和一个最大半径,在这个之间的geo-points
geo_polygon:
找出落在一个多边形范围内的geo-points,这个代价是相当昂贵的,如果你有这样的需求,建议使用 geo-shapes
每种filter的计算一个点是否落到一个区域的方式有些不同,但过程都是相似的。
所请求的区域被转换为quad/geohash前缀标记的范围,然后去倒排索引中搜索具有相同标记的文档。
建议:
Geo-filters 是相当耗费性能的,它适合用于在文档数量较少的时候。首先,你应该利用其它filter先过滤掉一部分数据。
Geo Bounding Box Filter
它是到目前为止最有效率的filter,因为他的计算非常简单。你只需提供上、下、左、右四个坐标,剩下的就是他会比较longitude 是否在左右坐标内,latitude 是否在上下坐标内
GET /attractions/restaurant/_search
{
"query": {
"filtered": {
"filter": {
"geo_bounding_box": {
"location": {
"top_left": {
"lat": 40.8,
"lon": -74.0
},
"bottom_right": {
"lat": 40.7,
"lon": -73.0
}
}
}
}
}
}
}
Optimizing Bounding Boxes
geo_bounding_box 是不需要将所有的geo-points都加载到内存的。因为它需要做的就是检查the lat and lon 是否落到了一个指定的区域。它可以用倒排索引去做个一次过滤。
为了使用这种优化,我们必须指定geo_point 的映射,设置 "lat_lon": true
PUT /attractions
{
"mappings": {
"restaurant": {
"properties": {
"name": {
"type": "string"
},
"location": {
"type": "geo_point",
"lat_lon": true
}
}
}
}
}
现在,当你执行下面的查询时,我们会告诉elasticsearch使用lat 和lon的索引
GET /attractions/restaurant/_search
{
"query": {
"filtered": {
"filter": {
"geo_bounding_box": {
"type": "indexed",
"location": {
"top_left": {
"lat": 40.8,
"lon": -74.0
},
"bottom_right": {
"lat": 40.7,
"lon": -73.0
}
}
}
}
}
}
}
Geo Distance Filter
geo_distance 这个过滤器会画个圆,来找到在这个落在这个圆内所有的文档
GET /attractions/restaurant/_search
{
"query": {
"filtered": {
"filter": {
"geo_distance": {
"distance": "1km",
"location": {
"lat": 40.715,
"lon": -73.988
}
}
}
}
}
}
geo-distance 的搜索是相当耗费性能的,为了优化,elasticsearch会首先会画一个内切于圆的正方形。然后用bounding-box filter 过滤掉一部分数据。然后在执行geo-distance filter去找到那些落在圆内的数据。
Faster Geo-Distance Calculations
两点之间的距离可以利用一个牺牲性能来提高精度的算法去计算
arc:
最慢但是精度最高的就是arc 了,一个把地球看成一个球体,但是精度还是有限的,因为实际上地球并不是一个真正的球体
plane:
plane 算法把地球看成扁平的。它很快但是精度低。在赤道精度最高。
sloppy_arc:
这么称呼他是因为这个算法是粗滤的计算。
在查询的时候,你可以和下面一样,指定一个算法
GET /attractions/restaurant/_search
{
"query": {
"filtered": {
"filter": {
"geo_distance": {
"distance": "1km",
"distance_type": "plane",
"location": {
"lat": 40.715,
"lon": -73.988
}
}
}
}
}
}
geo_distance_range Filter
geo_distance 和 geo_distance_range 的唯一区别是,geo_distance_range是一个圆环。
GET /attractions/restaurant/_search
{
"query": {
"filtered": {
"filter": {
"geo_distance_range": {
"gte": "1km",
"lt": "2km",
"location": {
"lat": 40.715,
"lon": -73.988
}
}
}
}
}
}
Sorting by Distance
查询结果是可以跟到一个点的距离排序的
GET /attractions/restaurant/_search
{
"query": {
"filtered": {
"filter": {
"geo_bounding_box": {
"type": "indexed",
"location": {
"top_left": {
"lat": 40.8,
"lon": -74.0
},
"bottom_right": {
"lat": 40.4,
"lon": -73.0
}
}
}
}
}
},
"sort": [
{
"_geo_distance": {
"location": {
"lat": 40.715,
"lon": -73.998
},
"order": "asc",
"unit": "km",
"distance_type": "plane"
}
}
]
}
你可以问问你自己,为什么要指定距离单位,排序?这不取决于我们用米、千米而产生不同的结果。真是的原因是排序的时候会返回结果
...
"hits": [
{
"_index": "attractions",
"_type": "restaurant",
"_id": "2",
"_score": null,
"_source": {
"name": "New Malaysia",
"location": {
"lat": 40.715,
"lon": -73.997
}
},
"sort": [
0.08425653647614346
]
},
...