聚合/项目子文档作为mongo中的顶级文档

时间:2016-10-11 00:38:17

标签: mongodb mongodb-query

在我的一个集合中经过几个聚合步骤(管道步骤)之后,我最终得到以下结果:

{
    "_id" : ObjectId("574e7722bffe901713d383bb"),
    "eventname" : "Ball Passed",
    "command" : {
        "_id" : ObjectId("57ec6b6f6c61e919b578fe7c"),
        "name" : "Run",
        "strike" : 15,
        "score" : true,
        "duration" : 123
    }
}
{
    "_id" : ObjectId("57ec6b6f6c61e919b578ff8a"),
    "eventname" : "Ball Passed",
    "command" : {
        "_id" : ObjectId("573d688d080cc2cbe8aecbbc"),
        "name" : "Run",
        "strike" : 12,
        "score" : false,
        "duration" : 597
    }
}

哪个好!

但是,在汇总的下一步中,我希望得到以下结果:

{
    "_id" : ObjectId("57ec6b6f6c61e919b578fe7c"),
    "name" : "Run",
    "strike" : 15,
    "duration" : 123
}
{
    "_id" : ObjectId("573d688d080cc2cbe8aecbbc"),
    "name" : "Run",
    "strike" : 12,
    "duration" : 597
}

如果您有通知,则command字段应成为顶级文档,并且应跳过command.score

如何一步实现这一目标?如果只能在一个步骤中完成,那么可以分多步进行?我想我可以使用$project

3 个答案:

答案 0 :(得分:13)

如果子文档中有很多很多字段,有时会使用新字段进行更新,那么投影就不是一个可行的选择。幸运的是,从3.4开始,MongoDB有一个名为 $replaceRoot 的新运算符。

您所要做的就是在管道末尾添加一个新阶段。

db.getCollection('sample').aggregate([
    {
        $replaceRoot: {newRoot: "$command"}
    },
    {
        $project: {score: 0 } //exclude score field
    }
])

这将为您提供所需的输出。

请注意,在聚合的情况下(特别是在 $group 阶段之后),'command'文档可能是一个数组,可能包含多个文档。在这种情况下,您需要首先 $unwind 数组才能使用 $replaceRoot

答案 1 :(得分:8)

正如您所猜测的那样,$project允许您这样做:

db.col.aggregate([
{
    $project : 
    {
        _id: "$command._id",
        name: "$command.name", 
        strike: "$command.strike", 
        duration: "$command.duration"
    }
}
]).pretty()

我插入了您之前的结果,上面的查询返回了这个:

{
    "_id" : ObjectId("57ec6b6f6c61e919b578fe7c"),
    "name" : "Run",
    "strike" : 15,
    "duration" : 123
}
{
    "_id" : ObjectId("573d688d080cc2cbe8aecbbc"),
    "name" : "Run",
    "strike" : 12,
    "duration" : 597
}

所以使用此$product 管道您的查询应该会产生您要查找的结果。

评论后更新

如果确切的结构不是您主要关注的问题,而是排除少数字段(无需列出要包含的所有字段),那么您可以使用find()代替aggregate()

aggregate的产品只允许您排除_id。这意味着您需要手动列出要包含的所有字段。
注意:从MongoDB版本3.4开始,可以排除$project阶段(https://docs.mongodb.com/manual/reference/operator/aggregation/project/#exclude-fields

中的字段

find但是,您可以列出要隐藏的字段。

替代

(1)您可以使用$out将汇总结果重定向到另一个集合:

{ $out : "commands" }

(2)即使结构不符合您的要求,您也可以进行find查询,隐藏字段:

db.commands.find({}, {_id:0, "command.score":0, eventname:0}).pretty()

它会返回这个,这与你想要的非常接近:

{
    "command" : {
        "_id" : ObjectId("57ec6b6f6c61e919b578fe7c"),
        "name" : "Run",
        "strike" : 15,
        "duration" : 123
    }
}

答案 2 :(得分:0)

Mongo 4.2开始,$replaceWith聚合运算符可用于将另一个文档(在本例中为子文档)替换为$replaceRoot的语法糖。

// { "eventname": "Ball Passed", "command": { "_id": "57e...", "name": "Run", "strike": 15, "score": true,  "duration": 123 } }
// { "eventname": "Ball Passed", "command": { "_id": "573...", "name": "Run", "strike": 12, "score": false, "duration": 597 } }
db.collection.aggregate([
  { $replaceWith: "$command" }, // replaces the document by the content of "command"
  { $unset: ["score"] }         // drops the "score" field
])
// { "_id" : "57e...", "name" : "Run", "strike" : 15, "duration" : 123 }
// { "_id" : "573...", "name" : "Run", "strike" : 12, "duration" : 597 }

还请注意,Mongo 4.2中还引入了$unset聚合运算符,作为$project的替代语法(仅用于删除字段时使用)。