ElasticSearch【有与无】【搜索引擎】【ES25】地理

目录

1.简介

1.1.地理坐标点

1.1.1.经纬度坐标格式

1.1.2.通过地理坐标点过滤

1.1.3.地理坐标盒模型过滤器

1.1.4.优化盒模型

1.1.5.地理距离过滤器

1.1.6.更快的地理距离计算

1.1.7.地理距离区间过滤器

1.1.8.按距离排序

1.2.Geohashes

1.2.1.Geohashes 映射

1.2.2.Geohash 单元查询

1.3.地理位置聚合

1.3.1.地理距离聚合

1.3.2.Geohash 网格聚合

1.3.3.地理边界聚合

1.4.地理形状

1.4.1.映射地理形状

1.4.2.索引地理形状

1.4.3.查询地理形状

1.4.4.在查询中使用已索引的形状


1.简介

  1. 地理坐标点
  2. Geohashes : 一种将经纬度坐标( lat/lon )编码成字符串的方式。
  3. 地理位置聚合
  4. 地理形状

1.1.地理坐标点

 地理坐标点 是指地球表面可以用经纬度描述的一个点。

地理坐标点可以用来计算两个坐标间的距离,还可以判断一个坐标是否在一个区域中,或在聚合中。

地理坐标点不能被动态映射(dynamic mapping)自动检测,而是需要显式声明对应字段类型为 geo-point

【举例】

PUT /attractions
{
  "mappings": {
    "restaurant": {
      "properties": {
        "name": {
          "type": "string"
        },
        "location": {
          "type": "geo_point"
        }
      }
    }
  }
}

1.1.1.经纬度坐标格式

PUT /attractions/restaurant/1
{
  "name":     "Chipotle Mexican Grill",
  "location": "40.715, -74.011"  // 字符串形式以半角逗号分割
}

PUT /attractions/restaurant/2
{
  "name":     "Pala Pizza",
  "location": { // 对象形式显式命名为 lat 和 lon
    "lat":     40.722,
    "lon":    -73.989
  }
}

PUT /attractions/restaurant/3
{
  "name":     "Mini Munchies Pizza",
  "location": [ -73.983, 40.719 ]  // 数组形式表示为 [lon,lat] 
}

可能所有人都至少一次踩过这个坑:地理坐标点用字符串形式表示时是纬度在前,经度在后( "latitude,longitude" ),
而数组形式表示时是经度在前,纬度在后( [longitude,latitude] )—顺序刚好相反。

其实,在 Elasticesearch 内部,不管字符串形式还是数组形式,都是经度在前,纬度在后。
不过早期为了适配 GeoJSON 的格式规范,调整了数组形式的表示方式。

因此,在使用地理位置的路上就出现了这么一个“捕熊器”,专坑那些不了解这个陷阱的使用者。

1.1.2.通过地理坐标点过滤

四种地理坐标点相关的过滤器可以用来选中或者排除文档:

  • geo_bounding_box

        找出落在指定矩形框中的点。

  • geo_distance

        找出与指定位置在给定距离内的点。

  • geo_distance_range

        找出与指定点距离在给定最小距离和最大距离之间的点。

  • geo_polygon

        找出落在多边形中的点。 这个过滤器使用代价很大 。当你觉得自己需要使用它,最好先看看 geo-shapes 。

这些过滤器判断点是否落在指定区域时的计算方法稍有不同,但过程类似。指定的区域被转换成一系列以quad/geohash为前缀的tokens,并被用来在倒排索引中搜索拥有相同tokens的文档。

地理坐标过滤器使用代价昂贵 — 所以最好在文档集合尽可能少的场景下使用。
你可以先使用那些简单快捷的过滤器,比如 term 或 range ,来过滤掉尽可能多的文档,最后才交给地理坐标过滤器处理。

布尔型过滤器 bool filter 会自动帮你做这件事。
它会优先让那些基于“bitset”的简单过滤器来过滤掉尽可能多的文档,然后依次才是更昂贵的地理坐标过滤器或者脚本类的过滤器。

1.1.3.地理坐标盒模型过滤器

这是目前为止最有效的地理坐标过滤器了,因为它计算起来非常简单。 你指定一个矩形的 顶部 , 底部 , 左边界 ,和 右边界 ,然后过滤器只需判断坐标的经度是否在左右边界之间,纬度是否在上下边界之间

【举例】

GET /attractions/restaurant/_search
{
  "query": {
    "filtered": {
      "filter": {
        "geo_bounding_box": {
          "location": { 
            "top_left": {
              "lat":  40.8,
              "lon": -74.0
            },
            "bottom_right": {
              "lat":  40.7,
              "lon": -73.0
            }
          }
        }
      }
    }
  }
}

1.1.4.优化盒模型

地理坐标盒模型过滤器 不需要把所有坐标点都加载到内存里。 因为它要做的 只是简单判断 lat 和 lon 坐标数值是否在给定的范围内,可以用倒排索引做一个 range 过滤来实现目标。

【举例】

PUT /attractions
{
  "mappings": {
    "restaurant": {
      "properties": {
        "name": {
          "type": "string"
        },
        "location": {
          "type":    "geo_point",
          "lat_lon": true // location.lat 和 location.lon 字段将被分别索引。它们可以被用于检索,但是不会在检索结果中返回
        }
      }
    }
  }
}

GET /attractions/restaurant/_search
{
  "query": {
    "filtered": {
      "filter": {
        "geo_bounding_box": {
          "type":    "indexed", // 设置 type 参数为 indexed (替代默认值 memory )来明确告诉 Elasticsearch 对这个过滤器使用倒排索引。
          "location": {
            "top_left": {
              "lat":  40.8,
              "lon": -74.0
            },
            "bottom_right": {
              "lat":  40.7,
              "lon":  -73.0
            }
          }
        }
      }
    }
  }
}

geo_point 类型的字段可以包含多个地理坐标点,但是针对经度纬度分别索引的这种优化方式只对包含单个坐标点的字段有效。

1.1.5.地理距离过滤器

地理距离过滤器( geo_distance )以给定位置为圆心画一个圆,来找出那些地理坐标落在其中的文档

【举例】

GET /attractions/restaurant/_search
{
  "query": {
    "filtered": {
      "filter": {
        "geo_distance": {
          "distance": "1km",  // 找出所有与指定点距离在 1km 内的 location 字段。
          "location": { // 中心点可以表示为字符串,数组或者(如示例中的)对象。
            "lat":  40.715,
            "lon": -73.988
          }
        }
      }
    }
  }
}

地理距离过滤器计算代价昂贵。为了优化性能,Elasticsearch 先画一个矩形框来围住整个圆形,这样就可以先用消耗较少的盒模型计算方式来排除掉尽可能多的文档。 然后只对落在盒模型内的这部分点用地理距离计算方式处理。

你需要判断你的用户,是否需要如此精确的使用圆模型来做距离过滤?
通常使用矩形模型 bounding box 是比地理距离更高效的方式,并且往往也能满足应用需求。

1.1.6.更快的地理距离计算

两点间的距离计算,有多种牺牲性能换取精度的算法

  • arc

最慢但最精确的是 arc 计算方式,这种方式把世界当作球体来处理。不过这种方式的精度有限,因为这个世界并不是完全的球体。

  • plane

plane 计算方式把地球当成是平坦的,这种方式快一些但是精度略逊。在赤道附近的位置精度最好,而靠近两极则变差。

  • sloppy_arc

如此命名,是因为它使用了 Lucene 的 SloppyMath 类。这是一种用精度换取速度的计算方式, 它使用 Haversine formula 来计算距离。它比 arc 计算方式快 4 到 5 倍,并且距离精度达 99.9%。这也是默认的计算方式。

【举例】

GET /attractions/restaurant/_search
{
  "query": {
    "filtered": {
      "filter": {
        "geo_distance": {
          "distance":      "1km",
          "distance_type": "plane",  // 使用更快但精度稍差的 plane 计算方法
          "location": {
            "lat":  40.715,
            "lon": -73.988
          }
        }
      }
    }
  }
}

1.1.7.地理距离区间过滤器

geo_distance 和 geo_distance_range 过滤器的唯一差别在于后者是一个环状的,它会排除掉落在内圈中的那部分文档。

【举例】

GET /attractions/restaurant/_search
{
  "query": {
    "filtered": {
      "filter": {
        "geo_distance_range": {
          "gte":    "1km", 
          "lt":     "2km", 
          "location": {
            "lat":  40.715,
            "lon": -73.988
          }
        }
      }
    }
  }
}

1.1.8.按距离排序

检索结果可以按与指定点的距离排序

【举例】

GET /attractions/restaurant/_search
{
  "query": {
    "filtered": {
      "filter": {
        "geo_bounding_box": {
          "type":       "indexed",
          "location": {
            "top_left": {
              "lat":  40.8,
              "lon": -74.0
            },
            "bottom_right": {
              "lat":  40.4,
              "lon": -73.0
            }
          }
        }
      }
    }
  },
  "sort": [
    {
      "_geo_distance": {
        "location": {  // 计算每个文档中 location 字段与指定的 lat/lon 点间的距离
          "lat":  40.715,
          "lon": -73.998
        },
        "order":         "asc",
        "unit":          "km",  // 将距离以 km 为单位写入到每个返回结果的 sort 键中。
        "distance_type": "plane"  // 使用快速但精度略差的 plane 计算方式
      }
    }
  ]
}

【结果】

...
  "hits": [
     {
        "_index": "attractions",
        "_type": "restaurant",
        "_id": "2",
        "_score": null,
        "_source": {
           "name": "New Malaysia",
           "location": {
              "lat": 40.715,
              "lon": -73.997
           }
        },
        "sort": [
           0.08425653647614346  // 位置距离
        ]
     },
...

地理距离排序可以对多个坐标点来使用,不管(这些坐标点)是在文档中还是排序参数中。
使用 sort_mode 来指定是否需要使用位置集合的 最小 ( min ) 最大 ( max )或者 平均 ( avg )距离。
 如此就可以返回 ``离我的工作地和家最近的朋友'' 这样的结果了。

按距离打分

有可能距离是决定返回结果排序的唯一重要因素,不过更常见的情况是距离会和其它因素。

遇到这种场景你需要在 功能评分查询 中指定方式让我们把这些因子处理后得到一个综合分。

另外按距离排序还有个缺点就是性能:需要对每一个匹配到的文档都进行距离计算。而 function_score 查询,在 rescore 语句 中可以限制只对前 n 个结果进行计算。

 

1.2.Geohashes

Geohashes 是一种将经纬度坐标( lat/lon )编码成字符串的方式。这么做的初衷只是为了让地理位置在 url 上呈现的形式更加友好,但现在 geohashes 已经变成一种在数据库中有效索引地理坐标点和地理形状的方式。

Geohashes 把整个世界分为 32 个单元的格子 —— 4 行 8 列 —— 每一个格子都用一个字母或者数字标识。

每一个单元还可以进一步被分解成新的 32 个单元,这些单元又可以继续被分解成 32 个更小的单元,不断重复下去。

换句话说, geohash 的长度越长,它的精度就越高。

geohash单元 过滤器 可以使用这些 geohash 前缀来找出与指定坐标点( lat/lon )相邻的位置。

 

1.2.1.Geohashes 映射

【举例】

PUT /attractions
{
  "mappings": {
    "restaurant": {
      "properties": {
        "name": {
          "type": "string"
        },
        "location": {
          "type":               "geo_point",
          "geohash_prefix":     true,  // 将 geohash_prefix 设为 true 来告诉 Elasticsearch 使用指定精度来索引 geohash 的前缀。
          "geohash_precision":  "1km"  // 精度可以是一个具体的数字,代表的 geohash 的长度,也可以是一个距离。 1km 的精度对应的 geohash 的长度是 7 。
        }
      }
    }
  }
}

通过如上设置, geohash 前缀中 1 到 7 的部分将被索引,所能提供的精度大约在 150 米。

 

1.2.2.Geohash 单元查询

geohash_cell 查询做的事情非常简单: 把经纬度坐标位置根据指定精度转换成一个 geohash ,然后查找所有包含这个 geohash 的位置——这是非常高效的查询。

【举例】

GET /attractions/restaurant/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "geohash_cell": {
          "location": {
            "lat":  40.718,
            "lon": -73.983
          },
          "precision": "2km"  // precision 字段设置的精度不能高于映射时 geohash_precision 字段指定的值。
        }
      }
    }
  }
}

【说明】

此查询将 lat/lon 坐标点转换成对应长度的 geohash,然后查找所有包含这个短语的位置。

然而,如上例中的写法可能不会返回 2km 内所有的餐馆。要知道 geohash 实际上仅是个矩形,而指定的点可能位于这个矩形中的任何位置。有可能这个点刚好落在了 geohash 单元的边缘附近,但过滤器会排除那些落在相邻单元的餐馆。

【修复】

GET /attractions/restaurant/_search
{
  "query": {
    "constant_score": {
      "filter": {
        "geohash_cell": {
          "location": {
            "lat":  40.718,
            "lon": -73.983
          },
          "neighbors": true,  // 此查询将会寻找对应的 geohash 和包围它的 geohashes 。
          "precision": "2km"
        }
      }
    }
  }
}

明显的, 2km 精度的 geohash 加上周围的单元,最终导致一个范围极大的搜索区域。此查询不是为精度而生,但是它非常有效率,而且可以作为更高精度的地理位置过滤器的前置过滤器。

将 precision 参数设置为一个距离可能会有误导性。2km 的 precision 会被转换成长度为 6 的 geohash 。
实际上它的尺寸是约 1.2km x 0.6km。你可能会发现明确的设置长度为 5 或 6 会更容易理解。

此查询的另一个优点是,相比 geo_bounding_box 查询,它支持一个字段中有多个坐标位置的情况。 设置 lat_lon 选项也是一个很有效的方式,但是它只在每个字段只有单个坐标点的情况下有效。

 

1.3.地理位置聚合

虽然按照地理位置对结果进行过滤或者打分很有用, 但是在地图上呈现信息给用户通常更加有用。一个查询可能会返回太多结果以至于不能单独地展现每一个地理坐标点,但是地理位置聚合可以用来将地理坐标聚集到更加容易管理的 buckets 中。

处理 geo_point 类型字段的三种聚合:

  • 地理位置距离

       将文档按照距离围绕一个中心点来分组。

  • geohash 网格

        将文档按照 geohash 范围来分组,用来显示在地图上。

  • 地理位置边界

       返回一个包含所有地理位置坐标点的边界的经纬度坐标,这对显示地图时缩放比例的选择非常有用。

1.3.1.地理距离聚合

geo_distance 聚合 对一些搜索非常有用

【举例】

GET /attractions/restaurant/_search
{
  "query": {
    "bool": {
      "must": {
        "match": { 
          "name": "pizza"
        }
      },
      "filter": {
        "geo_bounding_box": { // 筛选区域的结果
          "location": { 
            "top_left": {
              "lat":  40.8,
              "lon": -74.1
            },
            "bottom_right": {
              "lat":  40.4,
              "lon": -73.7
            }
          }
        }
      }
    }
  },
  "aggs": {
    "per_ring": {
      "geo_distance": {  // 聚合统计距离用户 1km 以内,1km 到 2km 的结果的数量。
        "field":    "location",
        "unit":     "km",
        "origin": {
          "lat":    40.712,
          "lon":   -73.988
        },
        "ranges": [
          { "from": 0, "to": 1 },
          { "from": 1, "to": 2 }
        ]
      }
    }
  },
  "post_filter": {  //  将结果缩小至那些在用户 1km 范围内。
    "geo_distance": {
      "distance":   "1km",
      "location": {
        "lat":      40.712,
        "lon":     -73.988
      }
    }
  }
}

【结果】

"hits": {
  "total":     1,
  "max_score": 0.15342641,
  "hits": [  // post_filter 已经将搜索结果缩小至仅在用户 1km 范围以内的披萨店。
     {
        "_index": "attractions",
        "_type":  "restaurant",
        "_id":    "3",
        "_score": 0.15342641,
        "_source": {
           "name": "Mini Munchies Pizza",
           "location": [
              -73.983,
              40.719
           ]
        }
     }
  ]
},
"aggregations": {
  "per_ring": {  // 聚合包括搜索结果加上其他在用户 2km 范围以内的披萨店。
     "buckets": [
        {
           "key":       "*-1.0",
           "from":      0,
           "to":        1,
           "doc_count": 1
        },
        {
           "key":       "1.0-2.0",
           "from":      1,
           "to":        2,
           "doc_count": 1
        }
     ]
  }
}

1.3.2.Geohash 网格聚合

通过一个查询返回的结果数量对在地图上单独的显示每一个位置点而言可能太多了。 geohash_grid 按照你定义的精度计算每一个点的 geohash 值而将附近的位置聚合在一起。

结果是一个网格—一个单元格表示一个可以显示在地图上的 geohash 。通过改变 geohash 的精度,你可以按国家或者城市街区来概括全世界。

聚合是稀疏的—它 仅返回那些含有文档的单元。 如果 geohashes 太精确,将产生太多的 buckets,它将默认返回那些包含了大量文档、最密集的10000个单元。 然而,为了计算哪些是最密集的 Top10000 ,它还是需要产生 所有 的 buckets 。可以通过以下方式来控制 buckets 的产生数量:

  1. 使用 geo_bounding_box 来限制结果。
  2. 为你的边界大小选择一个适当的 precision (精度)

【举例】

GET /attractions/restaurant/_search
{
  "size" : 0,
  "query": {
    "constant_score": {
      "filter": {
        "geo_bounding_box": {
          "location": { // 边界框将搜索限制在大纽约区的范围
            "top_left": {
              "lat":  40.8,
              "lon": -74.1
            },
            "bottom_right": {
              "lat":  40.4,
              "lon": -73.7
            }
          }
        }
      }
    }
  },
  "aggs": {
    "new_york": {
      "geohash_grid": { // Geohashes 精度为 5 大约是 5km x 5km
        "field":     "location",
        "precision": 5
      }
    }
  }
}

【结果】

...
"aggregations": {
  "new_york": {
     "buckets": [ // 每个 bucket 包含作为 key 的 geohash 值
        {
           "key": "dr5rs",
           "doc_count": 2
        },
        {
           "key": "dr5re",
           "doc_count": 1
        }
     ]
  }
}
...

要在地图上绘制这些 buckets,你需要一个将 geohash 转换成同等边界框或中心点的库。
JavaScript 和其他语言已有的库会为你执行这个转换,但你也可以从使用 geo-bounds-agg 的信息来进行类似的工作。

1.3.3.地理边界聚合

当为我们的用户显示一个地图的时候,放大包含数据的区域是有意义的;展示大量的空白空间是没有任何意义的。

geo_bounds 正好是这样的:它计算封装所有地理位置点需要的最小边界框

【举例】

GET /attractions/restaurant/_search
{
  "size" : 0,
  "query": {
    "constant_score": {
      "filter": {
        "geo_bounding_box": {
          "location": {
            "top_left": {
              "lat":  40,8,
              "lon": -74.1
            },
            "bottom_right": {
              "lat":  40.4,
              "lon": -73.9
            }
          }
        }
      }
    }
  },
  "aggs": {
    "new_york": {
      "geohash_grid": {
        "field":     "location",
        "precision": 5
      }
    },
    "map_zoom": { 
      "geo_bounds": { // geo_bounds 聚合将计算封装所有匹配查询文档所需要的最小边界框。
        "field":     "location"
      }
    }
  }
}

【结果】

...
"aggregations": {
  "map_zoom": {
     "bounds": {
        "top_left": {
           "lat":  40.722,
           "lon": -74.011
        },
        "bottom_right": {
           "lat":  40.715,
           "lon": -73.983
        }
     }
  },
...

事实上,我们甚至可以在每一个 geohash 单元内部使用 geo_bounds 聚合, 以免一个单元内的地理位置点仅集中在单元的一部分上

GET /attractions/restaurant/_search
{
  "size" : 0,
  "query": {
    "constant_score": {
      "filter": {
        "geo_bounding_box": {
          "location": {
            "top_left": {
              "lat":  40,8,
              "lon": -74.1
            },
            "bottom_right": {
              "lat":  40.4,
              "lon": -73.9
            }
          }
        }
      }
    }
  },
  "aggs": {
    "new_york": {
      "geohash_grid": {
        "field":     "location",
        "precision": 5
      },
      "aggs": {
        "cell": { // cell_bounds 子聚合会为每个 geohash 单元计算边界框。
          "geo_bounds": {
            "field": "location"
          }

        }
      }
    }
  }
}

【结果】

...
"aggregations": {
  "new_york": {
     "buckets": [
        {
           "key": "dr5rs",
           "doc_count": 2,
           "cell": {
              "bounds": {
                 "top_left": {
                    "lat":  40.722,
                    "lon": -73.989
                 },
                 "bottom_right": {
                    "lat":  40.719,
                    "lon": -73.983
                 }
              }
           }
        },

1.4.地理形状

复杂的形状——比如点集、线、多边形、多多边形、中空多边形——都是通过 geohash 单元 ``画出来'' 的,这些形状会转化为一个被它所覆盖到的 geohash 的集合。

实际上,两种类型的网格可以被用于 geo-shapes:默认geohash ,另外还有一种是 四叉树 。
四叉树与 geohash 类似,除了四叉树每个层级只有 4 个单元,而不是 32 。这种不同取决于编码方式的选择。

组成一个形状的 geohash 都作为一个单元被索引在一起。有了这些信息,通过查看是否有相同的 geohash 单元,就可以很轻易地检查两个形状是否有交集。

geo-shapes 有以下作用:判断查询的形状与索引的形状的关系;这些 关系 可能是以下之一:

  1. intersects:查询的形状与索引的形状有重叠(默认)。
  2. disjoint:查询的形状与索引的形状完全 不 重叠。
  3. within:索引的形状完全被包含在查询的形状中。

Geo-shapes 不能用于计算距离、排序、打分以及聚合。

 

1.4.1.映射地理形状

与 geo_point 类型的字段相似, 地理形状也必须在使用前明确映射

【举例】

PUT /attractions
{
  "mappings": {
    "landmark": {
      "properties": {
        "name": {
          "type": "string"
        },
        "location": {
          "type": "geo_shape"
        }
      }
    }
  }
}

你需要考虑修改两个非常重要的设置: 精度 和 距离误差

 

精度

精度 ( precision )参数 用来控制生成的 geohash 的最大长度。默认精度为 9 ,等同于尺寸在 5m x 5m 的geohash 。这个精度可能比你需要的精确得多。

精度越低,需要索引的单元就越少,检索时也会更快。当然,精度越低,地理形状的准确性就越差。你需要考虑自己的地理形状所需要的精度——即使减少1-2个等级的精度也能带来明显的消耗缩减收益。

你可以使用距离来指定精度 —— 如 50m 或 2km—不过这些距离最终也会转换成对应的[geohashes]等级。

 

距离误差

当索引一个多边形时,中间连续区域很容易用一个短 geohash 来表示。麻烦的是边缘部分,这些地方需要使用更精细的 geohashes 才能表示。

当你在索引一个小地标时,你会希望它的边界比较精确。让这些纪念碑一个叠着一个可不好。当索引整个国家时,你就不需要这么高的精度了。误差个50米左右也不可能引发战争。

距离误差 指定地理形状可以接受的最大错误率。它的默认值是 0.025 , 即 2.5% 。也就是说,大的地理形状(比如国家)相比小的地理形状(比如纪念碑)来说,容许更加模糊的边界。

0.025 是一个不错的初始值。不过如果容许更大的错误率,对应地理形状需要索引的单元就越少。

 

1.4.2.索引地理形状

地理形状通过 GeoJSON 来表示,这是一种开放的使用 JSON 实现的二维形状编码方式。 每个形状都包含了形状类型— point, line, polygon, envelope —和一个或多个经纬度点集合的数组。

在 GeoJSON 里,经纬度表示方式通常是 纬度 在前, 经度 在后。

【举例】

PUT /attractions/landmark/dam_square
{
    "name" : "Dam Square, Amsterdam",
    "location" : {
        "type" : "polygon", // type 参数指明了经纬度坐标集表示的形状类型。
        "coordinates" : [[  // lon/lat 列表描述了多边形的形状。
          [ 4.89218, 52.37356 ],
          [ 4.89205, 52.37276 ],
          [ 4.89301, 52.37274 ],
          [ 4.89392, 52.37250 ],
          [ 4.89431, 52.37287 ],
          [ 4.89331, 52.37346 ],
          [ 4.89305, 52.37326 ],
          [ 4.89218, 52.37356 ]
        ]]
    }
}

【GeoJSON 的语法】

1.用一个数组表示 经纬度 坐标点:

[lon,lat]

2.一组坐标点放到一个数组来表示一个多边形:

[[lon,lat],[lon,lat], ... ]

3.一个多边形( polygon )形状可以包含多个多边形;第一个表示多边形的外轮廓,后续的多边形表示第一个多边形内部的空洞:

[
  [[lon,lat],[lon,lat], ... ],  # main polygon
  [[lon,lat],[lon,lat], ... ],  # hole in main polygon
  ...
]

1.4.3.查询地理形状

[geo_shape 查询]不寻常的地方在于,它允许我们使用形状来做查询,而不仅仅是坐标点。

【举例】

GET /attractions/landmark/_search
{
  "query": {
    "geo_shape": {
      "location": {  // 查询使用 location 字段中的地理形状
        "shape": { // 查询中的形状是由 shape 键对应的内容表示
          "type":   "circle", 
          "radius": "1km", // 形状是一个半径为 1km 的圆形
          "coordinates": [  // 中心点坐标
            4.89994,
            52.37815
          ]
        }
      }
    }
  }
}

默认的,查询(或者过滤器 —— 工作方式相同)会从已索引的形状中寻找与指定形状有交集的部分。此外,可以把 relation 字段设置为 disjoint 来查找与指定形状不相交的部分,或者设置为 within 来查找完全落在查询形状中的。

【举例】

GET /attractions/landmark/_search
{
  "query": {
    "geo_shape": {
      "location": {
        "relation": "within",  // 只匹配完全落在查询形状中的已索引的形状
        "shape": {
          "type": "polygon",
          "coordinates": [[  // 多边形
              [4.88330,52.38617],
              [4.87463,52.37254],
              [4.87875,52.36369],
              [4.88939,52.35850],
              [4.89840,52.35755],
              [4.91909,52.36217],
              [4.92656,52.36594],
              [4.93368,52.36615],
              [4.93342,52.37275],
              [4.92690,52.37632],
              [4.88330,52.38617]
            ]]
        }
      }
    }
  }
}

1.4.4.在查询中使用已索引的形状

对于那些经常会在查询中使用的形状,可以把它们索引起来以便在查询中可以方便地直接引用名字。

【举例】

1.建立映射

PUT /attractions/_mapping/neighborhood
{
  "properties": {
    "name": {
      "type": "string"
    },
    "location": {
      "type": "geo_shape"
    }
  }
}

2.索引对应的形状

PUT /attractions/neighborhood/central_amsterdam
{
  "name" : "Central Amsterdam",
  "location" : {
      "type" : "polygon",
      "coordinates" : [[
        [4.88330,52.38617],
        [4.87463,52.37254],
        [4.87875,52.36369],
        [4.88939,52.35850],
        [4.89840,52.35755],
        [4.91909,52.36217],
        [4.92656,52.36594],
        [4.93368,52.36615],
        [4.93342,52.37275],
        [4.92690,52.37632],
        [4.88330,52.38617]
      ]]
  }
}

[查询]

GET /attractions/landmark/_search
{
  "query": {
    "geo_shape": {
      "location": {
        "relation": "within",
        "indexed_shape": {  // 指定 indexed_shape 而不是 shape ,Elasticesearch 就知道需要从指定的文档和 path 检索出对应的形状了
          "index": "attractions",
          "type":  "neighborhood",
          "id":    "central_amsterdam",
          "path":  "location"
        }
      }
    }
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

琴 韵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值