Typegoose: Updating a Doc with nested Objects has unexpected behaviour

Created on 4 Jul 2020  路  5Comments  路  Source: typegoose/typegoose

Versions

  • NodeJS: 13.12.0
  • Typegoose(NPM): 7.3.0-1
  • Typegoose(GIT): 0.0.0
  • mongoose: 5.9.21
  • mongodb: 0.0.0
  • nestjs / nestjs-typegoose: 7.1.28

What is the Problem?

When I have a nested Object, updating

  1. applies default values from prop({ default: xxxx}),
  2. does not apply validation prop({required: true})(which is correct)
  3. deletes fields that are not defined in the update object.

Code Example

class Baz {
    @prop({ default: 'You' })
    value1: string;
    @prop({ default: 'Me' })
    value2: string;
}

class Foo {
    @prop()
    aProp: number;
    @prop()
    baz: Baz;
}

interface FooDTO {
    aProp: number;
    baz: {
        value1: string;
        value2: string;
    }
}

class Test {

    constructor(
        @InjectModel(Foo)
        private readonly foo: ReturnModelType<typeof Foo>,
    ) {}
    async update(id: string, fooDTO: FooDTO): Promise<Foo> {
        const result = await this.foo.findByIdAndUpdate(
            id,
            fooDTO,
            { new: true },
        );
        console.log(result);
        return result;
    }
}

Do you know why it happens?

Currently I am clueless.

Reproducing the whole Issue takes me some more time I currently don't have.
Please let me know if you need a more detailed example and I will get to it asap.

not a typegoose issue question

Most helpful comment

https://mongoosejs.com/docs/api.html#model_Model.findByIdAndUpdate

See:

[options.overwrite=false] 芦Boolean禄 By default, if you don't include any update operators in update, Mongoose will wrap update in $set for you. This prevents you from accidentally overwriting the document. This option tells Mongoose to skip adding $set. An alternative to this would be using Model.findOneAndReplace({ _id: id }, update, options, callback).

All 5 comments

https://mongoosejs.com/docs/api.html#model_Model.findByIdAndUpdate

See:

[options.overwrite=false] 芦Boolean禄 By default, if you don't include any update operators in update, Mongoose will wrap update in $set for you. This prevents you from accidentally overwriting the document. This option tells Mongoose to skip adding $set. An alternative to this would be using Model.findOneAndReplace({ _id: id }, update, options, callback).

this is not an typegoose issue, closing

Reading this again, it seems that your subdocument is getting overwritten. This is normal behavior since mongoose only wraps top level properties in $set.

You'd need to write an update by hand using mongo operators to update the nested document without overwriting it entirely.

        const result = await this.foo.findByIdAndUpdate(
            id,
            { $set: { aProp: foodto.aProp, "baz.value1": fooDto.baz.value1, "baz.value2": fooDto.baz.value2 } },
            { new: true },
        );

Take a look at set: https://docs.mongodb.com/manual/reference/operator/update/set/#up._S_set

Or, you can first retrieve it from the db, apply your updates, and then re-save it.

Either way, this is not really a typegoose issue. :)

Also take a look here for how to automate this: https://github.com/Automattic/mongoose/issues/5285

Yes my bad. Thank you for the support anyway! :)
I find this a bit counter intuitive from mongodb / mongoose.

I solved this by using dot-object

https://github.com/Automattic/mongoose/issues/5285#issuecomment-439378282

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rubenvereecken picture rubenvereecken  路  5Comments

gserracalahorra picture gserracalahorra  路  6Comments

kerolloz picture kerolloz  路  6Comments

AbderrazzakB picture AbderrazzakB  路  3Comments

hasezoey picture hasezoey  路  10Comments