Contents

Elasticsearch中的地理空间支持

一 、简介

Elasticsearch 以其全文搜索功能而闻名,但它还具有完整的地理空间支持。

我们可以在上一篇文章 中找到有关设置 Elasticsearch 和入门的更多信息。

让我们看看如何在 Elasticsearch 中保存地理数据,以及如何使用地理查询来搜索这些数据。

2. 地理数据类型

要启用地理查询,我们需要手动创建索引的映射并显式设置字段映射。

为地理类型设置映射时,动态映射将不起作用。

Elasticsearch 提供了两种表示地理数据的方法:

  1. 使用地理点字段类型的经纬度对
  2. GeoJSON中使用geo-shape字段类型定义的复杂形状

让我们更深入地了解上述每个类别:

2.1. 地理点数据类型

地理点字段类型接受可用于:

  • 查找中心点一定距离内的点
  • 查找框或多边形内的点
  • 按地理位置或与中心点的距离聚合文档
  • 按距离对文档进行排序

以下是用于保存地理点数据的字段的示例映射:

PUT /index_name
{
    "mappings": {
        "TYPE_NAME": {
            "properties": {
                "location": { 
                    "type": "geo_point" 
                } 
            } 
        } 
    } 
}

正如我们从上面的示例中看到的,location字段的typegeo_point。因此,我们现在可以在位置字段的location中提供经纬度对。

2.2. 地理形状数据类型

geo_point不同,geo_shape提供了保存和搜索复杂形状(如多边形和矩形)的功能。当我们要搜索包含除地理点以外的形状的文档时,必须使用geo_shape数据类型。

让我们看一下地理形状数据类型的映射:

PUT /index_name
{
    "mappings": {
        "TYPE_NAME": {
            "properties": {
                "location": {
                    "type": "geo_shape"
                }
            }
        }
    }
}

最新版本的 Elasticsearch 将提供的地理形状分解为三角形网格。根据官方文档 ,这提供了近乎完美的空间分辨率。

3. 保存地理点数据的不同方法

3.1. 纬度经度对象

PUT index_name/index_type/1
{
    "location": { 
        "lat": 23.02,
        "lon": 72.57
    }
}

在这里,地理点location被保存为一个以经纬度为键的对象。

3.2. 纬度经度对

{
    "location": "23.02,72.57"
}

在这里,location表示为纯字符串格式的纬度-经度对。请注意,字符串格式的经纬度顺序。

3.3. 地理哈希

{
    "location": "tsj4bys"
}

如上例所示,我们还可以以地理哈希的形式提供地理点数据。我们可以使用在线工具 将经纬度转换为地理哈希。

3.4. 经度纬度数组

{
    "location": [72.57, 23.02]
}

当纬度和经度作为数组提供时,经纬度的顺序颠倒。最初,纬度-经度对用于字符串和数组中,但后来为了匹配GeoJSON 使用的格式,它被颠倒了。

4. 保存地理形状数据的不同方法

4.1. point

POST /index/type
{
    "location" : {
        "type" : "point",
        "coordinates" : [72.57, 23.02]
    }
}

在这里,我们尝试插入的地理形状类型是一个point。请看一下location字段,我们有一个由字段typecoordinates组成的嵌套对象。这些元字段帮助 Elasticsearch 识别地理形状及其实际数据。

4.2. linestring

POST /index/type
{
    "location" : {
        "type" : "linestring",
        "coordinates" : [[77.57, 23.02], [77.59, 23.05]]
    }
}

在这里,我们插入linestring地理形状。linestring的坐标由两点组成,即起点和终点。LineString地理形状对于导航用例非常有帮助。

4.3. polygon

POST /index/type
{
    "location" : {
        "type" : "polygon",
        "coordinates" : [
            [ [10.0, 0.0], [11.0, 0.0], [11.0, 1.0], [10.0, 1.0], [10.0, 0.0] ]
        ]
    }
}

在这里,我们插入polygon地理形状。请看一下上面例子中的坐标,polygon中的firstlast坐标应该总是匹配,即一个封闭的多边形。

Elasticsearch 还支持其他 GeoJSON 结构。其他支持的格式的完整列表如下:

  • MultiPoint
  • MultiLineString
  • MultiPolygon
  • GeometryCollection
  • Envelope
  • Circle

我们可以在官方 ES 网站 上找到上述支持格式的示例。

对于所有结构,内部typecoordinates都是必填字段。此外,由于其复杂的结构,目前在 Elasticsearch 中无法对地理形状字段进行排序和检索。因此,检索地理字段的唯一方法是从源字段。

5. ElasticSearch 地理查询

现在,我们知道如何插入包含地理形状的文档,让我们深入研究使用地理形状查询来获取这些记录。但在我们开始使用地理查询之前,我们需要以下 maven 依赖项来支持地理查询的 Java API:

<dependency>
    <groupId>org.locationtech.spatial4j</groupId>
    <artifactId>spatial4j</artifactId>
    <version>0.7</version> 
</dependency>
<dependency>
    <groupId>com.vividsolutions</groupId>
    <artifactId>jts</artifactId>
    <version>1.13</version>
    <exclusions>
        <exclusion>
            <groupId>xerces</groupId>
            <artifactId>xercesImpl</artifactId>
        </exclusion>
    </exclusions>
</dependency>

我们也可以在Maven 中央存储库 中搜索上述依赖项。 Elasticsearch 支持不同类型的地理查询,它们如下:

5.1.地理形状查询

这需要geo_shape映射。

geo_shape类型类似,geo_shape使用 GeoJSON 结构来查询文档。

下面是一个示例查询,用于获取位于给定左上角和右下角坐标内的所有文档:

{
    "query":{
        "bool": {
            "must": {
                "match_all": {}
            },
            "filter": {
                "geo_shape": {
                    "region": {
                        "shape": {
                            "type": "envelope",
                            "coordinates" : [[75.00, 25.0], [80.1, 30.2]]
                        },
                        "relation": "within"
                    }
                }
            }
        }
    }
}

在这里,*relation *确定了在搜索时使用的空间关系运算符。

以下是支持的运算符列表:

  • INTERSECTS –(默认)返回其geo_shape字段与查询几何相交的
  • DISJOINT – 检索其geo_shape字段与查询几何没有任何共同点的
  • WITHIN – 获取geo_shape字段在查询几何范围内的
  • CONTAINS – 返回其geo_shape字段包含查询几何的

同样,我们可以使用不同的 GeoJSON 形状进行查询。

上述查询的 Java 代码如下:

Coordinate topLeft = new Coordinate(74, 31.2);
Coordinate bottomRight = new Coordinate(81.1, 24);
GeoShapeQueryBuilder qb = QueryBuilders.geoShapeQuery("region", new EnvelopeBuilder(topLeft, bottomRight).buildGeometry());
qb.relation(ShapeRelation.INTERSECTS);

5.2. 地理边界框查询

地理边界框查询用于根据点位置获取所有文档。下面是一个示例边界框查询:

{
    "query": {
        "bool" : {
            "must" : {
                "match_all" : {}
            },
            "filter" : {
                "geo_bounding_box" : {
                    "location" : {
                        "bottom_left" : [28.3, 30.5],
                        "top_right" : [31.8, 32.12]
                    }
                }
            }
        }
    }
}

上述边界框查询的Java代码如下:

QueryBuilders
  .geoBoundingBoxQuery("location").setCorners(31.8, 30.5, 28.3, 32.12);

地理边界框查询支持与geo_point数据类型类似的格式。可以在官方网站 上找到支持格式的示例查询。

5.3. 地理距离查询

地理距离查询用于过滤所有带有指定点范围的文档。

这是一个示例geo_distance查询:

{
    "query": {
        "bool" : {
            "must" : {
                "match_all" : {}
            },
            "filter" : {
                "geo_distance" : {
                    "distance" : "10miles",
                    "location" : [31.131,29.976]
                }
            }
        }
    }
}

这是上述查询的 Java 代码:

QueryBuilders
  .geoDistanceQuery("location")
  .point(29.976, 31.131)
  .distance(10, DistanceUnit.MILES);

与*geo_point 类似,*地理距离查询也支持多种格式的位置坐标传递。有关支持格式的更多详细信息,请访问官方网站

5.4. 地理*Polygon *查询

用于过滤所有点位于给定点多边形内的记录的查询。

让我们快速浏览一个示例查询:

{
    "query": {
        "bool" : {
            "must" : {
                "match_all" : {}
            },
            "filter" : {
                "geo_polygon" : {
                    "location" : {
                        "points" : [
                        {"lat" : 22.733, "lon" : 68.859},
                        {"lat" : 24.733, "lon" : 68.859},
                        {"lat" : 23, "lon" : 70.859}
                        ]
                    }
                }
            }
        }
    }
}

在此查询的 Java 代码中:

List<GeoPoint> allPoints = new ArrayList<GeoPoint>(); 
allPoints.add(new GeoPoint(22.733, 68.859)); 
allPoints.add(new GeoPoint(24.733, 68.859)); 
allPoints.add(new GeoPoint(23, 70.859));
QueryBuilders.geoPolygonQuery("location", allPoints);

Geo Polygon Query 还支持以下格式:

  • lat-long 作为一个数组:[lon, lat]
  • lat-long 作为字符串:“lat, lon”
  • 哈希

为了使用此查询,geo_point数据类型是必需的。