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

5.9.28 reset fields in edge case #9313

Closed
paglias opened this issue Aug 10, 2020 · 10 comments
Closed

5.9.28 reset fields in edge case #9313

paglias opened this issue Aug 10, 2020 · 10 comments
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Milestone

Comments

@paglias
Copy link
Contributor

paglias commented Aug 10, 2020

Do you want to request a feature or report a bug?
Bug

What is the current behavior?

Using lodash's set method (https://lodash.com/docs/4.17.15#set) to set a nested property is resulting in all the other properties of the nested object to be set to undefined.

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

const mongoose = require("mongoose");
const _ = require("lodash");
const Schema = mongoose.Schema;

const schema = new Schema({
  nested: {
    prop1: { type: Number, default: 50 },

    prop2: {
      type: String, enum: ['val1', 'val2'], default: 'val1', required: true,
    },
    prop3: {
      prop4: { type: Number, default: 0 },
    },
  },

});

const Model = mongoose.model("model", schema);

mongoose.connect("mongodb://localhost:27017/test").then(async () => {
  let doc = await Model.create({});
  console.log('nested.prop2 after creation', doc.nested.prop2); // nested.prop2 is correctly set to the default value `val1`

  doc = await Model.findOne(doc._id).exec();
  console.log('nested.prop2 after fetch', doc.nested.prop2);  // nested.prop2 is correctly `val1`

  _.set(doc, 'nested.prop1', 45); 

  console.log('nested.prop2 after _.set', doc.nested.prop2); //  // nested.prop2 is now undefined, as well as all other properties in the `nested` object apart from `nested.prop1`

   await doc.save(); // this will fail because nested.prop2 is required
}).catch(err => console.log(err));

What is the expected behavior?

The properties are set correctly. Note that for some strange reason removing prop3 from the schema the bug doesn't appear. It looks like it only happens when there are at least 3 nested properties.

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

It started happening in 5.9.28, 5.9.27 works fine. On node 12

@LudwikJaniuk
Copy link

You're right, I can reproduce it too in mongoose 5.9.28. I'm curious how it happens, will try to take a look...

@paglias
Copy link
Contributor Author

paglias commented Aug 10, 2020

Yeah the conditions to replicate it are pretty strange, I'm not sure why it's happening but looking at the changes in 5.9.28 it should be due to these lines 5.9.27...5.9.28#diff-e51f9847487dfe7cf44f998bf9b8b8e8R141

@LudwikJaniuk
Copy link

That link doesn't work for me...

@paglias
Copy link
Contributor Author

paglias commented Aug 10, 2020

5.9.27...5.9.28 the changes for lib/helpers/document/compile.js at the set method

@LudwikJaniuk
Copy link

Very likely you're right. For reference that commit mentions #9293

@LudwikJaniuk
Copy link

I've cooked it down to remove lodash from the equation. Essentially this is what lodash is doing as part of its algorithm, it seems. Just assigning the "nested" field to itself makes it disappear...

const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const util = require("util");

const schema = new Schema({
  nested: {
    prop1: { type: Number, default: 50 },
    prop2: {
      type: String, enum: ['val1', 'val2'], default: 'val1', required: true,
    },
    prop3: {
      prop4: { type: Number, default: 0 },
    },
  },

});

const Model = mongoose.model("model", schema);


mongoose.connect("mongodb://localhost:27017/test").then(async () => {
  let doc = await Model.create({});
  console.log('nested.prop2 after creation', doc.nested.prop2); // nested.prop2 is correctly set to the default value `val1`

  doc = await Model.findOne(doc._id).exec();
  console.log('nested.prop2 after fetch', doc.nested.prop2);  // nested.prop2 is correctly `val1`

  // NEW CODE HERE, LOOK!
  console.log(util.inspect(doc, true)); // Has all properties as before
  doc.nested = doc.nested;
  console.log(util.inspect(doc, true)); // Now, missing nested completely

  // The rest is unimportant...
  await doc.save(); // this will fail because nested.prop2 is required
}).catch(err => console.log(err));

@LudwikJaniuk
Copy link

This, however, works:

  //...
  console.log(util.inspect(doc, true)); // Has all properties as before
  let ne = doc.nested;
  doc.nested = {prop1: ne.prop1, prop2: ne.prop2, prop3: ne.prop3}
  console.log(util.inspect(doc, true)); // This way, it still works and has all the properties
  //...

but this does not:

  //...
  console.log(util.inspect(doc, true)); // Has all properties as before
  let ne = doc.nested;
  doc.nested = Object.assign(ne, {});
  console.log(util.inspect(doc, true)); // nested is missing
  //...

That tells me the other hidden $-properies mongoose adds are tripping it up, and the logic for them is flawed with the version 28 addition, just as you said.

@mcolmant
Copy link

We got the same problem here, we had to revert back to a previous Mongoose version.

@matanarbel-startapp
Copy link

Same issue here...

@AbdelrahmanHafez AbdelrahmanHafez added the confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. label Aug 11, 2020
@AbdelrahmanHafez AbdelrahmanHafez added this to the 5.9.29 milestone Aug 11, 2020
@vkarpov15
Copy link
Collaborator

Thanks for reporting this issue. Bug is fixed and fix will be in 5.9.29 👍

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

6 participants