【问题标题】:Can't get my Geospatial Query to Work. Querying Mongo in Node无法让我的地理空间查询工作。在 Node 中查询 Mongo
【发布时间】:2015-11-04 14:12:42
【问题描述】:

一旦用户登录他/她的位置就会被报告。并继续每隔 15 分钟报告当前用户的位置。或者至少直到他们的会话结束。使用 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 分钟内登录的用户。然后我再过滤一次,将其他用户的最后报告位置与当前用户的位置进行比较。

【问题讨论】:

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


    【解决方案1】:

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

    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】:

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

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

      到这里:

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

      由于“类型”键被混淆,并且“点”不是猫鼬数据类型。

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

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

      那么您的查询非常简单(您不需要$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 真的有这样的$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 :(
      }
      

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

      【讨论】:

      • 我使用 for 循环让它工作。但是,我还想让它像您所说的那样在 mongo 查询之前执行循环。我的问题是 $in 是否通过 otherUsers 循环(并将其值视为数组/期望数组)。 Bc 我需要它遍历数组并将每个单独的元素作为自己的实体进行测试。
      • @irusHacker 正如我所说,您不需要循环。您按日期对用户进行初始查询,并以文档数组的形式返回。然后map() 操作只是将那些“用户名”值提取为一个数组。 .filter() 检查以从该列表中删除当前用户。 $in 条件接受一个参数数组进行过滤,是您想要的所有“用户名”值。
      • @irusHacker 此外,您自己发布且现在接受的答案不正确。正如我在开始时所展示的那样,需要更正的是 schema。此外,由于您已直接索引“location.coordinates”而不是顶级“位置”,因此地理空间数据现在被视为“旧坐标对”而不是“GeoJSON”对象,因为您正在存储它。这样做的结果是您的所有距离现在都将以“弧度”而不是“米”报告。正确的做法是完全按照我在答案中发布的内容进行操作。
      • 感谢您的回复。我的架构包含您的更改,我只是忘记将其包含在已接受的答案中。作为对不使用官方 GeoJSON 对象的回应。为什么要用弧度来测量。这甚至不是距离的测量,而是角度
      • @irusHacker 它也是点之间曲率的度量。将坐标作为数组而不是作为 GeoJSON 对象,您需要进行数学运算。为了记录,架构修复答案。
      【解决方案3】:

      这是可行的解决方案:

      我需要:

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

      【讨论】:

        猜你喜欢
        • 2022-01-22
        • 2011-06-01
        • 1970-01-01
        • 2012-05-14
        • 2022-01-17
        • 2017-11-18
        • 2016-07-10
        • 2019-01-12
        • 1970-01-01
        相关资源
        最近更新 更多