node.js - nested array in mongoose findOneandUpdate with arrayFilter

Viewed 1045

I know this is a duplicate question. But I have hit the same road block a lot of other seem to hit in updating nested arrays. I am able to manually code an index value, but obviously thats not useful in practical api deployment. So can arrayFilters be used on an array with a length of 1? should i restructure the prospect model, and revert this to a subdocument? Any help would be great.

the console Error

MongoError: No array filter found for identifier 'elem' in path 'resoStatus.representation.$[elem].document'

The document in mongo

:
Object_id:5f15a5fe911928412c858fb0
name:"State POA"
postedDate:2020-07-19T05:56:19.738+00:00
assigned:5efd0c3d75bb7122943e3a49

req.params.id: 5f15a5fe911928412c858fb0

the function that doesnt work

router.put(
  "/:id/resoStatus/representation/:id",
  upload,
  auth,
  async (req, res) => {
    console.log(req.params.id);
    const prospect = await Prospect.findOneAndUpdate(
      { "_id": req.body.prospectId },
      {
        "$set": {
          "resoStatus.representation.$[elem].document": req.file.filename,
          "resoStatus.representation.$[elem].updatedDate": req.file.uploadDate,
          "resoStatus.representation.$[elem].id": req.body.id,
        },
        upsert: true,
        arrayFilters: [{ "elem._id": ObjectID(req.params.id) }],
      },
      (err) => {
        if (err) res.status(400).json(err);
      }
    );

    console.log(prospect.resoStatus.representation);
    res.status(200).json(prospect);
  }
);

the function that does work

router.put(
  "/:id/resoStatus/representation/:id",
  upload,
  auth,
  async (req, res) => {
    console.log(req.params.id);
    const prospect = await Prospect.findOneAndUpdate(
      { "_id": req.body.prospectId },
      {
        "$set": {
          "resoStatus.representation.0.document": req.file.filename,
          "resoStatus.representation.0.updatedDate": req.file.uploadDate,
          "resoStatus.representation.0.id": req.body.id,
        },
        upsert: true,
        arrayFilters: [{ "elem._id": ObjectID(req.params.id) }],
      },
      (err) => {
        if (err) res.status(400).json(err);
      }
    );

    console.log(prospect.resoStatus.representation);
    res.status(200).json(prospect);
  }
);

the mongoose model

 representation: [
      {
        document: String,
        name: String,
        postedDate: Date,
        id: String,
        updatedDate: Date,
        endpoint: String,
        assigned: { type: mongoose.Schema.Types.ObjectId, ref: "user" },
      }]

1 Answers

You can use the $-operator to update the first element that matches the query document:

"$set": {
          "resoStatus.representation.$.document": req.file.filename,
          "resoStatus.representation.$.updatedDate": req.file.uploadDate,
          "resoStatus.representation.$.id": req.body.id,
        }

If you need to update all array elements of the matched document, you can use the all-positional operator:

"$set": {
          "resoStatus.representation.$[].document": req.file.filename,
          "resoStatus.representation.$[].updatedDate": req.file.uploadDate,
          "resoStatus.representation.$[].id": req.body.id,
        }
Related