Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save does not correctly $set subdocuments for timestamp updates #11603

Closed
Vogel612 opened this issue Mar 31, 2022 · 2 comments
Closed

Save does not correctly $set subdocuments for timestamp updates #11603

Vogel612 opened this issue Mar 31, 2022 · 2 comments
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Milestone

Comments

@Vogel612
Copy link

Vogel612 commented Mar 31, 2022

What is the current behavior?

When updating a subdocument with javascript and save(), the behaviour is different from dispatching an equivalent $set operation.
Notably this is relevant for timestamps on subdocuments.

If the current behavior is a bug, please provide the steps to reproduce.

The following test case demonstrates the issue observed.

import mongoose from "mongoose";
import assert from "assert";

describe("Test Repro for $set vs. save issues", () => {
    const childSchema = new mongoose.Schema({
        name: String
    }, { timestamps: { createdAt: "created", updatedAt: false }, _id: false });
    const testSchema = new mongoose.Schema({ child: childSchema, age: Number});
    before(async () => {
        await mongoose.connect('mongodb://localhost:27017/test');
    })
    beforeEach(async () => {
        await mongoose.connection.dropDatabase();
    });
    after(async () => {
        await mongoose.disconnect();
    });
    it("Mongoose $set behaves as expected", async () => {
        const Test = mongoose.model('Test', testSchema);

        await Test.create({ age: 10 });
        await Test.findOneAndUpdate({ child: { $exists: false } }, { $set: { child: { name: "Update Creation" }}});

        const updatedParent: any = await Test.findOne({ age: 10 });
        assert(typeof updatedParent.child.created !== "undefined");
    });
    it("Mongoose save behaves as expected", async () => {
        const Test = mongoose.model('Test', testSchema);
        await Test.create({ age: 10 });

        const parentToChange: any = await Test.findOne({ child: { $exists: false }});
        parentToChange.child = { name: "Update Creation" };
        await parentToChange.save();
        
        const updatedParent: any = await Test.findOne({ age: 10 });
        assert(typeof updatedParent.child.created !== "undefined");
    });
});

What is the expected behavior?

Both test cases should pass, yet only the explicit $set behaves as expected.

What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version.

I observed the behaviour after updating to version 6.2.9 from previously 5.11.2. In the 5.x version, this worked as expected.

@IslandRhythms IslandRhythms added the confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. label Mar 31, 2022
@IslandRhythms
Copy link
Collaborator

const mongoose = require('mongoose');

const assert = require('assert');

const childSchema = new mongoose.Schema({
    name: String
}, { timestamps: {createdAt: "created", updatedAt: false}, _id: false});

const testSchema = new mongoose.Schema({ child: childSchema, age: Number});

async function run() {
    await mongoose.connect('mongodb://localhost:27017');
    await mongoose.connection.dropDatabase();

    const Test = mongoose.model('Test', testSchema);

    await Test.create({ age: 10 });
    await Test.findOneAndUpdate({ child: { $exists: false } }, { $set: { child: { name: "Update Creation" }}});

    let updatedParent = await Test.findOne({ age: 10 });
    assert(typeof updatedParent.child.created !== "undefined");

    await Test.create({ age: 12 });

    const parentToChange = await Test.findOne({ child: { $exists: false }});
    parentToChange.child = { name: "Update Creation" };
    await parentToChange.save();
    
    updatedParent = await Test.findOne({ age: 12 });
    assert(typeof updatedParent.child.created !== "undefined"); // fails
}

run();

@vkarpov15
Copy link
Collaborator

Confirmed. It looks like the cause of this issue is that, because subdocument isNew is the same as parent isNew after #7048, schema timestamps aren't working correctly for subdocuments. We're working on a fix and should have a fix released within the next couple of days.

vkarpov15 added a commit that referenced this issue Apr 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Projects
None yet
Development

No branches or pull requests

3 participants