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

Map in EmbededDocument, the set function error! #10295

Closed
winnochan opened this issue May 26, 2021 · 3 comments
Closed

Map in EmbededDocument, the set function error! #10295

winnochan opened this issue May 26, 2021 · 3 comments
Assignees
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Milestone

Comments

@winnochan
Copy link

winnochan commented May 26, 2021

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

bug

What is the current behavior?

define a two level Map field in a NestedDocument, when call the inner Map's function (set), it will not automatically add the modified path to the path list.

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

const mongoose = require("mongoose");

const SecondMapSchema = new mongoose.Schema({
  data: { type: Map, of: Number, default: {}, _id: false },
});

const FirstMapSchema = new mongoose.Schema({
  data: { type: Map, of: SecondMapSchema, default: {}, _id: false },
});

const NestedSchema = new mongoose.Schema({
  data: { type: Map, of: SecondMapSchema, default: {}, _id: false },
});

const TestSchema = new mongoose.Schema({
  _id: Number,
  firstMap: { type: Map, of: FirstMapSchema, default: {}, _id: false },
  nested: { type: NestedSchema, default: {}, _id: false },
});

const Test = mongoose.model("Test", TestSchema);

async function test() {
  await mongoose.connect("mongodb://localhost:27017", {
    useCreateIndex: true,
    useNewUrlParser: true,
    useUnifiedTopology: true,
    useFindAndModify: false,
  });
  const doc = await Test.create({ _id: Date.now() });
  console.log(doc);

  // It's Ok!
  doc.firstMap.set("fisrt", {});
  console.log("Should include firstMap.first", doc.modifiedPaths());
  await doc.save();

  // It's Ok!
  doc.firstMap.get("fisrt").data.set("second", {});
  console.log("Should include firstMap.first.data.second", doc.modifiedPaths());
  await doc.save();

  // It's Ok!
  doc.firstMap.get("fisrt").data.get("second").data.set("final", 3);
  console.log(
    "Should include firstMap.first.data.second.data.final",
    doc.modifiedPaths()
  );
  await doc.save();

  // It's Ok!
  doc.nested.data.set("second", {});
  console.log("Should include nested.data.second", doc.modifiedPaths());
  await doc.save();

  // It's ERROR!
  doc.nested.data.get("second").data.set("final", 3);
  console.error(
    "Should include nested.data.second.data.final, but it's not, ERROR here!!! So data lossed!",
    doc.modifiedPaths()
  );
  await doc.save();

  // But this is OK, have to set the nested to {}, I don't know why
  const okDoc = await Test.create({ _id: Date.now(), nested: {} });
  okDoc.nested.data.set("second", {});
  await okDoc.save();
  okDoc.nested.data.get("second").data.set("final", 3);
  console.log(
    "Should include nested.data.second.data.final",
    okDoc.modifiedPaths()
  );
  await okDoc.save();

  await mongoose.disconnect();
}

test().catch((err) => console.error(err));

What is the expected behavior?

The behavior is written in the code's comment.

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

Node: v12.16.3
Mongo: 4.4.6
Mongoose: both 5.10.18 and 5.12.11 are tested and failed

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

IslandRhythms commented May 26, 2021

Just a heads up your 2nd console log is incorrect, firstMap.first.data.second didn't show up for me in there but it did on the next one

@hasezoey
Copy link
Collaborator

hasezoey commented May 26, 2021

Original issue is: typegoose/typegoose#541

Slightly modified version (hopefully better to understand):
[Typescript is used, but disabled with "any"]

Code
// NodeJS: 16.1.0
// MongoDB: 4.2-bionic (Docker)
import * as mongoose from "mongoose"; // mongoose@5.12.10
import { inspect } from "util";

const SecondMapSchema = new mongoose.Schema({
  data1: {
    _id: false,
    default: {},
    type: Map,
    of: Number
  }
});
const FirstMapSchema = new mongoose.Schema({
  data2: {
    _id: false,
    default: {},
    type: Map,
    of: SecondMapSchema
  }
});
const NestedSchema = new mongoose.Schema({
  data3: {
    _id: false,
    default: {},
    type: Map,
    of: SecondMapSchema
  }
});
const TestSchema = new mongoose.Schema({
  firstMap: {
    _id: false,
    default: {},
    type: Map,
    of: FirstMapSchema
  },
  nested: {
    _id: false,
    default: {},
    type: NestedSchema
  }
});

const TestModel: any = mongoose.model("Test", TestSchema);

(async () => {
  await mongoose.connect(`mongodb://localhost:27017/`, { useNewUrlParser: true, dbName: "verifyMASTER", useCreateIndex: true, useUnifiedTopology: true });

  const doc = await TestModel.create({});

  console.log(inspect(doc, false, 5));
  console.log("Should be empty", doc.modifiedPaths());

  // Ok!
  doc.firstMap.set("first", {});
  console.log("Should include firstMap.first", doc.modifiedPaths());
  await doc.save();

  // Ok!
  doc.firstMap.get("first").data2.set("second", {});
  console.log("Should include firstMap.first.data2.second", doc.modifiedPaths());
  await doc.save();

  // Ok!
  doc.firstMap.get("first").data2.get("second").data1.set("final", 3);
  console.log(
    "Should include firstMap.first.data2.second.data1.final",
    doc.modifiedPaths()
  );
  await doc.save();

  // Ok!
  doc.nested.data3.set("second", {});
  console.log("Should include nested.data3.second", doc.modifiedPaths());
  await doc.save();

  // ERROR!
  console.log("test1", doc.nested.data3);
  doc.nested.data3.get("second").data1.set("final", 3);
  console.log("test2", doc.nested.data3);
  console.log(
    "Should include nested.data3.second.data1.final",
    doc.modifiedPaths()
  );
  await doc.save();

  console.log(inspect(doc, false, 5));

  // This is OK, but have to set "nested" to "{}"
  const okDoc = await TestModel.create({ nested: {} });
  okDoc.nested.data3.set("second", {});
  await okDoc.save();

  okDoc.nested.data3.get("second").data1.set("final", 3);
  console.log(
    "Should include nested.data3.second.data1.final",
    okDoc.modifiedPaths()
  );
  await okDoc.save();

  await mongoose.disconnect();
})();
Console Output
{
  firstMap: Map(0) {},
  nested: { data3: Map(0) {} },
  _id: 60ae5fb396e9154292426042,
  __v: 0
}
Should be empty []
Should include firstMap.first [ 'firstMap', 'firstMap.first' ]
Should include firstMap.first.data2.second [
  'firstMap',
  'firstMap.first',
  'firstMap.first.data2',
  'firstMap.first.data2.second'
]
Should include firstMap.first.data2.second.data1.final [
  'firstMap',
  'firstMap.first',
  'firstMap.first.data2',
  'firstMap.first.data2.second',
  'firstMap.first.data2.second.data1',
  'firstMap.first.data2.second.data1.final'
]
Should include nested.data3.second [ 'nested', 'nested.data3', 'nested.data3.second' ]
test1 Map(1) {
  'second' => { data1: Map(0) {}, _id: 60ae5fb396e9154292426045 }
}
test2 Map(1) {
  'second' => { data1: Map(1) { 'final' => 3 }, _id: 60ae5fb396e9154292426045 }
}
Should include nested.data3.second.data1.final []
{
  firstMap: Map(1) {
    'first' => {
      data2: Map(1) {
        'second' => {
          data1: Map(1) { 'final' => 3 },
          _id: 60ae5fb396e9154292426044
        }
      },
      _id: 60ae5fb396e9154292426043
    }
  },
  nested: {
    data3: Map(1) {
      'second' => { data1: Map(1) { 'final' => 3 }, _id: 60ae5fb396e9154292426045 }
    }
  },
  _id: 60ae5fb396e9154292426042,
  __v: 0
}
Should include nested.data3.second.data1.final [
  'nested',
  'nested.data3',
  'nested.data3.second',
  'nested.data3.second.data1',
  'nested.data3.second.data1.final'
]

Reproduction repository (with correct branch): https://github.com/typegoose/typegoose-testing/tree/verify541 (including original typegoose classes)

@vkarpov15 vkarpov15 added this to the 5.12.12 milestone May 26, 2021
@winnochan
Copy link
Author

@IslandRhythms Sorry! I fixed the description in the second console.

@vkarpov15 vkarpov15 modified the milestones: 5.12.12, 5.12.13 May 27, 2021
@vkarpov15 vkarpov15 assigned vkarpov15 and unassigned IslandRhythms Jun 4, 2021
vkarpov15 added a commit that referenced this issue Jun 4, 2021
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

4 participants