万字长文:一文彻底搞懂Elasticsearch中Geo数据类型

2022-08-29  本文已影响0人  醉鱼java

在我们使用elasticsearch创建索引时,经常会遇到一种字段类型为geo_point的数据类型,该类型的字段接收经纬度的值,那么geo_point类型的字段可以用来做什么?

通过阅读本文,可以学到以上知识点,学到就是赚到,还不快快开始阅读吧

环境

帮助信息

Geopoint(点)

如何指定一个地理位置信息

Elasticsearch中geo_point类型的数据字段支持以下5种方式录入地理位置数据,分别如下:

首先我们还是先定义一个索引,创建一个数据类型为geo_point的字段

geo_point 类型字段支持的参数

在脚本中使用geopoints

当在脚本中访问geopoint类型的值时,该值做为GeoPoint对象返回,可以使用如下方式读取

def geopoint = doc['location'].value;
def lat      = geopoint.lat;
def lon      = geopoint.lon;

但是更推下如下这样读取

def lat      = doc['location'].lat;
def lon      = doc['location'].lon;

Geoshape(形状)

对几何形状进行索引和查询(矩形和多边形),当索引的数据和查询包含除了点以外的形状时应该使用geo_shape类型

通过上面这俩类型的描述,我们可以得出一个结论,就是geo_point表示一个点,geo_shape表示多个点连接线组成的形状

支持的映射参数

geo_shape 映射将GeoJSON几何对象映射到geo_shape,要是用该映射类型,必须显式的设置,这句话意思就是我们使用该数据类型的时候必须明确指定索引类型

创建索引

PUT /example
{
  "mappings": {
    "properties": {
      "location": {
        "type": "geo_shape"
      }
    }
  }
}

输入类型

地理位置形状信息可以使用GeoJSON或者WKT表示,下面是GeoJSONWKTElasticsearch中类型的对应关系

GeoJSON Type WKT Type Elasticsearch Type description
Point POINT Point 单一的经纬度坐标
LineString LINTSTRING lingstring 给定两个点或多个点组成的任意直线
Polygon POLYGON polygon 封闭的多边形,第一个点和最后一个点必须匹配,也就是n+1个点形成的n边多边形,至少4个顶点
MultiPoint MULTIPOINT multipoint 一组不相连但是可能相关的点
MultiLineString MULTILINESTRING multilinestring 单独的行字符串组成的数组
MultiPolygon MULTIPOLYGON multipolygon 单独的多边形数组
GeometryCollection GEOMETRYCOLLECTION geometrycollection 与multi开头的类型类似的一个GeoJSON形状,但是多个类型可以共同存在比如(Point和LineString同时存在)
N/A BBOX envelope 仅指定左上角和右下角两个点组成的边框或包

对于所有的类型都应该有子类型和字段坐标,GeoJSONWKT以及Elasticsearch中,坐标信息都是(经度,纬度),这与很多的地理信息API是不同的,地图地理信息API通常使用(纬度,经度),这一点是需要注意的一个地方,下面我将针对以上集中类型情况分别做一个写入数据的测试

排序和检索

由于形状的输入结构复杂度和索引表示形状的复杂性,目前不能对索引进行排序或者直接检索他们的字段,目前geo_shape只能通过_source来检索

基于地理位置信息的范围查询

参考:https://kucw.github.io/blog/2019/12/elasticsearch-geo-point/

Geo-bounding box query(矩形过滤)

https://www.elastic.co/guide/en/elasticsearch/reference/8.1/query-dsl-geo-bounding-box-query.html

可以对数据类型为geo_pointgeo_shape的值在矩形边框内进行检索,找出落在矩形内的点

Geopoint 值的匹配

下面是一个演示一个点在一个矩形边框范围的一个例子,首先还是创建一个索引

PUT /my_locations/_doc/1
{
  "desc":"故宫博物院",
  "pin": {
    "location": {
      "lat": 39.92375,
      "lon": 116.40348
    }
  }
}
GET my_locations/_search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": {
        "geo_bounding_box": {
          "pin.location": {
            "top_left": {
              "lat": 39.93872,
              "lon": 116.37958
            },
            "bottom_right": {
              "lat": 39.90924,
              "lon": 116.42475
            }
          }
        }
      }
    }
  }
}

查看返回结果可以获取到刚才插入的故宫博物院地点信息

Geoshape 值的匹配

Geo-distance query(圆形过滤)

https://www.elastic.co/guide/en/elasticsearch/reference/8.1/query-dsl-geo-distance-query.html#query-dsl-geo-distance-query

以给定位置作圆点,画一个给定距离的圆,以匹配圆范围内的文档数据,下面我们用例子来说明这个圆形过滤

扩展知识

在上文中我们也看到了,录入测试数据的方式支持多种,在使用过滤器查询数据的时候同样如此可以使用多种方式进行过滤,如下简单举例几种,比如

过滤查询接收的参数说明

geo_distance的过滤请求可以匹配一篇文档中的多个地点值,只要有一个地点值匹配,该文档就会被返回

ignore_unmapped字段的值设置为true,查询时如果字段不匹配会忽略报错,返回空文档;如果设置为false,遇到不匹配的字段会 抛出异常

如下示例,匹配pin.location1字段,我们知道该字段在索引中是不存在的,所以当ignore_unmapped设置为true时返回空文档,设置为false时抛出异常

GET my_locations,my_geoshapes/_search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": {
        "geo_distance": {
          "ignore_unmapped":true,
          "_name":"zuiyuquery",
          "distance": "5km",
          "pin.location1": "wx4g0gfw22x"
        }
      }
    }
  }
}

Geo-polygon query(自定义多边形过滤)

https://www.elastic.co/guide/en/elasticsearch/reference/8.1/query-dsl-geo-polygon-query.html

多边形过滤,顾名思义就是查询条件定义个多边形,返回该多边形区域内的点的文档信息

官网已经说明es7.12版本开始废弃,推荐使用geoshape,参考后文的 geoshape query

该查询接收的参数有如下

需要注意的是,查询的字段需要设置为geo_point类型,同样ignore_unmapped字段的值设置为true,查询时如果字段不匹配会忽略报错,返回空文档;如果设置为false,遇到不匹配的字段会 抛出异常

如下是对pin.location字段定义一个三条边的形状的过滤查询语句,三个点顺序【1,2,3】排列,此时该 查询会返回故宫的信息,但是刚才定的西单大悦城的信息就不会返回

GET my_locations/_search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": {
        "geo_polygon": {
          "pin.location": {
            "points": [
              { "lat": 39.93506, "lon": 116.41043 },
              { "lat": 39.91101, "lon": 116.42383 },
              { "lat": 39.92217, "lon": 116.35836 }
            ]
          }
        }
      }
    }
  }
}

同样的自定义查询语句也可以接收【字符串,数组,geohash】的地点信息

Geoshape query(形状查询)

https://www.elastic.co/guide/en/elasticsearch/reference/8.1/query-dsl-geo-shape-query.html

支持geo_shape和geo_point两种字段类型的过滤查询

形状查询,支持传入一个完整图形形状的数据或者提前写入的图形形状数据,首先还是先演示一下如何使用传入的图形形状数据检索

输入图形形状查询

预置图形形状查询

预置就是提前设置好图形的数据,保存在索引中,查询时根据提前定义好图形的名称来过滤数据,首先它有如下几个参数

查询示例如下

PUT /shapes
{
  "mappings": {
    "properties": {
      "location": {
        "type": "geo_shape"
      }
    }
  }
}

PUT /shapes/_doc/oneline
{
  "location": {
    "type": "envelope",
    "coordinates" : [[116.0, 40.0], [ 117.0, 39.0]]
  }
}

GET /example/_search
{
  "query": {
    "bool": {
      "filter": {
        "geo_shape": {
          "location": {
            "indexed_shape": {
              "index": "shapes",
              "id": "oneline",
              "path": "location"
            }
          }
        }
      }
    }
  }
}

基于地理位置信息或者到中心点距离的聚合统计

Geo网格聚合

https://www.elastic.co/guide/en/elasticsearch/reference/8.1/search-aggregations-bucket-geohashgrid-aggregation.html

网格聚合,将字段类型为geo_point或者geo_shape的数据划分为一个个的单元格,也就是一个个的单元格的桶中,既然是单元格那也就是有大有小,也就是高精确度与低精确度值,精度值的范围为1-121表示精确度最低,范围最大,12表示精确度最高但是范围也最小,甚至可能出现百万个以上的桶的聚合,所以这个是需要注意的地方,如果精确度要求比较高的话,可以使用geo_bounding box query过滤缩小聚合范围。网格聚合通过geohash来实现,通过指定聚合类型为geohash_grid来实现网格聚合,下面是演示demo

geo_point

geo_shape

geo_shapegeo_point类似

支持参数

圆点距离聚合

https://www.elastic.co/guide/en/elasticsearch/reference/8.1/search-aggregations-bucket-geodistance-aggregation.html

给定一圆点,以给出距离为半径画圆,将落在圆内的数据按照到原点的距离聚合,我们可以定一个起始值,一组范围的值来规定聚合桶的范围,下面跟我一起来看下如何使用到圆点距离的聚合

将地理位置信息加入到分数计算中

https://www.elastic.co/guide/en/elasticsearch/reference/8.1/query-dsl-function-score-query.html

满足以上三点就可以加入到分数计算中,gauss 使用高斯函数来衰减,具体参考官网,后面专门出一期衰减函数的文章

GET gugong_map/_search
{
  "query": {
    "function_score": {
      "gauss": {
        "location": {
          "origin": [116.385728,39.870814], 
          "scale": "3000m",// origin + offset,衰减率,也就得分衰减的速度
          "offset": "3000m",// 3000m以内的文档得分不处理,3000m以外的文档得分慢慢衰减  
          "decay": 0.5  // 从 origin 衰减到 scale 所得的评分_score,默认为0.5         
        }
      }
    }
  }
}

基于距离信息的排序

https://www.elastic.co/guide/en/elasticsearch/reference/8.1/sort-search-results.html#geo-sorting

需要指定圆点的坐标信息,根据到原点的距离进行排序,比如如下语句,查询name字段带的,根据到北京南站(116.385728,39.870814)的距离升序输出,坐标信息,与geo_point支持的方式相同,可以为数组字符串对象geohash

GET gugong_map/_search
{
  "sort" : [
    {
      "_geo_distance" : {
          "location" : [116.385728,39.870814],
          "order" : "asc",
          "unit" : "km",
          "mode" : "min",
          "distance_type" : "arc",
          "ignore_unmapped": true
      }
    }
  ],
  "query" : {
    "term" : { "name" : "站" }
  }
}

总结

在上面的章节中,我们由浅入深的学习了geo_pointgeo_shape两种geo数据类型,以及如何通过对这两字段进行检索,聚合排序,相信大家读到这也基本有了一个概念了,再深点还是要去看官方文档了,不过实践是检验真理的唯一标准,多实操吧,总没有错,加油!!!

如果有写的不对的地方欢迎指出哦!!!共同进步才能走的更远!!!

上一篇 下一篇

猜你喜欢

热点阅读