NestJs: Mongodb ignores certain fields when pushing new items into subarray

34 Views Asked by At

I'm having a hard time figuring out why something so ordinary behaves so weird.

The following is a mongoose schema configuration I use in my nestjs application to describe a subdocument item in an array located in a broader user schema (user.rewards[]):

@Schema()
export class Reward {
    // _id is automatically generated by mongoose
    _id: Types.ObjectId

    @Prop({
        type: String,
        required: true,
        minlength: 3,
        maxlength: 128,
    })
    name: string

    @Prop({
        type: String,
        required: true,
    })
    thumbnailUrl: string

    @Prop({
        type: String,
        required: true,
    })
    iconUrl: string

    @Prop({
        type: String,
        required: true,
        minlength: 10,
    })
    description: string

    @Prop({
        type: String,
        required: true,
        regex: /^#[0-9A-Fa-f]{6}$/i,
    })
    color: string

    @Prop({
        type: Number,
        min: 1,
        max: 999,
        default: 1,
    })
    pointsThreshold?: number

    @Prop({
        type: Number,
        min: 5,
        max: 60 * 24 * 3, // 3 days
        default: 5,
    })
    duration?: number

    @Prop({
        type: Date,
        default: Date.now,
    })
    createdAt?: Date

    @Prop({
        type: Boolean,
        default: true,
    })
    isAvailable?: boolean
}

When casually invoking the user.findByIdAndUpdate method to update the rewards subarray (which contains said schema), it behaves a bit unexpectedly. I'll provide an example:

// the new reward payload
{
    "name": "Vanilla break",
    "description": "Get a refreshing walk, on the way to McDonalds, don’t forge to treat yourself with a Single ice cream. Because why more?",
    "color": "#b1c2e4",
    "pointsThreshold": 50,
    "duration": 60,
    "thumbnailUrl": "http://url.com/some-thumbnail.jpg",
    "iconUrl": "https://url.com/some-icon.svg"
}

// => result object
{
    "name": "Vanilla break",
    "description": "Get a refreshing walk, on the way to McDonalds, don’t forge to treat yourself with a Single ice cream. Because why more?",
    "pointsThreshold": 50,
    "iconUrl": "http://url.com/some-icon.svg",
    "isAvailable": true,
    "createdAt": "2024-02-26T02:48:26.484Z"
    "_id": "68dbfbaaeaec7e1b9e3030e5",
    // `color`, `duration` and `schema` are missing.
}

As you can see, the actual object in mongodb seems to lack some fields, the types of fields that woudn't make sense to lack, given the previous schema.

This is how the logic works to achieve this:

/**
 * Method creates a new reward.
 * @param user the user object.
 * @param reward the new reward payload.
 * @returns the newly created reward.
 */
async createReward(
    user: UserDocument,
    reward: CreateRewardDto,
    files: { thumbnail: Express.Multer.File; icon: Express.Multer.File }
): Promise<Reward> {
    try {
        // Upload image to GCS
        const thumbnailUrl = await this.uploadRewardFile(files.thumbnail, 'thumbnail'),
            iconUrl = await this.uploadRewardFile(files.icon, 'icon')

        // Create reward
        const res = await this.userModel.findByIdAndUpdate(
            user._id,
            { $push: { rewards: { ...reward, thumbnailUrl, iconUrl } } },
            { new: true }
        )

        // Return the last created reward from the new list.
        return res.rewards.at(-1)
    } catch (err) {
        console.error(err)
        throw err
    }
}

Could this be a schema issue, or just the way mongodb accepts objects? There isn't really much around this code to care about - assume all variables and parameters are correct.

0

There are 0 best solutions below