我在两个属性($ user& $ game)上对Collection进行分组,以返回与$ score属性的最大值匹配的Document
这是我的方法:
•按分数降序排序,
•分组(用户/游戏),
•选择每个属性的每个$ first元素:
db.session.aggregate(
{ $sort: {score: -1} },
{ $group: {
_id: { user: "$user", game: "$game"},
id: { $first: "$_id"},
score: { $first: "$score" },
user: { $first: "$user" },
game: { $first: "$game" },
startDate: { $first: "$startDate" },
.... & all remaining properties
}
},
{ $project: {_id: "$id", score: 1, user: 1, game: 1, startDate: 1, ... } }
)
但我对此并不满意,因为它看起来有点笨拙和费力。如果我在Collection模式中需要一个属性,我需要在几个地方添加属性。
我确信有更好的方法可以做到这一点。有什么想法吗?
修改
我的架构定义为(在javascript中,Mongoose
):
var sessionSchema = new Schema({
user : { type: Schema.ObjectId, ref: 'User' },
game : { type: Schema.ObjectId, ref: 'Game' },
level : Number,
percent : Number,
score : Number,
startDate : Date,
endDate : Date,
});
有很多User
个,每个User
有几个Game
。 Session
是Game
在特定时间播放的User
。因此,User
可以为一个Session
提供多个Game
个。我只想要玩家获得高分的Session
类似的东西:
[ {_id: ..., user: u1, game: g1, score: 100, ...},
{_id: ..., user: u2, game: g1, score: 120, ...},
{_id: ..., user: u1, game: g2, score: 90, ...},
...
]
注意我已经有了一些有用的东西。我只是想知道是否还有更多优雅的。
答案 0 :(得分:1)
感谢@BlakesSeven,我最终得到了$$ROOT variable解决方案。
由于他的解决方案在我的文档层次结构中添加了一个额外的后代,我决定使用javascript map
方法在查询后展平结果。
这是完整的解决方案,使用node.js
& Mongoose
。
var query = Session.aggregate();
query.sort({ score: -1 });
query.group( {
_id: { user: "$user", quizz: "$quizz"},
doc: { "$first": "$$ROOT" }
});
query.project( {_id: 0, doc: 1 } );
query.exec( function(err, results) {
if (err)
res.status(500).json({error: err});
else {
// flattening the results array
results = results.map(function(session) {
return session["doc"];
});
res.status(200).json(results);
}
});
答案 1 :(得分:0)
A much better option would be to use the $replaceRoot
pipeline found in MongoDB server versions 3.4 and above. This will promote the embedded $$ROOT
document to the top level and replaces all other fields:
var query = Session.aggregate([
{ '$sort': { 'score': -1 } },
{
'$group': {
'_id': { 'user': '$user', 'quizz': '$quizz' },
'doc': { '$first': '$$ROOT' }
}
},
{ '$replaceRoot': { 'newRoot': '$doc' } }
]);
query.exec( function(err, results) {
if (err)
res.status(500).json({error: err});
else {
res.status(200).json(results);
}
});