最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - MongoDB $project embedded document to root level - Stack Overflow

programmeradmin0浏览0评论

Using the aggregate pipeline, I am trying to project an embedded document to the root level WITHOUT projecting each field individually.

For example, I want to project name from this collection to the root level:

[
    {
        _id: "1",
        name: {
            firstName: "John",
            lastname: "Peters"
        }
    },
    {
        _id: "2",
        name: {
            firstName: "Mary",
            lastname: "Jones"
        }
    }
]

This is what I am looking for:

[
    {
        firstName: "John",
        lastname: "Peters"
    },
    {
        firstName: "Mary",
        lastname: "Jones"
    }
]

Is there a way to do this without projecting each field individually? I don't want to have to do this:

db.collection.aggregate(
    [
        {
            $project : {
                "_id" : 0,
                "firstName" : "$name.firstName",
                "lastName" : "$name.lastName"
            }
        }
    ]

Using the aggregate pipeline, I am trying to project an embedded document to the root level WITHOUT projecting each field individually.

For example, I want to project name from this collection to the root level:

[
    {
        _id: "1",
        name: {
            firstName: "John",
            lastname: "Peters"
        }
    },
    {
        _id: "2",
        name: {
            firstName: "Mary",
            lastname: "Jones"
        }
    }
]

This is what I am looking for:

[
    {
        firstName: "John",
        lastname: "Peters"
    },
    {
        firstName: "Mary",
        lastname: "Jones"
    }
]

Is there a way to do this without projecting each field individually? I don't want to have to do this:

db.collection.aggregate(
    [
        {
            $project : {
                "_id" : 0,
                "firstName" : "$name.firstName",
                "lastName" : "$name.lastName"
            }
        }
    ]
Share Improve this question edited Feb 3, 2016 at 18:25 gnerkus 12k7 gold badges53 silver badges74 bronze badges asked Feb 3, 2016 at 17:11 Dave NewDave New 40k62 gold badges232 silver badges405 bronze badges 2
  • What's the rationale behind not taking the above approach? – chridam Commented Feb 3, 2016 at 17:24
  • use - $$ROOT, you won't get it in the top most level, but all the variables can be contained under a single field. – BatScream Commented Feb 3, 2016 at 21:21
Add a comment  | 

5 Answers 5

Reset to default 13

MongoDB 3.4 has the new stage in aggregation pipeline - $replaceRoot, which does exactly what was asked.

https://docs.mongodb.com/manual/reference/operator/aggregation/replaceRoot/

Here is the solution which uses JavaScript variable.

# Set Object for what to project
var projectWhat = {'_id' : 0};

# Fill Object with keys
Object.keys(db.coll.findOne().name).forEach(function(x){
    projectWhat[x] = "$name." + x;
});

# Do Aggregate
db.coll.aggregate([{$project : projectWhat}])

And the output will be

{ "firstName" : "John", "lastname" : "Peters" }
{ "firstName" : "Mary", "lastname" : "Jones" }

Hope this helps.

You can use $replaceRoot like this:

db.collection.aggregate(
    [
        {
            $replaceRoot : {
                newRoot: {"$name"}
            }
        }
    ]
)

Also if you have a field in the root document you want to retain you can use a $mergeObjects to combine it with your embedded object:

db.collection.aggregate(
    [
        {
            $replaceRoot : {
                newRoot: {
                   $mergeObjects: [
                      {"_id": "$_id"},
                      "$name"
                   ]
                }
            }
        }
    ]
)

This may be achieved by using $set to update all documents with the values in the name sub-document:

db.collection.find({ "name": {"$exists": 1 } }).forEach(function(doc) {
    var setName = {};
    for ( var k in doc.name ) {
        setName[k] = doc.name[k];
    }
    db.collection.update(
        { "_id": doc._id },
        { "$set": setName, "$unset": "name" }
    );
})

While I'll recommend you use $project because it would be more performant than this solution, I can understand why you wouldn't want to use $project.

Starting Mongo 4.2, the $replaceWith aggregation operator can be used to replace a document by another (in our case by a sub-document):

// { _id: "1", name: { firstName: "John", lastname: "Peters" } }
// { _id: "2", name: { firstName: "Mary", lastname: "Jones"  } }
db.collection.aggregate({ $replaceWith: "$name" })
// { firstName: "John", lastname: "Peters" }
// { firstName: "Mary", lastname: "Jones" }
发布评论

评论列表(0)

  1. 暂无评论