mongoose icon indicating copy to clipboard operation
mongoose copied to clipboard

Unexpected $set statement to update field of Map type

Open holem opened this issue 10 months ago • 0 comments

Prerequisites

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

Mongoose version

8.13.2

Node.js version

23.11.0

MongoDB server version

8.0.8

Typescript version (if applicable)

No response

Description

The document contains a field of type Map, where the values are arrays of something (e.g., numbers). If you do doc.map.get(key), modify the array (e.g., by adding a new value), and then call set(key), Mongoose generates an incorrect query for MongoDB. However, if you do the same thing but recreate the array after get(key) (e.g., [...doc.map.get(key)]), then Mongoose generates the correct query for MongoDB.

Steps to Reproduce


"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();
mongoose.set("debug", true);

const DocSchema = new mongoose.Schema({
  map: {
    type: Map,
    of: [{ type: Number }],
    default: new Map(),
  },
});
const Doc = mongoose.model("Doc", DocSchema);

const doc = await Doc.create({});
console.log("just created", doc);

doc.map.set("key", [1, 2]);
await doc.save();
console.log("key set", doc);

const list = doc.map.get("key");
// const list = [...doc.map.get("key")];
list.push(3);
doc.map.set("key", list);
await doc.save();
console.log("key get/push/set", doc);

const userLoaded = await Doc.findById(doc._id.toString());
console.log("from db", userLoaded);

Incorrect case (const list = doc.map.get("key");):

Mongoose version 8.13.2
Mongoose: docs.insertOne({ map: Map(0) {}, _id: ObjectId("67fac37873021776275453b0"), __v: 0 }, {})
just created {
  map: Map(0) {},
  _id: new ObjectId('67fac37873021776275453b0'),
  __v: 0
}
Mongoose: docs.updateOne({ _id: ObjectId("67fac37873021776275453b0"), __v: 0 }, { '$set': { 'map.key': [ 1, 2 ] }, '$inc': { __v: 1 } }, {})
key set {
  map: Map(1) { 'key' => [ 1, 2 ] },
  _id: new ObjectId('67fac37873021776275453b0'),
  __v: 1
}
Mongoose: docs.updateOne({ _id: ObjectId("67fac37873021776275453b0") }, { '$set': { 'map.$*': Map(1) { 'key' => [ 1, 2, 3 ] } }}, {})
key get/push/set {
  map: Map(1) { 'key' => [ 1, 2, 3 ] },
  _id: new ObjectId('67fac37873021776275453b0'),
  __v: 1
}
Mongoose: docs.findOne({ _id: ObjectId("67fac37873021776275453b0") }, {})
from db { _id: new ObjectId('67fac37873021776275453b0'), __v: 1 }

Correct case (const list = [...doc.map.get("key")];):

Mongoose version 8.13.2
Mongoose: docs.insertOne({ map: Map(0) {}, _id: ObjectId("67fac4c4564026e97c390825"), __v: 0 }, {})
just created {
  map: Map(0) {},
  _id: new ObjectId('67fac4c4564026e97c390825'),
  __v: 0
}
Mongoose: docs.updateOne({ _id: ObjectId("67fac4c4564026e97c390825"), __v: 0 }, { '$set': { 'map.key': [ 1, 2 ] }, '$inc': { __v: 1 } }, {})
key set {
  map: Map(1) { 'key' => [ 1, 2 ] },
  _id: new ObjectId('67fac4c4564026e97c390825'),
  __v: 1
}
Mongoose: docs.updateOne({ _id: ObjectId("67fac4c4564026e97c390825"), __v: 1 }, { '$set': { 'map.key': [ 1, 2, 3 ] }, '$inc': { __v: 1 } }, {})
key get/push/set {
  map: Map(1) { 'key' => [ 1, 2, 3 ] },
  _id: new ObjectId('67fac4c4564026e97c390825'),
  __v: 2
}
Mongoose: docs.findOne({ _id: ObjectId("67fac4c4564026e97c390825") }, {})
from db {
  _id: new ObjectId('67fac4c4564026e97c390825'),
  map: Map(1) { 'key' => [ 1, 2, 3 ] },
  __v: 2
}

Difference in 3rd query - docs.updateOne: incorrect - ... '$set': { 'map.$*': Map(1) { 'key' => [ 1, 2, 3 ] } ... correct - ... '$set': { 'map.key': [ 1, 2, 3 ] } ...

Expected Behavior

Mongoose should generate correct queries to MongoDB in both cases.

holem avatar Apr 12 '25 19:04 holem