ElasticSearch GeoLocation 官方文档翻译之 Geo Point

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 
        ]
     },
...

你可能感兴趣的:(ElasticSearch GeoLocation 官方文档翻译之 Geo Point)