MongoDB支持几种类型的地理空间索引,其中最常用的使2dsphere索引(用于地球表面类型的地图)和2d索引(用于平面地图和时间连续的数据)。
2dsphere
2dsphere允许使用GeoJSON格式指定点、线和多边形。点可以用形如[longitude, latitude]([经度,维度])的两个元素的数组表示:
线可以用一个由点组成的数组表示:
多边形的表示方式与线一样(都是由点组成的数组),但是TYPE不同:
loc字段的名字可以是任意的,但是其中的子对象是由GeoJson指定的,不能改变。
在ensureIndex中使用“2dsphere”选项就可以创建一个地理空间索引:
db.word.ensureIndex({"loc", "2dsphere"})
地理空间查询的类型
可以使用多种不同类型的地理空间查询:交集(intersection)、包含(within)以及接近(nerness)。查询时,需要将希望查找的内容指定为形如{"$geometry":geoJsonDesc}的GeoJson对象。
例如,可以使用“$geoIntersects”操作符找出与查询位置相交的文档:
这样就找到所有与eastVillage区域有交集的文档。
可以使用“$within”查询完全包含在某个区域的文档,例如“eastVillage”有哪些餐馆。
与第一个查询不同,这次不会返回那些只是经过eastVillage(比如街道)或者部分重叠(比如用于表示曼哈顿的多边形)的文档。
最后可以使用“$near”查询附近的位置:
注意:$near是唯一一个会对查询结果进行自动排序的地理空间操作,它返回的结果是按照距离由近及远排序的。
地理位置查询有一点非常有趣,不需要地理空间搜因就可以使用“$gepIntersects”或者“$within”(“$near”需要使用索引)。但是,建议在使用表示地理位置的字段上建立地理空间索引,这样可以显著提高查询速度。
符合地理空间索引
如果有其他类型的索引,可以将地理空间索引与其他字段组合在一起使用,以便对更复杂的查询进行优化。上面提到过一种可能的查询:”eastVillage 有哪些餐馆?“如果仅仅使用地理空间索引,我们只能找到eastVillage内的所有东西,但是如果要将”restaurants“或者是”pizza“单独查询出来,就需要使用其他索引中的字段了。
其他索引字段可以放在”2dsphere“字段前面也可以放在后面,这取决于我们希望首先使用其他索引的字段进行过滤还是首先使用位置进行过滤。应该讲那个能够过滤掉尽量多的结果的字段放在前面。
2d索引
对于非球面地图(游戏地图,时间连续的数据等),可以使用”2d“索引代替”2dsphere“。”2d“索引用于扁平的表面,而不是球面。”2d“索引不应该用在球体表面,否则极点附近会出现大量的扭曲变形。
文档中应该使用包含两个元素的数组表示2d索引字段:
2d索引只能对点进行索引。可以保存一个由点组成的数组,但是他只会被保存为由点组成的数组,不会被当成线。特别是对于”$within“查询来说,这时一项重要的区别。如果将降到保存为由点组成的数组,那么如果其中的某个点位于给定的形状之内,这个文档就会与$within匹配,但是,由这些点组成的线并不一定完全包含在这个形状之内。
默认情况下地理空间索引都是假设你的值都介于-180到180.可以根据需要在ensureIndex中设置更大或者更小的索引边界值:
db.star.trek.ensureIndex({“light-years”:”2d”},{“min”: -1000,“max”: 1000})
这回创建一个2000*2000大小的空间索引。
使用”2d“索引进行查询比使用“2dsphere”要简单许多,可以直接使用“$near”或者“$within”,而不必带有“$geometry”子对象。可以直接指定坐标:
db.hyrule.find({"tile}": {"$near":[20, 21]})
这样会返回hyrule集合内部的全部文档,按照距离(20,21)这个点的距离排序,如果没有指定文档数量限制,默认最多返回100个文档。如果不需要这么多结果,应该根据需要设置返回文档的数量以节省资源。例如下面代码只会返回距离(20,21)最近的10个文档:
db.hyrule.find({"tile}": {"$near":[20, 21]}).limit(10)
"$within"可以查询出某个形状(矩形,圆形或多边形)范围内的所有文档。如果使用矩形,可以指定“$box”选项:
db.hyrule.find({"tile}": {"$within":{"$box":[[10,20], [15,30]]}})
"$box"接受两个元素的数组,第一个元素指定左下角的坐标,第二个元素指定右上角坐标。
类似的,可以使用“$center”选项返回圆形范围内的所有文档,这个选项也是接受一个两元数组作为参数:第一个参数是一个点,用于指定圆心,第二个参数用户指定半径:
db.hyrule.find({"tile}": {"$within":{"$center":[[10,20], 5]}})
还可以使用多个点组成的数组来制定多边形:
db.hyrule.find({"tile}": {"$within":{"$polygon":[[10,20], [10,0],[-10, 0]]}})
这个会查询出包含给定三角形内的点的所有文档。列表中的最后一个点会被连接到第一个点,以便组成多边形。