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

Jac*_*ack 11 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
    }
}
Run Code Online (Sandbox Code Playgroud)

哪个好!

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

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

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

我怎样才能一步到位?如果只能在一个步骤中完成,那么可以分多步进行?我想我要用$project

Mar*_*nyi 17

如果子文档中有许多字段,有时会使用新字段进行更新,则投影不是可行的选项.幸运的是,自3.4以来,MongoDB有一个名为$ replaceRoot的新运算符.

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

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

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

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


ale*_*xbt 10

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

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

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

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

因此,管道查询本$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" }
Run Code Online (Sandbox Code Playgroud)

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

db.commands.find({}, {_id:0, "command.score":0, eventname:0}).pretty()
Run Code Online (Sandbox Code Playgroud)

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

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