无法使我的地理空间查询工作。在节点中查询 Mongo

Can't get my Geospatial Query to Work. Querying Mongo in Node

一旦用户登录 his/or,她的位置就会被报告。并继续每 15 分钟报告一次当前用户的位置。或者至少在他们的会话是 over.A 之前,用户位置使用 Locate 方法读入数据库。它存储在与该特定用户关联的数据库中的位置数组中。这一切都很好。

将使用扫描方法从数据库中读取用户位置。

我想知道如何围绕 location.coordinates 创建一个 2dsphere。我知道我必须以 location.coordinates 为目标,所以 mongo 知道将其视为 2dsphere。所以我知道我必须使用 User.ensureIndex({location: "2dsphere"})。我的问题/问题是:

1st) 应该是User.ensureIndex("location.coordinate": 2dsphere) 还是("location": 2dsphere)。这会放在 app.js.

的顶部吗

2nd) 在我的扫描方法中,我尝试创建逻辑并查询用户名除了在 300 米内之外还匹配的文档。所以最后我的问题是我最后一个 Mongo 结构正确的查询

这是我的 mongo 架构:

var userSchema = mongoose.Schema({
  username: {
    type: String,
    required: true,
    unique: true
  },
  password: {
    type: String,
    required: true
  },
  email: {
    type: String
  },
  location: 
    {
      type: "Point",
      coordinates: [Number, Number],
      datetime: Date
  },
  points: {type:Number, default:10000},
  imageUrl: {type:String, default:'/img/gravatar.jpg'}
});

我的 app.js 是这样设置的:

var User = require('../models/users.js');


var userController = {

 locate: function (req, res) {

  var data = req.body;
  var date = new Date();
  var username = req.user.username;

  User.findOne({username:username}, function(err, user) {
    if (err) return handleErr(err);

    find = {
      coordinates: [data.longitude, data.latitude],
      datetime: date
    };

    user.location.push(find);
    user.save();

   });

 },

 scan: function (req, res) {

  var date = new Date();
  var twentyb4 = date.setMinutes(date.getMinutes - 15);

  User.find({"datetime": {$gte: twentyb4}}, function (err, user) {
    if (err) return handleErr(err);

    for (var i = 0; i < user.length; i++) {

    //return users other than current user
    if(req.user.username !== user[i].username){


       User.find({$and: [{username: user[i].username}, {$near:   {$geometry: {type: "Point", coordinates: [ req.user.longitude, req.user.latiude ]}, $maxDistance: 300, $minDistance: 0}}]}, function(err, data){
        if (err) return handleErr(err);


          console.log(data.username);

        });

      }


    }

  });      

};

所以最终扫描功能要做的是找到数据库中不包括当前用户的所有用户。然后它过滤以仅获取在过去 20 分钟内登录的那些用户。然后我再过滤一次,将其他用户的最后报告位置与当前用户的位置进行比较。

您只需创建一次索引(ensureIndex 已弃用,createIndex 中存在相同的功能。由于您使用的是 mongoose,因此只需在您的架构上定义索引,例如

userSchema.index({location: '2dsphere'});

您的查询快完成了,这是正确的版本。

var geoJSONpoint = {
   type: 'Point',
   coordinates: [
       req.user.longitude,
       req.user.latitude
   ]
}

User.find({ username: user[i].username, location: { $near: { $geometry: geoJSONpoint, $maxDistance: 300 } } })
...

您的架构有误,您需要解决这个问题:

  location: 
    {
      type: "Point",
      coordinates: [Number, Number],
      datetime: Date
  },

为此:

  location: {
      "type": { "type": String, "default": "Point" },
      "coordinates": [Number, Number],
      "datetime": Date
  },

因为 "type" 关键字被混淆了,而且 "Point" 不是 mongoose 数据类型。

您还需要为地理空间查询定义的索引:

userSchema.index({ "location": "2dsphere" });

那么你的查询就很简单了(你不需要 $and 这里因为所有的查询参数已经是 "and" 条件了),你真的想要 $nearSphere 这里有真实的世界坐标:

User.find({ 
    "username": user[i].username,
    "$nearSphere": {
        "$geometry": {
            "type": "Point",
            "coordinates": [ parseFloat(req.user.longitude), parseFloat(req.user.latiude) ]
        }, 
        "$maxDistance": 300, 
        "$minDistance": 0
    }
}, function(err, data) {

});

也要小心注意那里的 parseFloat,因为如果 Mongoose 真的有智慧 "type cast" 并使用这样的 $nearSphere 查询,我马上就想不起来了。

我在这里也建议"don't loop",一次性获取除当前用户名以外的所有用户。简单的一些预过滤和 $in 运算符:

// Filter out username values only except for the current user
var otherUsers = user.map(function(u) {
    return u.username
}).filter(function(u) { return u != req.user.username });

if ( otherUsers.length > 0 ) {

    User.find({ 
        "username": { "$in": otherUsers },
        "$nearSphere": {
            "$geometry": {
            "type": "Point",
                "coordinates": [ parseFloat(req.user.longitude), parseFloat(req.user.latiude) ]
            }, 
            "$maxDistance": 300, 
            "$minDistance": 0
        }
    }, function(err, data) {

    });

} else {
   // no-one else is within the time :(
}

现在您在单个查询中测试了所有其他用户,因此您无需为循环结果和相关的异步控制操心,否则您确实需要这些。

这是有效的解决方案:

我需要:

var userSchema = mongoose.Schema({
  username: {
    type: String,
    required: true,
    unique: true
  },
  password: {
    type: String,
    required: true
  },
  location:
      {
        type: { type: String, default: "Point" },
        coordinates: [Number, Number],
        datetime: Date
      },
  email: {
    type: String
  }
})


userSchema.index({ "location.coordinates": "2dsphere"});  //In my model

这是我在节点(服务器)中的控制器:

scan: function (req, res) {

    var date = new Date();
    var minusmin = date.setMinutes(date.getMinutes() - 20);


    User.find({ "location.datetime": {$gte: minusmin}}, function (err, user) {

      if (err) return handleErr(err);


      for (var i = 0; i < user.length; i++) {

        //return users other than current user
        if(req.user.username !== user[i].username){



          User.find({ username: user[i].username, "location.coordinates": { $nearSphere: { $geometry: { type: "Point", coordinates: [ req.user.location.coordinates[0], req.user.location.coordinates[1] ]}, $maxDistance: 300 } } }, function(err, data){
              if (err) return handleErr(err);


              console.log(data);


          });


        }


      }

      res.send(user);

    });

  }