4

I have an database full of objects that contain information, as well as other arrays of objects. I would like to change the inner arrays to only be arrays with each index as an ObjectId type with their respective ObjectId

I am using the mongoose populate function to retrieve this information later in the program. So only the ObjectId is needed for the reference.

job { 
    _id: 1,
    name: "name",
    parts: [ 
        { _id: ObjectId("5c790ce7d3dc8d00ccc2688c"), name: "name"},
        { _id: ObjectId("5c790ce7d3dc8d00ccc2688b"), name: "name"},
        { _id: ObjectId("5c790ce7d3dc8d00ccc2688a"), name: "name"},
    ]
}

Desired Result

job {
    _id: 1,
    name: "name",
    parts: [
        ObjectId("5c790ce7d3dc8d00ccc2688c"),
        ObjectId("5c790ce7d3dc8d00ccc2688b"),
        ObjectId("5c790ce7d3dc8d00ccc2688a")
    ]
}

I tried a few mongoDB queries from the command line but none of them are quite giving the result I need. This one has no errors, but it doesn't seem to change anything.

db.jobs.update(
    {},
    {
        $set: {"parts.$[element]": "element._id"}
    },
    {
        multi: true,
        arrayFilters: [{ "element": {} }]
    }
)

I'm not sure if this is possible or not using only the mongo shell.

nmfb
  • 49
  • 2

2 Answers2

3

Mongo v4.2 introduced pipelined updates this allows the use of aggregation operators within the update and more importantly updating a document using it's own values. which is what you want to achieve.

db.jobs.updateMany(
    {},
    [
        {
            '$set': {
                'parts': {
                    '$map': {
                        'input': '$parts',
                        'as': 'part',
                        'in': '$$part._id'
                    }
                }
            }
        }
    ]);

Unfortunately this is not possible for earlier Mongo versions. Specifically $arrayFilters allows you to filter an array but again prior to the new update syntax accessing own values within the update is not possible.

You'll have to iterate over all documents and do the update one by one in code.

Tom Slabbaert
  • 21,288
  • 10
  • 30
  • 43
  • 1
    This worked perfectly, thank you! Also a not to future readers, you cannot execute this in Robo3t, you need to be in mongo shell – nmfb Jul 23 '20 at 14:26
1

As @Tom Slabbaert mentioned in the other answer you will have to use updates with aggregation pipelines available from v4.2 if you want to update the documents in bulk in one operation.

As an alternative to using $map, if you want only a single value at the top level and the value is accessible using the dot notation. You can simply use $set with the dot notation.

db.jobs.updateMany({}, [{
  $set: { parts: "$parts._id" }
}])
thammada.ts
  • 5,065
  • 2
  • 22
  • 33