1

一、背景

最近有个需求,需要获取某个位置附近的楼盘,比如:获取当前用户所在位置,方圆100km千米之内的楼盘信息。经过调研,发现可以使用 redismongodbelasticsearch等实现。经过考虑之后决定使用es来实现,此处简单记录下esgeo方面api的使用。

二、geo数据类型

es中存在2种地理位置数据类型,geo_pointgeo_shapees无法自动识别这种数据类型,需要在创建mapping的时候,自己手动指定。

1、geo_point

geo_point使用的是经纬度的坐标点,可以计算落在某个矩形内的点、以某个点为半径(圆)的点、某个多边形内的点(弃用了)、排序、聚合等操作。

geo_point

2、geo_shape

geo_shape表示的是一个复杂的图形,使用的是GeoJSON的格式来表示复杂的图形。比如:我们要表示一个图书馆的坐标位置,如果图书馆占的位置比较大,用一个点表示可能就不准了,此时就可以使用geo_shape来表示了。

不过这种数据类型也有缺点:比如不能排序等等(因为是多边形的点)。

三、此处对geo_point类型实战

1、背景

image-20210421161252713

1、图中的 ① ② ③ ④ 表示是需要加入到 es 中的建筑物

建筑物坐标距离地点相隔距离解释
上海站121.462311,31.256224上海站
上海静安洲际酒店121.460186,31.251281上海站586.24米上海站和该酒店大概像个586.24米
交通公园121.473939,31.253531上海站1146.45米
万业远景大厦121.448215,31.26229上海站1501.74米

2、图中的圆形、正方形、多边形表示后期需要使用 es 查询出来里面里面的地点。

3、图中的短小的箭头➡️表示边界。

2、插入地点数据

1、创建索引

PUT /geo_index
{
  "settings": {
    "number_of_shards": 2,
    "number_of_replicas": 2,
    "analysis": {
      "analyzer": {
        "default": {
          "type": "ik_max_word"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "building_name": {
        "type": "keyword"
      },
      "location": {
        // 此处手动指定数据类型
        "type": "geo_point"
      }
    }
  }
}

注意⚠️:

1、在索引中,我们自己指定来location字段的类型为geo_point类型。

2、building_name的字段类型为keyword表示不分词,这个字段只是为了测试,没有什么用。

3、不用指定索引的type,在es7中只有一个type。

2、插入地理位置数据

POST _bulk
{"create":{"_index":"geo_index","_id":1}}
{"building_name":"上海站","location":{"lat":31.256224,"lon":121.462311}}
{"create":{"_index":"geo_index","_id":2}}
{"building_name":"上海静安洲际酒店","location":"POINT (121.460186 31.251281)"}
{"create":{"_index":"geo_index","_id":3}}
{"building_name":"交通公园","location":"31.253531,121.473939"}
{"create":{"_index":"geo_index","_id":4}}
{"building_name":"万业远景大厦","location":[121.448215,31.26229]}

注意⚠️:

1、从上面可知:地理位置的插入的格式可以存在4种方式。

1、 {"lat":"","lon":""}
2、 "lat,lon"
3、 [Well-Known Text](https://docs.opengeospatial.org/is/12-063r5/12-063r5.html) "POINT (lon lat)"
4、 [lon,lat]
5、 还有一种 geohash 的格式

需要注意的是:使用 数组/Well-Known-Text 的格式的时候,经纬度是反过来的。

3、执行检索

1、geo_bounding_box 矩形过滤

矩形位置大概坐标

从上图可知左上角和右下方的坐标分别为 (121.444075,31.265395)和(121.468417,31.253845)

执行查询,应该可以查询出 上海站万业远景大厦

1、es查询语句
GET /geo_index/_search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": {
        "geo_bounding_box": {
          "location": {
            "top_left": {
              "lat": 31.265395,
              "lon": 121.444075
            },
            "bottom_right": {
              "lat": 31.253845,
              "lon": 121.468417
            }
          }
        }
      }
    }
  }
}
2、查询结果

矩形查询结果

从图中可以看到,查询出来了 上海站万业远景大厦,结果是正确的。

2、geo_distance 圆形查询

这个是距离查询,是以某个点向周围扩算的距离范围。

image-20210421161252713

在上一步的背景中,我们知道上海站的坐标(121.462311,31.256224),同时也知道了上海站距离各个周边的距离有多远,此处我们以上海站为中心,查询方圆600米的建筑物,可知只有上海静安洲际酒店上海站符合。

1、es查询语句
GET /geo_index/_search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": {
        "geo_distance": {
          "distance": "600m",
          "distance_type": "arc", 
          "_name":"optional_name",
          "location": {
            "lat": 31.256224,
            "lon": 121.462311
          }
        }
      }
    }
  }
}

注意⚠️:

1、distance_type的值存在2个 arcplane

  • arc:默认的方式,这种方式计算比较精确,但是比较慢,是把地球当作一个球体计算。
  • plane:这种方式计算比较快,但是可能不怎么准,越靠近赤道越准,是把地球当成平坦的进行计算。

2、distance后面可用的单位有kmmcmmmnmimiydftin

image-20210421164716919

2、查询结果

距离查询

3、geo_distance 查询并排序,返回距离相隔多少米

1、es 查询语句
GET /geo_index/_search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": {
        "geo_distance": {
          "distance": "600m",
          "distance_type": "arc",
          "_name": "optional_name",
          "location": {
            "lat": 31.256224,
            "lon": 121.462311
          }
        }
      }
    }
  },
  "sort": [
    {
      "_geo_distance": {
        "location": {
          "lat": 31.256224,
          "lon": 121.462311
        },
        "order": "desc",
        "unit": "m",
        "distance_type": "arc"
      }
    }
  ]
}

注意⚠️:

1、sort执行排序。

2、查询结果

距离排序

4、geo_distance聚合

需求:

1. 统计`上海站`500米之内的建筑物有多少。
 2. 统计`上海站`500-1000米之内的建筑物有多少。
 3. 统计`上海站`大于1000米的建筑物有多少。

1、es查询语句

GET /geo_index/_search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      }
    }
  },
  "aggs": {
    "rings_around_amsterdam": {
      "geo_distance": {
        "field": "location",
        "origin": {
          "lat": 31.256224,
          "lon": 121.462311
        },
        "unit": "m",
        "distance_type": "arc",
        "ranges": [
          {"to": 500,"key": "first"},
          {"from": 500,"to": 1000,"key": "second" },
          {"from": 1000,"key": "third"}
        ],
        "keyed": true
      }
    }
  }
}
2、查询结果

聚合

从上图中可以看到:

1、距离上海站在 0-500米之间的建筑物只有1个。

2、距离上海站在 500-1000之间的建筑物有1个。

3、距离上海站在 1000以上的有2个。

5、geo-polygon-多边形查询(过时)

在 es7.12 中已经过时了,推荐使用 geo_shape来实现

多边形查询过时了,推荐使用geo_shape查询

6、一个综合案例

1、需求:

1、查询语句query,查询出所有的数据,并过滤出以上海站为中心的3km内的所有的建筑物。

2、aggs,用于统计出上海在 500米以内、500-1000米、1000米之外的建筑物数量。

3、sort用于排序。

4、post_filter用于将结果缩小到上海站1000米以内。

2、查询结果
GET /geo_index/_search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
 // 过滤出上海站周围3km范围内的建筑物
      "filter": {
        "geo_distance": {
          "distance": "3km",
          "distance_type": "arc", 
          "_name":"optional_name",
          "location": {
            "lat": 31.256224,
            "lon": 121.462311
          }
        }
      }
    }
  },
 // 聚合上海站周围的建筑物的数量
  "aggs": {
    "rings_around_amsterdam": {
      "geo_distance": {
        "field": "location",
        "origin": {
          "lat": 31.256224,
          "lon": 121.462311
        },
        "unit": "m",
        "distance_type": "arc",
        "ranges": [
          {"to": 500,"key": "first"},
          {"from": 500,"to": 1000,"key": "second" },
          {"from": 1000,"key": "third"}
        ],
        "keyed": true
      }
    }
  },
// 对查询到的结果排序,并将距离放到响应数据的 sort 字段中。
  "sort": [
    {
      "_geo_distance": {
        "location": {
          "lat": 31.256224,
          "lon": 121.462311
        },
        "order": "desc",
        "unit": "m",
        "distance_type": "arc"
      }
    }
  ],
// 将结果缩小到上海站附近1km的范围内。
  "post_filter": {
    "geo_distance": {
      "distance": "1km",
      "location": {
        "lat": 31.256224,
        "lon": 121.462311
      }
    }
  }
}

四、参考文档

1、geo_point数据类型

2、距离单位

3、排序

4、矩形查询

5、圆形查询,距离查询

6、坐标拾取系统


huan1993
204 声望30 粉丝

java工程师