Elasticsearch中的地理空间支持
一 、简介
Elasticsearch 以其全文搜索功能而闻名,但它还具有完整的地理空间支持。
我们可以在上一篇文章 中找到有关设置 Elasticsearch 和入门的更多信息。
让我们看看如何在 Elasticsearch 中保存地理数据,以及如何使用地理查询来搜索这些数据。
2. 地理数据类型
要启用地理查询,我们需要手动创建索引的映射并显式设置字段映射。
为地理类型设置映射时,动态映射将不起作用。
Elasticsearch 提供了两种表示地理数据的方法:
- 使用地理点字段类型的经纬度对
- GeoJSON中使用geo-shape字段类型定义的复杂形状
让我们更深入地了解上述每个类别:
2.1. 地理点数据类型
地理点字段类型接受可用于:
- 查找中心点一定距离内的点
- 查找框或多边形内的点
- 按地理位置或与中心点的距离聚合文档
- 按距离对文档进行排序
以下是用于保存地理点数据的字段的示例映射:
PUT /index_name
{
"mappings": {
"TYPE_NAME": {
"properties": {
"location": {
"type": "geo_point"
}
}
}
}
}
正如我们从上面的示例中看到的,location字段的type是geo_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字段,我们有一个由字段type和coordinates组成的嵌套对象。这些元字段帮助 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中的first和last坐标应该总是匹配,即一个封闭的多边形。
Elasticsearch 还支持其他 GeoJSON 结构。其他支持的格式的完整列表如下:
- MultiPoint
- MultiLineString
- MultiPolygon
- GeometryCollection
- Envelope
- Circle
我们可以在官方 ES 网站 上找到上述支持格式的示例。
对于所有结构,内部type和coordinates都是必填字段。此外,由于其复杂的结构,目前在 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数据类型是必需的。