在MongoDB上使用Aggregate后返回完整文档

时间:2015-11-17 09:41:29

标签: mongodb aggregation-framework

我在两个属性($ 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有几个GameSessionGame在特定时间播放的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, ...},
  ...
]

注意我已经有了一些有用的东西。我只是想知道是否还有更多优雅的

2 个答案:

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