在MongoDB中创建地理空间索引- MongoDB的使用技巧
我了解到在MongoDB中有一种称为地理空间索引(Geospatial Index)的东西,用于搜索位置信息,于是决定尝试使用它。
当我尝试在随意收集的数据上创建索引时,遇到了各种麻烦,所以我记录下来作为备忘录。
首先,让我们来了解一下MongoDB的基本操作。
前提是指某个事件或情况发生之前所需的条件或前置条件。
将纬度(latitude)和经度(longitude)以字符串(String)的形式存储在名为”places”的集合中。
有些纬度和经度为空字符串,或者根本不存在。
[{
name: "aaa",
latitude: "33.333",
longitude: "134.4444"
},
{
name: "bbb",
latitude: "",
longitude: ""
,
{
name: "ccc"
}]
1. 几何空间指数是一个浮点数组。
在文件中,似乎需要将地理空间索引设置为数组,所以暂时我这样创建了一个数组。
db.places.find({latitude: {$ne: ""}}).forEach(function(r){
db.places.update({_id: r._id},{$set: {coordinates: [r.latitude, r.longitude]}})
})
可以使用2d索引(还有其他的索引,如2dsphere、geoHeystack等。关于2dsphere将在后文中提到)。
db.places.createIndex( { coodinates: "2d" })
#=> Point must only contain numeric elements
看起来,Array内似乎只能是数字。原因是将纬度和经度存储为字符串。重置并使用parseFloat进行转换。
db.places.update({}, {$unset: {coordinates: 1}}, {multi: true})
db.places.find({latitude: {$ne: ""}}).forEach(function(r){
db.places.update({_id: r._id},{$set: {coordinates: [parseFloat(r.latitude), parseFloat(r.longitude)]}})
})
db.places.createIndex( { coodinates: "2d" })
#=> exception: point not in interval of [ -180, 180 ]:: caused by :: {..., coordinates[non.0, non.0]}
听说似乎连纬度、经度都不存在,但是坐标却已经被创建出来了。
2. 确认纬度和经度的存在
刚才,我们只确认了{$ne: “”}为空的情况,但实际上还需要确认字段是否存在。因此,在执行上述重置命令后,我尝试了以下方法。
db.places.find({latitude: {$ne: ""}, latitude: {$exists: true}}).forEach(function(r){
db.places.update({_id: r._id},{$set: {coordinates: [parseFloat(r.latitude), parseFloat(r.longitude)]}})
})
那么,创建索引
db.places.createIndex( { coodinates: "2d" })
#=> exception: point not in interval of [ -180, 180 ]:: caused by :: {..., coordinates[non.0, non.0]}
刚才出现了同样的错误。
看起来,筛选条件有误,似乎没有起作用的是latitude: {$ne: “”}。
所以,我会进行修正并重新尝试。
db.places.find({latitude: {$ne: "", $exists: true}}).forEach(function(r){
db.places.update({_id: r._id},{$set: {coordinates: [parseFloat(r.latitude), parseFloat(r.longitude)]}})
})
db.places.createIndex( { coodinates: "2d" })
可以了。
尝试将地理空间索引从2D转换为2DSphere。
如果你的数据主要涉及经度和纬度,虽然2d索引可以支持基本的球面距离查询,但请考虑迁移到2dsphere索引。
如果只有纬度和经度数据,最好使用2dsphere,所以需要重新为2dsphere格式创建数据。
- 请执行上述重置命令。
db.places.update({}, {$unset: {coordinates: 1}}, {multi: true})
- 请输入以下命令,指定坐标和类型。请注意纬度和经度的顺序是相反的。
db.places.find({latitude: {$ne: "", $exists: true}}).forEach(function(r){
db.places.update({_id: r._id},{$set: {loc: {coordinates: [parseFloat(r.longitude), parseFloat(r.latitude)], type: "Point"}}})
})
- 输入2dsphere命令
db.places.createIndex( { loc: "2dsphere" })
做好了
试试使用geo_near函数。
db.runCommand({
geoNear: "places" ,
near: [ 0, 0 ],
spherical: true
})