How to flatten a subdocument into root level in MongoDB?

后端 未结 6 982
一个人的身影
一个人的身影 2020-12-01 13:53

For example, if I have a document like this

{
  a: 1,
  subdoc: {
          b: 2,
          c: 3
          }
}

How can I convert it into a

相关标签:
6条回答
  • 2020-12-01 14:18

    You can use MongoDB projection i.e $project aggregation framework pipeline operators as well. (recommended way). If you don't want to use project check this link

    db.collection.aggregation([{$project{ . . }}]);

    Below is the example for your case:

    db.collectionName.aggregate
    ([
        { $project: { a: 1, 'b': '$subdoc.b', 'c': '$subdoc.c'} }
    ]);
    

    Gives you the output as you expected i.e.

        {
            "a" : 1,
            "b" : 2,
            "c" : 3
        }
    
    0 讨论(0)
  • 2020-12-01 14:20

    You can use $replaceRoot with a $addFields stage as follows:

    db.collection.aggregate([
        { "$addFields": { "subdoc.a": "$a" } },
        { "$replaceRoot": { "newRoot": "$subdoc" }  }
    ])
    
    0 讨论(0)
  • 2020-12-01 14:24

    We can do this with $replaceWith an alias for $replaceRoot and the $mergeObjects operator.

    let pipeline = [
       {
          "$replaceWith": {
             "$mergeObjects": [ { "a": "$a" }, "$subdoc" ]
          }
       }
    ];
    

    or

    let pipeline = [
        {
            "$replaceRoot": {
                "newRoot": {
                    "$mergeObjects": [{ "a": "$a" }, "$subdoc" ]
                }
            }
        }
    ];
    
    db.collection.aggregate(pipeline)
    
    0 讨论(0)
  • 2020-12-01 14:24

    I guess the easiest way is altering the result after it returns, using map.

    collection.mapFunction = function(el) {
      el.b = el.subdoc.b;
      el.c = el.subdoc.c
      delete(el.subdoc);
      return el;
    }
    
    ...
    
    var result = await collection.aggregate(...).toArray();
    result = result.map(collection.mapFunction);
    
    0 讨论(0)
  • 2020-12-01 14:32

    Starting Mongo 4.2, the $replaceWith aggregation operator can be used to replace a document by another (in our case by a sub-document) as syntaxic sugar for the $replaceRoot mentioned by @chridam.

    We can thus first include within the sub-document the root field to keep using the $set operator (also introduced in Mongo 4.2 as an alias for $addFields) and then replace the whole document by the sub-document using $replaceWith:

    // { a: 1, subdoc: { b: 2, c: 3 } }
    db.collection.aggregate([
      { $set: { "subdoc.a": "$a" } },
      { $replaceWith: "$subdoc" }
    ])
    // { b: 2, c: 3, a: 1 }
    
    0 讨论(0)
  • 2020-12-01 14:38

    You can do this with the use of $set and $unset

    db.collection.update({},{ $set: { "b": 2, "c": 3 }, $unset: { "subdoc": 1 } })
    

    Of course if there are lots of documents in the collection, then much as above you need to loop the collection documents in order to get the values. MongoDB has no way of referring to a value of a field in an update operation alone:

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

    That also employs some safe usage of $exists in order to make sure you are selecting documents where the "subdoc" key is actually there.

    0 讨论(0)
提交回复
热议问题