无法让我的地理空间查询生效。在节点

时间:2015-08-12 06:10:13

标签: javascript node.js mongodb mongodb-query geospatial

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

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

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

1st)应该是User.ensureIndex(" location.coordinate":2dsphere)还是(" location":2dsphere)。这将是app.js的顶部。

第二)在我的扫描方法中,我试图创建逻辑和查询用户名匹配的文档除了在300米以外。所以最后我的问题是我的上一个Mongo查询结构正确

这是我的mongo Schema的样子:

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分钟内登录的用户。然后我再次将其过滤掉,将其他用户的上次报告位置与当前用户的位置进行比较。

3 个答案:

答案 0 :(得分:1)

您的架构错误,您需要解决此问题:

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

对此:

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

由于“类型”关键字混淆,“Point”不是mongoose数据类型。

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

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

然后你的查询非常简单(这里你不需要$and,因为所有的查询参数都已经是“和”条件了,你真的希望$nearSphere这里有真实世界坐标: / p>

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真的有聪明才能用这样的$nearSphere查询来“输入”。

我还建议在这里“不要循环”,只是一次性获取当前用户名以外的所有用户。很简单,有一些预过滤和$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 :(
}

现在,您在单个查询中测试了所有其他用户,因此您无需为循环结果和您真正需要的相关异步控制而烦恼。

答案 1 :(得分:0)

您只需要创建一次索引(不推荐使用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 } } })
...

答案 2 :(得分:0)

继承人的工作解决方案:

我需要:

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

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

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);

    });

  }