Can I easily return all of the fields of a subdocument as fields in the top level document using the aggregation framework?

后端 未结 3 1438
栀梦
栀梦 2021-01-02 04:33

I have a document similar to the following, from which I want to return the sub-fields of the current top level field as the top level fields in every document of the result

相关标签:
3条回答
  • 2021-01-02 04:54

    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 $replaceRoot:

    // { field1: { a: 1, b: 2, c: 3 }, field2: { d: 4, e: 5 } }
    // { field1: { a: 6, b: 7 }, field2: { d: 8 } }
    db.collection.aggregate({ $replaceWith: "$field1" })
    // { a: 1, b: 2, c: 3 }
    // { a: 6, b: 7 }
    
    0 讨论(0)
  • 2021-01-02 05:03

    You can use $replaceRoot aggregation operator since 3.4:

    db.getCollection('sample').aggregate([
        {
            $replaceRoot: {newRoot: "$field1"}
        }
    ])
    

    Provides output as expected:

    {
        "subfield" : {},
        "subfield2" : [],
        "subfield3" : 44,
        "subfield5" : "xyz"
    }
    
    0 讨论(0)
  • 2021-01-02 05:09

    It's generally hard to make MongoDB deal with ambiguous or parameterized json keys. I ran into a similar issue and the best solution was to modify the schema so that the members of the subdocument became elements in an array.

    However, I think this will get you close to what you want (all code should run directly in the Mongo shell). Assuming you have documents like:

    db.collection.insert({
      "_id": "doc1",
      "field1": {
               "subfield1": {"key1": "value1"},
               "subfield2": ["a", "b", "c"],
               "subfield3": 1,
               "subfield4": "a"
             },
      "field2": "other content"
    })
    
    db.collection.insert({ 
      "_id": "doc2",
      "field1": {
               "subfield1": {"key2": "value2"},
               "subfield2": [1, 2, 3],
               "subfield3": 2,
               "subfield4": "b"
    
             },
      "field2": "yet more content"
    })
    

    Then you can run an aggregation command that promotes the content of field1 while ignoring the rest of the document:

    db.collection.aggregate({
    "$group":{
        "_id": "$_id",
        "value": {"$push": "$field1"}
    }})
    

    This makes all the subfield* keys into top-level fields of an object, and that object is the only element in an array. It's clumsy, but workable:

    "result" : [
            {
                    "_id" : "doc2",
                    "value" : [
                            {
                                    "subfield1" : {"key2" : "value2"},
                                    "subfield2" : [1, 2, 3],
                                    "subfield3" : 2,
                                    "subfield4" : "b"
                            }
                    ]
            },
            {
                    "_id" : "doc1",
                    "value" : [
                            {
                                    "subfield1" : {"key1" : "value1"},
                                    "subfield2" : ["a","b","c"],
                                    "subfield3" : 1,
                                    "subfield4" : "a"
                            }
                    ]
            }
    ],
    "ok" : 1
    
    0 讨论(0)
提交回复
热议问题