ElasticSearch提供了两种与地理相关的数据类型,geo_point和geo_shape,前者用于保存地理位置,即一个具体的坐标,如 121.690096,31.202253 ;而后者则用于保存地理形状,如矩形和多边形。

 

一、 geo_point类型

   众所周知,地理位置由经度和纬度共同定义,所以geo_point定义地理位置坐标最基本的形式也是通过提供经度和纬度来实现。

  关于geo_point类型,应用比较简单,而且官网讲解的也比较清楚,这里就不再赘述了。可以直接查看如下的官网来查询如何应用:

  1. ​​https://www.elastic.co/guide/cn/elasticsearch/guide/current/geopoints.html​​ 

  (注:该链接上虽然是2.x版本,但是本知识点并不过时,依然可以看)


  將上面文档中比较重要的部分截取如下:

  有四种地理坐标点相关的过滤器可以用来选中或者排除文档:


       ​​geo_bounding_box​​  找出落在指定矩形框中的点。        ​​geo_distance​​  找出与指定位置在给定距离内的点。       ​​geo_distance_range​​  找出与指定点距离在给定最小距离和最大距离之间的点。​​  geo_polygon​找出落在多边形中的点。 这个过滤器使用代价很大 。当你觉得自己需要使用它,最好先看看 geo-shapes 


这些过滤器判断点是否落在指定区域时的计算方法稍有不同,但过程类似。

指定的区域被转换成一系列以quad/geohash为前缀的tokens,并被用来在倒排索引中搜索拥有相同tokens的文档。

 

地理坐标过滤器使用代价昂贵 — 所以最好在文档集合尽可能少的场景下使用。

可以先使用那些简单快捷的过滤器,比如 ​​term​​ 或 ​​range​​ ,来过滤掉尽可能多的文档,最后才交给地理坐标过滤器处理。

布尔型过滤器 ​​bool filter​​ 会自动帮你做这件事。它会优先让那些基于“bitset”的简单过滤器(见 ​​关于缓存​​ )来过滤掉尽可能多的文档,

然后依次才是更昂贵的地理坐标过滤器或者脚本类的过滤器。


 

 

2. ​​https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-geo-distance-query.html​​ 

(geo-distance查询: 以给定位置为圆心画一个圆,来找出那些地理坐标落在其中的doc)

3. ​​https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-geo-polygon-query.html​​   

(geo_polygon 查询:在地图上画一个多边形,搜索包含在多边形内部的doc)

4. ​​https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-geo-bounding-box-query.html​​ 

(geo_bounding_box 查询,查询包含在某个矩形中的doc)

 

二、 geo_shape类型

  官网:​​https://www.elastic.co/guide/en/elasticsearch/reference/7.x/geo-shape.html​

  geo_shape类型的字段,用于存储地理形状,支持GeoJSON及WKT中描述的大多数地理形状。

  所谓的GeoJSON及WKT(Well-Known Text),指的是用来表示GeoShape数据的【形式】。比如,同样表示一个点(point),用GeoJson来表示,则是:



{
"type": "Point",
"coordinates": [125.6, 10.1]
}


  而如果是用WKT来表示,则是:



POINT(125.6 10.1)


  因此,在使用geo_shape类型的时候,不用纠结于使用GeoJSON还是WKT,我这里选择的是GeoJson格式,下面的演示中,使用的也是GeoJson格式。

为了测试方便操作,我们先定义个index template:



{
"order": 0,
"index_patterns": [
"test-geo-*"
],
"mappings": {
"properties": {
"id": {
"type": "long"
},
"name": {
"type": "keyword"
},
"location_point": {
"type": "geo_point"
},
"location_shape": {
"type": "geo_shape"
}
}
},
"aliases": {}


注:上面的index template规定了,但凡是以test-geo作为前缀的index,index里面名为location_poit的字段,类型将被映射为geo_point类型,名为location_shape的字段,将会被设置为geo_shape类型。

如下图:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_elasticsearch

 

 

 

2.1 Point类型测试

我们往名为test-geo-01的index中,插入具有如下位置关系的两个经纬度点:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_point_02

 

 使用如下命令插入数据:



###### geo_shape point测试

# 插入数据

POST test-geo-01/_bulk

{"index": {"_id": 1}}

{ "id" : 1,"name": "天安门","location_shape" : {"type": "point", "coordinates": [116.3974,39.90888] }}

{"index": {"_id": 2}}

{ "id" : 2,"name": "故宫博物院","location_shape" : {"type": "point", "coordinates": [116.396971,39.915464] }}


 ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_polygon_03

 

 

 

2.1.1 point与polygon的关系测试

先判断这两个point,与如下图所示的polygon的位置关系:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_distance_04

 

 

 

 注:正如上图所示,查询用的polygon,在关系上就是包含了id=2的doc。因此:

1)当做within或者intersects查询时,查出来的是id=2的doc

2)若做的是disjoint查询,则查询出来的则是id=1的doc

查询命令如下:



GET test-geo-01/_search
{
"query": {
"geo_shape": {
"location_shape": {
"relation": "within",
"shape": {
"type": "polygon",
"coordinates": [[
[116.392379,39.917702],
[116.393752,39.913933],
[116.402206,39.913999],
[116.399632,39.918838],
[116.392379,39.917702]
]]
}
}
}
}
}


 

查询结果截图:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_distance_05

 

 

2.1.2 point与circle的关系测试

接下來判断,index中的两个point,与如下图的circle的位置关系

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_distance_06

 

 

可以看到,circle包含了id=1的点,但不包含id=2的点。

查询如下:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_point_07

 

 

 可以看到,报错了。报错显示不支持circle

由官网也可以看到相关描述:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_shape_08

 

 

 

因此,我们照着官网,我们先修改一下test-geo-01的mapping (新增一个location_shape2的字段



# 修改index的mapping,新增一个location_shape2字段
PUT test-geo-01/_mapping
{
"properties": {
"location_shape2": {
"type": "geo_shape",
"strategy": "recursive"
}
}
}


如下图:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_elasticsearch_09

 

 

 

接着更新test-geo-01的index的数据,把location_shape字段的数据,更新到location_shape2字段里面去

命令:



# 给test-geo-01添加一个location_shape2的字段,并且字段的值跟location_shape字段一致
POST test-geo-01/_bulk
{ "update": { "_id": "1"} }
{ "doc" : {"location_shape2" : {"type": "point", "coordinates": [116.3974,39.90888] }} }
{ "update": { "_id": "2"} }
{ "doc" : {"location_shape2" : {"type": "point", "coordinates": [116.396971,39.915464] }} }


结果如下:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_elasticsearch_10

 

 

 

更新完成之后,test-geo-01这个index中的内容如下:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_polygon_11

 

 

 接下来,我们再在location_shape2字段上,执行circle查询!就可以查询出来了:

命令:



GET test-geo-01/_search
{
"query": {
"geo_shape": {
"location_shape2": {
"relation": "within",
"shape": {
"type": "circle",
"radius": "500m",
"coordinates": [116.3974,39.90888]
}
}
}
}
}


执行结果:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_point_12

 

 

 

 

2.2 MultiPoint类型测试

 往geo-test-02的index中,插入如有如下位置关系的数据

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_shape_13

 

 

命令:



###### geo_shape multipoint测试
# 插入数据
POST test-geo-02/_bulk
{"index": {"_id": 1}}
{ "id" : 1,"name": "天安门,故宫博物院","location_shape" : {"type": "multipoint", "coordinates": [[116.3974,39.90888], [116.396971,39.915464]] }}
{"index": {"_id": 2}}
{ "id" : 2,"name": "普度寺,王府井大厦,凝和庙","location_shape" : {"type": "multipoint", "coordinates": [[116.404653,39.913028], [116.410017,39.914213], [116.402979,39.919019]] }}


通过上面的命令,我们把:1和2两个point,在id=1的doc中,3、4、5这3个point,则是在id=2的doc中

 

2.2.1 multipoint与polygon的关系测试

2.2.1.1 与如下图的polygon的关系判断结果:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_shape_14

 

 由图也可以看出,这个多边形与id=1和id=2的文档中的几个点,都没有任何的包含相交关系,所以无论是做within还是intersects关系查询,都是查询不到这两个doc的,如下图:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_point_15

 

 

而如果是做 disjoint查询,则可以把这两条数据查询出来,如下图:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_shape_16

 

 

 

 

2.2.1.2 与如下图的polygon的关系判断结果:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_polygon_17

 

 

 可以看到,这个多边形,把3和4这两个point给框住了。

 

此时执行within查询,结果如下:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_distance_18

 

 

 

 

执行intersects查询,结果如下:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_point_19

 

 

 

执行disjoint查询,结果如下:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_polygon_20

 

 

 

 

综上,可以得出结论:

对于multipoint而言,

1)当进行within查询时,之后某条doc,只有字段内的所有point都包含在多边形内,才会被查询出来;

2)进行intersects查询时,只要文档中有一个point包含在了多边形内,这条doc就会被查询出来

3)当进行disjoint查询时,只有文档中的所有point都不在多边形内,这条doc才会被查询出来。

 

2.3 Polygon 类型测试

作为测试,我们往test-geo-03的index中,插入具有如下关系的两个polygon:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_polygon_21

 

 

 其中,多边形1插入id=1的doc中,多边形2插入到id=2的doc中,命令如下:



###### geo_shape polygon测试
# 插入数据
POST test-geo-03/_bulk
{"index": {"_id": 1}}
{ "id" : 1,"name": "天安门西站","location_shape" : {"type": "polygon", "coordinates": [[[116.388259,39.908847], [116.388173,39.906033], [116.393666,39.906033], [116.395082,39.908864], [116.388259,39.908847]]] }}
{"index": {"_id": 2}}
{ "id" : 2,"name": "天安门东站","location_shape" : {"type": "polygon", "coordinates": [[[116.400318,39.909078], [116.400318,39.905934], [116.404567,39.906757], [116.400318,39.909078]]] }}


 

2.3.1 polygon与polygon的关系测试

2.3.1.1 下面,将测试这两条doc,与如下图所示的多边形的位置关系

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_shape_22

 

 正如上图所示,查询的多边形,与索引中的两个多边形,都完全不相交。显然,通过within或者intersects查询,都查询不到这两条doc。

但是,若是disjoint查询,则可以查询到这两条数据,如下图:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_elasticsearch_23

 

 

 

2.3.1.2 下面,将测试这两条doc,与如下图所示的多边形的位置关系

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_elasticsearch_24

 

 

那么此时,如果是做within查询,则这两条doc都不会被查询出来,但是如果是做intersects查询,则可以把id=2的doc查询出来;如果是做disjoint查询,则可以把id=1的doc查询出来。

如下图:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_polygon_25

 

 ElasticSearch地理类型字段-geo_point和geo_shape应用示例_elasticsearch_26

 

 

2.3.1.3 测试这两条doc,与如下图所示的多边形的位置关系

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_elasticsearch_27

 

 可以看到,查詢的多边形,把id=2的doc中的多边形给完全包含起来了。

此时显而易见的,若是intersects或者within查询,则都可以把id=2的文档查询出来,而如果是做disjoint查询,则可以把id=1的文档查询出来。

执行结果如下:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_shape_28

 

 ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_polygon_29

 

 

 

2.4 MultiPolygon 类型测试

作为测试,我们往test-geo-04的index中,插入如下位置关系的几个多边形,其中,多边形1和2,插入到id=1的doc中,多边形3、4和5,插入到id=2的doc中

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_elasticsearch_30

 

 

命令如下:



###### geo_shape multipolygon测试
# 插入数据
POST test-geo-04/_bulk
{"index": {"_id": 1}}
{ "id" : 1,"name": "天安门西站,天安门东站","location_shape" : {"type": "multipolygon", "coordinates": [[[[116.388259,39.908847], [116.388173,39.906033], [116.393666,39.906033], [116.395082,39.908864], [116.388259,39.908847]]], [[[116.400318,39.909078], [116.400318,39.905934], [116.404567,39.906757], [116.400318,39.909078]]]] }}
{"index": {"_id": 2}}
{ "id" : 2,"name": "故宫,普度寺,王府井","location_shape" : {"type": "multipolygon", "coordinates": [[[[116.395297,39.916155],[116.395469,39.914295],[116.397357,39.914197],[116.398344,39.914526],[116.398344,39.915842],[116.396756,39.916369],[116.395297,39.916155]]], [[[116.403666,39.914048],[116.403537,39.912551],[116.408043,39.912419],[116.403666,39.914048]]], [[[116.408858,39.9152],[116.408558,39.913407],[116.413193,39.913143],[116.408858,39.9152]]]] }}


ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_polygon_31

 

 

 

 

2.4.1 multipolygon与polygon的关系测试

 

2.4.1.1 测试这两条doc,与如下图所示的多边形的位置关系

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_distance_32

 

 

 由上图可知,查询的多边形,与index中的多边形,都没有相交或者包含关系。

显然,此时若是做within或者intersects查询,则都查询不到这两条doc,如果是做disjoint查询,则可以查询到这两条数据,如下图:

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_shape_33

 

 

 

2.4.1.2 测试这两条doc,与如下图所示的多边形的位置关系

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_shape_34

 

 

如上图,此时,查询的多边形,包含了id=2的doc中的1个多边形,并与其中一个多边形相交,与最后一个多边形不相交。

此时进行within查询,则一个文档也查询不到!

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_point_35

 

 

 

进行intersects查询,则可以把id=2的doc查询出来!

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_polygon_36

 

 

 

进行disjoint查询,则可以把id=1的doc查询出来

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_distance_37

 

 

 

 

2.4.1.3 测试这两条doc,与如下图所示的多边形的位置关系

ElasticSearch地理类型字段-geo_point和geo_shape应用示例_geo_distance_38