支持对地理空间数据的查询操作是MongoDB的一大特色。本节介绍MongoDB的地理空间功能。

备注:MongoDB 4.0 后对地理空间索引增加的支持包括:

1、地理空间查询操作符 $near 和 $nearSphere 支持查询的分片集合。

2、MongoDB 4.0 为 geoNear 聚合运算符和 geoNear 命令添加了一个选项 key,使用户可以在查询时指定要使用的地理空间索引。以前,要使用 geoNear 聚合运算符或 geoNear 命令,集合只能有一个地理空间索引。

地理空间数据

在MongoDB中有两种地理空间的数据存储格式:GeoJSON 和 旧式坐标对。

GeoJSON的对象

如果要计算球上的几何信息,需要将位置数据存储为 GeoJSON对象。

要指定GeoJSON数据,需要使用具有以下内容的嵌入式文档:

  • 一个名称字段type, 用于指定对象类型
  • 一个坐标字段coorfinates ,用于指定对象坐标。

如果指定经度和维度,需要按照顺序列出有效的经度(-180 ~ 180)和维度(-90 ~ 90)。

例如:

location: {
      type: "Point",
      coordinates: [-73.856077, 40.848447]
}

对GeoJSON对象的MongoDB地理空间查询在一个球体上进行计算;MongoDB使用WGS84参考系统对GeoJSON对象进行地理空间查询。

旧版坐标对

计算欧几里得平面上的距离,需要将数据存储为就坐标对并使用 2d 索引。如果通过将数据转换为GeoJSON Point类型,MongoDB也支持通过2dsphere索引对旧坐标对进行球面计算。

要将数据指定为旧式坐标对,可以使用数组(首选)或嵌入式文档。标准格式为:

< 字段>: [  < x > , < y >  ]

如果制定经度和纬度坐标,需要先列出经度在列出维度,即:

< 字段>: [ < 经度> , < 纬度>  ]
  • 有效的经度值介于-180和之间180,包括两者之间。
  • 有效的纬度值介于-90和之间90,包括两者之间。

地理空间索引

MongoDB提供以下地理空间索引类型以支持地理空间查询。

2dsphere

2dsphere 支持查询,用于计算类似地球球体上的几何形状。要创建 2dsphere 类型的索引,需要使用如下格式语法:

db.collection.createIndex( { <location field> : "2dsphere" } )

location field 是集合中的一个数据字段,其值必须是GeoJSON 或者 旧式坐标对。

2d

2d索引支持在二维平面上(欧几里得平面)计算几何相关的查询。如果是在球体上进行查询请使用2dsphere 索引。

要创建2d索引,请使用如下格式:

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

location field 是集合中的一个数据字段,其值必须是GeoJSON 或者 旧式坐标对。

地理空间的查询

注意:

对于球形查询,请使用2dsphere索引结果。

2d索引用于球形查询可能会导致错误的结果,维度越高产生的偏差将越大,例如将2d索引用于环绕两极的球形查询。

地理空间运算符

MongoDB提供以下地理空间查询运算符:

操作符

描述

举例

$geoIntersects

选择地理空间数据与指定的GeoJSON对象相交的文档,即数据和指定对象的交集为非空的文档。2dsphere索引支持 geoIntersects 操作符; geoIntersects使用 geometry操作符定义GeoJSON对象。

–查询与多边形相交的文档db.places.find({ loc: {KaTeX parse error: Expected '}', got 'EOF' at end of input: …eoIntersects: {geometry: { type: “Polygon”, coordinates:[[ [0,0],[3,6],[6,1],[0,0] ]]} } } } )

$geoWithin

选择具有完全存在于指定形状内的地理空间数据的文档,2dsphere和2d索引都支持geoWithin。geoWithin运用geometry操作符指定GeoJSON对象。

–查询完全存在于GeoJSON多边形内的所有loc数据。db.places.find({ loc: { $geoWithin: { $geometry: {type: “Polygon”,coordinates: [[ [0,0],[3,6],[6,1],[0,0] ]]} } } } )

$near

返回接近点的地理空间对象,需要地理空间索引。2dsphere和2d索引支持$near。

–查询离指定的GeoJson点至少1000米的文档db.places.find({ location:{ KaTeX parse error: Expected '}', got 'EOF' at end of input: near: {geometry: {type:“Point”,coordinates:[-73.9667,40.78]},maxDistance:5000} } } )

$nearSphere

返回接近球面点上的地理空间对象,2dsphere和2d索引支持$nearSphere

–查询离指定点至少1000米,至多5000米的位置db.places.find({ location:{ KaTeX parse error: Expected '}', got 'EOF' at end of input: nearSphere: {geometry: {type:“Point”,coordinates:[-73.9667,40.78]},maxDistance:5000} } } )

下表列出了每个地理空间操作所使用的地理空间查询运算符,受支持的查询:

操作符

参数类型

索引

支持查询

说明

$near 邻近查询

GeoJSON质心点在这个line和下一个line,

2dsphere

球面

另请参阅$ nearSphere运算符,该运算符在与GeoJSON和2dsphere索引一起使用时提供相同的功能

$near

legacy coordinates

2d

平面

$nearSphere

GeoJSON点

2dsphere

球面

提供与使用GeoJSON点和2dsphere索引的𝑛𝑒𝑎𝑟操作相同的功能对于球面查询,可能最好使用near操作相同的功能对于球面查询,可能最好使用nearSphere,它明确指定名称中的球形查询而不是$near运算符

$nearSphere

legacy coordinates

2d

球面

使用GeoJSON的点来代替

$geoWithin 内部查询

GeoJSON几何对象 { $geometry: … }

球面

查询完全在参数指定地理空间内的文档

$geoWithin

{ $box: … }

2d

平面

只能查询box框住的点

$geoWithin

{ $polygon: … }

2d

平面

同上

$geoWithin

{ $center: … }

2d

平面

同上,点加半径(弧度值) $geoWithin: { $center: [ [ <x>, <y> ] , <radius> ] }

$geoWithin

{ $centerSphere: … }

2d/2dsphere

球面

支持查询框住的GeoJSON对象

$geoIntersects 相交查询

{ $geometry: … } 多边形或多多边形

球面

查询与参数给定几何对象有相交关系的文档

几何说明

名称

描述

$box

使用传统坐标对来指定一个矩形框进行 $geoWithin查询。支持 2d 地理索引方式

$center

$geoWithin使用平面几何时,使用旧坐标对、指定圆以进行 查询。支持 2d 地理索引方式

$centerSphere

使用球形几何图形时,使用传统坐标对或GeoJSON格式指定一个圆 用于$geoWithin查询。支持2dsphere2D索引。

$geometry

为地理空间查询运算符指定GeoJSON格式的几何。

$maxDistance

指定最大距离以限制$near$nearSphere查询的结果。支持2dsphere2D索引。

$minDistance

指定最小距离以限制$near$nearSphere查询的结果。仅用于2dsphere索引。

$polygon

指定要使用旧式坐标对进行 $geoWithin查询的面。支持 2d 地理索引方式。

$uniqueDocs

从2.6版开始不推荐使用。即使查询确保文档多次匹配查询,相同文档也只返回一次。

示例

创建一个 place 集合如下:

db.places.insert( {
    name: "Central Park",
   location: { type: "Point", coordinates: [ -73.97, 40.77 ] },
   category: "Parks"
} );
db.places.insert( {
   name: "Sara D. Roosevelt Park",
   location: { type: "Point", coordinates: [ -73.9928, 40.7193 ] },
   category: "Parks"
} );
db.places.insert( {
   name: "Polo Grounds",
   location: { type: "Point", coordinates: [ -73.9375, 40.8303 ] },
   category: "Stadiums"
} );

并建立一个 2dsphere 的地理位置索引:

db.places.createIndex( { location: "2dsphere" } )

以下查询使用$near运算符返回距指定GeoJSON点至少1000米,最多5000米的文档,并按从最近到最远的顺序排序:

db.places.find(
   {
     location:
       { $near:
          {
            $geometry: { type: "Point",  coordinates: [ -73.9667, 40.78 ] },
            $minDistance: 1000,
            $maxDistance: 5000
          }
       }
   }
)

以下操作使用geoNear聚合操作返回与查询过滤器匹配的文档,该文档按距指定GeoJSON点最近到最远的顺序排序:{ category: "Parks" }:

db.places.aggregate( [
   {
      $geoNear: {
         near: { type: "Point", coordinates: [ -73.9667, 40.78 ] },
         spherical: true,
         query: { category: "Parks" },
         distanceField: "calcDistance"
      }
   }
] )

微信云开发支持的数据库地理位置结构集

在数据类型上,除了支持MongoDB提供的GeoJSON格式的地理位置文件以外,还支持微信云开发封装的数据类型接口。在查询操作符方面,微信云只支持部分数据常用的操作符。

数据类型接口

操作符

含义描述

操作方法

Point

构造一个地理位置 ”点“。方法接受两个必填参数,第一个是经度(longitude),第二个是纬度(latitude),务必注意顺序。

db.collection(‘todos’).add({ data: { description: ‘eat an apple’, location: db.Geo.Point(113, 23) } }).then(console.log).catch(console.error)

LineString

构造一个地理位置的 ”线“。一个线由两个或更多的点有序连接组成。

db.collection(‘todos’).add({ data: { description: ‘eat an apple’, location: db.Geo.LineString([ db.Geo.Point(113, 23), db.Geo.Point(120, 50), // … 可选更多点 ]) } }).then(console.log).catch(console.error)

Polygon

构造一个地理位置 ”多边形“

const { Polygon, LineString, Point } = db.Geo db.collection(‘todos’).add({ data: { description: ‘eat an apple’, location: Polygon([ // 外环 LineString([ Point(0, 0), Point(30, 20), Point(20, 30), Point(0, 0) ]), // 内环 LineString([ Point(10, 10), Point(16, 14), Point(14, 16), Point(10, 10) ]) ]) } }).then(console.log).catch(console.error)

MultiPoint

构造一个地理位置的 ”点“ 的集合。一个点集合由一个或更多的点组成。

db.collection(‘todos’).add({ data: { description: ‘eat an apple’, location: db.Geo.MultiPoint([ db.Geo.Point(113, 23), db.Geo.Point(120, 50), // … 可选更多点 ]) } }).then(console.log).catch(console.error)

MultiLineString

构造一个地理位置 ”线“ 集合。一个线集合由多条线组成。

const { LineString, MultiLineString, Point } = db.Geo db.collection(‘todos’).add({ data: { description: ‘eat an apple’, location: MultiLineString([ LineString([ Point(0, 0), Point(30, 20), Point(20, 30), Point(0, 0) ]), LineString([ Point(10, 10), Point(16, 14), Point(14, 16), Point(10, 10) ]) ]) } }).then(console.log).catch(console.error)

MultiPolygon

构造一个地理位置 ”多边形“ 集合。一个多边形集合由多个多边形组成。

const { MultiPolygon, Polygon, LineString, Point } = db.Geo db.collection(‘todos’).add({ data: { description: ‘eat an apple’, location: MultiPolygon([ Polygon([ LineString([ Point(50, 50), Point(60, 80), Point(80, 60), Point(50, 50) ]), ]), Polygon([ LineString([ Point(0, 0), Point(30, 20), Point(20, 30), Point(0, 0) ]), LineString([ Point(10, 10), Point(16, 14), Point(14, 16), Point(10, 10) ]) ]), ]) } }).then(console.log).catch(console.error)

地理位置查询操作符

需要对查询字段建立地理位置索引。

geoNear

按从近到远的顺序,找出字段值在给定点的附近的记录。

  • 参数

属性

类型

必填

说明

geometry

GeoPoint


地理位置点 (Point)

maxDistance

number


选填,最大距离,单位为米

minDistance

number


选填,最小距离,单位为米

  • 示例代码:找出离给定位置 1 公里到 5 公里范围内的记录
const _ = db.command
db.collection('restaurants').where({
  location: _.geoNear({
    geometry: db.Geo.Point(113.323809, 23.097732),
    minDistance: 1000,
    maxDistance: 5000,
  })
}).get()
geoWithin

找出字段值在指定区域内的记录,无排序。指定的区域必须是多边形(Polygon)或多边形集合(MultiPolygon)。

  • 参数

属性

类型

必填

说明

geometry

Object


地理信息结构,Polygon,MultiPolygon,或 { centerSphere }

  • 示例代码1:给定多边形
const _ = db.command
const { Point, LineString, Polygon } = db.Geo
db.collection('restaurants').where({
  location: _.geoWithin({
    geometry: Polygon([
      LineString([
        Point(0, 0),
        Point(3, 2),
        Point(2, 3),
        Point(0, 0)
      ])
    ]),
  })
})
  • 示例代码 2:给定圆形
const _ = db.command
db.collection('restaurants').where({
  location: _.geoWithin({
    centerSphere: [
      [-88, 30],
      10 / 6378.1,									// 半径需以弧度计,比如需要 10km 的半径,则用距离除以地球半径 6378.1km 得出的数字。
    ]
  })
})
geoIntersects

找出给定的地理位置图形相交的记录。

  • 参数

属性

类型

必填

说明

geometry

Object


地理信息结构,Point

  • 示例代码:找出和一个多边形相交的记录
const _ = db.command
const { Point, LineString, Polygon } = db.Geo
db.collection('restaurants').where({
  location: _.geoIntersects({
    geometry: Polygon([
      LineString([
        Point(0, 0),
        Point(3, 2),
        Point(2, 3),
        Point(0, 0)
      ])
    ]),
  })
})