在习惯了SQL之后,我遇到了mongoDB这个问题。 首先,我使用的是猫鼬。
现在,问题。我有一个名为User
的集合。
var UserSchema = new Schema ({
id : ObjectId,
name : {type : String, trim : true, required : true},
email: {type:String, trim:true, required: true, index: { unique: true }},
password: {type:String, required: true, set: passwordToMD5},
age: {type:Number, min: 18, required: true, default: 18},
gender: {type: Number, default:0, required: true},
height: {type: Number, default:180, min: 140, max: 220},
_eye_color: {type: ObjectId, default: null},
location: {
lon: {type: Number, default: 0},
lat: {type: Number, default: 0}
},
status: {type:Number, required: true, default:0}
},{
toObject: { virtuals: true },
toJSON: { virtuals: true },
collection:"user"});
现在我需要从这个集合中选择所有用户并按特殊属性(比如“rank”)对它们进行排序。这个等级是根据它们与点的距离,年龄与给定年龄相比等的某些逻辑来计算的......
所以现在我想知道如何选择这个等级然后在排序中使用它?我尝试使用虚拟数据,它们可以方便地计算其他信息,但遗憾的是,无法通过虚拟字段对find()
结果进行排序。
当然我可以在虚拟中计算这个等级,然后选择所有记录,然后在回调中,做一些javascript。但在这种情况下,当我选择所有用户然后排序然后限制时,javascript部分可能需要太长时间......
我想使用 mapreduce ,但我不确定它会做我想要的。
如果我的任务可以在 mongoDB / mongoose 中完成,有人可以给我一个提示吗?
编辑1
我也尝试使用聚合框架,起初它似乎是具有$project
能力的最佳解决方案。但是,当我需要进行排名计算时,我发现聚合不支持很多数学函数,如sin
,cos
和sqrt
。而且在投影中也不可能使用预定义的常用javascript函数。我的意思是,函数被调用,但我无法将当前记录字段传递给它。
{$project: {
distance_from_user: mUtils.getDistance(point, this.location)
}
内部功能第二个attr是“未定义”。
所以我想用聚合框架进行排名计算是不可能的。
编辑2 好的,我知道每个人都告诉我不要使用mapreduce,因为它对实时查询不好,但由于我不能使用聚合,我想我会尝试mapreduce。所以,假设我有这张地图缩小。
function map() {
emit(1, // Or put a GROUP BY key here
{name: this.name, // the field you want stats for
age: this.age,
lat: this.location.lat,
lon: this.location.lon,
distance:0,
rank:0
});
}
function reduce(key, values) {
return val;
}
function finalize(key, value){
return value;
}
var command = {'mapreduce': "user", 'map': map.toString(), 'reduce': reduce.toString(), query:{$and: [{gender: user_params.gender}, {_id: {$ne: current_user_id}}]}, 'out': {inline:1}};
mongoose.connection.db.executeDbCommand(command, function(error, result){
if(error) {
log(error);
return;
}
log(result);
return;
});
我应该在减少(或者更改地图)中为每个用户计算排名?
答案 0 :(得分:1)
唯一真正的解决方案是计算每个文档的排名并将其存储在文档中。由于只要文档中的值保持不变,此值就会保持不变,只要您更新影响它的字段,就可以简单地计算此值。
Map / reduce肯定不是一个很好的解决方案,也不是任何其他类型的聚合。如果您正在使用MongoDB,那么预先计算您的排名并将其与文档一起存储是唯一可扩展的选项。
答案 1 :(得分:1)
你知道这样的事情所需的计算量 - 如果你每次用户登录时都这样做,那么当很多人在较短的时间内登录时你会有一些有趣的负载峰值 - 以及你的页面(接口)将受到严重的资源限制(这是不好的)
我推荐你一些不同的东西 - 保持每个登录用户的排名并间隔更新它们:保持“短会话”和“长会话”(长会话 - 你在网络浏览器中使用的那个 - 短 - “在线,目前使用网站“)并定期为”短期活跃“用户生成排名,很少为长期会话登录。像每五分钟一样。更多的可扩展 - 如果用户不满意他没有计算他的排名 - 你可能总是调整系统以按需计算他的排名。
在这种情况下你可能会使用mapredurce - 你的map函数应该只发出你需要的数据来计算给定用户的等级(如年龄,纬度,长度,你需要的任何东西)和测试用户的结果(等级)它是空的)。对于reduce函数,你需要查看mapreduce的排序(它在很大程度上取决于你创建排名的方式) - 你还要计算其他用户的排名(或某种子值)。
答案 2 :(得分:0)
它看起来像是MongoDB + Hadoop的一个很好的用例。
这presentation显示了这种组合的一些可能性。