Home > OS >  mongoose findByIdAndUpdate array of object not working
mongoose findByIdAndUpdate array of object not working

Time:07-21

I try to update array of object with mongoose methodes. When i try with vanila JS it worked but with mongoose not.

model:

const exampleSchema = new mongoose.Schema({
    arrayOfObjects: [
        { name: String, id: mongoose.Schema.Types.ObjectId },
    ],
});

find and update by vanila js

const example = await Example.findById(req.body.propertyX);
const validIndex = example.arrayOfObjects.findIndex((v) => v.propertyY === req.body.Y);
if (validIndex === -1) {
    example.arrayOfObjects.push({ propertyY: req.body.Y, propertyZ: req.body.Z });
} else {
    example.arrayOfObjects[validIndex] = { propertyY: req.body.Y, propertyZ: req.body.Z };
    console.log('update');
}
await recipe.save();

but when I try use findByIdAndUpdate , $set methode dont work (even $push not working...push is pushing new object id without req.body fields)

mongoose findByIdAndUpdate

const example = await Example.findByIdAndUpdate(req.body.x, {
    // arrayOfObjects: { $push: { propertyY: req.body.Y, propertyX: req.body.X} },
    $set: { 'arrayOfObjects.$.propertyY': req.body.Y, 'arrayOfObjects.$.propertyX': req.body.X },
});

CodePudding user response:

The issue is with your understand of the positional operator $, from the docs:

the positional $ operator acts as a placeholder for the first element that matches the query document, and

This means it excepts to find a match in the array based on the query, in your case the query does not contain anything regarding the voted array, so you get the following error:

[The positional operator did not find the match needed from the query.]


So what can we do? actually doing the update you want is not so trivial, it only became possible in recent years with the introduction of pipelined updates which allow you to use aggregation operators in your update body, now we can do what you want like so:

db.collection.findByIdAndUpdate(req.body.postId,
[
  {
    $set: {
      voted: {
        $ifNull: [
          "$voted",
          []
        ]
      }
    }
  },
  {
    $set: {
      voted: {
        $concatArrays: [
          {
            $filter: {
              input: "$voted",
              cond: {
                $ne: [
                  "$$this.voterId",
                  req.body.userId
                ]
              }
            }
          },
          [
            {
              $mergeObjects: [
                {
                  $ifNull: [
                    {
                      $arrayElemAt: [
                        {
                          $filter: {
                            input: "$voted",
                            cond: {
                              $eq: [
                                "$$this.voterId",
                                req.body.userId
                              ]
                            }
                          }
                        },
                        0
                      ]
                    },
                    {}
                  ]
                },
                {
                  voteRank: req.body.rank,
                  voterId: req.body.userId
                }
              ]
            }
          ]
        ]
      }
    }
  }
])

Mongo Playground

You can drop the $mergeObjects operator if you don't need it, I added it incase the object could have additional properties that you want to preserve throughout an update. but probably not the case. It then simplifies the code a little:

db.collection.findByIdAndUpdate(req.body.postId,
[
    {
        $set: {
            voted: {
                $ifNull: [
                    '$voted',
                    [],
                ],
            },
        },
    },
    {
        $set: {
            voted: {
                $concatArrays: [
                    {
                        $filter: {
                            input: '$voted',
                            cond: {
                                $ne: [
                                    '$$this.voterId',
                                    req.body.userId,
                                ],
                            },
                        },
                    },
                    [
                       {
                          voteRank: req.body.rank,
                          voterId: req.body.userId
                       }
                    ],
                ],
            },
        },
    },
]);
  • Related