Search code examples
mongodbmongoosesavedefault

Why mongoose save() doesn't update existing document with default values which are a nested schema?


I have the following schema mongoose:

const mongoose = require('mongoose')
const Schema = mongoose.Schema

const getRating = ({ maxScore }) => ({
  type: new Schema(
    {
      score: {
        type: Number,
        default: 0
      },
      maxScore: {
        type: Number,
        default: function() {
          console.log('inside default')
          return maxScore
        }
      }
    },
    { _id: false }
  )
})

const BrandRating = new Schema(
  {
    varietyOfProducts: {
      overall: getRating({ maxScore: 18 }),
      userFeedback: getRating({ maxScore: 9 }),
      other: getRating({ maxScore: 9 })
    }
  { _id: false }

const Brand = new Schema({
  rating: {
    type: BrandRating,
    required: true
  },
  slug: String
)

I have already a lot of Brand documents in mongodb and now I need to rating object which is a nested schema with each field also being a nested schema with default values. So now I want to run over all Brand documents in the db and just save them so that the defaults are applied. However, they're not applied and I'm not sure what the problem is:

    const [brand] = await Brand.find({ 'slug': 'test' })
    await brand.save()
    console.log('brand', brand) // doesn't have any of the new rating default values, logs { slug: 'test' }  
    // { slug: 'test' }  

Also I'm not seeing that the console.log statement is even called inside default mongoose function.


Solution

  • After some help from @thammada.ts I finally figured out how to set defaults. The key takeaway is to set an empty object as the default on BrandRating schema and define the actual default on the lowest possible level that is inside getRating:

    const mongoose = require('mongoose')
    const Schema = mongoose.Schema
    
    const getRating = ({ maxScore }) => ({
      type: new Schema(
        {
          score: {
            type: Number
          },
          maxScore: {
            type: Number
          }
        },
        { _id: false }
      ),
      default: {
        score: 0,
        maxScore
      }
    })
    
    const BrandRating = new Schema(
      {
        varietyOfProducts: {
          overall: getRating({ maxScore: 18 }),
          userFeedback: getRating({ maxScore: 9 }),
          other: getRating({ maxScore: 9 })
        }
      { _id: false }
    
    const Brand = new Schema({
      rating: {
        type: BrandRating,
        required: true,
        default: {}
      },
      slug: String
    )