Elasticsearch(022):es常见的字段映射类型之地理形状类型(geo_shape、多边的复杂的地址形状)
概念
在上个小节,我们学习了geo_point
的数据类型。
这一小节我们来学习geo_shape
数据类型,它有助于索引和搜索 任意地理形状,例如矩形和多边形。当正在索引的数据或正在执行的查询包含除点以外的其他形状时,应使用它。
通过上面的概念描述,我们可以得出geo_point
和geo_shape
的最大区别是: geo_point
表示一个点;geo_shape
则表示有多个点连成线组成的形状。 学过数学的肯定很容易理解。
实际开发中,如果我们的地理坐标是一个地理形状,则可以使用地理形状数据类型进行插入、查询文档。比如说学校、大商场这种面积比较大的地理坐标,都需要geo_shape
来表示。
- mapping params
geo_shape
映射将geo_json
几何对象映射到geo_shape
类型。要启用它,用户必须将字段显式映射到geo_shape
类型。
地理形状数据类型的参数选项
选项 | 说明 | 默认值 |
---|---|---|
tree | 将使用的PrefixTree实现的名称:GeohashPrefixTree为geohash,QuadPrefixTree为quadtree。 | geohash |
precision | 可以使用此参数代替tree_levels来为tree_levels参数设置适当的值。该值指定所需的精度,Elasticsearch将计算最佳的tree_levels值以兑现该精度。该值应为数字,后跟可选的距离单位。有效距离单位包括:英寸,英寸,码,码,英里,英里,公里,公里,米,米,厘米,厘米,毫米,毫米。 | meters |
tree_levels | PrefixTree使用的最大层数。这可用于控制形状表示的精度,从而控制索引多少项。默认为所选PrefixTree实现的默认值。由于此参数要求对底层实现有一定程度的了解,因此用户可以改用precision参数。但是,Elasticsearch仅在内部使用tree_levels参数,即使您使用precision参数,这也是通过映射API返回的结果 | 50m |
strategy | 策略参数定义了如何在索引和搜索时表示形状的方法。它还会影响可用的功能,因此建议让Elasticsearch自动设置此参数。有两种可用的策略:递归和术语。术语策略仅支持点类型(points_only参数将自动设置为true),而递归策略则支持所有形状类型。(重要:请参见前缀树以获取更多详细信息) | recursive |
distance_error_pct | 用作PrefixTree关于其精度的提示。默认值为0.025(2.5%),最大支持值为0.5。性能注意:如果明确定义了precision或tree_level定义,则此值将默认为0。这样可以保证映射中定义的级别的空间精度。对于低误差的高分辨率形状(例如,误差小于0.001的1m大形状),这可能会导致大量内存使用。为了提高索引性能(以查询准确性为代价),显式定义tree_level或precision以及合理的distance_error_pct,请注意,较大的形状将具有更大的误报率。 | 0.025 |
orientation | (可选)定义如何解释多边形/多多边形的顶点顺序。此参数定义两个坐标系规则(右手或左手)之一,可以用三种不同的方式来指定每个规则。1.右手规则:右,逆时针,逆时针2. 2.左手规则:左,顺时针,顺时针。默认方向(逆时针)符合OGC标准,该标准以逆时针顺序定义外环顶点,而内环顶点(孔)以顺时针顺序定义。在geo_shape映射中设置此参数可显式设置geo_shape字段的坐标列表的顶点顺序,但可以在每个单独的GeoJSON文档中覆盖。 | ccw |
points_only | 将此选项设置为true(默认为false)可仅为点形状配置geo_shape字段类型(注意:尚不支持多点)。当已知仅索引点时,这将优化geohash和四叉树的索引和搜索性能。目前,无法对geo_point字段类型执行geo_shape查询。此选项通过改善geo_shape字段上的点性能来弥合差距,从而使geo_shape查询在仅点字段上是最佳的。 | false |
ignore_malformed | 如果为true,则会忽略格式错误的geojson形状。如果为假(默认),则格式错误的geojson形状会引发异常并拒绝整个文档。 | false |
- 前缀树
为了有效地表示索引中的形状,使用PrefixTree的实现将Shapes转换为代表网格正方形的一系列哈希(通常称为“栅格”)。树的概念来自这样一个事实,即PrefixTree使用多个网格层,每个网格层的精度更高,以表示地球。
可以认为这是在更高的缩放级别下增加地图或图像的细节级别。
es中提供了多个PrefixTree实现。
-
GeohashPrefixTree 对网格正方形使用geohash。Geohash是交错的纬度和经度的位的base32编码的字符串。
因此,哈希越长,它越精确。添加到geohash的每个字符代表另一个树级别,并为geohash增加5位精度。Geohash表示一个矩形区域,并具有32个子矩形。Elasticsearch中的最大级别数为24。 -
QuadPrefixTree 对网格正方形使用四叉树。与geohash相似,四叉树将纬度和经度的比特交织在一起,结果哈希被置位。
四叉树中的树级别代表此位集中的2位,每个坐标一个。Elasticsearch中四叉树的最大级别数为50。 -
空间策略
PrefixTree实现依赖于SpatialStrategy来将提供的Shape分解为近似的网格正方形。每种策略都回答以下问题:
- 可以索引哪种类型的形状?
- 可以使用哪些类型的查询操作和形状?
- 每个字段是否支持多个Shape?
提供以下策略实施(具有相应的功能):
策略 | 支持的形状 | 支持的查询 | 多个形状 |
---|---|---|---|
recursive | All | INTERSECTS, DISJOINT, WITHIN, CONTAINS(相交,不相交,内含) | Yes |
term | Points | INTERSECTS (相交) | Yes |
- 准确性
Geo_shape
不提供100%的准确性,并且取决于它的配置方式,它可能对INTERSECTS
,WITHIN
和CONTAINS
查询返回一些误报,而对DISJOINT
查询返回一些误报。
为了减轻这种情况,为tree_levels
参数选择适当的值并相应地调整期望很重要。例如,一个点可能在特定网格像元的边界附近,因此可能与仅与它紧邻的像元匹配的查询不匹配-即使形状非常接近该点。
示例使用
比如说我们有一些大商场的地理信息。我们想要实现在某个地理位置附近多少米范围有哪些大商场?
1. mapping定义
PUT my_index
{
"mappings": {
"docs": {
"properties": {
"name": {"type": "text"},
"location": {
"type": "geo_shape"
}
}
}
}
}
2. 添加数据
PUT my_index/docs/1000
{
"name": "中心商场",
"location": {
"type": "point",
"coordinates": [121.392496,31.245827]
}
}
PUT my_index/docs/1001
{
"name": "城北商场",
"location": {
"type": "point",
"coordinates": [121.392496,31.30]
}
}
PUT my_index/docs/1002
{
"name": "城南商场",
"location": {
"type": "point",
"coordinates": [121.392496,31.013]
}
}
PUT my_index/docs/1003
{
"name": "城西商场",
"location": {
"type": "point",
"coordinates": [121.821,31.245827]
}
}
PUT my_index/docs/1004
{
"name": "城东商场",
"location": {
"type": "point",
"coordinates": [122.392496,31.245827]
}
}
上例中大量的方括号可能看起来让人困惑,不过实际上 GeoJSON
的语法非常简单.
-
用一个数组表示
经纬度
坐标点[lon,lat]
-
一组坐标点放到一个数组来表示一个多边形
[[lon,lat],[lon,lat], ... ]
-
一个多边形( polygon )形状可以包含多个多边形;第一个表示多边形的外轮廓,后续的多边形表示第一个多边形内部的空洞
[
[[lon,lat],[lon,lat], ... ], # main polygon
[[lon,lat],[lon,lat], ... ], # hole in main polygon
...
]
3. 查询
比如说查询 查询指定位置10KM范围内的大商场信息数据。
GET my_index/docs/_search
{
"query": {
"bool": {
"must": {
"match_all": {}
},
"filter": {
"geo_shape": {
"location": {
"shape": {
"type": "circle",
"radius": "10km",
"coordinates": [121.392496, 31.3]
}
}
}
}
}
}
}
返回结果集
{
"took": 4,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1,
"hits": [
{
"_index": "my_index",
"_type": "docs",
"_id": "1001",
"_score": 1,
"_source": {
"name": "城北商场",
"location": {
"type": "point",
"coordinates": [
121.392496,
31.3
]
}
}
},
{
"_index": "my_index",
"_type": "docs",
"_id": "1000",
"_score": 1,
"_source": {
"name": "中心商场",
"location": {
"type": "point",
"coordinates": [
121.392496,
31.245827
]
}
}
}
]
}
}
发现有两个商场符合条件。
后面有geo_shape更复杂的应用,在这里我们的重点和学习geo_shape
这种类型概念和简单使用。