2

I have the following schema:

const itemsSchema = new mongoose.Schema({
    name: String,
    checkedValue: String
});

const listSchema = new mongoose.Schema({
    name: String,
    items: [itemsSchema]
});

I want to update data like this: listSchema>items>checkedValue. I have performed the following operation:

db.collections.update({
  "name": "Home"
},
{
  "$set": {
    "items.$[i].checkedValue": "On"
  }
},
{
  arrayFilters: [
    {
      "i.items._id": ObjectId("646ec7836f8ba817e80e3372")
    }
  ]
})

But I am getting an error no document found. How can I update the specific document?

Sample data:

[
  {
    _id: ObjectId("646ec7916f8ba817e80e3377"),
    name: 'Home',
    items: [
      {
        name: 'home 1',
        checkedValue: 'Off',
        _id: ObjectId("646ec7966f8ba817e80e3380")
      },
      {
        name: 'home 2',
        checkedValue: 'Off',
        _id: ObjectId("646ec79a6f8ba817e80e338c")
      },
      {
        name: 'home 3',
        checkedValue: 'Off',
        _id: ObjectId("646efd38b1bc960c52ce5078")
      }
    ],
    __v: 3
  },
  {
    _id: ObjectId("646ed4136f8ba817e80e339b"),
    name: 'School',
    items: [
      {
        name: 'School 1',
        checkedValue: 'Off',
        _id: ObjectId("646efd45b1bc960c52ce509c")
      },
      {
        name: 'School 2',
        checkedValue: 'Off',
        _id: ObjectId("646efd4cb1bc960c52ce50a9")
      },
      {
        name: 'School 3',
        checkedValue: 'Off',
        _id: ObjectId("646efd52b1bc960c52ce50b1")
      }
    ],
    __v: 4
  }
]
Fahad
  • 65
  • 1
  • 5
  • It is because the objectId:646ec7836f8ba817e80e3372 you search for in items , is not in the name:Home document – R2D2 May 25 '23 at 06:42

3 Answers3

1

the object id you gave in the array filter is not in the items list. anyway even if it did the way you have written arrayFilters has to be changed to i._id. Think of i as an individual element in the items array

db.collection.update({
  name: "Home"
},
{
  $set: {
    "items.$[i].checkedValue": "On"
  }
},
{
  arrayFilters: [
    {
      "i._id": ObjectId("646ec7966f8ba817e80e3380")
    }
  ]
})

playground

cmgchess
  • 7,996
  • 37
  • 44
  • 62
  • It works fine at the link you have provided. But maybe it's deprecated now. So, I am getting an error. Alternatively I have tried to use ```List.collection.findOneAndUpdate({name:listName},{$set:{"items.$[i].checkedValue":"Off"}},{arrayFilters:[{"i._id":checkedItemId}]}); ```. But the value is not updating. – Fahad May 25 '23 at 09:06
  • Hi which update method did you use. updateone? Update many? Is your model List. If so shouldn't it be list.update? – cmgchess May 25 '23 at 09:35
  • I have tried both ```updateOne()``` and ```findOneAndUpdate()``` method. My model name is ```List```. I have tried using ```List.collection.findOneAndUpdate()``` and ```List.findOneAndUpdate()```. But, the value is not changing. Thanks. – Fahad May 25 '23 at 10:05
  • btw which mongodb version are you using. also which mongoose version – cmgchess May 25 '23 at 11:24
  • mongodb: 5.5.0, mongoose: 7.2.0 – Fahad May 25 '23 at 12:40
  • Not really sure why it isn't working maybe try going through GitHub issues? https://github.com/Automattic/mongoose/issues?q=Arrayfilters+ – cmgchess May 25 '23 at 14:25
  • Actually I am taking the value for the ```arrayFilter``` by ```const checkedItemId = req.body.checkedItemId;```. The ```req.body``` part send me back the id as an string. Should I convert it to an ObjectId before passing it to the ```arrayFilter```? I have tried that also by ```const checkedItemId = new mongoose.Types.ObjectId(req.body.checkedItemId)``` but it popoulates an ObjectId with a **new** keyword in front. Which may be not equal to the original `_id` saved in the database. Is there any way to remove the new keyword from the front? – Fahad May 25 '23 at 14:38
  • yes! you need to cast it to an ObjectId. don't use new https://stackoverflow.com/a/8393613/13583510 – cmgchess May 25 '23 at 16:17
1

First of all, your written query is incorrect as my perspective. Because you have used db.collections.update({ }). In here, it should be db.collection.update({ })

After that, to perform the query you need to write it as,

arrayFilters: [
    {
      "i._id": ObjectId("646ec7966f8ba817e80e3380")
    }
  ]

Then your first index item will be updated.

  • Thanks for your correction. ```update``` is deprecated now. So it is not recognized. Rather I have tried this ```List.collection.findOneAndUpdate({name:listName},{$set:{"items.$[i].checkedValue":"Off"}},{arrayFilters:[{"i._id":checkedItemId}]});```. But the value is not updating. – Fahad May 25 '23 at 09:08
  • @Fahad If it was deprecated, you can find another solution from [mongoose documentation](https://mongoosejs.com/docs/documents.html) – Kanchana Kariyawasam May 25 '23 at 09:16
0

When sending value to the arrayFilter, I have to convert the _id to an ObjectId. This is done by the following code:

const checkedItemId = req.body.checkedItemId;
const checkedItemObjectId = new mongoose.Types.ObjectId(checkedItemId);

And to update the value in the subdocument, the following updateOne() method worked fine for me:

                List.collection.updateOne(  
                    { 'items.checkedValue': 'Off' },
                    { '$set': { 'items.$[element].checkedValue': 'On' } },
                    {
                      arrayFilters: [
                        {
                          'element._id': { '$eq': checkedItemObjectId }
                        }
                      ]
                    })
Fahad
  • 65
  • 1
  • 5