Mongoose更新多个地理空间索引,没有限制

时间:2012-03-28 19:06:08

标签: node.js mongodb limit geospatial mongoose

我有一些带有地理位置索引的Mongoose模型:

var User = new Schema({
  "name" : String,
  "location" : {
     "id" : String,
     "name" : String,
     "loc" : { type : Array, index : '2d'}
  }
});

我正在尝试更新某个区域中的所有项目 - 例如:

User.update({ "location.loc" : { "$near" : [ -122.4192, 37.7793 ], "$maxDistance" : 0.4 } }, { "foo" : "bar" },{ "multi" : true }, function(err){
    console.log("done!");
});

但是,这似乎只更新了前100条记录。查看文档时,如果您未设置限制,则地理空间索引find上会显示原生限制。

(来自docs使用limit()指定要返回的最大点数(如果未指定,则默认限制为100)

这似乎也适用于更新,无论multi标志是什么,这是一个巨大的阻力。如果我应用更新,它只会更新前100个。

现在,我能想到解决这个问题的唯一方法就是做一些像这样可怕的事情:

Model.find({"location.loc" : { "$near" : [ -122.4192, 37.7793 ], "$maxDistance" : 0.4 } },{limit:0},function(err,results){
   var ids = results.map(function(r){ return r._id; });
   Model.update({"_id" : { $in : ids }},{"foo":"bar"},{multi:true},function(){
      console.log("I have enjoyed crippling your server.");
   });
});

虽然我甚至不完全确定它会起作用(并且可以通过仅选择_id来进行温和优化),但我真的想避免保留{{1}的数组。内存中的ids,因为这个数字可能会非常大。

修改 上述黑客攻击甚至无效,看起来只有n find仍会返回100个结果。因此,在绝望和沮丧的行为中,我编写了一个递归方法来通过id分页,然后返回它们,以便我可以使用上面的方法进行更新。我在下面添加了该方法作为答案,但不接受它,希望有人能找到更好的方法。

据我所知,这是mongo服务器核心的一个问题,所以mongoose和node-mongodb-native不应该受到责备。然而,这真的是愚蠢的,因为geospacial指数是使用mongo而不是其他一些更强大的NoSQL商店的少数几个理由之一。

有没有办法实现这个目标?即使在node-mongodb-native或mongo shell中,我也无法找到一种方法来设置(或者在这种情况下,通过设置为0来删除)更新的限制。

1 个答案:

答案 0 :(得分:0)

我很想看到这个问题已经解决,但我无法找到一种方法来设置更新限制,经过大量研究后,似乎无法实现。此外,问题中的黑客甚至无法正常工作,我仍然只能获得100条记录并且limit设置为0

直到mongo中修复了这个问题,这就是我如何解决它:( !!警告:UGLY HACKS AHEAD:!!)

var getIdsPaginated = function(query,batch,callback){
  // set a default batch if it isn't passed.
  if(!callback){
    callback = batch;
    batch = 10000;
  }
  // define our array and a find method we can call recursively.
  var all = [],
      find = function(skip){
        // skip defaults to 0
        skip = skip || 0;
        this.find(query,['_id'],{limit:batch,skip:skip},function(err,items){
          if(err){
            // if an error is thrown, call back with it and how far we got in the array.
            callback(err,all);
          } else if(items && items.length){
            // if we returned any items, grab their ids and put them in the 'all' array
            var ids = items.map(function(i){ return i._id.toString(); });
            all = all.concat(ids);
            // recurse
            find.call(this,skip+batch);
          } else {
            // we have recursed and not returned any ids. This means we have them all.
            callback(err,all);
          }
        }.bind(this));
      };
  // start the recursion
  find.call(this);
}

此方法将返回一个巨大的_id数组。因为它们已经被编入索引,所以它实际上非常快,但它仍然比必要的次数更多地调用数据库。当此方法回调时,您可以使用ids进行更新,如下所示:

Model.update(ids,{'foo':'bar'},{multi:true},function(err){ console.log('hooray, more than 100 records updated.'); });

这不是解决此问题的最优雅方法,您可以通过根据预期结果设置批次来调整效率,但显然能够在$ near查询时简单地调用更新(或查找)限制真的会有所帮助。