From a62b0ec4bf4521f118d3abb6f94ce9b7b3294ef1 Mon Sep 17 00:00:00 2001 From: Lorand Horvath <72015221+lorand-horvath@users.noreply.github.com> Date: Thu, 16 Nov 2023 11:18:55 +0200 Subject: [PATCH 01/31] Bump mongodb driver to 5.9.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7373405da55..cc6a75c1362 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "dependencies": { "bson": "^5.5.0", "kareem": "2.5.1", - "mongodb": "5.9.0", + "mongodb": "5.9.1", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", From d8f94f9ffcf90702ea14c435285b4f52ab91f01f Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 16 Nov 2023 18:00:48 -0500 Subject: [PATCH 02/31] types(model): support calling `Model.validate()` with `pathsToSkip` option Re: #14003 --- types/models.d.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/types/models.d.ts b/types/models.d.ts index cfa23bc40da..e4d62b3947d 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -461,8 +461,11 @@ declare module 'mongoose' { /** Casts and validates the given object against this model's schema, passing the given `context` to custom validators. */ validate(): Promise; - validate(optional: any): Promise; - validate(optional: any, pathsToValidate: PathsToValidate): Promise; + validate(obj: any): Promise; + validate( + obj: any, + pathsOrOptions: PathsToValidate | { pathsToSkip?: PathsToValidate } + ): Promise; /** Watches the underlying collection for changes using [MongoDB change streams](https://www.mongodb.com/docs/manual/changeStreams/). */ watch(pipeline?: Array>, options?: mongodb.ChangeStreamOptions & { hydrate?: boolean }): mongodb.ChangeStream; From 6ee82c15117f35349b11e5dad9b8b2088738f2e8 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 16 Nov 2023 18:05:28 -0500 Subject: [PATCH 03/31] test: add test for #14003 --- test/types/models.test.ts | 8 ++++++++ types/models.d.ts | 6 ++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/test/types/models.test.ts b/test/types/models.test.ts index 7865a5e6855..8bc4033deda 100644 --- a/test/types/models.test.ts +++ b/test/types/models.test.ts @@ -751,3 +751,11 @@ function gh13957() { const repository = new RepositoryBase(TestModel); expectType>(repository.insertMany([{ name: 'test' }])); } + +async function gh14003() { + const schema = new Schema({ name: String }); + const TestModel = model('Test', schema); + + await TestModel.validate({ name: 'foo' }, ['name']); + await TestModel.validate({ name: 'foo' }, { pathsToSkip: ['name'] }); +} diff --git a/types/models.d.ts b/types/models.d.ts index e4d62b3947d..b8796641af4 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -462,10 +462,8 @@ declare module 'mongoose' { /** Casts and validates the given object against this model's schema, passing the given `context` to custom validators. */ validate(): Promise; validate(obj: any): Promise; - validate( - obj: any, - pathsOrOptions: PathsToValidate | { pathsToSkip?: PathsToValidate } - ): Promise; + validate(obj: any, pathsOrOptions: PathsToValidate): Promise; + validate(obj: any, pathsOrOptions: { pathsToSkip?: pathsToSkip }): Promise; /** Watches the underlying collection for changes using [MongoDB change streams](https://www.mongodb.com/docs/manual/changeStreams/). */ watch(pipeline?: Array>, options?: mongodb.ChangeStreamOptions & { hydrate?: boolean }): mongodb.ChangeStream; From 0e3b20570e39d60d7959474e74589c9d708c43d5 Mon Sep 17 00:00:00 2001 From: Pratham Vaidya Date: Fri, 17 Nov 2023 04:39:34 +0530 Subject: [PATCH 04/31] Fix: Mongoose types incorrect for when includeResultMetadata: true is set (#14078) * Added Seperate type for Middlewares that support includeResultMetadata * Added Return Type as Raw Result when includeResultMetadata is TRUE for all middlewares * Added Seperate post query type for middlewares that support includeResultMetadata * Added Seperate post query type for middlewares that support includeResultMetadata, Fixed Lint * Updated MongooseRawResultQueryMiddleware and removed duplicate and redundant middlewares * Removed 'findByIdAndRemove' type definations : DEPRECATED, Fixed Linting Errors * Removed test cases for 'findByIdAndRemove', Added test case for 'findOneAndDeleteRes', 'findByIdAndDeleteRes' * Updated findOneAndDelete, Added test case for findOneAndUpdate, findOneAndReplace middlewares --- test/types/middleware.test.ts | 14 ++++++++++++-- test/types/models.test.ts | 13 ++++++++++--- types/index.d.ts | 1 + types/middlewares.d.ts | 4 +++- types/models.d.ts | 32 +++++++------------------------- 5 files changed, 33 insertions(+), 31 deletions(-) diff --git a/test/types/middleware.test.ts b/test/types/middleware.test.ts index a0f3e53caa1..47fa0ed03d6 100644 --- a/test/types/middleware.test.ts +++ b/test/types/middleware.test.ts @@ -1,4 +1,4 @@ -import { Schema, model, Model, Document, SaveOptions, Query, Aggregate, HydratedDocument, PreSaveMiddlewareFunction } from 'mongoose'; +import { Schema, model, Model, Document, SaveOptions, Query, Aggregate, HydratedDocument, PreSaveMiddlewareFunction, ModifyResult } from 'mongoose'; import { expectError, expectType, expectNotType, expectAssignable } from 'tsd'; const preMiddlewareFn: PreSaveMiddlewareFunction = function(next, opts) { @@ -109,7 +109,17 @@ schema.post>('countDocuments', function(count, next) { }); schema.post>('findOneAndDelete', function(res, next) { - expectType(res); + expectType | null>(res); + next(); +}); + +schema.post>('findOneAndUpdate', function(res, next) { + expectType | null>(res); + next(); +}); + +schema.post>('findOneAndReplace', function(res, next) { + expectType | null>(res); next(); }); diff --git a/test/types/models.test.ts b/test/types/models.test.ts index 7865a5e6855..fe68d015f07 100644 --- a/test/types/models.test.ts +++ b/test/types/models.test.ts @@ -673,9 +673,6 @@ async function gh13705() { const findByIdAndDeleteRes = await TestModel.findByIdAndDelete('0'.repeat(24), { lean: true }); expectType(findByIdAndDeleteRes); - const findByIdAndRemoveRes = await TestModel.findByIdAndRemove('0'.repeat(24), { lean: true }); - expectType(findByIdAndRemoveRes); - const findByIdAndUpdateRes = await TestModel.findByIdAndUpdate('0'.repeat(24), {}, { lean: true }); expectType(findByIdAndUpdateRes); @@ -709,6 +706,16 @@ async function gh13746() { expectType(findOneAndUpdateRes.lastErrorObject?.updatedExisting); expectType(findOneAndUpdateRes.lastErrorObject?.upserted); expectType(findOneAndUpdateRes.ok); + + const findOneAndDeleteRes = await TestModel.findOneAndDelete({ _id: '0'.repeat(24) }, { includeResultMetadata: true }); + expectType(findOneAndDeleteRes.lastErrorObject?.updatedExisting); + expectType(findOneAndDeleteRes.lastErrorObject?.upserted); + expectType(findOneAndDeleteRes.ok); + + const findByIdAndDeleteRes = await TestModel.findByIdAndDelete('0'.repeat(24), { includeResultMetadata: true }); + expectType(findByIdAndDeleteRes.lastErrorObject?.updatedExisting); + expectType(findByIdAndDeleteRes.lastErrorObject?.upserted); + expectType(findByIdAndDeleteRes.ok); } function gh13904() { diff --git a/types/index.d.ts b/types/index.d.ts index 3f94524ba98..d8d9e0c629a 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -336,6 +336,7 @@ declare module 'mongoose' { post(method: MongooseDistinctDocumentMiddleware|MongooseDistinctDocumentMiddleware[], options: SchemaPostOptions & SchemaPostOptions, fn: PostMiddlewareFunction): this; post(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, options: SchemaPostOptions & { document: true, query: false }, fn: PostMiddlewareFunction): this; // this = Query + post>(method: MongooseRawResultQueryMiddleware|MongooseRawResultQueryMiddleware[], fn: PostMiddlewareFunction | ModifyResult>>): this; post>(method: MongooseDefaultQueryMiddleware|MongooseDefaultQueryMiddleware[], fn: PostMiddlewareFunction>): this; post>(method: MongooseDistinctQueryMiddleware|MongooseDistinctQueryMiddleware[], options: SchemaPostOptions, fn: PostMiddlewareFunction>): this; post>(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, options: SchemaPostOptions & { document: false, query: true }, fn: PostMiddlewareFunction>): this; diff --git a/types/middlewares.d.ts b/types/middlewares.d.ts index 43ca1974b81..9302b9b7d48 100644 --- a/types/middlewares.d.ts +++ b/types/middlewares.d.ts @@ -5,7 +5,9 @@ declare module 'mongoose' { type MongooseDistinctDocumentMiddleware = 'save' | 'init' | 'validate'; type MongooseDocumentMiddleware = MongooseDistinctDocumentMiddleware | MongooseQueryAndDocumentMiddleware; - type MongooseDistinctQueryMiddleware = 'count' | 'estimatedDocumentCount' | 'countDocuments' | 'deleteMany' | 'distinct' | 'find' | 'findOne' | 'findOneAndDelete' | 'findOneAndRemove' | 'findOneAndReplace' | 'findOneAndUpdate' | 'replaceOne' | 'updateMany'; + type MongooseRawResultQueryMiddleware = 'findOneAndUpdate' | 'findOneAndReplace' | 'findOneAndDelete'; + type MongooseDistinctQueryMiddleware = 'estimatedDocumentCount' | 'countDocuments' | 'deleteMany' | 'distinct' | 'find' | 'findOne' | 'findOneAndDelete' | 'findOneAndReplace' | 'findOneAndUpdate' | 'replaceOne' | 'updateMany'; + type MongooseDefaultQueryMiddleware = MongooseDistinctQueryMiddleware | 'updateOne' | 'deleteOne'; type MongooseQueryMiddleware = MongooseDistinctQueryMiddleware | MongooseQueryAndDocumentMiddleware; diff --git a/types/models.d.ts b/types/models.d.ts index cfa23bc40da..ddb28542ed7 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -548,21 +548,9 @@ declare module 'mongoose' { >; findByIdAndDelete( id?: mongodb.ObjectId | any, - options?: QueryOptions | null - ): QueryWithHelpers; - - /** Creates a `findByIdAndRemove` query, filtering by the given `_id`. */ - findByIdAndRemove( - id: mongodb.ObjectId | any, - options: QueryOptions & { lean: true } - ): QueryWithHelpers< - GetLeanResultType | null, - ResultDoc, - TQueryHelpers, - TRawDocType, - 'findOneAndDelete' - >; - findByIdAndRemove( + options?: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndDelete'>; + findByIdAndDelete( id?: mongodb.ObjectId | any, options?: QueryOptions | null ): QueryWithHelpers; @@ -584,11 +572,6 @@ declare module 'mongoose' { update: UpdateQuery, options: QueryOptions & { rawResult: true } ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndUpdate'>; - findByIdAndUpdate( - id: mongodb.ObjectId | any, - update: UpdateQuery, - options: QueryOptions & { includeResultMetadata: true } - ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndUpdate'>; findByIdAndUpdate( id: mongodb.ObjectId | any, update: UpdateQuery, @@ -615,6 +598,10 @@ declare module 'mongoose' { TRawDocType, 'findOneAndDelete' >; + findOneAndDelete( + filter?: FilterQuery, + options?: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndDelete'>; findOneAndDelete( filter?: FilterQuery, options?: QueryOptions | null @@ -643,11 +630,6 @@ declare module 'mongoose' { replacement: TRawDocType | AnyObject, options: QueryOptions & { rawResult: true } ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndReplace'>; - findOneAndReplace( - filter: FilterQuery, - replacement: TRawDocType | AnyObject, - options: QueryOptions & { includeResultMetadata: true } - ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndReplace'>; findOneAndReplace( filter: FilterQuery, replacement: TRawDocType | AnyObject, From e6d2fbe5fa246d6b6e527b79809a2f9130887ed3 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 16 Nov 2023 18:19:55 -0500 Subject: [PATCH 05/31] types: fix issues backporting #14078 to 7.x --- types/middlewares.d.ts | 2 +- types/models.d.ts | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/types/middlewares.d.ts b/types/middlewares.d.ts index 9302b9b7d48..5cf3af901c0 100644 --- a/types/middlewares.d.ts +++ b/types/middlewares.d.ts @@ -6,7 +6,7 @@ declare module 'mongoose' { type MongooseDocumentMiddleware = MongooseDistinctDocumentMiddleware | MongooseQueryAndDocumentMiddleware; type MongooseRawResultQueryMiddleware = 'findOneAndUpdate' | 'findOneAndReplace' | 'findOneAndDelete'; - type MongooseDistinctQueryMiddleware = 'estimatedDocumentCount' | 'countDocuments' | 'deleteMany' | 'distinct' | 'find' | 'findOne' | 'findOneAndDelete' | 'findOneAndReplace' | 'findOneAndUpdate' | 'replaceOne' | 'updateMany'; + type MongooseDistinctQueryMiddleware = 'count' | 'estimatedDocumentCount' | 'countDocuments' | 'deleteMany' | 'distinct' | 'find' | 'findOne' | 'findOneAndDelete' | 'findOneAndReplace' | 'findOneAndRemove' | 'findOneAndUpdate' | 'replaceOne' | 'updateMany'; type MongooseDefaultQueryMiddleware = MongooseDistinctQueryMiddleware | 'updateOne' | 'deleteOne'; type MongooseQueryMiddleware = MongooseDistinctQueryMiddleware | MongooseQueryAndDocumentMiddleware; diff --git a/types/models.d.ts b/types/models.d.ts index ddb28542ed7..24ceae501d2 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -555,6 +555,18 @@ declare module 'mongoose' { options?: QueryOptions | null ): QueryWithHelpers; + /** Creates a `findByIdAndRemove` query, filtering by the given `_id`. */ + findByIdAndRemove( + id: mongodb.ObjectId | any, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + GetLeanResultType | null, + ResultDoc, + TQueryHelpers, + TRawDocType, + 'findOneAndDelete' + >; + /** Creates a `findOneAndUpdate` query, filtering by the given `_id`. */ findByIdAndUpdate( id: mongodb.ObjectId | any, @@ -572,6 +584,11 @@ declare module 'mongoose' { update: UpdateQuery, options: QueryOptions & { rawResult: true } ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndUpdate'>; + findByIdAndUpdate( + id: mongodb.ObjectId | any, + update: UpdateQuery, + options: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndUpdate'>; findByIdAndUpdate( id: mongodb.ObjectId | any, update: UpdateQuery, @@ -630,6 +647,11 @@ declare module 'mongoose' { replacement: TRawDocType | AnyObject, options: QueryOptions & { rawResult: true } ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndReplace'>; + findOneAndReplace( + filter: FilterQuery, + replacement: TRawDocType | AnyObject, + options: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndReplace'>; findOneAndReplace( filter: FilterQuery, replacement: TRawDocType | AnyObject, From 64317eb3b8129c33a2ab4de362f7706f1d4e4356 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Fri, 17 Nov 2023 07:26:17 -0500 Subject: [PATCH 06/31] docs: remove "DEPRECATED" warning mistakenly added to `read()` tags param Fix #13980 --- lib/aggregate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/aggregate.js b/lib/aggregate.js index a160f57420c..450f9e34d12 100644 --- a/lib/aggregate.js +++ b/lib/aggregate.js @@ -665,7 +665,7 @@ Aggregate.prototype.unionWith = function(options) { * await Model.aggregate(pipeline).read('primaryPreferred'); * * @param {String|ReadPreference} pref one of the listed preference options or their aliases - * @param {Array} [tags] optional tags for this query. DEPRECATED + * @param {Array} [tags] optional tags for this query. * @return {Aggregate} this * @api public * @see mongodb https://www.mongodb.com/docs/manual/applications/replication/#read-preference From 66f23ac43ecf3717c6f78e5c2cdafdb322abfe57 Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Fri, 17 Nov 2023 17:59:03 -0500 Subject: [PATCH 07/31] initial draft --- scripts/website.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/scripts/website.js b/scripts/website.js index 7f6a8044788..4f5039355b2 100644 --- a/scripts/website.js +++ b/scripts/website.js @@ -76,6 +76,38 @@ const tests = [ ...acquit.parse(fs.readFileSync(path.join(testPath, 'docs/schemas.test.js')).toString()) ]; +function refreshDocs() { + deleteAllHtmlFiles(); + if (process.env.DOCS_DEPLOY) { + moveDocsToTemp(); + } +} + +function deleteAllHtmlFiles() { + fs.unlinkSync('../index.html'); + const locations = ['../docs','../docs/tutorials', '../docs/typescript'] + for (let i = 0; i < locations.length; i++) { + const files = fs.readdirSync(locations[i]); + for (let index = 0; index < files.length; index++) { + if (files[index].endsWith('.html')) { + fs.unlinkSync(files[index]); + } + } + } + const folders = ['../docs/api', '../docs/source/_docs', '../tmp']; + for (let i = 0; i < folders.length; i++) { + fs.rmdirSync(folders[i]) + } +} + +function moveDocsToTemp() { + const folder = '../docs/7.x'; + const directory = fs.readdirSync(folder); + for (let i = 0; i < directory.length; i++) { + fs.renameSync(`${folder}/${directory[i]}`, `./tmp/${directory[i]}`); + } +} + /** * Array of array of semver numbers, sorted with highest number first * @example From 782db453cfe9020cf02c32064b14088a22ed5f38 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sat, 18 Nov 2023 16:12:34 -0500 Subject: [PATCH 08/31] test(query): remove unnecessary query test code Fix #13970 --- test/query.test.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/test/query.test.js b/test/query.test.js index b69f0ea374e..7840e9510b7 100644 --- a/test/query.test.js +++ b/test/query.test.js @@ -1373,7 +1373,7 @@ describe('Query', function() { }); describe('setOptions', function() { - it('works', async function() { + it('works', function() { const q = new Query(); q.setOptions({ thing: 'cat' }); q.setOptions({ populate: ['fans'] }); @@ -1397,16 +1397,6 @@ describe('Query', function() { assert.equal(q.options.hint.index2, -1); assert.equal(q.options.readPreference.mode, 'secondary'); assert.equal(q.options.readPreference.tags[0].dc, 'eu'); - - const Product = db.model('Product', productSchema); - const [, doc2] = await Product.create([ - { numbers: [3, 4, 5] }, - { strings: 'hi there'.split(' '), w: 'majority' } - ]); - - const docs = await Product.find().setOptions({ limit: 1, sort: { _id: -1 }, read: 'n' }); - assert.equal(docs.length, 1); - assert.equal(docs[0].id, doc2.id); }); it('populate as array in options (gh-4446)', function() { From 0cfad3080c468630ae48c6189528a931902e00e8 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sat, 18 Nov 2023 16:24:44 -0500 Subject: [PATCH 09/31] fix: upgrade mongodb -> 5.9.1 to fix #13829 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7373405da55..cc6a75c1362 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "dependencies": { "bson": "^5.5.0", "kareem": "2.5.1", - "mongodb": "5.9.0", + "mongodb": "5.9.1", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", From 020a8f3a48990fd7d4588964755420b90844baef Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 20 Nov 2023 15:41:20 -0500 Subject: [PATCH 10/31] chore: move cleanup logic from npm scripts to website.js script --- package.json | 2 +- scripts/website.js | 65 ++++++++++++++++++++++++++++++---------------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index cc6a75c1362..d3954404199 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "docs:prepare:publish:stable": "npm run docs:checkout:gh-pages && npm run docs:merge:stable && npm run docs:clean:stable && npm run docs:generate && npm run docs:generate:search", "docs:prepare:publish:5x": "npm run docs:checkout:5x && npm run docs:merge:5x && npm run docs:clean:stable && npm run docs:generate && npm run docs:copy:tmp && npm run docs:checkout:gh-pages && npm run docs:copy:tmp:5x", "docs:prepare:publish:6x": "npm run docs:checkout:6x && npm run docs:merge:6x && npm run docs:clean:stable && env DOCS_DEPLOY=true npm run docs:generate && npm run docs:move:6x:tmp && npm run docs:checkout:gh-pages && npm run docs:copy:tmp:6x", - "docs:prepare:publish:7x": "git checkout 7.x && npm run docs:clean:stable && env DOCS_DEPLOY=true npm run docs:generate && mv ./docs/7.x ./tmp && npm run docs:checkout:gh-pages && rimraf ./docs/7.x && ncp ./tmp ./docs/7.x", + "docs:prepare:publish:7x": "env DOCS_DEPLOY=true npm run docs:generate && npm run docs:checkout:gh-pages && rimraf ./docs/7.x && ncp ./tmp ./docs/7.x", "docs:check-links": "blc http://127.0.0.1:8089 -ro", "lint": "eslint .", "lint-js": "eslint . --ext .js --ext .cjs", diff --git a/scripts/website.js b/scripts/website.js index 4f5039355b2..e8e3d6ba99b 100644 --- a/scripts/website.js +++ b/scripts/website.js @@ -4,9 +4,11 @@ Error.stackTraceLimit = Infinity; const acquit = require('acquit'); const fs = require('fs'); +const fsextra = require('fs-extra'); const path = require('path'); const pug = require('pug'); const pkg = require('../package.json'); +const rimraf = require('rimraf'); const transform = require('acquit-require'); const childProcess = require("child_process"); @@ -76,35 +78,45 @@ const tests = [ ...acquit.parse(fs.readFileSync(path.join(testPath, 'docs/schemas.test.js')).toString()) ]; -function refreshDocs() { - deleteAllHtmlFiles(); - if (process.env.DOCS_DEPLOY) { - moveDocsToTemp(); - } -} - function deleteAllHtmlFiles() { - fs.unlinkSync('../index.html'); - const locations = ['../docs','../docs/tutorials', '../docs/typescript'] - for (let i = 0; i < locations.length; i++) { - const files = fs.readdirSync(locations[i]); + try { + fs.unlinkSync('./index.html'); + } catch (err) { + if (err.code !== 'ENOENT') { + throw err; + } + } + const foldersToClean = [ + './docs', + './docs/tutorials', + './docs/typescript', + './docs/api', + './docs/source/_docs', + './tmp' + ]; + for (const folder of foldersToClean) { + let files = []; + try { + files = fs.readdirSync(folder); + } catch (err) { + if (err.code === 'ENOENT') { + continue; + } + } for (let index = 0; index < files.length; index++) { if (files[index].endsWith('.html')) { - fs.unlinkSync(files[index]); + fs.unlinkSync(`${folder}/${files[index]}`); } } } - const folders = ['../docs/api', '../docs/source/_docs', '../tmp']; - for (let i = 0; i < folders.length; i++) { - fs.rmdirSync(folders[i]) - } } function moveDocsToTemp() { - const folder = '../docs/7.x'; + rimraf.sync('./tmp'); + const folder = `./docs/7.x`; const directory = fs.readdirSync(folder); for (let i = 0; i < directory.length; i++) { - fs.renameSync(`${folder}/${directory[i]}`, `./tmp/${directory[i]}`); + fsextra.moveSync(`${folder}/${directory[i]}`, `./tmp/${directory[i]}`); } } @@ -520,7 +532,6 @@ async function copyAllRequiredFiles() { return; } - const fsextra = require('fs-extra'); await Promise.all(pathsToCopy.map(async v => { const resultPath = path.resolve(cwd, path.join('.', versionObj.versionedPath, v)); await fsextra.copy(v, resultPath); @@ -537,8 +548,16 @@ exports.cwd = cwd; // only run the following code if this file is the main module / entry file if (isMain) { - console.log(`Processing ~${files.length} files`); - Promise.all([pugifyAllFiles(), copyAllRequiredFiles()]).then(() => { - console.log("Done Processing"); - }) + (async function main() { + console.log(`Processing ~${files.length} files`); + + await deleteAllHtmlFiles(); + await pugifyAllFiles(); + await copyAllRequiredFiles(); + if (process.env.DOCS_DEPLOY) { + await moveDocsToTemp(); + } + + console.log('Done Processing'); + })(); } From a848de06bbb5ec9c822d0eba49e2399b66a2e2a9 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 19 Nov 2023 14:44:05 -0500 Subject: [PATCH 11/31] Merge pull request #14099 from csy1204/fix/gh-14098 fix(populate): fix curPath to update appropriately --- lib/helpers/populate/assignVals.js | 3 +- test/model.populate.test.js | 52 ++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/lib/helpers/populate/assignVals.js b/lib/helpers/populate/assignVals.js index 92f0ebecd05..9a30ce28299 100644 --- a/lib/helpers/populate/assignVals.js +++ b/lib/helpers/populate/assignVals.js @@ -144,7 +144,7 @@ module.exports = function assignVals(o) { const parts = _path.split('.'); let cur = docs[i]; - const curPath = parts[0]; + let curPath = parts[0]; for (let j = 0; j < parts.length - 1; ++j) { // If we get to an array with a dotted path, like `arr.foo`, don't set // `foo` on the array. @@ -167,6 +167,7 @@ module.exports = function assignVals(o) { cur[parts[j]] = {}; } cur = cur[parts[j]]; + curPath += parts[j + 1] ? `.${parts[j + 1]}` : ''; // If the property in MongoDB is a primitive, we won't be able to populate // the nested path, so skip it. See gh-7545 if (typeof cur !== 'object') { diff --git a/test/model.populate.test.js b/test/model.populate.test.js index e53c2f7de47..d8db2a67124 100644 --- a/test/model.populate.test.js +++ b/test/model.populate.test.js @@ -8259,6 +8259,58 @@ describe('model: populate:', function() { assert.deepEqual(populatedRides[1].files, []); }); + it('doesnt insert empty document when lean populating a path within an underneath non-existent document array (gh-14098)', async function() { + const userSchema = new mongoose.Schema({ + fullName: String, + company: String + }); + const User = db.model('User', userSchema); + + const fileSchema = new mongoose.Schema({ + _id: String, + uploaderId: { + type: mongoose.ObjectId, + ref: 'User' + } + }, { toObject: { virtuals: true }, toJSON: { virtuals: true } }); + fileSchema.virtual('uploadedBy', { + ref: 'User', + localField: 'uploaderId', + foreignField: '_id', + justOne: true + }); + + const contentSchema = new mongoose.Schema({ + memo: String, + files: { type: [fileSchema], default: [] } + }, { toObject: { virtuals: true }, toJSON: { virtuals: true }, _id: false }); + + const postSchema = new mongoose.Schema({ + title: String, + content: { type: contentSchema } + }, { toObject: { virtuals: true }, toJSON: { virtuals: true } }); + const Post = db.model('Test1', postSchema); + + const user = await User.create({ fullName: 'John Doe', company: 'GitHub' }); + await Post.create([ + { title: 'London-Paris' }, + { + title: 'Berlin-Moscow', + content: { + memo: 'Not Easy', + files: [{ _id: '123', uploaderId: user._id }] + } + } + ]); + await Post.updateMany({}, { $unset: { 'content.files': 1 } }); + const populatedRides = await Post.find({}).populate({ + path: 'content.files.uploadedBy', + justOne: true + }).lean(); + assert.equal(populatedRides[0].content.files, undefined); + assert.equal(populatedRides[1].content.files, undefined); + }); + it('sets empty array if populating undefined path (gh-8455)', async function() { const TestSchema = new Schema({ thingIds: [mongoose.ObjectId] From 4b065f2aed1391dc9b0b5f5f914d7f01847a3723 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Wed, 22 Nov 2023 21:24:48 -0500 Subject: [PATCH 12/31] refactor: address code review comments --- scripts/website.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/scripts/website.js b/scripts/website.js index e8e3d6ba99b..c3063df5807 100644 --- a/scripts/website.js +++ b/scripts/website.js @@ -8,7 +8,6 @@ const fsextra = require('fs-extra'); const path = require('path'); const pug = require('pug'); const pkg = require('../package.json'); -const rimraf = require('rimraf'); const transform = require('acquit-require'); const childProcess = require("child_process"); @@ -103,20 +102,20 @@ function deleteAllHtmlFiles() { continue; } } - for (let index = 0; index < files.length; index++) { - if (files[index].endsWith('.html')) { - fs.unlinkSync(`${folder}/${files[index]}`); + for (const file of files) { + if (file.endsWith('.html')) { + fs.unlinkSync(`${folder}/${file}`); } } } } function moveDocsToTemp() { - rimraf.sync('./tmp'); - const folder = `./docs/7.x`; + fs.rmSync('./tmp', { recursive: true }); + const folder = versionObj.versionedPath.replace(/^\//, ''); const directory = fs.readdirSync(folder); - for (let i = 0; i < directory.length; i++) { - fsextra.moveSync(`${folder}/${directory[i]}`, `./tmp/${directory[i]}`); + for (const file of directory) { + fsextra.moveSync(`${folder}/${file}`, `./tmp/${file}`); } } From b2b0acc638103ec9cfb324e713242d6103871b29 Mon Sep 17 00:00:00 2001 From: hasezoey Date: Thu, 23 Nov 2023 18:02:29 +0100 Subject: [PATCH 13/31] style(scripts/website): directly call "markdown.use" --- scripts/website.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/website.js b/scripts/website.js index 7f6a8044788..e3c94eee7cd 100644 --- a/scripts/website.js +++ b/scripts/website.js @@ -31,7 +31,7 @@ require('acquit-ignore')(); const { marked: markdown } = require('marked'); const highlight = require('highlight.js'); const { promisify } = require("util"); -const renderer = { +markdown.use({ heading: function(text, level, raw, slugger) { const slug = slugger.slug(raw); return ` @@ -40,7 +40,7 @@ const renderer = { \n`; } -}; +}); markdown.setOptions({ highlight: function(code, language) { if (!language) { @@ -52,7 +52,6 @@ markdown.setOptions({ return highlight.highlight(code, { language }).value; } }); -markdown.use({ renderer }); const testPath = path.resolve(cwd, 'test') From 319979c588d045467c8a5ae5da2ecc60cf6ac7e4 Mon Sep 17 00:00:00 2001 From: hasezoey Date: Thu, 23 Nov 2023 18:06:19 +0100 Subject: [PATCH 14/31] chore(scripts/website): refactor acquit file loading to make use of parallel promises also improve use-ability --- scripts/website.js | 63 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/scripts/website.js b/scripts/website.js index e3c94eee7cd..e6a0953ade4 100644 --- a/scripts/website.js +++ b/scripts/website.js @@ -55,25 +55,50 @@ markdown.setOptions({ const testPath = path.resolve(cwd, 'test') -const tests = [ - ...acquit.parse(fs.readFileSync(path.join(testPath, 'geojson.test.js')).toString()), - ...acquit.parse(fs.readFileSync(path.join(testPath, 'docs/transactions.test.js')).toString()), - ...acquit.parse(fs.readFileSync(path.join(testPath, 'schema.alias.test.js')).toString()), - ...acquit.parse(fs.readFileSync(path.join(testPath, 'model.middleware.test.js')).toString()), - ...acquit.parse(fs.readFileSync(path.join(testPath, 'docs/date.test.js')).toString()), - ...acquit.parse(fs.readFileSync(path.join(testPath, 'docs/lean.test.js')).toString()), - ...acquit.parse(fs.readFileSync(path.join(testPath, 'docs/cast.test.js')).toString()), - ...acquit.parse(fs.readFileSync(path.join(testPath, 'docs/findoneandupdate.test.js')).toString()), - ...acquit.parse(fs.readFileSync(path.join(testPath, 'docs/custom-casting.test.js')).toString()), - ...acquit.parse(fs.readFileSync(path.join(testPath, 'docs/getters-setters.test.js')).toString()), - ...acquit.parse(fs.readFileSync(path.join(testPath, 'docs/virtuals.test.js')).toString()), - ...acquit.parse(fs.readFileSync(path.join(testPath, 'docs/defaults.test.js')).toString()), - ...acquit.parse(fs.readFileSync(path.join(testPath, 'docs/discriminators.test.js')).toString()), - ...acquit.parse(fs.readFileSync(path.join(testPath, 'docs/promises.test.js')).toString()), - ...acquit.parse(fs.readFileSync(path.join(testPath, 'docs/schematypes.test.js')).toString()), - ...acquit.parse(fs.readFileSync(path.join(testPath, 'docs/validation.test.js')).toString()), - ...acquit.parse(fs.readFileSync(path.join(testPath, 'docs/schemas.test.js')).toString()) +/** additional test files to scan, relative to `test/` */ +const additionalTestFiles = [ + 'geojson.test.js', + 'schema.alias.test.js' ]; +/** ignored files from `test/docs/` */ +const ignoredTestFiles = [ + // ignored because acquit does not like "for await" + 'asyncIterator.test.js' +]; + +/** + * Load all test file contents with acquit + * @returns {Object[]} acquit ast array + */ +async function getTests() { + const promiseArray = []; + + for (const file of additionalTestFiles) { + const filePath = path.join(testPath, file); + promiseArray.push(fs.promises.readFile(filePath).then(v => ({value: v.toString(), path: filePath}))); + } + + const testDocs = path.resolve(testPath, 'docs'); + + for (const file of await fs.promises.readdir(testDocs)) { + if (ignoredTestFiles.includes(file)) { + continue; + } + + const filePath = path.join(testDocs, file); + promiseArray.push(fs.promises.readFile(filePath).then(v => ({value: v.toString(), path: filePath}))); + } + + return (await Promise.all(promiseArray)).flatMap(v => { + try { + return acquit.parse(v.value); + } catch (err) { + // add a file path to a acquit error, for better debugging + err.filePath = v.path; + throw err; + } + }) +} /** * Array of array of semver numbers, sorted with highest number first @@ -351,7 +376,7 @@ async function pugify(filename, options, isReload = false) { let contents = fs.readFileSync(path.resolve(cwd, inputFile)).toString(); if (options.acquit) { - contents = transform(contents, tests); + contents = transform(contents, await getTests()); contents = contents.replaceAll(/^```acquit$/gmi, "```javascript"); } From c8a8da2a1d957b518c64506c34f396974cbae25a Mon Sep 17 00:00:00 2001 From: hasezoey Date: Thu, 23 Nov 2023 18:17:07 +0100 Subject: [PATCH 15/31] chore(scripts/website): convert a comment to JSDOC --- scripts/website.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/website.js b/scripts/website.js index e6a0953ade4..f99758280ff 100644 --- a/scripts/website.js +++ b/scripts/website.js @@ -435,7 +435,7 @@ async function pugify(filename, options, isReload = false) { }); } -// extra function to start watching for file-changes, without having to call this file directly with "watch" +/** extra function to start watching for file-changes, without having to call this file directly with "watch" */ function startWatch() { Object.entries(docsFilemap.fileMap).forEach(([file, fileValue]) => { let watchPath = path.resolve(cwd, file); From 93fb4e8563a6e48710a340e462ee669bac8a43da Mon Sep 17 00:00:00 2001 From: hasezoey Date: Thu, 23 Nov 2023 18:22:04 +0100 Subject: [PATCH 16/31] chore(scripts/website): add some missing ";" --- scripts/website.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/website.js b/scripts/website.js index f99758280ff..626a64d8407 100644 --- a/scripts/website.js +++ b/scripts/website.js @@ -53,7 +53,7 @@ markdown.setOptions({ } }); -const testPath = path.resolve(cwd, 'test') +const testPath = path.resolve(cwd, 'test'); /** additional test files to scan, relative to `test/` */ const additionalTestFiles = [ @@ -503,7 +503,7 @@ const pathsToCopy = [ 'docs/js', 'docs/css', 'docs/images' -] +]; /** Copy all static files when versionedDeploy is used */ async function copyAllRequiredFiles() { From 9e3185ad40d24836b75ff1ef113e9451b2d3fa44 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Fri, 24 Nov 2023 10:16:08 -0500 Subject: [PATCH 17/31] types(query): base filters and projections off of RawDocType instead of DocType so autocomplete doesn't show populate Fix #14077 --- types/query.d.ts | 92 ++++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/types/query.d.ts b/types/query.d.ts index 1d66e75221c..2234e66f656 100644 --- a/types/query.d.ts +++ b/types/query.d.ts @@ -217,7 +217,7 @@ declare module 'mongoose' { allowDiskUse(value: boolean): this; /** Specifies arguments for an `$and` condition. */ - and(array: FilterQuery[]): this; + and(array: FilterQuery[]): this; /** Specifies the batchSize option. */ batchSize(val: number): this; @@ -265,7 +265,7 @@ declare module 'mongoose' { comment(val: string): this; /** Specifies this query as a `count` query. */ - count(criteria?: FilterQuery): QueryWithHelpers< + count(criteria?: FilterQuery): QueryWithHelpers< number, DocType, THelpers, @@ -275,7 +275,7 @@ declare module 'mongoose' { /** Specifies this query as a `countDocuments` query. */ countDocuments( - criteria?: FilterQuery, + criteria?: FilterQuery, options?: QueryOptions ): QueryWithHelpers; @@ -291,10 +291,10 @@ declare module 'mongoose' { * collection, regardless of the value of `single`. */ deleteMany( - filter?: FilterQuery, + filter?: FilterQuery, options?: QueryOptions ): QueryWithHelpers; - deleteMany(filter: FilterQuery): QueryWithHelpers< + deleteMany(filter: FilterQuery): QueryWithHelpers< any, DocType, THelpers, @@ -309,10 +309,10 @@ declare module 'mongoose' { * option. */ deleteOne( - filter?: FilterQuery, + filter?: FilterQuery, options?: QueryOptions ): QueryWithHelpers; - deleteOne(filter: FilterQuery): QueryWithHelpers< + deleteOne(filter: FilterQuery): QueryWithHelpers< any, DocType, THelpers, @@ -324,7 +324,7 @@ declare module 'mongoose' { /** Creates a `distinct` query: returns the distinct values of the given `field` that match `filter`. */ distinct( field: string, - filter?: FilterQuery + filter?: FilterQuery ): QueryWithHelpers, DocType, THelpers, RawDocType, 'distinct'>; /** Specifies a `$elemMatch` query condition. When called with one argument, the most recent path passed to `where()` is used. */ @@ -364,71 +364,71 @@ declare module 'mongoose' { /** Creates a `find` query: gets a list of documents that match `filter`. */ find( - filter: FilterQuery, - projection?: ProjectionType | null, + filter: FilterQuery, + projection?: ProjectionType | null, options?: QueryOptions | null ): QueryWithHelpers, DocType, THelpers, RawDocType, 'find'>; find( - filter: FilterQuery, - projection?: ProjectionType | null + filter: FilterQuery, + projection?: ProjectionType | null ): QueryWithHelpers, DocType, THelpers, RawDocType, 'find'>; find( - filter: FilterQuery - ): QueryWithHelpers, DocType, THelpers, RawDocType, 'find'>; + filter: FilterQuery + ): QueryWithHelpers, DocType, THelpers, RawDocType, 'find'>; find(): QueryWithHelpers, DocType, THelpers, RawDocType, 'find'>; /** Declares the query a findOne operation. When executed, returns the first found document. */ findOne( - filter?: FilterQuery, - projection?: ProjectionType | null, + filter?: FilterQuery, + projection?: ProjectionType | null, options?: QueryOptions | null ): QueryWithHelpers; findOne( - filter?: FilterQuery, - projection?: ProjectionType | null + filter?: FilterQuery, + projection?: ProjectionType | null ): QueryWithHelpers; findOne( - filter?: FilterQuery - ): QueryWithHelpers; + filter?: FilterQuery + ): QueryWithHelpers; /** Creates a `findOneAndDelete` query: atomically finds the given document, deletes it, and returns the document as it was before deletion. */ findOneAndDelete( - filter?: FilterQuery, + filter?: FilterQuery, options?: QueryOptions | null ): QueryWithHelpers; /** Creates a `findOneAndRemove` query: atomically finds the given document and deletes it. */ findOneAndRemove( - filter?: FilterQuery, + filter?: FilterQuery, options?: QueryOptions | null ): QueryWithHelpers; /** Creates a `findOneAndUpdate` query: atomically find the first document that matches `filter` and apply `update`. */ findOneAndUpdate( - filter: FilterQuery, - update: UpdateQuery, + filter: FilterQuery, + update: UpdateQuery, options: QueryOptions & { rawResult: true } ): QueryWithHelpers, DocType, THelpers, RawDocType, 'findOneAndUpdate'>; findOneAndUpdate( - filter: FilterQuery, - update: UpdateQuery, + filter: FilterQuery, + update: UpdateQuery, options: QueryOptions & { upsert: true } & ReturnsNewDoc ): QueryWithHelpers; findOneAndUpdate( - filter?: FilterQuery, - update?: UpdateQuery, + filter?: FilterQuery, + update?: UpdateQuery, options?: QueryOptions | null ): QueryWithHelpers; /** Declares the query a findById operation. When executed, returns the document with the given `_id`. */ findById( id: mongodb.ObjectId | any, - projection?: ProjectionType | null, + projection?: ProjectionType | null, options?: QueryOptions | null ): QueryWithHelpers; findById( id: mongodb.ObjectId | any, - projection?: ProjectionType | null + projection?: ProjectionType | null ): QueryWithHelpers; findById( id: mongodb.ObjectId | any @@ -443,22 +443,22 @@ declare module 'mongoose' { /** Creates a `findOneAndUpdate` query, filtering by the given `_id`. */ findByIdAndUpdate( id: mongodb.ObjectId | any, - update: UpdateQuery, + update: UpdateQuery, options: QueryOptions & { rawResult: true } ): QueryWithHelpers; findByIdAndUpdate( id: mongodb.ObjectId | any, - update: UpdateQuery, + update: UpdateQuery, options: QueryOptions & { upsert: true } & ReturnsNewDoc ): QueryWithHelpers; findByIdAndUpdate( id?: mongodb.ObjectId | any, - update?: UpdateQuery, + update?: UpdateQuery, options?: QueryOptions | null ): QueryWithHelpers; findByIdAndUpdate( id: mongodb.ObjectId | any, - update: UpdateQuery + update: UpdateQuery ): QueryWithHelpers; /** Specifies a `$geometry` condition */ @@ -472,7 +472,7 @@ declare module 'mongoose' { get(path: string): any; /** Returns the current query filter (also known as conditions) as a POJO. */ - getFilter(): FilterQuery; + getFilter(): FilterQuery; /** Gets query options. */ getOptions(): QueryOptions; @@ -481,7 +481,7 @@ declare module 'mongoose' { getPopulatedPaths(): Array; /** Returns the current query filter. Equivalent to `getFilter()`. */ - getQuery(): FilterQuery; + getQuery(): FilterQuery; /** Returns the current update operations as a JSON object. */ getUpdate(): UpdateQuery | UpdateWithAggregationPipeline | null; @@ -551,7 +551,7 @@ declare module 'mongoose' { maxTimeMS(ms: number): this; /** Merges another Query or conditions object into this one. */ - merge(source: Query | FilterQuery): this; + merge(source: Query | FilterQuery): this; /** Specifies a `$mod` condition, filters documents for documents whose `path` property is a number that is equal to `remainder` modulo `divisor`. */ mod(path: K, val: number): this; @@ -579,10 +579,10 @@ declare module 'mongoose' { nin(val: Array): this; /** Specifies arguments for an `$nor` condition. */ - nor(array: Array>): this; + nor(array: Array>): this; /** Specifies arguments for an `$or` condition. */ - or(array: Array>): this; + or(array: Array>): this; /** * Make this query throw an error if no documents match the given `filter`. @@ -639,7 +639,7 @@ declare module 'mongoose' { * not accept any [atomic](https://www.mongodb.com/docs/manual/tutorial/model-data-for-atomic-operations/#pattern) operators (`$set`, etc.) */ replaceOne( - filter?: FilterQuery, + filter?: FilterQuery, replacement?: DocType | AnyObject, options?: QueryOptions | null ): QueryWithHelpers; @@ -698,9 +698,9 @@ declare module 'mongoose' { setOptions(options: QueryOptions, overwrite?: boolean): this; /** Sets the query conditions to the provided JSON object. */ - setQuery(val: FilterQuery | null): void; + setQuery(val: FilterQuery | null): void; - setUpdate(update: UpdateQuery | UpdateWithAggregationPipeline): void; + setUpdate(update: UpdateQuery | UpdateWithAggregationPipeline): void; /** Specifies an `$size` query condition. When called with one argument, the most recent path passed to `where()` is used. */ size(path: K, val: number): this; @@ -738,8 +738,8 @@ declare module 'mongoose' { * the `multi` option. */ updateMany( - filter?: FilterQuery, - update?: UpdateQuery | UpdateWithAggregationPipeline, + filter?: FilterQuery, + update?: UpdateQuery | UpdateWithAggregationPipeline, options?: QueryOptions | null ): QueryWithHelpers; @@ -748,8 +748,8 @@ declare module 'mongoose' { * `update()`, except it does not support the `multi` or `overwrite` options. */ updateOne( - filter?: FilterQuery, - update?: UpdateQuery | UpdateWithAggregationPipeline, + filter?: FilterQuery, + update?: UpdateQuery | UpdateWithAggregationPipeline, options?: QueryOptions | null ): QueryWithHelpers; From 6def405aa87d5267bdd85a0f514edc1472d147d6 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Fri, 24 Nov 2023 10:46:21 -0500 Subject: [PATCH 18/31] fix(populate): set populated docs in correct order when populating virtual underneath doc array with justOne (#14105) Fix #14018 --- .../populate/assignRawDocsToIdStructure.js | 8 +- lib/helpers/populate/assignVals.js | 3 +- test/model.populate.test.js | 82 +++++++++++++++++++ 3 files changed, 90 insertions(+), 3 deletions(-) diff --git a/lib/helpers/populate/assignRawDocsToIdStructure.js b/lib/helpers/populate/assignRawDocsToIdStructure.js index c08671e5918..95b84e0bb03 100644 --- a/lib/helpers/populate/assignRawDocsToIdStructure.js +++ b/lib/helpers/populate/assignRawDocsToIdStructure.js @@ -32,9 +32,13 @@ const kHasArray = Symbol('assignRawDocsToIdStructure.hasArray'); */ function assignRawDocsToIdStructure(rawIds, resultDocs, resultOrder, options, recursed) { - // honor user specified sort order + // honor user specified sort order, unless we're populating a single + // virtual underneath an array (e.g. populating `employees.mostRecentShift` where + // `mostRecentShift` is a virtual with `justOne`) const newOrder = []; - const sorting = options.sort && rawIds.length > 1; + const sorting = options.isVirtual && options.justOne && rawIds.length > 1 + ? false : + options.sort && rawIds.length > 1; const nullIfNotFound = options.$nullIfNotFound; let doc; let sid; diff --git a/lib/helpers/populate/assignVals.js b/lib/helpers/populate/assignVals.js index 9a30ce28299..017066add17 100644 --- a/lib/helpers/populate/assignVals.js +++ b/lib/helpers/populate/assignVals.js @@ -19,7 +19,8 @@ module.exports = function assignVals(o) { // `o.options` contains options explicitly listed in `populateOptions`, like // `match` and `limit`. const populateOptions = Object.assign({}, o.options, userOptions, { - justOne: o.justOne + justOne: o.justOne, + isVirtual: o.isVirtual }); populateOptions.$nullIfNotFound = o.isVirtual; const populatedModel = o.populatedModel; diff --git a/test/model.populate.test.js b/test/model.populate.test.js index d8db2a67124..4e638a90178 100644 --- a/test/model.populate.test.js +++ b/test/model.populate.test.js @@ -10694,4 +10694,86 @@ describe('model: populate:', function() { [company._id.toString(), company2._id.toString()] ); }); + + it('sets populated docs in correct order when populating virtual underneath document array with justOne (gh-14018)', async function() { + const shiftSchema = new mongoose.Schema({ + employeeId: mongoose.Types.ObjectId, + startedAt: Date, + endedAt: Date + }); + const Shift = db.model('Shift', shiftSchema); + + const employeeSchema = new mongoose.Schema({ + name: String + }); + employeeSchema.virtual('mostRecentShift', { + ref: Shift, + localField: '_id', + foreignField: 'employeeId', + options: { + sort: { startedAt: -1 } + }, + justOne: true + }); + const storeSchema = new mongoose.Schema({ + location: String, + employees: [employeeSchema] + }); + const Store = db.model('Store', storeSchema); + + const store = await Store.create({ + location: 'Tashbaan', + employees: [ + { _id: '0'.repeat(24), name: 'Aravis' }, + { _id: '1'.repeat(24), name: 'Shasta' } + ] + }); + + const employeeAravis = store.employees + .find(({ name }) => name === 'Aravis'); + const employeeShasta = store.employees + .find(({ name }) => name === 'Shasta'); + + await Shift.insertMany([ + { employeeId: employeeAravis._id, startedAt: new Date('2011-06-01'), endedAt: new Date('2011-06-02') }, + { employeeId: employeeAravis._id, startedAt: new Date('2013-06-01'), endedAt: new Date('2013-06-02') }, + { employeeId: employeeShasta._id, startedAt: new Date('2015-06-01'), endedAt: new Date('2015-06-02') } + ]); + + const storeWithMostRecentShifts = await Store.findOne({ location: 'Tashbaan' }) + .populate('employees.mostRecentShift') + .select('-__v') + .exec(); + assert.equal( + storeWithMostRecentShifts.employees[0].mostRecentShift.employeeId.toHexString(), + '0'.repeat(24) + ); + assert.equal( + storeWithMostRecentShifts.employees[1].mostRecentShift.employeeId.toHexString(), + '1'.repeat(24) + ); + + await Shift.findOne({ employeeId: employeeAravis._id }).sort({ startedAt: 1 }).then((s) => s.deleteOne()); + + const storeWithMostRecentShiftsNew = await Store.findOne({ location: 'Tashbaan' }) + .populate('employees.mostRecentShift') + .select('-__v') + .exec(); + assert.equal( + storeWithMostRecentShiftsNew.employees[0].mostRecentShift.employeeId.toHexString(), + '0'.repeat(24) + ); + assert.equal( + storeWithMostRecentShiftsNew.employees[0].mostRecentShift.startedAt.toString(), + new Date('2013-06-01').toString() + ); + assert.equal( + storeWithMostRecentShiftsNew.employees[1].mostRecentShift.employeeId.toHexString(), + '1'.repeat(24) + ); + assert.equal( + storeWithMostRecentShiftsNew.employees[1].mostRecentShift.startedAt.toString(), + new Date('2015-06-01').toString() + ); + }); }); From e3cccc30f17a8781d344c112f6b3935620b18161 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Fri, 24 Nov 2023 12:11:30 -0500 Subject: [PATCH 19/31] perf(array): use push() instead of concat() for $push atomics re: #11380 --- lib/types/array/methods/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/types/array/methods/index.js b/lib/types/array/methods/index.js index deb4e801f29..50691aedaee 100644 --- a/lib/types/array/methods/index.js +++ b/lib/types/array/methods/index.js @@ -374,7 +374,7 @@ const methods = { if (val != null && utils.hasUserDefinedProperty(val, '$each')) { atomics.$push = val; } else { - atomics.$push.$each = atomics.$push.$each.concat(val); + atomics.$push.$each.push(val); } } else { atomics[op] = val; From 62d5302ed75adcc92b4fe0eb40631c2910312a68 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Fri, 24 Nov 2023 12:22:16 -0500 Subject: [PATCH 20/31] perf: correct way to avoid calling concat() re: #11380 --- lib/types/array/methods/index.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/types/array/methods/index.js b/lib/types/array/methods/index.js index 50691aedaee..28ed50ddc95 100644 --- a/lib/types/array/methods/index.js +++ b/lib/types/array/methods/index.js @@ -374,7 +374,13 @@ const methods = { if (val != null && utils.hasUserDefinedProperty(val, '$each')) { atomics.$push = val; } else { - atomics.$push.$each.push(val); + if (val.length < 10000) { + atomics.$push.$each.push(...val); + } else { + for (const v of val) { + atomics.$push.$each.push(v); + } + } } } else { atomics[op] = val; @@ -711,7 +717,7 @@ const methods = { 'with different `$position`'); } atomic = values; - ret = [].push.apply(arr, values); + ret = _basePush.apply(arr, values); } this._registerAtomic('$push', atomic); From 865a8f2dc846c23a1fe1e200909b13301472729f Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Fri, 24 Nov 2023 12:25:39 -0500 Subject: [PATCH 21/31] perf: one more quick improvement --- lib/types/array/methods/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/types/array/methods/index.js b/lib/types/array/methods/index.js index 28ed50ddc95..f29b31ed32b 100644 --- a/lib/types/array/methods/index.js +++ b/lib/types/array/methods/index.js @@ -374,7 +374,9 @@ const methods = { if (val != null && utils.hasUserDefinedProperty(val, '$each')) { atomics.$push = val; } else { - if (val.length < 10000) { + if (val.length === 1) { + atomics.$push.$each.push(val); + } else if (val.length < 10000) { atomics.$push.$each.push(...val); } else { for (const v of val) { From b0476908f46a75eff7e85eb812aad2224fca3fd1 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Fri, 24 Nov 2023 12:35:57 -0500 Subject: [PATCH 22/31] perf: avoid mutating internal state if setting state to current re: #11380 --- lib/statemachine.js | 6 +++++- lib/types/array/methods/index.js | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/statemachine.js b/lib/statemachine.js index 70b1beca695..02fbc03e0fc 100644 --- a/lib/statemachine.js +++ b/lib/statemachine.js @@ -65,7 +65,11 @@ StateMachine.ctor = function() { */ StateMachine.prototype._changeState = function _changeState(path, nextState) { - const prevBucket = this.states[this.paths[path]]; + const prevState = this.paths[path]; + if (prevState === nextState) { + return; + } + const prevBucket = this.states[prevState]; if (prevBucket) delete prevBucket[path]; this.paths[path] = nextState; diff --git a/lib/types/array/methods/index.js b/lib/types/array/methods/index.js index f29b31ed32b..f9c1e2b80ba 100644 --- a/lib/types/array/methods/index.js +++ b/lib/types/array/methods/index.js @@ -375,7 +375,7 @@ const methods = { atomics.$push = val; } else { if (val.length === 1) { - atomics.$push.$each.push(val); + atomics.$push.$each.push(val[0]); } else if (val.length < 10000) { atomics.$push.$each.push(...val); } else { From 7d26ad2024f4f113971b0668b5ddfa16698c504e Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Fri, 24 Nov 2023 21:24:50 -0500 Subject: [PATCH 23/31] perf: avoid double-calling setters when pushing onto an array Fix #11380 Re: #13456 --- lib/types/array/methods/index.js | 9 ++------- test/types.array.test.js | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/types/array/methods/index.js b/lib/types/array/methods/index.js index f9c1e2b80ba..c798464b6d8 100644 --- a/lib/types/array/methods/index.js +++ b/lib/types/array/methods/index.js @@ -411,8 +411,7 @@ const methods = { addToSet() { _checkManualPopulation(this, arguments); - let values = [].map.call(arguments, this._mapCast, this); - values = this[arraySchemaSymbol].applySetters(values, this[arrayParentSymbol]); + const values = [].map.call(arguments, this._mapCast, this); const added = []; let type = ''; if (values[0] instanceof ArraySubdocument) { @@ -423,7 +422,7 @@ const methods = { type = 'ObjectId'; } - const rawValues = utils.isMongooseArray(values) ? values.__array : this; + const rawValues = utils.isMongooseArray(values) ? values.__array : values; const rawArray = utils.isMongooseArray(this) ? this.__array : this; rawValues.forEach(function(v) { @@ -690,10 +689,7 @@ const methods = { _checkManualPopulation(this, values); - const parent = this[arrayParentSymbol]; values = [].map.call(values, this._mapCast, this); - values = this[arraySchemaSymbol].applySetters(values, parent, undefined, - undefined, { skipDocumentArrayCast: true }); let ret; const atomics = this[arrayAtomicsSymbol]; this._markModified(); @@ -925,7 +921,6 @@ const methods = { values = arguments; } else { values = [].map.call(arguments, this._cast, this); - values = this[arraySchemaSymbol].applySetters(values, this[arrayParentSymbol]); } const arr = utils.isMongooseArray(this) ? this.__array : this; diff --git a/test/types.array.test.js b/test/types.array.test.js index f1a9192dc97..ccc83e02558 100644 --- a/test/types.array.test.js +++ b/test/types.array.test.js @@ -1915,4 +1915,26 @@ describe('types array', function() { } }); }); + + it('calls array setters (gh-11380)', function() { + let called = 0; + const Test = db.model('Test', new Schema({ + intArr: [{ + type: Number, + set: v => { + ++called; + return Math.floor(v); + } + }] + })); + + assert.equal(called, 0); + const doc = new Test({ intArr: [3.14] }); + assert.deepStrictEqual(doc.intArr, [3]); + assert.equal(called, 1); + + doc.intArr.push(2.718); + assert.deepStrictEqual(doc.intArr, [3, 2]); + assert.equal(called, 2); + }); }); From c041378539dfda181fee0590c232b02686064a8d Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 26 Nov 2023 07:53:29 -0500 Subject: [PATCH 24/31] chore: add extra check to prevent copying non-versioned deploy to tmp, handle ENOENT error, consistently check for truthy DOCS_DEPLOY --- scripts/website.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/scripts/website.js b/scripts/website.js index c3063df5807..98e112bf008 100644 --- a/scripts/website.js +++ b/scripts/website.js @@ -111,7 +111,16 @@ function deleteAllHtmlFiles() { } function moveDocsToTemp() { - fs.rmSync('./tmp', { recursive: true }); + if (!versionObj.versionedPath) { + throw new Error('Cannot move unversioned deploy to /tmp'); + } + try { + fs.rmSync('./tmp', { recursive: true }); + } catch (err) { + if (err.code !== 'ENOENT') { + throw err; + } + } const folder = versionObj.versionedPath.replace(/^\//, ''); const directory = fs.readdirSync(folder); for (const file of directory) { @@ -258,7 +267,7 @@ const versionObj = (() => { getLatestVersionOf(5), ] }; - const versionedDeploy = process.env.DOCS_DEPLOY === "true" ? !(base.currentVersion.listed === base.latestVersion.listed) : false; + const versionedDeploy = !!process.env.DOCS_DEPLOY ? !(base.currentVersion.listed === base.latestVersion.listed) : false; const versionedPath = versionedDeploy ? `/docs/${base.currentVersion.path}` : ''; From c0c408c8ac011cd15f483337ae2ec0b3add477fa Mon Sep 17 00:00:00 2001 From: hasezoey Date: Sun, 26 Nov 2023 16:33:10 +0100 Subject: [PATCH 25/31] chore(scripts/website): change "getTests" to be sync as per review comment --- scripts/website.js | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/scripts/website.js b/scripts/website.js index 626a64d8407..9213311b6ca 100644 --- a/scripts/website.js +++ b/scripts/website.js @@ -70,34 +70,26 @@ const ignoredTestFiles = [ * Load all test file contents with acquit * @returns {Object[]} acquit ast array */ -async function getTests() { - const promiseArray = []; - - for (const file of additionalTestFiles) { - const filePath = path.join(testPath, file); - promiseArray.push(fs.promises.readFile(filePath).then(v => ({value: v.toString(), path: filePath}))); - } - +function getTests() { const testDocs = path.resolve(testPath, 'docs'); + const filesToScan = [ + ...additionalTestFiles.map(v => path.join(testPath, v)), + ...fs.readdirSync(testDocs).filter(v => !ignoredTestFiles.includes(v)).map(v => path.join(testDocs, v)) + ]; - for (const file of await fs.promises.readdir(testDocs)) { - if (ignoredTestFiles.includes(file)) { - continue; - } + const retArray = []; - const filePath = path.join(testDocs, file); - promiseArray.push(fs.promises.readFile(filePath).then(v => ({value: v.toString(), path: filePath}))); - } - - return (await Promise.all(promiseArray)).flatMap(v => { + for (const file of filesToScan) { try { - return acquit.parse(v.value); + retArray.push(acquit.parse(fs.readFileSync(file).toString())); } catch (err) { // add a file path to a acquit error, for better debugging - err.filePath = v.path; + err.filePath = file; throw err; } - }) + } + + return retArray.flat(); } /** @@ -376,7 +368,7 @@ async function pugify(filename, options, isReload = false) { let contents = fs.readFileSync(path.resolve(cwd, inputFile)).toString(); if (options.acquit) { - contents = transform(contents, await getTests()); + contents = transform(contents, getTests()); contents = contents.replaceAll(/^```acquit$/gmi, "```javascript"); } From 47bd314c10aedf0d34049bd6a9a385f6cb0b8437 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 27 Nov 2023 14:32:58 -0500 Subject: [PATCH 26/31] Update scripts/website.js Co-authored-by: hasezoey --- scripts/website.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/website.js b/scripts/website.js index 98e112bf008..eaabea581bc 100644 --- a/scripts/website.js +++ b/scripts/website.js @@ -562,7 +562,7 @@ if (isMain) { await deleteAllHtmlFiles(); await pugifyAllFiles(); await copyAllRequiredFiles(); - if (process.env.DOCS_DEPLOY) { + if (!!process.env.DOCS_DEPLOY && !!versionObj.versionedPath) { await moveDocsToTemp(); } From 80495ae3461d4ddd2da170482e0fb03d7f2b74f7 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 27 Nov 2023 14:35:00 -0500 Subject: [PATCH 27/31] Update package.json Co-authored-by: hasezoey --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d3954404199..454a66e175d 100644 --- a/package.json +++ b/package.json @@ -96,7 +96,7 @@ "docs:prepare:publish:stable": "npm run docs:checkout:gh-pages && npm run docs:merge:stable && npm run docs:clean:stable && npm run docs:generate && npm run docs:generate:search", "docs:prepare:publish:5x": "npm run docs:checkout:5x && npm run docs:merge:5x && npm run docs:clean:stable && npm run docs:generate && npm run docs:copy:tmp && npm run docs:checkout:gh-pages && npm run docs:copy:tmp:5x", "docs:prepare:publish:6x": "npm run docs:checkout:6x && npm run docs:merge:6x && npm run docs:clean:stable && env DOCS_DEPLOY=true npm run docs:generate && npm run docs:move:6x:tmp && npm run docs:checkout:gh-pages && npm run docs:copy:tmp:6x", - "docs:prepare:publish:7x": "env DOCS_DEPLOY=true npm run docs:generate && npm run docs:checkout:gh-pages && rimraf ./docs/7.x && ncp ./tmp ./docs/7.x", + "docs:prepare:publish:7x": "env DOCS_DEPLOY=true npm run docs:generate && npm run docs:checkout:gh-pages && rimraf ./docs/7.x && mv ./tmp ./docs/7.x", "docs:check-links": "blc http://127.0.0.1:8089 -ro", "lint": "eslint .", "lint-js": "eslint . --ext .js --ext .cjs", From a26d3048069bddbb404fc234bc93d97f35e15b2b Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 27 Nov 2023 14:36:53 -0500 Subject: [PATCH 28/31] Update package.json Co-authored-by: hasezoey --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 454a66e175d..dd8138eca3d 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "docs:merge:6x": "git merge 6.x", "docs:test": "npm run docs:generate && npm run docs:generate:search", "docs:view": "node ./scripts/static.js", - "docs:prepare:publish:stable": "npm run docs:checkout:gh-pages && npm run docs:merge:stable && npm run docs:clean:stable && npm run docs:generate && npm run docs:generate:search", + "docs:prepare:publish:stable": "npm run docs:checkout:gh-pages && npm run docs:merge:stable && npm run docs:generate && npm run docs:generate:search", "docs:prepare:publish:5x": "npm run docs:checkout:5x && npm run docs:merge:5x && npm run docs:clean:stable && npm run docs:generate && npm run docs:copy:tmp && npm run docs:checkout:gh-pages && npm run docs:copy:tmp:5x", "docs:prepare:publish:6x": "npm run docs:checkout:6x && npm run docs:merge:6x && npm run docs:clean:stable && env DOCS_DEPLOY=true npm run docs:generate && npm run docs:move:6x:tmp && npm run docs:checkout:gh-pages && npm run docs:copy:tmp:6x", "docs:prepare:publish:7x": "env DOCS_DEPLOY=true npm run docs:generate && npm run docs:checkout:gh-pages && rimraf ./docs/7.x && mv ./tmp ./docs/7.x", From 140a118dda67cbf9d9ce62ba0eac1ad0efd13154 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 27 Nov 2023 14:53:43 -0500 Subject: [PATCH 29/31] chore: correctly clean relative to version path --- scripts/website.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/scripts/website.js b/scripts/website.js index eaabea581bc..50cfaf4d767 100644 --- a/scripts/website.js +++ b/scripts/website.js @@ -79,22 +79,24 @@ const tests = [ function deleteAllHtmlFiles() { try { - fs.unlinkSync('./index.html'); + console.log('Delete', path.join(versionObj.versionedPath, 'index.html')); + fs.unlinkSync(path.join(versionObj.versionedPath, 'index.html')); } catch (err) { if (err.code !== 'ENOENT') { throw err; } } const foldersToClean = [ - './docs', - './docs/tutorials', - './docs/typescript', - './docs/api', - './docs/source/_docs', + path.join('.', versionObj.versionedPath, 'docs'), + path.join('.', versionObj.versionedPath, 'docs', 'tutorials'), + path.join('.', versionObj.versionedPath, 'docs', 'typescript'), + path.join('.', versionObj.versionedPath, 'docs', 'api'), + path.join('.', versionObj.versionedPath, 'docs', 'source', '_docs'), './tmp' ]; for (const folder of foldersToClean) { let files = []; + try { files = fs.readdirSync(folder); } catch (err) { @@ -104,7 +106,8 @@ function deleteAllHtmlFiles() { } for (const file of files) { if (file.endsWith('.html')) { - fs.unlinkSync(`${folder}/${file}`); + console.log('Delete', path.join(folder, file)); + fs.unlinkSync(path.join(folder, file)); } } } From 280bd4a2c59c9d149a9bf0f0b13709d89450b4c4 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sat, 25 Nov 2023 15:52:23 -0500 Subject: [PATCH 30/31] types: make property names show up in intellisense for UpdateQuery Fix #14090 --- types/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/index.d.ts b/types/index.d.ts index d8d9e0c629a..05578ccc3ca 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -634,7 +634,7 @@ declare module 'mongoose' { * { age: 30 } * ``` */ - export type UpdateQuery = _UpdateQuery & AnyObject; + export type UpdateQuery = AnyKeys & _UpdateQuery & AnyObject; /** * A more strict form of UpdateQuery that enforces updating only From 79aab0c0d357fc2bafb428c4ab18d2d246473892 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 27 Nov 2023 17:04:04 -0500 Subject: [PATCH 31/31] chore: release 7.6.6 --- CHANGELOG.md | 12 ++++++++++++ package.json | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 282262f803b..0da10349286 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +7.6.6 / 2023-11-27 +================== + * perf: avoid double-running setter logic when calling `push()` #14120 #11380 + * fix(populate): set populated docs in correct order when populating virtual underneath doc array with justOne #14105 #14018 + * fix: bump mongodb driver -> 5.9.1 #14084 #13829 [lorand-horvath](https://github.com/lorand-horvath) + * types: allow defining document array using [{ prop: String }] syntax #14095 #13424 + * types: correct types for when includeResultMetadata: true is set #14078 #13987 [prathamVaidya](https://github.com/prathamVaidya) + * types(query): base filters and projections off of RawDocType instead of DocType so autocomplete doesn't show populate #14118 #14077 + * types: make property names show up in intellisense for UpdateQuery #14123 #14090 + * types(model): support calling Model.validate() with pathsToSkip option #14088 #14003 + * docs: remove "DEPRECATED" warning mistakenly added to read() tags param #13980 + 7.6.5 / 2023-11-14 ================== * fix: handle update validators and single nested doc with numeric paths #14066 #13977 diff --git a/package.json b/package.json index dd8138eca3d..05507004181 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mongoose", "description": "Mongoose MongoDB ODM", - "version": "7.6.5", + "version": "7.6.6", "author": "Guillermo Rauch ", "keywords": [ "mongodb",