How to add a modifier to a nested document with mongodb

I have this model
ex:

    const PostSchema = nongoose.Schema(
      {
    body: {
      type: String,
      trim: true,
      maxlength: 250,
      required: true,
    },
    comments: {
      required: true,
      type: [
        {
          creatorId: {
            type: String,
            required: true,
          },
          creator: {
            type: String,
            required: true,
          },
          timestamp: Number,
          body: {
            type: String,
            required: true,
            trim: true,
          },
          likes: {
            type: [String],
            required: true,
          },
          replies: {
            require: true,
            type: [
              {
                creatorId: String,
                creatorName: String,
                timestamp: Number,
                body: {
                  type: String,
                },
              },
            ],
          },
        },
      ],
    },
      },
      {
    timestamps: true,
      }
    );

I want to append isReady= true to each comments every time I get all the posts .
Basically i do not want to add it to my db

Hello @Dimer_Bwimba_Mihanda.

In such case, you are trying to transform your data. You need to use an Aggregation Query with the $addFields stage. See example code in the MongoDB Manual for Adding Fields to an Embedded Document.

2 Likes

thank you very much, very helpful

but tho
I have the following collection

{
        "likes": [],
        "_id": "6086f47a3e8c0411f0a66d22",
        "creator": "dimer",
        "picture": "",
        "title": "",
        "body": "hello world",
        "comments": [
            {
                "isReady": true,
                "likes": [],
                "_id": "6086fcf33e8c0411f0a66d25",
                "creatorId": "607e50a16e852544d41a1d9d",
                "creator": "dimer",
                "body": "hello worl nigger",
                "replies": [],
                "timestamp": 1619459315854
            },
        ],
        "createdAt": "2021-04-26T17:12:26.632Z",
        "updatedAt": "2021-04-27T04:22:28.159Z",
        "__v": 0
    }

I want to push into comment.replies a new reply if the comment and the post exists. How to Update or Insert object into a nested array with conditions?

This means you already know the post id (or some other identifier), and the comments’s id. It is an update operation on the post collection. For this you need to use arrayFilters option for an update method.

Here are some examples of updating a nested array based upon conditions:

3 Likes

wt i want to do is to push an reply object on the comment.replies point if the post and the comment exist , how would you do it man please … i’m kindda confused for three days now .

Hello @Dimer_Bwimba_Mihanda, here is the update statement for your requirement; I have used the sample document from your earlier reply.

var LIKES = "like working with arrays" // substitute your value here

db.collection.updateOne(
    { _id: "6086f47a3e8c0411f0a66d22", "comments._id": "6086fcf33e8c0411f0a66d25" },
    { $push: { "comments.$[elem].likes" : LIKES } },
    { arrayFilters: [ { "elem._id": "6086fcf33e8c0411f0a66d25" } ] }
  )
1 Like

This is the response

{
    "n": 1,
    "nModified": 1,
    "opTime": {
        "ts": "6956576048502800385",
        "t": 20
    },
    "electionId": "7fffffff0000000000000014",
    "ok": 1,
    "$clusterTime": {
        "clusterTime": "6956576048502800385",
        "signature": {
            "hash": "1HeawKdZ//28Bu9f7UXBHlejnto=",
            "keyId": "6933816398326005762"
        }
    },
    "operationTime": "6956576048502800385"
}

this is what i tried

    const reply = {
      creatorId: user_ID,
      creator: req.body.creator,
      body: req.body.body,
      timestamp: new Date().getTime(),
    };
    console.log("reply", reply);
   // substitute your value here

    return await PostModel.updateOne(
      {
        _id: post_ID,
        "comments._id": comment_ID,
      },
      { $push: { "comments.$[elem].replies": reply } },
      { arrayFilters: [{ "elem._id": comment_ID }] },
        (err, doc) => {
          return res.status(201).send(doc);
        }
    );

I try to add a new object to the replies

Hello @Dimer_Bwimba_Mihanda, now you can query the collection and see if the new reply is added to the comments.replies array of that particular post’s document.

1 Like

you’re amazing man . please bear with me man one last time , :pleading_face: why Am i getin that response please . i wanna get the the new doc that i just added please

That is the document the updateOne method returns. It has details like matchedCount, modifiedCount, etc. The Mongoose ODM and your deployment might have some variation about it (I think that is what you are seeing).

To get the updated document in the update’s return value, you need to use findOneAndUpdate or findAndModify methods. Both the methods have a parameter where you can specify to return the updated document. Note that the Mongoose ODM has corresponding methods, but may have slightly different syntax.

1 Like

For my comments it was pretty easy …

return await PostModel.findByIdAndUpdate(
     post_ID,
      {
        $addToSet: {
          comments: {
            creatorId: req.body.creatorId,
            creator: req.body.creator,
            body: req.body.body,
            likes: [],
            replies: [],
            timestamp: new Date().getTime(),
          },
        },
      },
      { new: true },
      (err, docs) => {
        if (!err) return res.send({ docs });
        else return res.status(400).send(err);
      }
    );

I wanna do the same with my replies , but I cant access it chrome-capture

Hello @Dimer_Bwimba_Mihanda, I think you can use the Mongoose’s findOneAndUpdate method, and this allows specify a filter and the arrayFilters as option.

i fund the solution :dancer: :dancer: :dancer: :dancer: :dancer: :dancer: :sweat: thank you man

 const query = { _id: post_ID };
    const update = { $push: { "comments.$[elem].replies": reply } };
    const options = { new: true, arrayFilters: [{ "elem._id": comment_ID }] };
    await PostModel.findOneAndUpdate(query, update, options);
    let updated = await PostModel.findOne({ _id: post_ID });

    return res.status(200).send({
      data: updated.comments.find((comment) => comment._id.equals(comment_ID)),
    });

This is another way (I think less work) to get the updated comments:

let updatedDoc = await PostModel.findOneAndUpdate(query, update, options);
const updatedComments = updatedDoc.comments 

- or -

You can specify projection option to get the updated comments:

const options = { 
    new: true, 
    arrayFilters: [{ "elem._id": comment_ID }],
    projection: { _id: 0, comments: 1 }  
};

let updatedComments = await PostModel.findOneAndUpdate(query, update, options);
1 Like

how about error handling , i aint see nun ,
my replies model:

 replies: {
            require: true,
            type: [
              {
                isReady: {
                  type: Boolean,
                  default: true,
                },
                creatorId: {
                  type: String,
                  required: true,
                  trim: true,
                },
                creator: {
                  type: String,
                  required: true,
                  trim: true,
                },
                body: {
                  type: String,
                  required: true,
                  trim: true,
                  min: [3, "Must be at least 6, got {VALUE}"],
                },
                timestamp: Number,
              },
            ],
          },

i tried this but still not getting any error if the body is empty or the creator

await PostModel.findOneAndUpdate(query, update, options, (err, doc) => {
      // if (!err) return res.status(201).send(doc);

      return res.status(400).send(err);
    });

@Dimer_Bwimba_Mihanda, see this article on Promises and Callbacks - which has different ways of working with MongoDB collection methods in a NodeJS application. The content also includes exception handling. Also, some of the API methods have the error handling mechanism - see the API documentation. I suggest you figure yourself how your overall application works with errors that occur during the processing. Still better, do some research - there are quite a few articles online you can browse and learn from.

That said, there is also a MongoDB University course - MongoDB for Javascript Developers - its an online video based training and free. There you can learn important aspects of NodeJS / Javascript programming with MongoDB., including using various methods and error handling in an application.

1 Like

I read the doc . Then i try some … but why i can’t block it on the model level , like i pretty much specified that the creator is required but it keep on updating my collection anyway.

you how can you do it please , i’m just currious to know your solution … i’m kindda lost in all this tutorials

@Dimer_Bwimba_Mihanda, I have already provided you with a solution for the post’s question. If you have more questions please feel free to create new post / thread and look for answers. The links I had provided are useful for you to work further.

1 Like