Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/migrating_to_9.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,17 @@ await Model.updateOne({}, [{ $set: { newProp: 'test2' } }], { updatePipeline: tr

[MongoDB no longer supports the `background` option for indexes as of MongoDB 4.2](https://www.mongodb.com/docs/manual/core/index-creation/#index-operations). Mongoose 9 will no longer set the background option by default and Mongoose 9 no longer supports setting the `background` option on `Schema.prototype.index()`.

## `mongoose.isValidObjectId()` returns false for numbers

In Mongoose 8, you could create a new ObjectId from a number, and `isValidObjectId()` would return `true` for numbers. In Mongoose 9, `isValidObjectId()` will return `false` for numbers and you can no longer create a new ObjectId from a number.

```javascript
// true in mongoose 8, false in mongoose 9
mongoose.isValidObjectId(6);

// Works in Mongoose 8, throws in Mongoose 9
new mongoose.Types.ObjectId(6);

## Subdocument `deleteOne()` hooks execute only when subdocument is deleted

Currently, calling `deleteOne()` on a subdocument will execute the `deleteOne()` hooks on the subdocument regardless of whether the subdocument is actually deleted.
Expand Down
14 changes: 9 additions & 5 deletions lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -2805,6 +2805,13 @@ Model.insertOne = async function insertOne(doc, options) {
Model.watch = function(pipeline, options) {
_checkContext(this, 'watch');

options = options || {};
const watchOptions = options?.hydrate !== undefined ?
utils.omit(options, ['hydrate']) :
{ ...options };
options.model = this;


const changeStreamThunk = cb => {
pipeline = pipeline || [];
prepareDiscriminatorPipeline(pipeline, this.schema, 'fullDocument');
Expand All @@ -2813,18 +2820,15 @@ Model.watch = function(pipeline, options) {
if (this.closed) {
return;
}
const driverChangeStream = this.$__collection.watch(pipeline, options);
const driverChangeStream = this.$__collection.watch(pipeline, watchOptions);
cb(null, driverChangeStream);
});
} else {
const driverChangeStream = this.$__collection.watch(pipeline, options);
const driverChangeStream = this.$__collection.watch(pipeline, watchOptions);
cb(null, driverChangeStream);
}
};

options = options || {};
options.model = this;

return new ChangeStream(changeStreamThunk, pipeline, options);
};

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"license": "MIT",
"dependencies": {
"kareem": "git+https://github.com/mongoosejs/kareem.git#vkarpov15/remove-isasync",
"mongodb": "~6.20.0",
"mongodb": "6.20.0-dev.20251028.sha.447dad7e",
"mpath": "0.9.0",
"mquery": "5.0.0",
"ms": "2.1.3",
Expand Down Expand Up @@ -52,6 +52,7 @@
"moment": "2.30.1",
"mongodb-memory-server": "10.2.1",
"mongodb-runner": "^5.8.2",
"mongodb-client-encryption": "7.0.0-alpha.1",
"ncp": "^2.0.0",
"nyc": "15.1.0",
"pug": "3.0.3",
Expand Down
3 changes: 0 additions & 3 deletions scripts/configure-cluster-with-encryption.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
export CWD=$(pwd)
export DRIVERS_TOOLS_PINNED_COMMIT=4e18803c074231ec9fc3ace8f966e2c49d9874bb

# install extra dependency
npm install --no-save mongodb-client-encryption

# set up mongodb cluster and encryption configuration if the data/ folder does not exist
if [ ! -d "data" ]; then

Expand Down
2 changes: 1 addition & 1 deletion scripts/tsc-diagnostics-check.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const fs = require('fs');

const stdin = fs.readFileSync(0).toString('utf8');
const maxInstantiations = isNaN(process.argv[2]) ? 310000 : parseInt(process.argv[2], 10);
const maxInstantiations = isNaN(process.argv[2]) ? 350000 : parseInt(process.argv[2], 10);

console.log(stdin);

Expand Down
6 changes: 4 additions & 2 deletions test/docs/validation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -381,8 +381,10 @@ describe('validation docs', function() {
err.errors['numWheels'].message;
// acquit:ignore:start
assert.equal(err.errors['numWheels'].name, 'CastError');
assert.equal(err.errors['numWheels'].message,
'Cast to Number failed for value "not a number" (type string) at path "numWheels"');
assert.match(
err.errors['numWheels'].message,
/^Cast to Number failed for value "not a number" \(type string\) at path "numWheels"/
);
// acquit:ignore:end
});

Expand Down
4 changes: 2 additions & 2 deletions test/double.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,9 @@ describe('Double', function() {
assert.ok(err);
assert.ok(err.errors['myDouble']);
assert.equal(err.errors['myDouble'].name, 'CastError');
assert.equal(
assert.match(
err.errors['myDouble'].message,
'Cast to Double failed for value "helloworld" (type string) at path "myDouble"'
/^Cast to Double failed for value "helloworld" \(type string\) at path "myDouble"/
);
});
});
Expand Down
2 changes: 1 addition & 1 deletion test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,7 @@ describe('mongoose module:', function() {
assert.ok(mongoose.isValidObjectId('5f5c2d56f6e911019ec2acdc'));
assert.ok(mongoose.isValidObjectId('608DE01F32B6A93BBA314159'));
assert.ok(mongoose.isValidObjectId(new mongoose.Types.ObjectId()));
assert.ok(mongoose.isValidObjectId(6));
assert.ok(!mongoose.isValidObjectId(6));
assert.ok(!mongoose.isValidObjectId({ test: 42 }));
});

Expand Down
24 changes: 12 additions & 12 deletions test/int32.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,9 @@ describe('Int32', function() {
assert.ok(err);
assert.ok(err.errors['myInt']);
assert.equal(err.errors['myInt'].name, 'CastError');
assert.equal(
assert.match(
err.errors['myInt'].message,
'Cast to Int32 failed for value "-42.4" (type number) at path "myInt"'
/^Cast to Int32 failed for value "-42.4" \(type number\) at path "myInt"/
);
});
});
Expand All @@ -319,9 +319,9 @@ describe('Int32', function() {
assert.ok(err);
assert.ok(err.errors['myInt']);
assert.equal(err.errors['myInt'].name, 'CastError');
assert.equal(
assert.match(
err.errors['myInt'].message,
'Cast to Int32 failed for value "helloworld" (type string) at path "myInt"'
/^Cast to Int32 failed for value "helloworld" \(type string\) at path "myInt"/
);
});
});
Expand All @@ -337,9 +337,9 @@ describe('Int32', function() {
assert.ok(err);
assert.ok(err.errors['myInt']);
assert.equal(err.errors['myInt'].name, 'CastError');
assert.equal(
assert.match(
err.errors['myInt'].message,
'Cast to Int32 failed for value "1.2" (type string) at path "myInt"'
/^Cast to Int32 failed for value "1\.2" \(type string\) at path "myInt"/
);
});
});
Expand All @@ -355,9 +355,9 @@ describe('Int32', function() {
assert.ok(err);
assert.ok(err.errors['myInt']);
assert.equal(err.errors['myInt'].name, 'CastError');
assert.equal(
assert.match(
err.errors['myInt'].message,
'Cast to Int32 failed for value "NaN" (type number) at path "myInt"'
/^Cast to Int32 failed for value "NaN" \(type number\) at path "myInt"/
);
});
});
Expand All @@ -373,9 +373,9 @@ describe('Int32', function() {
assert.ok(err);
assert.ok(err.errors['myInt']);
assert.equal(err.errors['myInt'].name, 'CastError');
assert.equal(
assert.match(
err.errors['myInt'].message,
'Cast to Int32 failed for value "2147483648" (type number) at path "myInt"'
/^Cast to Int32 failed for value "2147483648" \(type number\) at path "myInt"/
);
});
});
Expand All @@ -391,9 +391,9 @@ describe('Int32', function() {
assert.ok(err);
assert.ok(err.errors['myInt']);
assert.equal(err.errors['myInt'].name, 'CastError');
assert.equal(
assert.match(
err.errors['myInt'].message,
'Cast to Int32 failed for value "-2147483649" (type number) at path "myInt"'
/^Cast to Int32 failed for value "-2147483649" \(type number\) at path "myInt"/
);
});
});
Expand Down
6 changes: 4 additions & 2 deletions test/model.findOneAndUpdate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1354,8 +1354,10 @@ describe('model: findOneAndUpdate:', function() {
const update = { $push: { addresses: { street: 'not a num' } } };
const error = await Person.findOneAndUpdate({}, update).then(() => null, err => err);
assert.ok(error.message.indexOf('street') !== -1);
assert.equal(error.reason.message,
'Cast to Number failed for value "not a num" (type string) at path "street"');
assert.match(
error.reason.message,
/^Cast to Number failed for value "not a num" \(type string\) at path "street"/
);
});

it('projection option as alias for fields (gh-4315)', async function() {
Expand Down
4 changes: 2 additions & 2 deletions test/model.query.casting.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ describe('model query casting', function() {

describe('$elemMatch', function() {
it('should cast String to ObjectId in $elemMatch', async function() {
const commentId = new mongoose.Types.ObjectId(111);
const commentId = new mongoose.Types.ObjectId('1'.repeat(24));

const post = new BlogPostB({ comments: [{ _id: commentId }] });
const id = post._id.toString();
Expand All @@ -447,7 +447,7 @@ describe('model query casting', function() {
});

it('should cast String to ObjectId in $elemMatch inside $not', async function() {
const commentId = new mongoose.Types.ObjectId(111);
const commentId = new mongoose.Types.ObjectId('1'.repeat(24));

const post = new BlogPostB({ comments: [{ _id: commentId }] });
const id = post._id.toString();
Expand Down
4 changes: 2 additions & 2 deletions test/types/base.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ function gh10139() {
}

function gh12100() {
mongoose.syncIndexes({ continueOnError: true, noResponse: true });
mongoose.syncIndexes({ continueOnError: false, noResponse: true });
mongoose.syncIndexes({ continueOnError: true, sparse: true });
mongoose.syncIndexes({ continueOnError: false, sparse: true });
}

function setAsObject() {
Expand Down
4 changes: 2 additions & 2 deletions test/types/models.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,8 +481,8 @@ function gh12100() {

const Model = model('Model', schema);

Model.syncIndexes({ continueOnError: true, noResponse: true });
Model.syncIndexes({ continueOnError: false, noResponse: true });
Model.syncIndexes({ continueOnError: true, sparse: true });
Model.syncIndexes({ continueOnError: false, sparse: true });
}

(function gh12070() {
Expand Down