Search code examples
node.jsmongooseschemamongoose-schema

Include only the path of Mongoose schema which is relevant to value of another path


Let's say I have a Schema like this:

Schema({
    username: String,
    privilege: {
        type: String,
        enum: ["admin", "pro", "user", "guest"],
        required: false,
        default: "user"
    },
    tagType: { // A little badge that appears next to users' names on the site.
        ///???
    }
});

For the tagType, I would like to be able to define something like this if privilege is admin:

{
    color: String,
    image: String,
    text: {
        type: String,
        required: true,
        enum: ["I'm the highest rank", "I'm an admin", "Admin privileges are great"]
    },
    dateAdded: Date
}

Meanwhile, if privilege is pro:

{
    color: String,
    text: {
        type: String,
        required: false,
        enum: ["This website is great", "I'm am a Pro user", "I get a nice tag!"],
        default: "This website is great"
    },
    dateAdded: Date
}

Meanwhile, for all user or guest entries, no option should be allowed.

Is this sort of dynamic cross-checking possible? I have tried reading through the docs, but despite things that seem much more complex being in there, I couldn't find any way to achieve this.


Solution

  • You can implement this by using Discriminators

    const mongoose = require("mongoose");
    const Schema = mongoose.Schema;
    
    const userSchema = new Schema(
      {
        username: String,
        privilege: {
          type: String,
          enum: ["admin", "pro", "user", "guest"],
          required: false,
          default: "user"
        }
      },
      { discriminatorKey: "privilege" }
    );
    
    const User = mongoose.model("User", userSchema);
    
    User.discriminator(
      "admin",
      new Schema({
        tagType: {
          color: String,
          image: String,
          text: {
            type: String,
            required: true,
            enum: ["I'm the highest rank","I'm an admin","Admin privileges are great"]
          },
          dateAdded: { type: Date, default: Date.now }
        }
      })
    );
    
    User.discriminator(
      "pro",
      new Schema({
        tagType: {
          color: String,
          text: {
            type: String,
            required: false,
            enum: ["This website is great","I'm am a Pro user","I get a nice tag!"],
            default: "This website is great"
          },
          dateAdded: { type: Date, default: Date.now }
        }
      })
    );
    
    module.exports = User;
    

    With this model and schema you can create a user with tagType it will be ignored.

    Sample code to create user:

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

    Sample request body:

    {
        "username": "user 1",
        "tagType": {
            "color": "red",
            "text": "I'm an admin"
        }
    }
    

    Response:

    {
        "privilege": "user",
        "_id": "5dfa07043a20814f80d60d6b",
        "username": "user 1",
        "__v": 0
    }
    

    Sample request to create an admin: (note that we added "privilege": "admin")

    {
        "username": "admin 1",
        "privilege": "admin",
        "tagType": {
            "color": "red",
            "text": "I'm an admin"
        }
    }
    

    Response: (note that tagType is saved)

    {
        "_id": "5dfa07a63a20814f80d60d6d",
        "privilege": "admin",
        "username": "admin 1",
        "tagType": {
            "color": "red",
            "text": "I'm an admin",
            "dateAdded": "2019-12-18T11:04:06.461Z"
        },
        "__v": 0
    }