Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB Aggregation Framework - Dynamic Field Rename

I am finding the MongoDB aggregation framework to be extremely powerful - it seems like a good option to flatten out an object. My schema uses a an array of sub objects in an array called materials. The number of materials is variable, but a specific field category will be unique across objects in the array. I would like to use the aggregation framework to flatten the structure and dynamically rename the fields based on the value of the category field. I could not find an easy way to accomplish this using a $project along with $cond. Is there a way?

The reason for the array of material objects is to allow simple searching:

e.g. { 'materials.name' : 'XYZ' } pulls back any document where "XYZ" is found.

E.g. of before and after document

{
"_id" : ObjectId("123456"),
"materials" : [
    {
        "name" : "XYZ",
        "type" : "Red",
        ...
        "category" : "A"
    },
    {
        "name" : "ZYX",
        "type" : "Blue",
        ...
        "category" : "B"
    }]
}

to

{
"material_A_name" : "XYZ",
"material_A_type" : "Red",
...
"material_B_name" : "ZYX",
"material_B_type" : "Blue",
...
}
like image 975
user2234151 Avatar asked Apr 02 '13 01:04

user2234151


People also ask

How do I rename a aggregate field in MongoDB?

Introduction to the MongoDB $rename operator In this case, you can use the $rename operator. The $rename is a field update operator that allows you to rename a field in a document to the new one. The $rename operator has the following syntax: { $rename: { <field_name>: <new_field_name>, ...}}

How do I change the key name of an object in MongoDB?

MongoDB – Rename Operator ($rename) MongoDB provides different types of field update operators to update the values of the fields of the documents and $rename operator is one of them. This operator is used to update the names of the fields with new names.

Can we use $and in aggregate MongoDB?

You can use $and with aggregation but you don't have to write it, and is implicit using different filters, in fact you can pipe those filters in case one of them needs a different solution.


2 Answers

There is a request for something like this in jira https://jira.mongodb.org/browse/SERVER-5947 - vote it up if you would like to have this feature.

Meanwhile, there is a work-around if you know up front what the possible values of the keys will be (i.e. all unique values of "category") and I have some sample code on it on my blog.

like image 151
Asya Kamsky Avatar answered Oct 03 '22 15:10

Asya Kamsky


This would be useful from MongoDB v4.4,

  • $map to iterate loop of materials array
  • $map to iterate loop of name and type fields after converting to array using $objectToArray, concat your key fields requirement as per fields and value using $concat,
  • back to first $map convert returned result from second $map from array to object using $arrayToObject
  • $unwind deconstruct materials array
  • $group by null and merge materials object to one object
  • $replaceRoot to replace object in root
db.collection.aggregate([
  {
    $project: {
      materials: {
        $map: {
          input: "$materials",
          as: "m",
          in: {
            $arrayToObject: [
              {
                $map: {
                  input: {
                    $objectToArray: {
                      name: "$$m.name",
                      type: "$$m.type"
                    }
                  },
                  in: {
                    k: { $concat: ["material", "_", "$$m.category", "_", "$$this.k"] },
                    v: "$$this.v"
                  }
                }
              }
            ]
          }
        }
      }
    }
  },
  { $unwind: "$materials" },
  {
    $group: {
      _id: null,
      materials: { $mergeObjects: "$materials" }
    }
  },
  { $replaceRoot: { newRoot: "$materials" } }
])

Playground

like image 30
turivishal Avatar answered Oct 03 '22 16:10

turivishal