Skip to content

model.set() for nested Schema inside Map field #15461

Closed
@holem

Description

@holem

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Mongoose version

8.15.1

Node.js version

v22.15.0

MongoDB server version

8.0.9

Typescript version (if applicable)

No response

Description

If you have model with Map field (see repro script, MetaSchema.items), you can use model.set(path, value) only if path does not refer inside the Schema nested in the Map, i.e.:

item.set("items.m3", { field: "field3", arr: [4] }); // OK
item.set("items.m2.field", "replaced"); // does not work
item.set("items.m2.arr", [1]); // does not work

Also, if you have nested Map inside Map field (see repro script, MetaSchema.nested), an error is thrown when trying to call set() with a nested Schema with a nested Map, i.e.:

item.set("nested.inserted", { map: new Map([["a1", 1]]) }); // causes error at `save()` stage
// MongoServerError: Updating the path 'nested.$*.map' would create a conflict at 'nested.$*.map'
item.set("nested.inserted2.map", new Map([["a1", 1]])); // does not work at all

Steps to Reproduce

Repro script:

"use strict";

import mongoose from "mongoose";

console.log("Mongoose version", mongoose.version);

await mongoose.connect("mongodb://127.0.0.1:27017/mongoose_test");
await mongoose.connection.dropDatabase();

const MetaSchema = new mongoose.Schema({
  items: {
    type: Map,
    of: new mongoose.Schema(
      {
        field: { type: String, required: true },
        arr: { type: [Number] },
      },
      { _id: false }
    ),
    default: new Map(),
  },
  nested: {
    type: Map,
    of: new mongoose.Schema(
      {
        map: {
          type: Map,
          of: Number,
          required: true,
        },
      },
      { _id: false }
    ),
    default: new Map(),
  },
});

const MetaModel = mongoose.model("MetaModel", MetaSchema);

await MetaModel.create({
  items: new Map([
    ["m1", { field: "field1", arr: [1, 2] }],
    ["m2", { field: "field2", arr: [] }],
  ]),
  nested: new Map([
    [
      "key",
      {
        map: new Map([
          ["k7", 7],
          ["k8", 8],
          ["k9", 9],
        ]),
      },
    ],
    [
      "key2",
      {
        map: new Map([["k7", 7]]),
      },
    ],
  ]),
});
const item = await MetaModel.findOne();

console.log("item", item);

item.set("items.m1.field", "changed");
item.set("items.m2.field", "replaced");
item.set("items.m2.arr", [1]);
item.set("items.m3", { field: "field3", arr: [4] });
item.set("nested.inserted", { map: new Map([["a1", 1]]) });
item.set("nested.inserted2.map", new Map([["a1", 1]]));

console.log(item.modifiedPaths());
console.log(item);

await item.save();

process.exit(0);

output:

Mongoose version 8.15.1
item {
  _id: new ObjectId('68400944e3a759c9f5472135'),
  items: Map(2) {
    'm1' => { field: 'field1', arr: [Array] },
    'm2' => { field: 'field2', arr: [] }
  },
  nested: Map(2) { 'key' => { map: [Map] }, 'key2' => { map: [Map] } },
  __v: 0
}
[
  'items',
  'items.$*',
  'items.$*.field',
  'items.$*.arr',
  'items.m3',
  'nested',
  'nested.$*',
  'nested.$*.map',
  'nested.$*.map.a1',
  'nested.inserted'
]
{
  _id: new ObjectId('68400944e3a759c9f5472135'),
  items: Map(3) {
    'm1' => { field: 'field1', arr: [Array] },
    'm2' => { field: 'field2', arr: [] },
    'm3' => { field: 'field3', arr: [Array] }
  },
  nested: Map(3) {
    'key' => { map: [Map] },
    'key2' => { map: [Map] },
    'inserted' => { map: [Map] }
  },
  __v: 0
}
/Users/holem/Documents/github/mongoose-map-test/node_modules/mongodb/lib/operations/update.js:75
            throw new error_1.MongoServerError(res.writeErrors[0]);
                  ^

MongoServerError: Updating the path 'nested.$*.map' would create a conflict at 'nested.$*.map'
    at UpdateOneOperation.execute (/Users/holem/Documents/github/mongoose-map-test/node_modules/mongodb/lib/operations/update.js:75:19)
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async tryOperation (/Users/holem/Documents/github/mongoose-map-test/node_modules/mongodb/lib/operations/execute_operation.js:207:20)
    at async executeOperation (/Users/holem/Documents/github/mongoose-map-test/node_modules/mongodb/lib/operations/execute_operation.js:75:16)
    at async Collection.updateOne (/Users/holem/Documents/github/mongoose-map-test/node_modules/mongodb/lib/collection.js:207:16) {
  errorLabelSet: Set(0) {},
  errorResponse: {
    index: 0,
    code: 40,
    errmsg: "Updating the path 'nested.$*.map' would create a conflict at 'nested.$*.map'"
  },
  index: 0,
  code: 40
}

Node.js v22.15.0

Expected Behavior

It would be great if model.set() would work with nested Schemas and with nested fields in these Schemas of any supported types (including Maps).

Metadata

Metadata

Assignees

No one assigned

    Labels

    confirmed-bugWe've confirmed this is a bug in Mongoose and will fix it.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions