geohash之2d 地理空间索引

  • 2019 年 10 月 4 日
  • 筆記

个人博客:https://suveng.github.io/blog/​​​​​​​ 2d 地理空间索引 概述 2D地理空间索引可以将文档与二维空间中的位置(例如地图上的点)相关联。MongoDB将位置字段中的二维坐标解释为点,并且可以将这些点编入特殊索引类型以支持基于位置的查询。地理空间索引提供特殊的地理空间查询操作。例如,您可以基于与其他位置的邻近度或基于指定区域中的包含查询文档。

地理空间索引支持对坐标字段和 其他字段(例如商业或景点类型)的查询。例如,您可能会写一个查询来查找餐馆距离酒店的特定距离,或查找某个特定邻域内的博物馆。

本文档介绍了如何在文档中存储位置数据以及如何创建地理空间索引。有关查询存储在地理空间索引中的数据的信息,请参阅使用2d索引查询地空间。

存储位置数据 要使用2d地理空间索引,您必须在预定的二维坐标系(例如经度和纬度)上对位置数据建模。您将文档的位置数据存储为字段中的两个坐标,该字段包含二维数组或具有两个字段的嵌入式文档。考虑以下两个例子:

loc : [ x, y ]    loc : { x: 1, y: 2 }

所有文件必须以相同的顺序存储位置数据。如果您将纬度和经度用作坐标系,请始终先存储经度。MongoDB的二维球形指数运算符只能识别[ 经度, 纬度 ]排序。 创建地理空间索引 重要 MongoDB只支持每个集合一个地理空间索引。 要创建地理空间索引,请使用值为2d的ensureIndex方法作为集合的位置字段。考虑下面的原型:

db.collection.ensureIndex( { <location field> : "2d" } )

在查询位置数据时,MongoDB的地理空间操作使用此索引。

在创建索引时,MongoDB会将位置数据转换为二进制 geohash值,并使用位置数据和索引的位置范围计算这些值,如 位置范围中所述。2d索引的默认范围为经度和纬度,并使用边界值-180(含180)和180(不含)。

重要 2d索引的默认边界允许应用程序插入无效纬度大于90或小于-90的文档。没有定义具有这种无效点的地理空间查询的行为。 在创建2d索引时,MongoDB提供了以下选项: 位置范围 所有2d地理空间索引都有由坐标范围定义的边界。默认情况下,2d地理空间索引假设经度和纬度的边界为-180(含180 度), 即180度(即[-180,180 ))。MongoDB返回一个错误,并拒绝指定范围之外的坐标数据的文档。

要创建一个非默认位置范围的索引,在创建2d索引时,使用包含ensureIndex()操作的min和max选项 ,如以下原型所示:

db.collection.ensureIndex( { <location field>: "2d" } ,                             { min: <lower bound> , max: <upper bound> } )

位置精度 2d索引在 内部使用所有坐标数据的 geohash表示。地理杂凑具有精确度,由散列中的位数决定。更多的位允许索引提供更高精度的结果,而更少的位仅索引提供更精确的限制结果。

精度较低的索引对插入操作的处理开销较低,并且占用较少的空间; 然而,更高精度的索引意味着查询将需要扫描索引的较小部分以返回结果。实际存储的值始终用于最终查询处理中,并且索引精度不会影响查询的准确性。

默认情况下,地理空间索引使用26位精度,大致相当于使用默认范围-180至180的2英尺或大约60厘米的精度。您可以配置2d 地理空间索引,精度高达32位。

要配置除默认值以外的位置精度,请使用ensureIndex()方法中的 位选项,如以下原型中所示:

db.collection.ensureIndex( {<location field>: "2d"} ,                             { bits: <bit precision> } )

有关位和精度之间关系的更多信息,请参阅Geohash值。

复合地理空间索引 如果只有具有位置数据的字段是第一个字段,则第二个地理空间索引可能是复合的。复合地理空间索引可以构建主要在基于位置的字段上选择的查询,也可以选择第二个条件。例如,您可以使用这种索引来支持特定区域内地毯批发商的查询。

注解 在应用地理空间标准后,地理空间查询将只使用其他查询参数。如果您的地理空间查询条件查询选择大量文档,则附加查询将仅筛选结果集,而不会导致更具针对性的查询。 要创建包含两个字段的地理空间索引,请先指定位置字段,然后再指定第二个字段。例如,要在本地位置字段和产品字段上创建复合索引(按升序排序),可以执行以下操作:

db.storeInfo.ensureIndex( { loc: "2d", product: 1 } );

这会创建一个索引,以支持对正好位置字段(即loc)的查询,以及对loc和 产品的查询。

Haystack Haystack索引为来自同一地理区域的文档创建“桶”,以提高限于该区域的查询的性能。

干草堆索引中的每个桶都包含指定接近给定经度和纬度的所有文档。使用 bucketSize的参数ensureIndex)(确定接近。甲 bucketSize的5创建组位置的值是5个单位指定的经度和纬度的范围内的索引。

bucketSize还决定索引的粒度。您可以将参数调整为数据分布,以便通常只搜索二维空间的非常小的区域。此外,由桶定义的区域可以重叠:因此文档可以存在于多个桶中。

为了建立一个干草堆索引,使用bucketSize参数在 ensureIndex()方法,如在以下原型:

db.collection.ensureIndex({ <location field>: "geoHaystack", type: 1 },                            { bucketSize: <bucket value> })
Example Consider a collection with documents that contain fields similar to the following:  { _id : 100, pos: { long : 126.9, lat : 35.2 }, type : "restaurant"}  { _id : 200, pos: { long : 127.5, lat : 36.1 }, type : "restaurant"}  { _id : 300, pos: { long : 128.0, lat : 36.7 }, type : "national park"}

以下操作会创建一个干草堆索引,其中包含存储密钥在经度或纬度1个单位内的存储桶。

db.collection.ensureIndex( { pos : “geoHaystack”, type : 1 }, { bucketSize : 1 } ) 因此,该索引将具有值200的_id字段存储在两个不同桶中的文档中:

  1. 在包含_id字段值为100的文档的存储桶中,
  2. 在包含_id字段值为300的文档的存储桶中。 要使用干草堆索引进行查询,请使用geoSearch 命令。有关命令的详细信息,请参阅查询Haystack索引。

干草堆索引是根据位置返回文档和完全匹配单个附加条件的理想选择 。这些索引不一定适合将最近的文档返回到特定位置。

地理空间干草堆索引不支持球形查询。

默认情况下,使用干草堆索引的查询返回50个文档。

距离计算 在执行2d 地理空间查询之前,MongoDB会执行距离计算。默认情况下,MongoDB使用平面几何来计算点之间的距离。MongoDB还支持使用球面几何的距离计算,以提供基于球体或地球的地理空间信息的准确距离。 球形查询使用Radians作为距离 为了使球形操作员正常工作,您必须将距离转换为弧度,然后将弧度转换为应用程序使用的距离单位。

转换:

到弧度的距离:用与距离测量相同的单位将距离除以球体的半径(例如地球)。 弧度距离:将弧度测量值乘以要转换距离的单位系统中的球体半径(例如地球)。 地球的半径约为3963.192英里或 6378.137公里。

以下查询将返回位置 集合中的文档,位于圆心[ -74,40.74 ] 描述的圆圈内, 半径为100英里:

db.places.find( { loc: { $within: { $centerSphere: [ [ -74, 40.74 ] ,                                                       100 / 3963.192 ] } } } )

您也可以使用geoNear的distanceMultiplier选项 在mongod 进程中转换弧度,而不是在应用程序代码中。请参阅 距离乘数部分。

下面的球形2D查询,返回集合中的所有文件的地方内100英里的点[ -74, 40.74 ]。

db.runCommand( { geoNear: "places",                   near: [ -74, 40.74 ],                   spherical: true                 }  )     
         output of the above command would be:
{     // [ ... ]     "results" : [        {           "dis" : 0.01853688938212826,           "obj" : {              "_id" : ObjectId( ... )              "loc" : [                 -73,                 40              ]           }        }     ],     "stats" : {        // [ ... ]        "avgDistance" : 0.01853688938212826,        "maxDistance" : 0.01853714811400047     },     "ok" : 1  }

警告 围绕极点或从-180到180经度的过渡的球形查询会产生错误。 注解 虽然地理空间索引的默认类地界限在-180和180之间,但纬度的有效值介于-90和90之间。

Geohash值 要创建地理空间索引,MongoDB会计算 指定范围内坐标对的geohash值,并为该点的地理散列编制索引。

要计算geohash值,请连续将2D地图划分为象限。然后,为每个象限分配一个两位值。例如,四个象限的两位表示将是:

01  11    00  10

这两个位的值,00,01,10,和11,每个代表每个象限内的象限和所有点的。对于具有两位分辨率的地理散列,左下象限中的所有点将具有00的地理散列。左上象限将具有01的geohash 。右下角和右上角的分别为10 和11。

为了提供更高的精度,继续将每个象限划分为子象限。每个子象限都将包含象限的地理哈希值与子象限的值连接起来。为右上象限中的地理散列是11,而对于子象限的地理散列将是(从左上角的顺时针方向):1101, 1111,1110,和1100分别。

要计算更精确的geohash,请继续划分子象限并连接每个分区的两位标识符。给定点的散列标识符中的“比特”越多,散列可以描述的可能区域越小,地理空间索引的分辨率越高。

地理空间索引和分片 你不能使用地理空间索引作为片键分片集合时。但是,您可以在分片集合上创建和维护地理空间索引,并使用不同的字段作为分片键。您的应用程序可能会使用geoNear和$ within查询地理空间数据 ; 但是,使用$ near的查询 不支持分片集合。 多地点文件

2.0新版功能:支持文档中的多个位置。

尽管2d索引不支持文档中的多个坐标集,但您可以使用多键索引来将多个坐标对存储并索引到单个文档中。在最简单的例子中,您可能有一个包含坐标数组的字段(例如locs),如下面的原型数据模型所示:

{   "_id": ObjectId(...),   "locs": [             [ 55.5, 42.3 ],             [ -74, 44.74 ],             { "lat": 55.3, "long": 40.2 }           ]  }

数组的值可以是阵列保持的坐标,如在[ 55.5, 42.3 ]或嵌入文档中{ “LAT”: 55.3, “长”: 40.2 }。

然后,您可以在locs字段上创建一个地理空间索引,如下所示:

db.places.ensureIndex( { "locs": "2d" } )

您还可以将位置数据建模为子文档内的字段。在这种情况下,文档将包含包含文档阵列的字段(例如地址),其中每个文档具有保存位置坐标的字段(例如,loc:)。考虑下面的原型数据模型:

{   "_id": ObjectId(...),   "name": "...",   "addresses": [                  {                   "context": "home",                   "loc": [ 55.5, 42.3 ]                  },                  {                   "context": "home",                   "loc": [ -74, 44.74 ]                  }                ]  }

然后,在地址.loc字段中创建地理空间索引,如下例所示:

db.records.ensureIndex( { "addresses.loc": "2d" } )

对于具有多个坐标值的文档,如果多个索引坐标对满足查询约束,则查询可能会多次返回同一文档。使用uniqueDocs参数将 geoNear或$ uniqueDocs运算符与$内部结合使用。

要在多位置文档查询中包含距离字段的位置字段,请 在geoNear命令中指定includeLocs: true。