Mendi Sterenfeld
Mendi Sterenfeld

Reputation: 397

Mongoose: only one unique boolean key should be true

I have two schema collections:

Campaigns:{
   _id: "someGeneratedID"
   //...a lot of key value pairs.
   //then i have teams which is an, array of teams from the Team schema.
   teams:teams: [{ type: mongoose.Schema.Types.ObjectId, ref: "Team" }],
}

Teams:{
    campaignId: { type: mongoose.Schema.Types.ObjectId, ref: "Campaign" },
    isDefault: { type: Boolean, default: false },
}

Now I would like that when I add teams to the collection, it should throw an error if there are more than 2 isDefault:true per campaignId.

so the following shouldn't be allowed:

teams:[
   {
    campaignId:1,
    teamName:"John Doe"
    isDefault:true,
   }
   {
    campaignId:1,
    teamName:"Jane Doe"
    isDefault:true
   }
]

I found this answer on SO:

teamSchema.index(
  { isDefault: 1 },
  {
    unique: true,
    partialFilterExpression: { isDefault: true },
  }

But couldn't manage to also check for the campaignId.

Thanks in advance.

ps: can you also provide an explanation for what's happening in the index method?

Upvotes: 0

Views: 313

Answers (1)

amda
amda

Reputation: 354

I think that the simplest way to approach this is via Mongoose's middleware pre('save'). This method will give you a way to check all the campaigns listed in the collection in order to check if any of the items is already set as default.

teamSchema.pre("save", async function (next) {
  try {
    if ((this.isNew || this.isModified("isDefault") && this.isDefault) {
      const previousDefault = await mongoose.models["Team"].findOne({ isDefault: true, campaignId: this.campaignId });
      if (previousDefault) {
        throw new Error('There is already default team for this campaign');
      }
    }
    next();
  } catch (error) {
    throw error;
  }
});

This way, if any team, either new or already existing, is set as default for a given campaign, before its record is saved, the whole collection will be searched for any entry with isDefault already set to true. If at least one item is found, we will throw an error. If not, next() guarantees the save() method will go on.

Upvotes: 1

Related Questions