Setting Object IDs in updateMany

Hello.

I’m using mongoose in Express and I need to add IDs to many objects inside of array inside of array. Assume that my schema looks like this:

{
   _id: something,
   property:value,
   animals: [
     {
       dogs: [
         { name: 'Dog1' },
         { name: 'Dog2' }
       ],
       cats: [
         { name: 'Cat1' },
         { name: 'Cat2' }
       ]
     }
   ]
}

I want to achieve something like this:

{
   _id: something,
   property:value,
   animals: [
     {
       dogs: [
         { name: 'Dog1', _id: uniqueID },
         { name: 'Dog2', _id: uniqueID }
       ],
       cats: [
         { name: 'Cat1', _id: uniqueID },
         { name: 'Cat2', _id: uniqueID }
       ]
     }
   ]
}

My code

db.collection('collectionName').updateMany(
  { animals: { $ne: [] } },
       {
          $set: {
            'animals.$[].cats.$[]._id': mongoose.Types.ObjectId(),
            'animals.$[].dogs.$[]._id': mongoose.Types.ObjectId()
          }
       }
 );

It works but IDs are exactly the same. Now I know mongoose.Types.ObjectId() is a client-side function and it returns only once when running query so I have the same IDs in all objects.

How to achieve unique IDs?

Hi @Patryk_Luczak and welcome to MongoDB community forums!!

I believe we can achieve this by using application-level code. Therefore, I have attempted to achieve the same using the following code. I’m sharing the code for your reference:

mongoose.connect('<ConnectionString>', { useNewUrlParser: true, useUnifiedTopology: true })
    .then(() => {
        const AnimalSchema = new mongoose.Schema({
            animals: [{
                cats: [{ name: String, _id: mongoose.Types.ObjectId }],
                dogs: [{ name: String, _id: mongoose.Types.ObjectId }]
            }]
        },
        { collection: "<collectionName>"});
        const AnimalModel = mongoose.model('Animal', AnimalSchema);

        AnimalModel.find({ animals: { $ne: [] } }).then(animals => {
            const bulkWriteOperations = [];

            animals.forEach(animal => {
                animal.animals.forEach(({ cats, dogs }) => {
                    cats.forEach(cat => {
                        bulkWriteOperations.push({
                            updateOne: {
                                filter: { _id: animal._id, 'animals.cats.name': cat.name },
                                update: { $set: { 'animals.$[i].cats.$[j]._id': mongoose.Types.ObjectId() } },
                                arrayFilters: [{ 'i.cats.name': cat.name }, { 'j.name': cat.name }]
                            }
                        });
                    });

                    dogs.forEach(dog => {
                        bulkWriteOperations.push({
                            updateOne: {
                                filter: { _id: animal._id, 'animals.dogs.name': dog.name },
                                update: { $set: { 'animals.$[i].dogs.$[j]._id': mongoose.Types.ObjectId() } },
                                arrayFilters: [{ 'i.dogs.name': dog.name }, { 'j.name': dog.name }]
                            }
                        });
                    });
                });
            });

            AnimalModel.bulkWrite(bulkWriteOperations).then(result => {
                console.log(`${result.modifiedCount} documents updated`);
                mongoose.connection.close();
            });
        });
    })
    .catch(err => console.error(err));

The output for the following would be:

[
  {
    _id: ObjectId("6448ba860bc375d62e0654fd"),
    property: 'sampletest1',
    animals: [
      {
        dogs: [
          { name: 'Dog1', _id: ObjectId("6448bf41f1fa51ee3fa874b2") },
          { name: 'Dog2', _id: ObjectId("6448bf41f1fa51ee3fa874b3") }
        ],
        cats: [
          { name: 'Cat1', _id: ObjectId("6448bf41f1fa51ee3fa874b0") },
          { name: 'Cat2', _id: ObjectId("6448bf41f1fa51ee3fa874b1") }
        ]
      }
    ]
  }
]

In the above code, forEach has been used with BulkWrite to achieve the results.

Additionally, for my own understanding, could you please explain the reason why you are modeling the _id in such a way? Generally in MongoDB, the _id field in a subdocument is added as a reference to another collection in the document.

Let us know if you have any further queries.

Regards
Aasawari

1 Like