Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 results array:

{ 
  field1: {
    subfield1: {},
    subfield2: [],
    subfield3: 44,
    subfield5: xyz
  },
  field2: {
    othercontent: {}
  }
}

I want the results of my aggregation query to return the following (the contents of field1 as the top level document):

{
  subfield1: {},
  subfield2: [],
  subfield3: 44,
  subfield5: xyz
}

Can this be done with $project and the aggregation framework without defining every sub fields to return as a top level field?

like image 751
Gary Sharpe Avatar asked Oct 17 '13 20:10

Gary Sharpe


People also ask

What does MongoDB aggregation return?

Returns: A cursor to the documents produced by the final stage of the aggregation pipeline operation, or if you include the explain option, the document that provides details on the processing of the aggregation operation. If the pipeline includes the $out operator, aggregate() returns an empty cursor.

How does the aggregate method work in MongoDB?

In MongoDB, aggregation operations process the data records/documents and return computed results. It collects values from various documents and groups them together and then performs different types of operations on that grouped data like sum, average, minimum, maximum, etc to return a computed result.

When should I use aggregate in MongoDB?

You can use aggregation operations to: Group values from multiple documents together. Perform operations on the grouped data to return a single result. Analyze data changes over time.

Which of the following pipeline is used for aggregation in MongoDB?

Mongoid exposes MongoDB's aggregation pipeline, which is used to construct flows of operations that process and return results. The aggregation pipeline is a superset of the deprecated map/reduce framework functionality.


2 Answers

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"
}
like image 156
Martin Tarjányi Avatar answered Oct 18 '22 19:10

Martin Tarjányi


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
like image 21
SuperAce99 Avatar answered Oct 18 '22 18:10

SuperAce99