Search code examples
mongodbmongoose

Mongoose schema set timestamp on nested document


I have a schema that looks as follows:

{
  "_id": "5073c76a23ce3abf0f000001",
  "asker": {
    "userId": "fooId",
    "firstName": "foo",
    "lastName": "bar",
    "points": "10",
    "aboutMe": "Something about me"
  },
  "isBounty": false,
  "tags": ["mongodb", "nosql", "mongodb-query"],
  "title": "Is there merit to adding flat properties additional to a duplicate nested",
  "descriptionMd": "Question description",
  "offeredPoints": 10,
  "slug": "is-there-merit-to-adding-flat-properties-additional-to-a-duplicate-nested",
  "biddings": [{
    "biddingId": "_biddingId",
    "respondent": {
      "userId": "fooId",
      "firstName": "foo",
      "lastName": "bar",
      "points": "10",
      "aboutMe": "Something about me"
    },
    "biddingPoints": 10,
    "createdAt": "2019-03-21T08:00:00",
    "lastUpdatedAt": "2019-03-21T08:00:00"
  }],
  "acceptedBidding": "_biddingId",
  "answers": [
    {
      "respondent": {
        "address": {
          "userId": "fooId",
          "firstName": "foo",
          "lastName": "bar",
          "points": "10",
          "aboutMe": "Something about me"
        }
      },
      "answerTextMd": "Answer 1",
      "reviewRequested": true,
      "reviewer": {
        "userId": "fooId",
        "firstName": "foo",
        "lastName": "bar",
        "points": "10",
        "aboutMe": "Something about me"
      },
      "reviewStatus": "ANSWER_ACCEPTED",
      "createdAt": "2019-03-21T08:00:00",
      "lastUpdatedAt": "2019-03-21T08:00:00"
    }
  ],
  "createdAt": "2019-03-21T08:00:00",
  "lastUpdatedAt": "2019-03-21T08:00:00"
}

This schema is meant for a Q&A forum and I prefer to keep all data embedded in the question document.

The requirements are as follows:

  • Timestamp created and updated are set on the Question document
  • Timestamp should be put on the nested biddings and answers as well

I know the default way to put a timestamp on the Question document:

const mySchema = new mongoose.Schema( {name: String}, {timestamps: true} );

How do I put the timestamp dynamically on the nested documents on update?
Is there an advised way of doing, or should I just put the fields there myself and update them manually?


Solution

  • You can also apply mongoose schema timestamps options to the inner schemas.

    For example in the following schema, I applied timestamps: true option to the inner biddings schema.

    const mongoose = require("mongoose");
    
    const forumSchema = new mongoose.Schema(
      {
        title: { type: String, required: true },
        biddings: [
          {
            type: new mongoose.Schema(
              {
                biddingId: String,
                biddingPoints: Number
              },
              { timestamps: true }
            )
          }
        ]
      },
      { timestamps: true }
    );
    
    const Forum = mongoose.model("Forum", forumSchema);
    
    module.exports = Forum;
    

    Now let's test it:

    I created a forum document with the following code:

    const Forum = require("../models/forum");
    
    router.post("/forums", async (req, res) => {
      const result = await Forum.create(req.body);
      res.send(result);
    });
    

    Request body:

    {
        "title": "Title 1",
        "biddings": [
            {
                "biddingId": "bidding1",
                "biddingPoints": 10
            },
            {
                "biddingId": "bidding2",
                "biddingPoints": 30
            }
        ]
    }
    

    Response: (as you see timestamps are both applied to the parent and sub documents)

    {
        "_id": "5e3073b3a2890b03b029e92c",
        "title": "Title 1",
        "biddings": [
            {
                "_id": "5e3073b3a2890b03b029e92e",
                "biddingId": "bidding1",
                "biddingPoints": 10,
                "createdAt": "2020-01-28T17:47:31.376Z",
                "updatedAt": "2020-01-28T17:47:31.376Z"
            },
            {
                "_id": "5e3073b3a2890b03b029e92d",
                "biddingId": "bidding2",
                "biddingPoints": 30,
                "createdAt": "2020-01-28T17:47:31.376Z",
                "updatedAt": "2020-01-28T17:47:31.376Z"
            }
        ],
        "createdAt": "2020-01-28T17:47:31.376Z",
        "updatedAt": "2020-01-28T17:47:31.376Z",
        "__v": 0
    }
    

    Now let's update the bidding point with the _id:5e3073b3a2890b03b029e92e

    router.put("/forums/:forumId/biddings/:biddingId",
      async (req, res) => {
        let points = req.body.points;
    
        try {
          let result = await Forum.findByIdAndUpdate(
            req.params.forumId,
            {
              $set: {
                "biddings.$[inner].biddingPoints": points
              }
            },
            {
              arrayFilters: [{ "inner._id": req.params.biddingId }],
              new: true
            }
          );
    
          if (!result) return res.status(404);
    
          res.send(result);
        } catch (err) {
          console.log(err);
          res.status(500).send("Something went wrong");
        }
      }
    );
    

    The url will be like this: http://.../forums/5e3073b3a2890b03b029e92c/biddings/5e3073b3a2890b03b029e92e

    Request: (it means I want to update the points to 50 of the bidding with _id:5e3073b3a2890b03b029e92e:

    {
        "points": 50
    }
    

    Response: (as you see updatedAt field value of the updated bidding changed automatically from 2020-01-28T17:47:31.376Z to 2020-01-28T17:50:03.855Z )

    {
        "_id": "5e3073b3a2890b03b029e92c",
        "title": "Title 1",
        "biddings": [
            {
                "_id": "5e3073b3a2890b03b029e92e",
                "biddingId": "bidding1",
                "biddingPoints": 50,
                "createdAt": "2020-01-28T17:47:31.376Z",
                "updatedAt": "2020-01-28T17:50:03.855Z"   ==> UPDATED
            },
            {
                "_id": "5e3073b3a2890b03b029e92d",
                "biddingId": "bidding2",
                "biddingPoints": 30,
                "createdAt": "2020-01-28T17:47:31.376Z",
                "updatedAt": "2020-01-28T17:47:31.376Z"
            }
        ],
        "createdAt": "2020-01-28T17:47:31.376Z",
        "updatedAt": "2020-01-28T17:50:03.855Z",
        "__v": 0
    }