4

I have a collection like this: Each document contains Message field which in turn contains array of Fields. Each document underneath Fields array has Value and Name properties.

[
    {
        "_id" : ObjectId("55711efed0103b1598140076"),
        "Message" : {
            "Fields" : [ 
                {
                    "Value" : 131,
                    "Name" : "Options",
                }, 
                {
                    "Value" : 8,
                    "Name" : "Length",
                }       
            ]
        }
    },

    {
        "_id" : ObjectId("55711efed0103b1598140077"),
        "Message" : {
            "Fields" : [ 
                {
                    "Value" : 65,
                    "Name" : "Options",
                }, 
                {
                    "Value" : 13,
                    "Name" : "Length",
                },
                {
                    "Value" : 101,
                    "Name" : "Width",
                }                   
            ]
        }
    }
]

After finding documents using db.Collection.find({}), I would like to project such that - it parses each Field underneath Message.Fields and project them in new document using Type as property name and Value as value. The output would look like following:

[
    {
        "_id" : ObjectId("55711efed0103b1598140076"),
        "Options" : 131,
        "Length" : 8
    },
    {
        "_id" : ObjectId("55711efed0103b1598140077"),
        "Options" : 65,
        "Length" : 13,
        "Width" : 101
    },
]

Is this achievable using function() or aggregate or any other way in MongoDB?

1
  • There is unnecessary comma in the field array at each object in last key value pair. Commented Jun 5, 2015 at 5:50

3 Answers 3

3

You can use aggregation and the $cond operator

db.collection.aggregate(
    [
      { $unwind: "$Message.Fields" },
      { $project: { fields: "$Message.Fields" }}, 
      { $project: 
          { 
              Option: 
                {
                  $cond: [{ $eq: [ "$fields.Name", "Options" ]}, "$fields.Value", 0 ]
                }, 
              Length: 
                {
                  $cond: [{ $eq: [ "$fields.Name", "Length" ]}, "$fields.Value", 0 ]
                }, 
              Width: 
                { 
                  $cond: [{ $eq: [ "$fields.Name", "Width" ]}, "$fields.Value", 0]
                }
          }
        }, 
        { $group: { 
                    _id: "$_id", 
                    Options: { $sum: "$Option" }, 
                    Width: { $sum: "$Width" }, 
                    Length: { $sum: "$Length" }
                  }
         } 
    ]
)

Result:

{
        "_id" : ObjectId("55711efed0103b1598140077"),
        "Options" : 65,
        "Width" : 101,
        "Length" : 13
}
{
        "_id" : ObjectId("55711efed0103b1598140076"),
        "Options" : 131,
        "Width" : 0,
        "Length" : 8
}
Sign up to request clarification or add additional context in comments.

Comments

1

With MongoDB version 3.4.4 and newer, use $arrayToObject to convert the "$Message.Fields" array into the desired object but prior to using the above operator you would have to reshape the properties of the objects in the array to be the keys k and var using $map. This is necessary for the $arrayToObject to convert the list into an object of key/value pair.

Upon converting the object you can merge this with the _id field and then replace the root with this merged doc using $replaceRoot.

The following pipeline shows this approach:

db.collection.aggregate([
    { "$replaceRoot": {
        "newRoot": {
            "$mergeObjects": [
                { _id: "$_id" },
                { "$arrayToObject": {
                    "$map": {
                        "input": "$Message.Fields",
                        "in": {
                            "k": "$$this.Name",
                            "v": "$$this.Value"
                        }
                    }
                } }
            ]
        }
    } }
])

Use the map() method from the find() cursor which applies a function to each document visited by the cursor and collects the return values from successive application into an array where you can project the needed fields into the desired result:

var result = db.collection.find().map(function (doc){
    var obj = {};
    obj["_id"] = doc._id
    doc.Message.Fields.forEach(function (field){
        obj[field.Name] = field.Value;
    })
    return obj;
});
printjson(result);

Output:

[
    {
        "_id" : ObjectId("55711efed0103b1598140076"),
        "Options" : 131,
        "Length" : 8
    },
    {
        "_id" : ObjectId("55711efed0103b1598140077"),
        "Options" : 65,
        "Length" : 13,
        "Width" : 101
    }
]

Comments

1

I am using the follwing script to generate the above result using mongo shell.

var result=[];

var cursor=db.collection.find().forEach( 

function(myDoc) 
{ 
    print(myDoc)

        var obj1={};
        obj1._id=myDoc._id;
        var len_of_fields=myDoc.Message.Fields.length;

        if(len_of_fields==2)
        {
          var j=0;         
          obj1.Options=myDoc.Message.Fields[j].Value;    
          obj1.Length=myDoc.Message.Fields[j+1].Value;
          result.push(obj1)  
        }
        else if(len_of_fields==3)
        {

             var j=0;
            obj1.Options=myDoc.Message.Fields[j].Value;
            obj1.Length=myDoc.Message.Fields[j+1].Value;
            obj1.Width=myDoc.Message.Fields[j+2].Value;

              result.push(obj1)
        }   

    print( "DOC: ", result); 

} 

);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.