支持对地理空间数据的查询操作是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 | { $centerSphere: … } | 2d/2dsphere | 球面 | 支持查询框住的GeoJSON对象 |
$geoIntersects 相交查询 | { $geometry: … } 多边形或多多边形 | 球面 | 查询与参数给定几何对象有相交关系的文档 |
几何说明
名称 | 描述 |
使用传统坐标对来指定一个矩形框进行 $geoWithin查询。支持 2d 地理索引方式 | |
$geoWithin使用平面几何时,使用旧坐标对、指定圆以进行 查询。支持 2d 地理索引方式 | |
使用球形几何图形时,使用传统坐标对或GeoJSON格式指定一个圆 用于$geoWithin查询。支持2dsphere和 2D索引。 | |
为地理空间查询运算符指定GeoJSON格式的几何。 | |
指定最大距离以限制$near 和$nearSphere查询的结果。支持2dsphere和 2D索引。 | |
指定最小距离以限制$near 和$nearSphere查询的结果。仅用于2dsphere索引。 | |
指定要使用旧式坐标对进行 $geoWithin查询的面。支持 2d 地理索引方式。 | |
从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 | 是 | 地理位置点 (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)
])
]),
})
})