Jagadeesh
Jagadeesh

Reputation: 2097

Mongodb Array and object got mixed up inside array

I have the below document in mongodb, where grades key is an array with the combination of objects and array. I have several records like this in my db. i wanted to update the array and have it as an object for all such records. How can i achieve this with update query ?

 {
        _id: 4,
        grades: [
           { grade: 80, mean: 75, std: 8 },
           { grade: 85, mean: 90, std: 5 },
           [{ grade: 85, mean: 85, std: 8 }]
        ]
      }

Expected output :

 {
        _id: 4,
        grades: [
           { grade: 80, mean: 75, std: 8 },
           { grade: 85, mean: 90, std: 5 },
           { grade: 85, mean: 85, std: 8 }
        ]
      }

Upvotes: 1

Views: 180

Answers (1)

turivishal
turivishal

Reputation: 36114

It is not possible to do this using single update query in MongoDB 3.4,

1) You can do 2 queries first find and second update in loop, You can execute in mongo shell or any MongoDB editor:

  • find() query to project require field and loop the result
  • for loop the grades array
  • check if condition the grades element is array then concat otherwise return same
  • update() query to update new grades array
db.getCollection('grades').find({}, { grades: 1 }).forEach(function (doc) {
    var grades = [];
    for(var i = 0; i < doc.grades.length; i++) {
        if (Array.isArray(doc.grades[i])) {
            grades = grades.concat(doc.grades[i]);
        }
        else {
            grades.push(doc.grades[i]);
        }
    }
    // update
    db.getCollection('grades').update(
        { _id: doc._id }, 
        { $set: { grades: grades } }
    )
})

2) you can try aggregation query and export updated results in the new collection using single query,

  • $reduce to iterate loop of grades array
  • $type to get data type of the element
  • $concatArrays to concat multiple arrays
  • $cond check condition if element type is array then concat directly both arrays otherwise concat with array into array
  • $out to export collection in new collection
db.collection.aggregate([
  {
    $addFields: {
      grades: {
        $reduce: {
          input: "$grades",
          initialValue: [],
          in: {
            $cond: [
              { $eq: [{ $type: "$$this" }, "array"] },
              { $concatArrays: ["$$value", "$$this"] },
              { $concatArrays: ["$$value", ["$$this"]] }
            ]
          }
        }
      }
    }
  },
  { $out: "collection name" }
])

Playground


3) Alternate option using update with aggregation pipeline query above MongoDB 4.2,

db.collection.updateMany({},
  [{
    $set: {
      grades: {
        $reduce: {
          input: "$grades",
          initialValue: [],
          in: {
            $cond: [
              { $eq: [{ $type: "$$this" }, "array"] },
              { $concatArrays: ["$$value", "$$this"] },
              { $concatArrays: ["$$value", ["$$this"]] }
            ]
          }
        }
      }
    }
  }]
)

Playground

Upvotes: 1

Related Questions