From 2a6e76f4d2d8cd96918265b790a105b7017f33c0 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 6 Feb 2024 12:45:07 -0500 Subject: [PATCH 1/2] fix: include virtuals in document array `toString()` output if `toObject.virtuals` set Fix #14315 --- lib/document.js | 3 ++- lib/schema.js | 12 ++++++++++++ lib/types/documentArray/methods/index.js | 11 +++++++++++ lib/types/subdocument.js | 6 +----- test/document.test.js | 19 +++++++++++++++++++ types/models.d.ts | 14 ++++++++++---- 6 files changed, 55 insertions(+), 10 deletions(-) diff --git a/lib/document.js b/lib/document.js index 731be3301c7..94ee25ded8c 100644 --- a/lib/document.js +++ b/lib/document.js @@ -4287,7 +4287,8 @@ Document.prototype.inspect = function(options) { opts = options; opts.minimize = false; } - const ret = this.toObject(opts); + + const ret = arguments.length > 0 ? this.toObject(opts) : this.toObject(); if (ret == null) { // If `toObject()` returns null, `this` is still an object, so if `inspect()` diff --git a/lib/schema.js b/lib/schema.js index 5941b8ebf5e..94435b86e09 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -2144,6 +2144,18 @@ Schema.prototype.set = function(key, value, tags) { if (key === 'strictQuery') { _propagateOptionsToImplicitlyCreatedSchemas(this, { strictQuery: value }); } + if (key === 'toObject') { + value = { ...value }; + // Avoid propagating transform to implicitly created schemas re: gh-3279 + delete value.transform; + _propagateOptionsToImplicitlyCreatedSchemas(this, { toObject: value }); + } + if (key === 'toJSON') { + value = { ...value }; + // Avoid propagating transform to implicitly created schemas re: gh-3279 + delete value.transform; + _propagateOptionsToImplicitlyCreatedSchemas(this, { toJSON: value }); + } return this; }; diff --git a/lib/types/documentArray/methods/index.js b/lib/types/documentArray/methods/index.js index 109f3450a0f..00b47c434ba 100644 --- a/lib/types/documentArray/methods/index.js +++ b/lib/types/documentArray/methods/index.js @@ -13,6 +13,8 @@ const arrayPathSymbol = require('../../../helpers/symbols').arrayPathSymbol; const arraySchemaSymbol = require('../../../helpers/symbols').arraySchemaSymbol; const documentArrayParent = require('../../../helpers/symbols').documentArrayParent; +const _baseToString = Array.prototype.toString; + const methods = { /*! * ignore @@ -22,6 +24,15 @@ const methods = { return this.toObject(internalToObjectOptions); }, + toString() { + return _baseToString.call(this.__array.map(subdoc => { + if (subdoc != null && subdoc.$__ != null) { + return subdoc.toString(); + } + return subdoc; + })); + }, + /*! * ignore */ diff --git a/lib/types/subdocument.js b/lib/types/subdocument.js index 8f9dd94ce60..8ea50d03ecc 100644 --- a/lib/types/subdocument.js +++ b/lib/types/subdocument.js @@ -400,11 +400,7 @@ Subdocument.prototype.populate = function() { */ Subdocument.prototype.inspect = function() { - return this.toObject({ - transform: false, - virtuals: false, - flattenDecimals: false - }); + return this.toObject(); }; if (util.inspect.custom) { diff --git a/test/document.test.js b/test/document.test.js index bd0de3fbb62..8f67cf1d74e 100644 --- a/test/document.test.js +++ b/test/document.test.js @@ -12953,6 +12953,25 @@ describe('document', function() { }; assert.equal(person.address.zip, 54321); }); + + it('includes virtuals in doc array toString() output if virtuals enabled on toObject (gh-14315)', function() { + const schema = new Schema({ + docArr: [{ childId: mongoose.ObjectId }] + }); + schema.virtual('docArr.child', { ref: 'Child', localField: 'docArr.childId', foreignField: '_id' }); + schema.set('toObject', { virtuals: true }); + schema.set('toJSON', { virtuals: true }); + const Test = db.model('Test', schema); + const Child = db.model('Child', new Schema({ + name: String + })); + + const child = new Child({ name: 'test child' }); + const doc = new Test({ docArr: [{ childId: child._id }] }); + doc.docArr[0].child = child; + assert.ok(doc.docArr.toString().includes('child'), doc.docArr.toString()); + assert.ok(doc.docArr.toString().includes('test child'), doc.docArr.toString()); + }); }); describe('Check if instance function that is supplied in schema option is availabe', function() { diff --git a/types/models.d.ts b/types/models.d.ts index caa4ea62696..fa3a23cf464 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -675,9 +675,9 @@ declare module 'mongoose' { findOneAndUpdate( filter: FilterQuery, update: UpdateQuery, - options: QueryOptions & { lean: true } + options: QueryOptions & { includeResultMetadata: true, lean: true } ): QueryWithHelpers< - GetLeanResultType | null, + ModifyResult, ResultDoc, TQueryHelpers, TRawDocType, @@ -686,8 +686,14 @@ declare module 'mongoose' { findOneAndUpdate( filter: FilterQuery, update: UpdateQuery, - options: QueryOptions & { includeResultMetadata: true } - ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndUpdate'>; + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + GetLeanResultType | null, + ResultDoc, + TQueryHelpers, + TRawDocType, + 'findOneAndUpdate' + >; findOneAndUpdate( filter: FilterQuery, update: UpdateQuery, From 0643eddc362183f2be19cb660529b82cf1f2678e Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 6 Feb 2024 12:48:55 -0500 Subject: [PATCH 2/2] chore: remove mistakenly committed changes --- types/models.d.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/types/models.d.ts b/types/models.d.ts index fa3a23cf464..caa4ea62696 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -675,9 +675,9 @@ declare module 'mongoose' { findOneAndUpdate( filter: FilterQuery, update: UpdateQuery, - options: QueryOptions & { includeResultMetadata: true, lean: true } + options: QueryOptions & { lean: true } ): QueryWithHelpers< - ModifyResult, + GetLeanResultType | null, ResultDoc, TQueryHelpers, TRawDocType, @@ -686,14 +686,8 @@ declare module 'mongoose' { findOneAndUpdate( filter: FilterQuery, update: UpdateQuery, - options: QueryOptions & { lean: true } - ): QueryWithHelpers< - GetLeanResultType | null, - ResultDoc, - TQueryHelpers, - TRawDocType, - 'findOneAndUpdate' - >; + options: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndUpdate'>; findOneAndUpdate( filter: FilterQuery, update: UpdateQuery,