我有一些带有地理位置索引的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来删除)更新的限制。
答案 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查询时简单地调用更新(或查找)限制真的会有所帮助。