elasticsearch 深入 —— 地理位置
地理位置
我们拿着纸质地图漫步城市的日子一去不返了。得益于智能手机,我们现在总是可以知道 自己所处的准确位置,也预料到网站会使用这些信息。我想知道从当前位置步行 5 分钟内可到的那些餐馆,对伦敦更大范围内的其他餐馆并不感兴趣。
但地理位置功能仅仅是 Elasticsearch 的冰山一角,Elasticsearch 的妙处在于,它让你可以把地理位置、全文搜索、结构化搜索和分析结合到一起。
例如:告诉我提到 vitello tonnato 这种食物、步行 5 分钟内可到、且晚上 11 点还营业的餐厅,然后结合用户评价、距离、价格排序。另一个例子:给我展示一幅整个城市8月份可用假期出租物业的地图,并计算出每个区域的平均价格。
Elasticsearch 提供了 两种表示地理位置的方式:用纬度-经度表示的坐标点使用 geo_point
字段类型,以 GeoJSON 格式定义的复杂地理形状,使用 geo_shape
字段类型。
Geo-points 允许你找到距离另一个坐标点一定范围内的坐标点、计算出两点之间的距离来排序或进行相关性打分、或者聚合到显示在地图上的一个网格。另一方面,Geo-shapes 纯粹是用来过滤的。它们可以用来判断两个地理形状是否有重合或者某个地理形状是否完全包含了其他地理形状。
地理坐标点
地理坐标点 是指地球表面可以用经纬度描述的一个点。 地理坐标点可以用来计算两个坐标间的距离,还可以判断一个坐标是否在一个区域中,或在聚合中。
地理坐标点不能被动态映射 (dynamic mapping)自动检测,而是需要显式声明对应字段类型为 geo-point
:
PUT /attractions
{
"mappings": {
"restaurant": {
"properties": {
"name": {
"type": "text"
},
"location": {
"type": "geo_point"
}
}
}
}
}
经纬度坐标格式
如上例,location
字段被声明为 geo_point
后,我们就可以索引包含了经纬度信息的文档了。 经纬度信息的形式可以是字符串、数组或者对象:
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 ]
}
字符串形式以半角逗号分割,如 "lat,lon" 。
对象形式显式命名为 lat 和 lon 。
数组形式表示为 [lon,lat] 。
可能所有人都至少一次踩过这个坑:地理坐标点用字符串形式表示时是纬度在前,经度在后(
"latitude,longitude"
),而数组形式表示时是经度在前,纬度在后([longitude,latitude]
)—顺序刚好相反。其实,在 Elasticesearch 内部,不管字符串形式还是数组形式,都是经度在前,纬度在后。不过早期为了适配 GeoJSON 的格式规范,调整了数组形式的表示方式。
因此,在使用地理位置的路上就出现了这么一个“捕熊器”,专坑那些不了解这个陷阱的使用者。
通过地理坐标点过滤
有四种地理坐标点相关的过滤器 可以用来选中或者排除文档:
找出落在指定矩形框中的点。
找出与指定位置在给定距离内的点。
找出与指定点距离在给定最小距离和最大距离之间的点。
geo_polygon
找出落在多边形中的点。 这个过滤器使用代价很大 。当你觉得自己需要使用它,最好先看看 geo-shapes。
这些过滤器判断点是否落在指定区域时的计算方法稍有不同,但过程类似。指定的区域被转换成一系列以quad/geohash为前缀的tokens,并被用来在倒排索引中搜索拥有相同tokens的文档。
地理坐标过滤器使用代价昂贵 — 所以最好在文档集合尽可能少的场景下使用。你可以先使用那些简单快捷的过滤器,比如
term
或range
,来过滤掉尽可能多的文档,最后才交给地理坐标过滤器处理。布尔型过滤器
bool
filter 会自动帮你做这件事。 它会优先让那些基于“bitset”的简单过滤器(见 关于缓存 )来过滤掉尽可能多的文档,然后依次才是更昂贵的地理坐标过滤器或者脚本类的过滤器。
Geo Bounding Box 查询
这是目前为止最有效的地理坐标过滤器了,因为它计算起来非常简单。 你指定一个矩形的 顶部
, 底部
, 左边界
,和 右边界
,然后过滤器只需判断坐标的经度是否在左右边界之间,纬度是否在上下边界之间:
PUT /my_locations
{
"mappings": {
"_doc": {
"properties": {
"pin": {
"properties": {
"location": {
"type": "geo_point"
}
}
}
}
}
}
}
PUT /my_locations/_doc/1
{
"pin" : {
"location" : {
"lat" : 40.12,
"lon" : -71.34
}
}
}
然后可以使用geo_bounding_box
过滤器执行以下简单查询 :
GET /_search
{
"query": {
"bool" : {
"must" : {
"match_all" : {}
},
"filter" : {
"geo_bounding_box" : {
"pin.location" : {
"top_left" : {
"lat" : 40.73,
"lon" : -74.1
},
"bottom_right" : {
"lat" : 40.01,
"lon" : -71.12
}
}
}
}
}
}
}
pin.location这些坐标也可以用 bottom_left 和 top_right 来表示
geo_bounding_box查询选项
选项 | 描述 |
---|---|
|
用于标识过滤器的可选名称字段 |
|
设置为 |
|
设置为 |
接受的格式
与geo_point类型可以接受地理坐标点的不同格式的方式大致相同,过滤器也可以接受它:
Lat Lon As Properties
GET /_search
{
"query": {
"bool" : {
"must" : {
"match_all" : {}
},
"filter" : {
"geo_bounding_box" : {
"pin.location" : {
"top_left" : {
"lat" : 40.73,
"lon" : -74.1
},
"bottom_right" : {
"lat" : 40.01,
"lon" : -71.12
}
}
}
}
}
}
}
Lat Lon As Array
在[lon, lat]
这里格式化,注意,lon / lat的顺序,以符合GeoJSON。
GET /_search
{
"query": {
"bool" : {
"must" : {
"match_all" : {}
},
"filter" : {
"geo_bounding_box" : {
"pin.location" : {
"top_left" : "40.73, -74.1",
"bottom_right" : "40.01, -71.12"
}
}
}
}
}
}
Lat Lon As String
格式化lat,lon作为字符串
GET /_search
{
"query": {
"bool" : {
"must" : {
"match_all" : {}
},
"filter" : {
"geo_bounding_box" : {
"pin.location" : {
"top_left" : "40.73, -74.1",
"bottom_right" : "40.01, -71.12"
}
}
}
}
}
}
边界框为 Well-Known 文本(WKT)
GET /_search
{
"query": {
"bool" : {
"must" : {
"match_all" : {}
},
"filter" : {
"geo_bounding_box" : {
"pin.location" : {
"wkt" : "BBOX (-74.1, -71.12, 40.73, 40.01)"
}
}
}
}
}
}
Geohash
GET /_search
{
"query": {
"bool" : {
"must" : {
"match_all" : {}
},
"filter" : {
"geo_bounding_box" : {
"pin.location" : {
"top_left" : "dr5r9ydj2y73",
"bottom_right" : "drj7teegpus6"
}
}
}
}
}
}
顶点
边界框的顶点可以通过设定top_left
和 bottom_right
或通过top_right
和bottom_left
参数。更过名topLeft
,bottomRight
,topRight
和bottomLeft
支持。可以使用简单名称top
,而不是分别设置值left
,bottom
并right
分别设置值。
GET /_search
{
"query": {
"bool" : {
"must" : {
"match_all" : {}
},
"filter" : {
"geo_bounding_box" : {
"pin.location" : {
"top" : 40.73,
"left" : -74.1,
"bottom" : 40.01,
"right" : -71.12
}
}
}
}
}
}
geo_point类型
该过滤器要求的geo_point
要在相关领域的集合类型。
每个文档的多位置
过滤器可以处理每个文档的多个位置/点。一旦单个位置/点与过滤器匹配,文档将包含在过滤器中
输入
默认情况下,边界框执行的类型设置为memory
,这意味着在内存中检查doc是否在边界框范围内。在某些情况下,indexed
选项执行速度会更快(但请注意,geo_point
在这种情况下,类型必须具有lat和lon索引)。请注意,使用索引选项时,不支持每个文档字段的多个位置。这是一个例子:
GET /_search
{
"query": {
"bool" : {
"must" : {
"match_all" : {}
},
"filter" : {
"geo_bounding_box" : {
"pin.location" : {
"top_left" : {
"lat" : 40.73,
"lon" : -74.1
},
"bottom_right" : {
"lat" : 40.10,
"lon" : -71.12
}
},
"type" : "indexed"
}
}
}
}
}
忽略未映射
设置true
为该ignore_unmapped
选项时,将忽略未映射的字段,并且不匹配此查询的任何文档。在查询可能具有不同映射的多个索引时,这非常有用。设置为 false
(默认值)时,如果未映射字段,查询将引发异常。
精度注意事项
Geopoints的精度有限,并且在索引时间内总是向下舍入。在查询时间期间,边界框的上边界向下舍入,而下边界向上舍入。因此,由于舍入误差,沿着下边界(边界框的左边缘和左边缘)的点可能不会进入边界框。同时,查询可以选择上边界(顶边和右边)旁边的点,即使它们位于边缘稍外。圆周误差应小于纬度上的4.20e-8度,经度上小于8.39e-8度,即使在赤道上也会误差小于1cm。
GeoShape查询
过滤使用geo_shape
类型索引的文档。
需要geo_shape
映射。
该geo_shape
查询使用相同的网格方形表示作为 geo_shape
映射,以查找具有与查询形状相交的形状的文档。它还将使用与字段映射定义的相同的PrefixTree配置。
该查询支持两种定义查询形状的方法,方法是提供整个形状定义,或者引用另一个索引中预先索引的形状的名称。以下通过示例定义两种格式。
内联形状定义
与geo_shape
类型类似,geo_shape
查询使用 GeoJSON来表示形状。
鉴于以下指数:
PUT /example
{
"mappings": {
"_doc": {
"properties": {
"location": {
"type": "geo_shape"
}
}
}
}
}
POST /example/_doc?refresh
{
"name": "Wind & Wetter, Berlin, Germany",
"location": {
"type": "point",
"coordinates": [13.400544, 52.530286]
}
}
以下查询将使用Elasticsearch的envelope
GeoJSON扩展来查找该点 :
GET /example/_search
{
"query":{
"bool": {
"must": {
"match_all": {}
},
"filter": {
"geo_shape": {
"location": {
"shape": {
"type": "envelope",
"coordinates" : [[13.0, 53.0], [14.0, 52.0]]
},
"relation": "within"
}
}
}
}
}
}
预索引形状
Query还支持使用已在另一个索引和/或索引类型中编入索引的形状。当您具有对应用程序有用的预定义形状列表并且您希望使用逻辑名称(例如New Zealand)引用它而不是每次都必须提供它们的坐标时,这尤其有用。在这种情况下,只需要提供:
id
- 包含预索引形状的文档的ID。index
- 预索引形状所在的索引的名称。默认为形状。type
- 预索引形状所在的索引类型。path
- 指定为包含预索引形状的路径的字段。默认为形状。routing
- 如果需要,形状文档的路由。
以下是使用具有预索引形状的过滤器的示例:
PUT /shapes
{
"mappings": {
"_doc": {
"properties": {
"location": {
"type": "geo_shape"
}
}
}
}
}
PUT /shapes/_doc/deu
{
"location": {
"type": "envelope",
"coordinates" : [[13.0, 53.0], [14.0, 52.0]]
}
}
GET /example/_search
{
"query": {
"bool": {
"filter": {
"geo_shape": {
"location": {
"indexed_shape": {
"index": "shapes",
"type": "_doc",
"id": "deu",
"path": "location"
}
}
}
}
}
}
}
空间关系
所述geo_shape策略映射参数确定其可在搜索时间被使用的空间关系的运营商。
以下是可用的空间关系运算符的完整列表:
"relation": "within"
INTERSECTS
- (默认)返回其geo_shape
字段与查询几何体相交的所有文档。DISJOINT
- 返回其geo_shape
字段与查询几何图形无任何共同点的所有文档。WITHIN
- 返回其geo_shape
字段在查询几何中的所有文档。CONTAINS
- 返回其geo_shape
字段包含查询几何的所有文档。
忽略未映射的
设置true
为该ignore_unmapped
选项时,将忽略未映射的字段,并且不匹配此查询的任何文档。在查询可能具有不同映射的多个索引时,这非常有用。设置为 false
(默认值)时,如果未映射字段,查询将引发异常。
Geo Distance 查询
过滤仅包含与地理位置相距特定距离内的匹配的文档。假设以下映射和索引文档:
PUT /my_locations
{
"mappings": {
"_doc": {
"properties": {
"pin": {
"properties": {
"location": {
"type": "geo_point"
}
}
}
}
}
}
}
PUT /my_locations/_doc/1
{
"pin" : {
"location" : {
"lat" : 40.12,
"lon" : -71.34
}
}
}
然后可以使用geo_distance
过滤器执行以下简单查询:
GET /my_locations/_search
{
"query": {
"bool" : {
"must" : {
"match_all" : {}
},
"filter" : {
"geo_distance" : {
"distance" : "200km",
"pin.location" : {
"lat" : 40,
"lon" : -70
}
}
}
}
}
}
接受的格式
以geo_point
类似的方式,类型可以接受地理点的不同表示,过滤器也可以接受它:
Lat Lon As Properties
GET /my_locations/_search
{
"query": {
"bool" : {
"must" : {
"match_all" : {}
},
"filter" : {
"geo_distance" : {
"distance" : "12km",
"pin.location" : {
"lat" : 40,
"lon" : -70
}
}
}
}
}
}
Lat Lon As Array
在[lon, lat]
这里格式化,注意,lon / lat的顺序,以符合GeoJSON。
GET /my_locations/_search
{
"query": {
"bool" : {
"must" : {
"match_all" : {}
},
"filter" : {
"geo_distance" : {
"distance" : "12km",
"pin.location" : [-70, 40]
}
}
}
}
}
Lat Lon As String
格式化lat,lon
。
GET /my_locations/_search
{
"query": {
"bool" : {
"must" : {
"match_all" : {}
},
"filter" : {
"geo_distance" : {
"distance" : "12km",
"pin.location" : "40,-70"
}
}
}
}
}
Geohash
GET /my_locations/_search
{
"query": {
"bool" : {
"must" : {
"match_all" : {}
},
"filter" : {
"geo_distance" : {
"distance" : "12km",
"pin.location" : "drm3btev3e86"
}
}
}
}
}
选项
以下是过滤器允许的选项:
|
圆的半径以指定位置为中心。落入此圈的点被认为是匹配。在 |
|
如何计算距离。可以是 |
|
用于标识查询的可选名称字段 |
|
设置为 |
geo_point类型
该过滤器要求的geo_point
要在相关领域的集合类型。
每个文档的多位置
该geo_distance
过滤器可以用每份文件的多个位置/点工作。一旦单个位置/点与过滤器匹配,文档将包含在过滤器中。
忽略未映射的
设置true
为该ignore_unmapped
选项时,将忽略未映射的字段,并且不匹配此查询的任何文档。在查询可能具有不同映射的多个索引时,这非常有用。设置为 false
(默认值)时,如果未映射字段,查询将引发异常。
elasticsearch 深入 —— 地理位置的更多相关文章
- elasticsearch geo_point 地理位置过滤 按经度排序
elasticsearch 支持强大的经纬度坐标过滤. 1.首先要建立坐标类型的字段'type' ='geo_point' es存储的值是这样的: "poi": [ ...
- 使用ElasticSearch完成百万级数据查询附近的人功能
上一篇文章介绍了ElasticSearch使用Repository和ElasticSearchTemplate完成构建复杂查询条件,简单介绍了ElasticSearch使用地理位置的功能. 这一篇我们 ...
- Elasticsearch系列---几个高级功能
概要 本篇主要介绍一下搜索模板.映射模板.高亮搜索和地理位置的简单玩法. 标准搜索模板 搜索模板search tempalte高级功能之一,可以将我们的一些搜索进行模板化,使用现有模板时传入指定的参数 ...
- Elasticsearch 在地理信息空间索引的探索和演进
vivo 互联网服务器团队- Shuai Guangying 本文梳理了Elasticsearch对于数值索引实现方案的升级和优化思考,从2015年至今数值索引的方案经历了多个版本的迭代,实现思路从最 ...
- 实战ELK(9) Elasticsearch地理位置
地理坐标点(geo-point) 是指地球表面可以用经纬度描述的一个点.地理坐标点可以用来计算两个坐标位置间的距离,或者判断一个点是否在一个区域中.地理坐标点不能被动态映射(dynamic mappi ...
- Elasticsearch地理位置总结
更多内容请参考 : https://www.felayman.com 翻译版本:https://es.xiaoleilu.com/310_Geopoints/00_Intro.html 官方原文:ht ...
- elasticsearch地理位置查询
elasticsearch地理位置查询 一.背景 二.geo数据类型 1.geo_point 2.geo_shape 三.此处对geo_point类型实战 1.背景 2.插入地点数据 1.创建索引 2 ...
- ElasticSearch 5学习(10)——结构化查询(包括新特性)
之前我们所有的查询都属于命令行查询,但是不利于复杂的查询,而且一般在项目开发中不使用命令行查询方式,只有在调试测试时使用简单命令行查询,但是,如果想要善用搜索,我们必须使用请求体查询(request ...
- Elasticsearch 教程--数据
在Elasticsearch中,每一个文档都有一个版本号码.每当文档产生变化时(包括删除),_version就会增大.在<版本控制>中,我们将会详细讲解如何使用_version的数字来确认 ...
随机推荐
- [python 学习] sys模块
sys.stdout sys.stdin
- $mona$要成为高端玩家
\(mona\)要成为高端玩家! 好在撑过了联赛,接下来要向高端玩家冲击啦! 新时期当然要有新的学习规划啦! 最近的更新(有什么就在这里说啦) 随便更更. \(FFT\)刷着打算先看看生成函数. 感觉 ...
- django的安装和初步使用
安装参考:步骤也可以参考这个 很详细 https://blog.csdn.net/zww1984774346/article/details/54408759 如果想在终端查看项目结构 需要用到tre ...
- socket函数库简单封装
#pragma once #ifndef WINSOCK_H #include<WinSock2.h> #pragma comment(lib,"ws2_32.lib" ...
- HY 的惩罚 (Trie 树,博弈论)
[问题描述] hy 抄题解又被老师抓住了,现在老师把他叫到了办公室. 老师要 hy 和他玩一个游 戏.如果 hy 输了,老师就要把他开除信息组; 游戏分为 k 轮.在游戏开始之前,老师会将 n 个由英 ...
- [Ctsc2015]misc
https://lydsy.com/JudgeOnline/problem.php?id=4055 题解 观察题目要我们求的东西: \[ ans[k]=\sum_{i}\sum_j \frac{a_i ...
- laravel5.6 ORM 关联模型,一对一和一对多
Laravel5.6 关联模型的操作,主要是一对一,一对多,多对多等操作.下面示例主要解析前面两个操作用法比较常用.(操作和用法TP5类似)将关联查询使用语法hasOne.hasMany.belong ...
- VBox 启动虚拟机失败 - NtCreateFile(\Device\VBoxDrvStub)
在Vbox(5.0.8 版本)启动Ubuntu的虚拟机时,遇到错误信息: NtCreateFile(\Device\VBoxDrvStub) failed: 0xc000000034 STATUS_O ...
- POJ 1753 Flip Game (状压+暴力)
题目链接:http://poj.org/problem?id=1753 题意: 给你一个4*4的棋盘,上面有两种颜色的棋子(一种黑色,一种白色),你一次可以选择一个棋子翻转它(黑色变成白色,同理反之) ...
- Python分析《武林外传》
我一向比较喜欢看武侠电影.小说,但是06年武林外传开播的时候并没有追剧,简单扫几眼之后发现他们都不会那种飞来飞去的打,一点也不刺激.09年在南京培训的时候,日子简单无聊透顶,大好的周末不能出门,只能窝 ...