Skip to content

[BUG] deleteOne() and clear() silently fail on nested map subdocuments #15969

@AbdelrahmanHafez

Description

@AbdelrahmanHafez

Prerequisites

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

Mongoose version

9.1.4

Node.js version

20.x

MongoDB server version

6.x

Typescript version (if applicable)

N/A

Description

re #15682 #15678 #15350
When calling deleteOne() on a subdocument that is nested inside a map, which is itself inside another map's subdocument, the operation silently fails. The subdocument is not removed and no changes are tracked.

Similarly, calling clear() on a nested map produces incorrect MongoDB update paths with duplicated path segments.

This affects any schema structure with the pattern: Map > Subdocument > Map > Subdocument

Steps to Reproduce

const mongoose = require('mongoose');
const { Schema } = mongoose;

const memberSchema = new Schema({ name: String }, { _id: false });
const teamSchema = new Schema({
  members: { type: Map, of: memberSchema }
}, { _id: false });
const companySchema = new Schema({
  teams: { type: Map, of: teamSchema }
});

const Company = mongoose.model('Company', companySchema);

// Simulate document loaded from database using init()
const company = new Company();
company.init({
  _id: new mongoose.Types.ObjectId(),
  teams: {
    engineering: {
      members: {
        john: { name: 'John' },
        sarah: { name: 'Sarah' }
      }
    }
  }
});

// Bug 1: deleteOne() silently fails
const john = company.teams.get('engineering').members.get('john');
john.deleteOne();

console.log('After deleteOne():');
console.log('  john still exists?', company.teams.get('engineering').members.get('john') != null);
// Expected: false
// Actual: true

console.log('  getChanges():', JSON.stringify(company.getChanges()));
// Expected: { "$set": { "teams.engineering.members.john": null } }
// Actual: {}

// Bug 2: clear() produces wrong path
const company2 = new Company();
company2.init({
  _id: new mongoose.Types.ObjectId(),
  teams: {
    engineering: {
      members: {
        john: { name: 'John' }
      }
    }
  }
});

const membersMap = company2.teams.get('engineering').members;
membersMap.clear();

console.log('After clear():');
console.log('  getChanges():', JSON.stringify(company2.getChanges()));
// Expected: { "$set": { "teams.engineering.members": {} } }
// Actual: { "$unset": { "teams.engineering.teams.$*.members": 1 } }
// Note: path is duplicated and contains wildcard

Expected Behavior

  1. deleteOne() should remove the subdocument from the map and track the change so that save() persists the removal to MongoDB.
  2. clear() should empty the map and produce a valid MongoDB update path like teams.engineering.members, not a duplicated path like teams.engineering.teams.engineering.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions