在我之前的文章 “开始使用 Elasticsearch (3)”,我展示了一些 Geo 查询的一些案例:
在今天的文章中,我将详述 Geo grid 查询。它用于匹配与 GeoGrid 聚合中的网格单元相交的 geo_point 和 geo_shape 值。该查询旨在通过提供存储桶的键来匹配落在 geogrid 聚合存储桶内的文档。 对于 geohash 和 geotile 网格,查询可用于 geo_point 和 geo_shape 字段。 对于 geo_hex 网格,它只能用于 geo_point 字段。
假设以下文档被索引:
- PUT /my_locations
- {
- "mappings": {
- "properties": {
- "location": {
- "type": "geo_point"
- }
- }
- }
- }
-
- PUT /my_locations/_doc/1?refresh
- {
- "location" : "POINT(4.912350 52.374081)",
- "city": "Amsterdam",
- "name": "NEMO Science Museum"
- }
-
- PUT /my_locations/_doc/2?refresh
- {
- "location" : "POINT(4.405200 51.222900)",
- "city": "Antwerp",
- "name": "Letterenhuis"
- }
-
- PUT /my_locations/_doc/3?refresh
- {
- "location" : "POINT(2.336389 48.861111)",
- "city": "Paris",
- "name": "Musée du Louvre"
- }
上面的三个点可以在地图上表示如下:

可能有些开发者对于 geohash 还是不很了解。如果你想了解的话,请阅读我之前的文章 “Elasticsearch:理解 Elastic Maps 中的 geohash 及其聚合”。使用 geohash_grid 聚合,可以根据文档的 geohash 值对文档进行分组:
- GET /my_locations/_search?filter_path=aggregations
- {
- "size" : 0,
- "aggs" : {
- "grouped" : {
- "geohash_grid" : {
- "field" : "location",
- "precision" : 2
- }
- }
- }
- }
在上面,我们请求的精度为 2。上面返回的结果为:
- {
- "aggregations": {
- "grouped": {
- "buckets": [
- {
- "key": "u1",
- "doc_count": 2
- },
- {
- "key": "u0",
- "doc_count": 1
- }
- ]
- }
- }
- }
我们可以看出来数据被分成两个组。它们分别位于不同的 geohash 的网格中。
当然,我们也可以把精度设置为更低,比如:
- GET /my_locations/_search?filter_path=aggregations
- {
- "size" : 0,
- "aggs" : {
- "grouped" : {
- "geohash_grid" : {
- "field" : "location",
- "precision" : 1
- }
- }
- }
- }
在上面,precision 为 1,那么上面搜索返回的结果为:
- {
- "aggregations": {
- "grouped": {
- "buckets": [
- {
- "key": "u",
- "doc_count": 3
- }
- ]
- }
- }
- }
Geohash 的网格是这样的:

当精度越低,那么它包含的地理面积就越大,这样极有可能把相近的地理位置点聚合到同一个网格中。从我们返回的数据中,我们可以看得出来 u0 及 u1 应该是在包含我们上面三个数据的网格里。它们处于 u 的网格中。
有关 geotile grid 的聚合,我有在之前的文章 “开始使用 Elasticsearch (3)” 中有详述。使用 geotile_grid 聚合,可以根据其 geotile 值对文档进行分组:
- GET /my_locations/_search?filter_path=aggregations
- {
- "size" : 0,
- "aggs" : {
- "grouped" : {
- "geotile_grid" : {
- "field" : "location",
- "precision" : 6
- }
- }
- }
- }
上面的命令返回的结果为:
- {
- "aggregations": {
- "grouped": {
- "buckets": [
- {
- "key": "6/32/21",
- "doc_count": 2
- },
- {
- "key": "6/32/22",
- "doc_count": 1
- }
- ]
- }
- }
- }
我们可以根据返回的数据的 key 值来显示该桶所在位置的 png 文件:

从上面,我们可以看出来,它包含 Amsterdam 及 Antwerp 两个位置,而另外一幅图则包含 Paris:

使用 geotile_grid 聚合,可以根据其 geotile 值对文档进行分组:
- GET /my_locations/_search?filter_path=**.hits
- {
- "query": {
- "geo_grid" :{
- "location" : {
- "geotile" : "6/32/22"
- }
- }
- }
- }
上面是使用上面返回的 geotile 值 6/32/22 来进行查询的。我可以通过它来确定到底是那个一个文档匹配:
- {
- "hits": {
- "hits": [
- {
- "_index": "my_locations",
- "_id": "3",
- "_score": 1,
- "_source": {
- "location": "POINT(2.336389 48.861111)",
- "city": "Paris",
- "name": "Musée du Louvre"
- }
- }
- ]
- }
- }
显然它对应于 Paris 这个位置。
使用 geohex_grid 聚合,可以根据其 geohex 值对文档进行分组。特别值得指出的是,geo-hex-agg 是一个需要版权才可以实现的功能。我们需要启动白金版试用功能:


我们再次执行上面的聚合:
- GET /my_locations/_search?filter_path=aggregations
- {
- "size" : 0,
- "aggs" : {
- "grouped" : {
- "geohex_grid" : {
- "field" : "location",
- "precision" : 1
- }
- }
- }
- }
上面聚合的结果为:
- {
- "aggregations": {
- "grouped": {
- "buckets": [
- {
- "key": "81197ffffffffff",
- "doc_count": 2
- },
- {
- "key": "811fbffffffffff",
- "doc_count": 1
- }
- ]
- }
- }
- }
我们可以通过使用具有以下语法的存储桶键执行 geo_grid 查询来提取其中一个存储桶上的文档:
- GET /my_locations/_search?filter_path=**.hits
- {
- "query": {
- "geo_grid" :{
- "location" : {
- "geohex" : "811fbffffffffff"
- }
- }
- }
- }
上面的命令返回的结果为:
- {
- "hits": {
- "hits": [
- {
- "_index": "my_locations",
- "_id": "3",
- "_score": 1,
- "_source": {
- "location": "POINT(2.336389 48.861111)",
- "city": "Paris",
- "name": "Musée du Louvre"
- }
- }
- ]
- }
- }
从上面,我们可以看出来 Paris 这个位置处于 geohex 值为 811fbffffffffff 的网格中。