Repository: Automattic/mongoose (JavaScript)

Issue:
Mongoose not sending shard key on deleteOne()
### 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.19.*

### Node.js version

24.*

### MongoDB server version

6.*

### Typescript version (if applicable)

_No response_

### Description

This bug is similar to https://github.com/Automattic/mongoose/issues/7393, but the fix was applied using `'remove'` when it should now be using `'deleteOne'`.

https://github.com/Automattic/mongoose/blob/master/lib/plugins/sharding.js#L19-L22

### Steps to Reproduce

See the related issue: https://github.com/Automattic/mongoose/issues/7393

### Expected Behavior

The query to delete the document using `deleteOne` contains the shard key.


Discussion:

Patch:
diff --git a/lib/model.js b/lib/model.js
index 138d79cc32d..3929413e767 100644
--- a/lib/model.js
+++ b/lib/model.js
@@ -802,8 +802,18 @@ Model.prototype.deleteOne = function deleteOne(options) {
     }
   }
 
-  query.pre(function queryPreDeleteOne(cb) {
-    self.constructor._middleware.execPre('deleteOne', self, [self], cb);
+  query.pre(async function queryPreDeleteOne() {
+    await new Promise((resolve, reject) => {
+      self.constructor._middleware.execPre('deleteOne', self, [self], err => {
+        if (err) reject(err);
+        else resolve();
+      });
+    });
+    // Apply custom where conditions _after_ document deleteOne middleware for
+    // consistency with save() - sharding plugin needs to set $where
+    if (self.$where != null) {
+      this.where(self.$where);
+    }
   });
   query.pre(function callSubdocPreHooks(cb) {
     each(self.$getAllSubdocs(), (subdoc, cb) => {
diff --git a/lib/plugins/sharding.js b/lib/plugins/sharding.js
index 7d905f31c0f..187a6323f6b 100644
--- a/lib/plugins/sharding.js
+++ b/lib/plugins/sharding.js
@@ -16,7 +16,7 @@ module.exports = function shardingPlugin(schema) {
     applyWhere.call(this);
     next();
   });
-  schema.pre('remove', function shardingPluginPreRemove(next) {
+  schema.pre('deleteOne', { document: true, query: false }, function shardingPluginPreRemove(next) {
     applyWhere.call(this);
     next();
   });


Test patch:
diff --git a/test/sharding.test.js b/test/sharding.test.js
new file mode 100644
index 00000000000..91762aa493d
--- /dev/null
+++ b/test/sharding.test.js
@@ -0,0 +1,37 @@
+'use strict';
+
+const assert = require('assert');
+const start = require('./common');
+
+const mongoose = start.mongoose;
+
+describe('plugins.sharding', function() {
+  let db;
+
+  before(function() {
+    db = start();
+  });
+
+  after(async function() {
+    await db.close();
+  });
+
+  beforeEach(() => db.deleteModel(/.*/));
+  afterEach(() => require('./util').clearTestData(db));
+  afterEach(() => require('./util').stopRemainingOps(db));
+
+  it('applies shard key to deleteOne (gh-15701)', async function() {
+    const TestModel = db.model('Test', new mongoose.Schema({ name: String, shardKey: String }));
+    const doc = await TestModel.create({ name: 'test', shardKey: 'test1' });
+    doc.$__.shardval = { shardKey: 'test2' };
+    let res = await doc.deleteOne();
+    assert.strictEqual(res.deletedCount, 0);
+    doc.$__.shardval = { shardKey: 'test1' };
+    res = await doc.deleteOne();
+    assert.strictEqual(res.deletedCount, 1);
+
+    await TestModel.create({ name: 'test2', shardKey: 'test2' });
+    res = await TestModel.deleteOne({ name: 'test2' });
+    assert.strictEqual(res.deletedCount, 1);
+  });
+});