在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
})