From 05ce5771ab2ceafc51d8c7f27fbe6e52fa23e390 Mon Sep 17 00:00:00 2001 From: "Laisson R. Silveira" Date: Tue, 8 Feb 2022 15:18:30 -0300 Subject: [PATCH 01/19] Fix broken link from findandmodify method deprecation --- docs/deprecations.md | 2 +- lib/query.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/deprecations.md b/docs/deprecations.md index f8a3472bc54..b17953659c0 100644 --- a/docs/deprecations.md +++ b/docs/deprecations.md @@ -60,7 +60,7 @@ If you use [`Model.findOneAndUpdate()`](/docs/api.html#model_Model.findOneAndUpd by default you'll see one of the below deprecation warnings. ``` -DeprecationWarning: Mongoose: `findOneAndUpdate()` and `findOneAndDelete()` without the `useFindAndModify` option set to false are deprecated. See: https://mongoosejs.com/docs/deprecations.html#findandmodify +DeprecationWarning: Mongoose: `findOneAndUpdate()` and `findOneAndDelete()` without the `useFindAndModify` option set to false are deprecated. See: https://mongoosejs.com/docs/5.x/docs/deprecations.html#findandmodify DeprecationWarning: collection.findAndModify is deprecated. Use findOneAndUpdate, findOneAndReplace or findOneAndDelete instead. ``` diff --git a/lib/query.js b/lib/query.js index 4eb5b4a4cde..65817f65fc0 100644 --- a/lib/query.js +++ b/lib/query.js @@ -3777,7 +3777,7 @@ const _legacyFindAndModify = util.deprecate(function(filter, update, opts, cb) { collection.collection._findAndModify(filter, sort, update, opts, _cb); }, 'Mongoose: `findOneAndUpdate()` and `findOneAndDelete()` without the ' + '`useFindAndModify` option set to false are deprecated. See: ' + - 'https://mongoosejs.com/docs/deprecations.html#findandmodify'); + 'https://mongoosejs.com/docs/5.x/docs/deprecations.html#findandmodify'); /*! * Override mquery.prototype._mergeUpdate to handle mongoose objects in From 6a197316564742c0422309e1b5fecfa4faec126e Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 18 Jul 2022 22:21:57 -0400 Subject: [PATCH 02/19] fix(schema): disallow setting __proto__ when creating schema with dotted properties Fix #12085 --- lib/schema.js | 7 +++++++ test/schema.test.js | 10 ++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/schema.js b/lib/schema.js index c740902d2fd..895e452a36c 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -478,6 +478,10 @@ Schema.prototype.add = function add(obj, prefix) { const keys = Object.keys(obj); for (const key of keys) { + if (utils.specialProperties.has(key)) { + continue; + } + const fullPath = prefix + key; if (obj[key] == null) { @@ -663,6 +667,9 @@ Schema.prototype.path = function(path, obj) { let fullPath = ''; for (const sub of subpaths) { + if (utils.specialProperties.has(sub)) { + throw new Error('Cannot set special property `' + sub + '` on a schema'); + } fullPath = fullPath += (fullPath.length > 0 ? '.' : '') + sub; if (!branch[sub]) { this.nested[fullPath] = true; diff --git a/test/schema.test.js b/test/schema.test.js index f5adc214066..711054abccd 100644 --- a/test/schema.test.js +++ b/test/schema.test.js @@ -2682,4 +2682,14 @@ describe('schema', function() { assert.equal(TestSchema.path('testprop.$*').instance, 'Number'); assert.equal(TestSchema.path('testprop.$*').options.ref, 'OtherModel'); }); + + it('disallows setting special properties with `add()` or constructor (gh-12085)', async function() { + const maliciousPayload = '{"__proto__.toString": "Number"}'; + + assert.throws(() => { + mongoose.Schema(JSON.parse(maliciousPayload)); + }, /__proto__/); + + assert.ok({}.toString()); + }); }); From 5eb11dd5d434ba24ea10d19e5eb2054a276bb22e Mon Sep 17 00:00:00 2001 From: Shubanker Chourasia Date: Fri, 19 Aug 2022 13:48:46 +0530 Subject: [PATCH 03/19] made function non async --- test/schema.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/schema.test.js b/test/schema.test.js index 711054abccd..d2bff3ce34e 100644 --- a/test/schema.test.js +++ b/test/schema.test.js @@ -2682,8 +2682,8 @@ describe('schema', function() { assert.equal(TestSchema.path('testprop.$*').instance, 'Number'); assert.equal(TestSchema.path('testprop.$*').options.ref, 'OtherModel'); }); - - it('disallows setting special properties with `add()` or constructor (gh-12085)', async function() { + + it('disallows setting special properties with `add()` or constructor (gh-12085)', function() { const maliciousPayload = '{"__proto__.toString": "Number"}'; assert.throws(() => { From 4d813fa01bb9170f4e0a91d0f3f5234bc163d501 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 21 Aug 2022 17:12:24 -0400 Subject: [PATCH 04/19] test: fix @types/node version in tests re: #12297 --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 8434ae258a1..856290dcf29 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@babel/preset-env": "7.10.4", "@typescript-eslint/eslint-plugin": "4.10.0", "@typescript-eslint/parser": "4.10.0", + "@types/node": "16.x", "acquit": "1.x", "acquit-ignore": "0.1.x", "acquit-require": "0.1.x", From b9e985c629e05a9a73f23ae42027e6c215c6a9d1 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 21 Aug 2022 17:16:39 -0400 Subject: [PATCH 05/19] test: more strict @types/node version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 856290dcf29..4a70cd5cb62 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@babel/preset-env": "7.10.4", "@typescript-eslint/eslint-plugin": "4.10.0", "@typescript-eslint/parser": "4.10.0", - "@types/node": "16.x", + "@types/node": "16.11.23", "acquit": "1.x", "acquit-ignore": "0.1.x", "acquit-require": "0.1.x", From dfc4ad750bf91ae5743ed8dce676bf5a96041a6d Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 21 Aug 2022 19:12:33 -0400 Subject: [PATCH 06/19] test: try upgrading npm for node v4 tests re: #12297 --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9c1b78b454e..7a4eb6af80d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,9 +18,9 @@ jobs: with: node-version: ${{ matrix.node }} - - name: Upgrade Node 5 npm + - name: Upgrade Node <= 5 npm run: npm install -g npm@3 - if: ${{ matrix.node == 5 }} + if: ${{ matrix.node <= 5 }} - run: npm install From a1144dc0220929de0e9b7faf93d793c10e77f094 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 21 Aug 2022 19:19:33 -0400 Subject: [PATCH 07/19] test: run node 7 tests with upgraded npm re: #12297 --- .github/workflows/test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7a4eb6af80d..861870b0dc7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,6 +22,10 @@ jobs: run: npm install -g npm@3 if: ${{ matrix.node <= 5 }} + - name: Upgrade Node 7 npm + run: npm install -g npm@6 + if: ${{ matrix.node == 7 }} + - run: npm install - name: Setup From ca7996b125c1baf24b056c8fef10cfdd24dc1ae9 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 22 Aug 2022 13:28:12 -0400 Subject: [PATCH 08/19] chore: release 5.13.15 --- History.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/History.md b/History.md index 6891820d86a..dc466dbb1c0 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +5.13.15 / 2022-08-22 +==================== + * fix: backport fix for CVE-2022-2564 #12281 [shubanker](https://github.com/shubanker) + * docs: fix broken link from findandmodify method deprecation #11366 [laissonsilveira](https://github.com/laissonsilveira) + 5.13.14 / 2021-12-27 ==================== * fix(timestamps): avoid setting createdAt on documents that already exist but dont have createdAt #11024 diff --git a/package.json b/package.json index 4a70cd5cb62..cbb17a6b225 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mongoose", "description": "Mongoose MongoDB ODM", - "version": "5.13.14", + "version": "5.13.15", "author": "Guillermo Rauch ", "keywords": [ "mongodb", From 0096c3d1329842b476d1186139dcf68bd36d5922 Mon Sep 17 00:00:00 2001 From: Richard De Avila Date: Thu, 10 Nov 2022 13:25:52 -0800 Subject: [PATCH 09/19] Safely handling NamespaceNotFound errors when calling diffIndexes on model with no documents --- lib/model.js | 8 ++++++++ test/model.indexes.test.js | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/model.js b/lib/model.js index 412803aca9f..d1aac13b6e9 100644 --- a/lib/model.js +++ b/lib/model.js @@ -1427,6 +1427,14 @@ Model.diffIndexes = function diffIndexes(options, callback) { return this.db.base._promiseOrCallback(callback, cb => { cb = this.$wrapCallback(cb); this.listIndexes((err, indexes) => { + if (err) { + // If the collection does not exist, it does not have indexes; safely proceed + if (err.codeName === 'NamespaceNotFound') { + indexes = []; + } else { + return cb(err, { toDrop, toCreate }); + } + } const schemaIndexes = this.schema.indexes(); // Iterate through the indexes created in mongodb and // compare against the indexes in the schema. diff --git a/test/model.indexes.test.js b/test/model.indexes.test.js index e00e52c08bf..41ba6d97e23 100644 --- a/test/model.indexes.test.js +++ b/test/model.indexes.test.js @@ -70,6 +70,15 @@ describe('model', function() { }); }); + it('should have schema-defined indexes in toCreate when calling diffIndexes on model with no documents (gh-12676)', () => { + const MySchema = new Schema({ + name: { type: String, index: true } + }); + + const MyModel = db.model('my-model', MySchema); // This collection should not exist in DB; no documents + return MyModel.diffIndexes().then((res) => assert.deepEqual(res.toCreate[0], { name: 1 })); + }); + it('of embedded documents', function(done) { const BlogPosts = new Schema({ _id: { type: ObjectId, index: true }, From 86056b7d0b8eaf280aff8b4d5fbf28df9161d066 Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Wed, 21 Dec 2022 11:42:03 -0500 Subject: [PATCH 10/19] explain sorting better --- docs/queries.md | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/docs/queries.md b/docs/queries.md index 10c6fb1cb25..f75e1eb92a7 100644 --- a/docs/queries.md +++ b/docs/queries.md @@ -241,6 +241,91 @@ const queryRes = await Person.findOne({ _id: idString }); const aggRes = await Person.aggregate([{ $match: { _id: idString } }]) ``` +

Sorting

+ +[Sorting](/docs/api.html#query_Query-sort) is how you can ensure you db requests come back in the desired order. + +```javascript +const personSchema = new mongoose.Schema({ + age: Number +}); + +const Person = mongoose.model('Person', personSchema); +for (let i = 0; i < 10; i++) { + await Person.create({ age: i }); +} + +await Person.find().sort({ age: -1 }); // returns age starting from 10 as the first entry +await Person.find().sort({ age: 1 }); // returns age starting from 0 as the first entry +``` + +

Sorting with Multiple Fields

+ +When sorting with mutiple fields, It is important to understand that the order you send the sort fields will determine the order of priority. + +```javascript +const personSchema = new mongoose.Schema({ + age: Number, + name: String, + weight: Number +}); + +const Person = mongoose.model('Person', personSchema); +const iterations = 5; +for (let i = 0; i < iterations; i++) { + await Person.create({ + age: Math.abs(2-i), + name: 'Test'+i, + weight: Math.floor(Math.random() * 100) + 1 + }); +} + +await Person.find().sort({ age: 1, weight: -1 }); // returns age starting from 0, but while keeping that order will then sort by weight. +``` + +You can view the output of a single run of this block below. +As you can see, age was sorted from 0 to 2 and then to break ties it would sort by whichever document had a bigger weight. + +```javascript +[ + { + _id: new ObjectId("63a335a6b9b6a7bfc186cb37"), + age: 0, + name: 'Test2', + weight: 67, + __v: 0 + }, + { + _id: new ObjectId("63a335a6b9b6a7bfc186cb35"), + age: 1, + name: 'Test1', + weight: 99, + __v: 0 + }, + { + _id: new ObjectId("63a335a6b9b6a7bfc186cb39"), + age: 1, + name: 'Test3', + weight: 73, + __v: 0 + }, + { + _id: new ObjectId("63a335a6b9b6a7bfc186cb33"), + age: 2, + name: 'Test0', + weight: 65, + __v: 0 + }, + { + _id: new ObjectId("63a335a6b9b6a7bfc186cb3b"), + age: 2, + name: 'Test4', + weight: 62, + __v: 0 + } +] +``` +

Next Up

Now that we've covered `Queries`, let's take a look at [Validation](validation.html). From 3eb4092f5154c102c66968b98824f6444dc5cdb1 Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Wed, 21 Dec 2022 16:10:06 -0500 Subject: [PATCH 11/19] made requested changes --- docs/queries.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/queries.md b/docs/queries.md index f75e1eb92a7..bb465d477b7 100644 --- a/docs/queries.md +++ b/docs/queries.md @@ -243,7 +243,7 @@ const aggRes = await Person.aggregate([{ $match: { _id: idString } }])

Sorting

-[Sorting](/docs/api.html#query_Query-sort) is how you can ensure you db requests come back in the desired order. +[Sorting](/docs/api.html#query_Query-sort) is how you can ensure you query results come back in the desired order. ```javascript const personSchema = new mongoose.Schema({ @@ -259,9 +259,7 @@ await Person.find().sort({ age: -1 }); // returns age starting from 10 as the fi await Person.find().sort({ age: 1 }); // returns age starting from 0 as the first entry ``` -

Sorting with Multiple Fields

- -When sorting with mutiple fields, It is important to understand that the order you send the sort fields will determine the order of priority. +When sorting with mutiple fields, the order of the sort keys determines what key MongoDB server sorts by first. ```javascript const personSchema = new mongoose.Schema({ @@ -284,7 +282,7 @@ await Person.find().sort({ age: 1, weight: -1 }); // returns age starting from 0 ``` You can view the output of a single run of this block below. -As you can see, age was sorted from 0 to 2 and then to break ties it would sort by whichever document had a bigger weight. +As you can see, age is sorted from 0 to 2 but when age is equal, sorts by weight. ```javascript [ From 1cbe8e8c00183e5185c1ada483cd54d456295beb Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 22 Dec 2022 17:33:52 -0500 Subject: [PATCH 12/19] fix(query): fix unexpected validation error when doing findOneAndReplace() with a nullish value --- lib/helpers/query/castUpdate.js | 8 +++++--- test/query.test.js | 12 +++++++++++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/helpers/query/castUpdate.js b/lib/helpers/query/castUpdate.js index 50d56509610..2e1dde1a6c0 100644 --- a/lib/helpers/query/castUpdate.js +++ b/lib/helpers/query/castUpdate.js @@ -85,9 +85,11 @@ module.exports = function castUpdate(schema, obj, options, context, filter) { val = ret[op]; hasDollarKey = hasDollarKey || op.startsWith('$'); const toUnset = {}; - for (const key of Object.keys(val)) { - if (val[key] === undefined) { - toUnset[key] = 1; + if (val != null) { + for (const key of Object.keys(val)) { + if (val[key] === undefined) { + toUnset[key] = 1; + } } } diff --git a/test/query.test.js b/test/query.test.js index f291c5d226f..a02a37896d0 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -4314,7 +4314,7 @@ describe('Query', function() { assert.strictEqual(found[0].title, 'burrito bowl'); }); - it('update operation should remove fields set to undefined (gh-12794)', async() => { + it('update operation should remove fields set to undefined (gh-12794) (gh-12821)', async function() { const m = new mongoose.Mongoose(); await m.connect(start.uri); @@ -4338,5 +4338,15 @@ describe('Query', function() { ).lean(); assert.ok('title' in updatedDoc === false); + + const replacedDoc = await Test.findOneAndReplace( + { + _id: doc._id + }, + { title: undefined }, + { returnOriginal: false } + ).lean(); + + assert.ok('title' in replacedDoc === false); }); }); From 1482a1d27bae4c1e8d1b8263f32ccb95b0d8b23b Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 22 Dec 2022 21:32:46 -0500 Subject: [PATCH 13/19] fix(schema): propagate strictQuery to implicitly created schemas for embedded discriminators Fix #12796 --- lib/schema.js | 3 +++ test/schema.documentarray.test.js | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/lib/schema.js b/lib/schema.js index 70558e06f24..0bd2a8aadcd 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -1250,6 +1250,9 @@ Schema.prototype.interpretAsType = function(path, obj, options) { if (options.hasOwnProperty('strict')) { childSchemaOptions.strict = options.strict; } + if (options.hasOwnProperty('strictQuery')) { + childSchemaOptions.strictQuery = options.strictQuery; + } if (this._userProvidedOptions.hasOwnProperty('_id')) { childSchemaOptions._id = this._userProvidedOptions._id; diff --git a/test/schema.documentarray.test.js b/test/schema.documentarray.test.js index 3217e165d88..d9ccb6c1f6b 100644 --- a/test/schema.documentarray.test.js +++ b/test/schema.documentarray.test.js @@ -75,6 +75,15 @@ describe('schema.documentarray', function() { done(); }); + it('propagates strictQuery to implicitly created schemas (gh-12796)', function() { + const schema = new Schema({ + arr: [{ name: String }] + }, { strictQuery: 'throw' }); + + assert.equal(schema.childSchemas.length, 1); + assert.equal(schema.childSchemas[0].schema.options.strictQuery, 'throw'); + }); + it('supports set with array of document arrays (gh-7799)', function() { const subSchema = new Schema({ title: String From 730abafc214d74c2bc6dac43a71bf14f11253df8 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Fri, 23 Dec 2022 10:51:26 -0500 Subject: [PATCH 14/19] test(model): make timeseries test more durable by avoiding autoIndex and drop/init race Fix #12643 --- test/model.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/model.test.js b/test/model.test.js index 6157f53e3b5..5728e286114 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -6916,10 +6916,12 @@ describe('Model', function() { granularity: 'hours' }, autoCreate: false, + autoIndex: false, expireAfterSeconds: 86400 }); - const Test = db.model('Test', schema); + const Test = db.model('Test', schema, 'Test'); + await Test.init(); await Test.collection.drop().catch(() => {}); await Test.createCollection(); From 3f675a93612b279543b90c4ed30d5abbcb9245d3 Mon Sep 17 00:00:00 2001 From: hasezoey Date: Fri, 23 Dec 2022 18:22:39 +0100 Subject: [PATCH 15/19] test(query.test): add write-concern option in hopes this fixes some inconsistent tests in replica set mode --- test/query.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/query.test.js b/test/query.test.js index a02a37896d0..3d1220a0cc4 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -1564,7 +1564,7 @@ describe('Query', function() { const Product = db.model('Product', productSchema); Product.create( { numbers: [3, 4, 5] }, - { strings: 'hi there'.split(' ') }, function(err, doc1, doc2) { + { strings: 'hi there'.split(' '), w: 'majority' }, function(err, doc1, doc2) { assert.ifError(err); Product.find().setOptions({ limit: 1, sort: { _id: -1 }, read: 'n' }).exec(function(err, docs) { assert.ifError(err); From 0c75a1f85c58ae7b42df55101ecbd56df358a9e4 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Fri, 23 Dec 2022 12:33:48 -0500 Subject: [PATCH 16/19] add version to 5.x docs search build re: #12548 --- docs/search.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/search.js b/docs/search.js index d093bc812e2..ca18f2bae13 100644 --- a/docs/search.js +++ b/docs/search.js @@ -6,6 +6,7 @@ const filemap = require('./source'); const fs = require('fs'); const pug = require('pug'); const mongoose = require('../'); +let { version } = require('../package.json'); const markdown = require('marked'); const highlight = require('highlight.js'); @@ -15,12 +16,14 @@ markdown.setOptions({ } }); -mongoose.set('useCreateIndex', true); +// 5.13.5 -> 5.x, 6.8.2 -> 6.x, etc. +version = version.slice(0, version.indexOf('.')) + '.x'; const contentSchema = new mongoose.Schema({ title: { type: String, required: true }, body: { type: String, required: true }, - url: { type: String, required: true } + url: { type: String, required: true }, + version: { type: String, required: true, default: version } }); contentSchema.index({ title: 'text', body: 'text' }); const Content = mongoose.model('Content', contentSchema, 'Content'); @@ -30,7 +33,6 @@ const files = Object.keys(filemap); for (const filename of files) { const file = filemap[filename]; - console.log(file) if (file.api) { // API docs are special, raw content is in the `docs` property for (const _class of file.docs) { @@ -42,6 +44,7 @@ for (const filename of files) { }); const err = content.validateSync(); if (err != null) { + console.log(content); throw err; } contents.push(content); @@ -49,7 +52,7 @@ for (const filename of files) { } } else if (file.markdown) { let text = fs.readFileSync(filename, 'utf8'); - text = markdown(text); + text = markdown.parse(text); const content = new Content({ title: file.title, @@ -114,15 +117,18 @@ for (const filename of files) { run().catch(error => console.error(error.stack)); async function run() { - await mongoose.connect(config.uri, { useNewUrlParser: true, dbName: 'mongoose' }); + await mongoose.connect(config.uri, { dbName: 'mongoose' }); - await Content.deleteMany({}); + await Content.deleteMany({ version }); for (const content of contents) { + if (version !== '6.x') { + content.url = `/docs/${version}/docs${content.url}`; + } await content.save(); } const results = await Content. - find({ $text: { $search: 'validate' } }, { score: { $meta: 'textScore' } }). + find({ $text: { $search: 'validate' }, version }, { score: { $meta: 'textScore' } }). sort({ score: { $meta: 'textScore' } }). limit(10); From c409b2c25584438a38e2498b2b100b89769b3a04 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sat, 24 Dec 2022 13:06:42 -0500 Subject: [PATCH 17/19] fix(discriminator): apply built-in plugins to discriminator schema even if `mergeHooks` and `mergePlugins` are both false Fix #12696 --- lib/helpers/model/discriminator.js | 3 +++ lib/helpers/schema/applyBuiltinPlugins.js | 12 ++++++++++ lib/plugins/clearValidating.js | 28 ----------------------- lib/plugins/index.js | 7 ++++++ test/model.discriminator.test.js | 24 +++++++++++++++++++ 5 files changed, 46 insertions(+), 28 deletions(-) create mode 100644 lib/helpers/schema/applyBuiltinPlugins.js delete mode 100644 lib/plugins/clearValidating.js create mode 100644 lib/plugins/index.js diff --git a/lib/helpers/model/discriminator.js b/lib/helpers/model/discriminator.js index 96443002ab1..0c843df10da 100644 --- a/lib/helpers/model/discriminator.js +++ b/lib/helpers/model/discriminator.js @@ -1,6 +1,7 @@ 'use strict'; const Mixed = require('../../schema/mixed'); +const applyBuiltinPlugins = require('../schema/applyBuiltinPlugins'); const defineKey = require('../document/compile').defineKey; const get = require('../get'); const utils = require('../../utils'); @@ -40,6 +41,8 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu model.base._applyPlugins(schema, { skipTopLevel: !applyPluginsToDiscriminators }); + } else if (!mergeHooks) { + applyBuiltinPlugins(schema); } const key = model.schema.options.discriminatorKey; diff --git a/lib/helpers/schema/applyBuiltinPlugins.js b/lib/helpers/schema/applyBuiltinPlugins.js new file mode 100644 index 00000000000..cbf999dd01d --- /dev/null +++ b/lib/helpers/schema/applyBuiltinPlugins.js @@ -0,0 +1,12 @@ +'use strict'; + +const builtinPlugins = require('../../plugins'); + +module.exports = function applyBuiltinPlugins(schema) { + for (const plugin of Object.values(builtinPlugins)) { + plugin(schema, { deduplicate: true }); + } + schema.plugins = Object.values(builtinPlugins). + map(fn => ({ fn, opts: { deduplicate: true } })). + concat(schema.plugins); +}; \ No newline at end of file diff --git a/lib/plugins/clearValidating.js b/lib/plugins/clearValidating.js deleted file mode 100644 index 50264e33a6b..00000000000 --- a/lib/plugins/clearValidating.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -/*! - * ignore - */ - -module.exports = function clearValidating(schema) { - // `this.$__.validating` tracks whether there are multiple validations running - // in parallel. We need to clear `this.$__.validating` before post hooks for gh-8597 - const unshift = true; - schema.s.hooks.post('validate', false, function clearValidatingPostValidate() { - if (this.$isSubdocument) { - return; - } - - this.$__.validating = null; - }, unshift); - - schema.s.hooks.post('validate', false, function clearValidatingPostValidateError(error, res, next) { - if (this.$isSubdocument) { - next(); - return; - } - - this.$__.validating = null; - next(); - }, unshift); -}; diff --git a/lib/plugins/index.js b/lib/plugins/index.js new file mode 100644 index 00000000000..69fa6ad284c --- /dev/null +++ b/lib/plugins/index.js @@ -0,0 +1,7 @@ +'use strict'; + +exports.removeSubdocs = require('./removeSubdocs'); +exports.saveSubdocs = require('./saveSubdocs'); +exports.sharding = require('./sharding'); +exports.trackTransaction = require('./trackTransaction'); +exports.validateBeforeSave = require('./validateBeforeSave'); diff --git a/test/model.discriminator.test.js b/test/model.discriminator.test.js index 3e34bfa7b62..a7eb5469a32 100644 --- a/test/model.discriminator.test.js +++ b/test/model.discriminator.test.js @@ -2077,4 +2077,28 @@ describe('model', function() { schema.pre('save', function testHook12604() {}); } }); + + it('applies built-in plugins if mergePlugins and mergeHooks disabled (gh-12696) (gh-12604)', async function() { + const shapeDef = { name: String }; + const shapeSchema = Schema(shapeDef, { discriminatorKey: 'kind' }); + + const Shape = db.model('Test', shapeSchema); + + let subdocSaveCalls = 0; + const nestedSchema = Schema({ test: String }); + nestedSchema.pre('save', function() { + ++subdocSaveCalls; + }); + + const squareSchema = Schema({ ...shapeDef, nested: nestedSchema }); + const Square = Shape.discriminator( + 'Square', + squareSchema, + { mergeHooks: false, mergePlugins: false } + ); + + assert.equal(subdocSaveCalls, 0); + await Square.create({ nested: { test: 'foo' } }); + assert.equal(subdocSaveCalls, 1); + }); }); From e64f652af89a086963797278d5957490b02f917d Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 26 Dec 2022 17:05:40 -0500 Subject: [PATCH 18/19] Update lib/helpers/schema/applyBuiltinPlugins.js Co-authored-by: hasezoey --- lib/helpers/schema/applyBuiltinPlugins.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/helpers/schema/applyBuiltinPlugins.js b/lib/helpers/schema/applyBuiltinPlugins.js index cbf999dd01d..8bd7319cbb1 100644 --- a/lib/helpers/schema/applyBuiltinPlugins.js +++ b/lib/helpers/schema/applyBuiltinPlugins.js @@ -9,4 +9,4 @@ module.exports = function applyBuiltinPlugins(schema) { schema.plugins = Object.values(builtinPlugins). map(fn => ({ fn, opts: { deduplicate: true } })). concat(schema.plugins); -}; \ No newline at end of file +}; From ffefa87c895287826e3f39178b3f6e9492121e9e Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 26 Dec 2022 17:09:28 -0500 Subject: [PATCH 19/19] refactor: use builtinPlugins list instead of importing all plugins --- lib/helpers/schema/applyBuiltinPlugins.js | 2 +- lib/index.js | 14 ++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/lib/helpers/schema/applyBuiltinPlugins.js b/lib/helpers/schema/applyBuiltinPlugins.js index cbf999dd01d..8bd7319cbb1 100644 --- a/lib/helpers/schema/applyBuiltinPlugins.js +++ b/lib/helpers/schema/applyBuiltinPlugins.js @@ -9,4 +9,4 @@ module.exports = function applyBuiltinPlugins(schema) { schema.plugins = Object.values(builtinPlugins). map(fn => ({ fn, opts: { deduplicate: true } })). concat(schema.plugins); -}; \ No newline at end of file +}; diff --git a/lib/index.js b/lib/index.js index 0b76506cb0e..2109d3eec6d 100644 --- a/lib/index.js +++ b/lib/index.js @@ -19,21 +19,17 @@ const Types = require('./types'); const Query = require('./query'); const Model = require('./model'); const applyPlugins = require('./helpers/schema/applyPlugins'); +const builtinPlugins = require('./plugins'); const driver = require('./driver'); const promiseOrCallback = require('./helpers/promiseOrCallback'); const legacyPluralize = require('./helpers/pluralize'); const utils = require('./utils'); const pkg = require('../package.json'); const cast = require('./cast'); -const removeSubdocs = require('./plugins/removeSubdocs'); -const saveSubdocs = require('./plugins/saveSubdocs'); -const trackTransaction = require('./plugins/trackTransaction'); -const validateBeforeSave = require('./plugins/validateBeforeSave'); const Aggregate = require('./aggregate'); const PromiseProvider = require('./promise_provider'); const printStrictQueryWarning = require('./helpers/printStrictQueryWarning'); -const shardingPlugin = require('./plugins/sharding'); const trusted = require('./helpers/query/trusted').trusted; const sanitizeFilter = require('./helpers/query/sanitizeFilter'); const isBsonType = require('./helpers/isBsonType'); @@ -108,13 +104,7 @@ function Mongoose(options) { configurable: false, enumerable: true, writable: false, - value: [ - [saveSubdocs, { deduplicate: true }], - [validateBeforeSave, { deduplicate: true }], - [shardingPlugin, { deduplicate: true }], - [removeSubdocs, { deduplicate: true }], - [trackTransaction, { deduplicate: true }] - ] + value: Object.values(builtinPlugins).map(plugin => ([plugin, { deduplicate: true }])) }); }