From 491be54c0282f8f51d8df026d99f0c2ed0189e44 Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Mon, 12 Feb 2024 11:23:01 -0500 Subject: [PATCH 1/8] Update model.hydrate.test.js --- test/model.hydrate.test.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/model.hydrate.test.js b/test/model.hydrate.test.js index 6cdc333ce8d..cabdf7d225f 100644 --- a/test/model.hydrate.test.js +++ b/test/model.hydrate.test.js @@ -99,5 +99,23 @@ describe('model', function() { assert.equal(hydrated.test, 'test'); assert.deepEqual(hydrated.schema.tree, C.schema.tree); }); + it('should deeply hydrate the document with the `hydratedPopulatedDocs` option (gh-4727)', async function() { + const userSchema = new Schema({ + name: String + }); + const companySchema = new Schema({ + name: String, + users: [{ ref: 'User', type: Schema.Types.ObjectId }] + }); + + const User = db.model('UserTestHydrate', userSchema); + const Company = db.model('CompanyTestHyrdrate', companySchema); + + const users = [{ _id: new Schema.ObjectId(), name: 'Val'}]; + const company = { _id: new Schema.ObjectId(), name: 'Booster', users: [users[0]] }; + + const C = Company.hydrate(company, null, { hydratedPopulatedDocs: true }); + assert.equal(C.users[0].name, 'Val'); + }); }); }); From eb449c489bebfebd1760d800c4cf92392a4ffe1c Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Mon, 12 Feb 2024 11:26:20 -0500 Subject: [PATCH 2/8] fix id generation --- test/model.hydrate.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/model.hydrate.test.js b/test/model.hydrate.test.js index cabdf7d225f..51358f08aa9 100644 --- a/test/model.hydrate.test.js +++ b/test/model.hydrate.test.js @@ -111,8 +111,8 @@ describe('model', function() { const User = db.model('UserTestHydrate', userSchema); const Company = db.model('CompanyTestHyrdrate', companySchema); - const users = [{ _id: new Schema.ObjectId(), name: 'Val'}]; - const company = { _id: new Schema.ObjectId(), name: 'Booster', users: [users[0]] }; + const users = [{ _id: new mongoose.Types.ObjectId(), name: 'Val'}]; + const company = { _id: new mongoose.Types.ObjectId(), name: 'Booster', users: [users[0]] }; const C = Company.hydrate(company, null, { hydratedPopulatedDocs: true }); assert.equal(C.users[0].name, 'Val'); From e80cff1d33e10860e1502e9c1577342929f9bc42 Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Mon, 12 Feb 2024 16:52:54 -0500 Subject: [PATCH 3/8] progress --- lib/document.js | 3 --- lib/model.js | 2 +- test/model.hydrate.test.js | 1 + 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/document.js b/lib/document.js index 731be3301c7..870a20261d1 100644 --- a/lib/document.js +++ b/lib/document.js @@ -694,7 +694,6 @@ Document.prototype.$__init = function(doc, opts) { init(this, doc, this._doc, opts); markArraySubdocsPopulated(this, opts.populated); - this.$emit('init', this); this.constructor.emit('init', this); @@ -703,7 +702,6 @@ Document.prototype.$__init = function(doc, opts) { null; applyDefaults(this, this.$__.selected, this.$__.exclude, hasIncludedChildren, false, this.$__.skipDefaults); - return this; }; @@ -746,7 +744,6 @@ function init(self, obj, doc, opts, prefix) { } path = prefix + i; schemaType = docSchema.path(path); - // Should still work if not a model-level discriminator, but should not be // necessary. This is *only* to catch the case where we queried using the // base model and the discriminated model has a projection diff --git a/lib/model.js b/lib/model.js index 93e34c2eb33..a596c864833 100644 --- a/lib/model.js +++ b/lib/model.js @@ -3830,6 +3830,7 @@ Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, op * @param {Object|String|String[]} [projection] optional projection containing which fields should be selected for this document * @param {Object} [options] optional options * @param {Boolean} [options.setters=false] if true, apply schema setters when hydrating + * @param {Boolean} [options.hydratedPopulatedDocs=false] if true, populates the docs if passing pre-populated data * @return {Document} document instance * @api public */ @@ -3843,7 +3844,6 @@ Model.hydrate = function(obj, projection, options) { } obj = applyProjection(obj, projection); } - const document = require('./queryHelpers').createModel(this, obj, projection); document.$init(obj, options); return document; diff --git a/test/model.hydrate.test.js b/test/model.hydrate.test.js index 51358f08aa9..9e4e9ce3d40 100644 --- a/test/model.hydrate.test.js +++ b/test/model.hydrate.test.js @@ -115,6 +115,7 @@ describe('model', function() { const company = { _id: new mongoose.Types.ObjectId(), name: 'Booster', users: [users[0]] }; const C = Company.hydrate(company, null, { hydratedPopulatedDocs: true }); + console.log('what is C', C); assert.equal(C.users[0].name, 'Val'); }); }); From e6769c9893259a714f6fe92a6452ef428b191b4f Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Tue, 13 Feb 2024 15:44:43 -0500 Subject: [PATCH 4/8] `hydratedPopulatedDocs` option --- lib/document.js | 5 ++--- test/model.hydrate.test.js | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/document.js b/lib/document.js index 870a20261d1..b9f39a82c12 100644 --- a/lib/document.js +++ b/lib/document.js @@ -767,15 +767,14 @@ function init(self, obj, doc, opts, prefix) { } } else { // Retain order when overwriting defaults - if (doc.hasOwnProperty(i) && obj[i] !== void 0) { + if (doc.hasOwnProperty(i) && obj[i] !== void 0 && !opts.hydratedPopulatedDocs) { delete doc[i]; } if (obj[i] === null) { doc[i] = schemaType._castNullish(null); } else if (obj[i] !== undefined) { const wasPopulated = obj[i].$__ == null ? null : obj[i].$__.wasPopulated; - - if (schemaType && !wasPopulated) { + if ((schemaType && !wasPopulated) && !opts.hydratedPopulatedDocs) { try { if (opts && opts.setters) { // Call applySetters with `init = false` because otherwise setters are a noop diff --git a/test/model.hydrate.test.js b/test/model.hydrate.test.js index 9e4e9ce3d40..51358f08aa9 100644 --- a/test/model.hydrate.test.js +++ b/test/model.hydrate.test.js @@ -115,7 +115,6 @@ describe('model', function() { const company = { _id: new mongoose.Types.ObjectId(), name: 'Booster', users: [users[0]] }; const C = Company.hydrate(company, null, { hydratedPopulatedDocs: true }); - console.log('what is C', C); assert.equal(C.users[0].name, 'Val'); }); }); From 7c696a3c92366b67de6c49704253902ea6281283 Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Tue, 13 Feb 2024 15:46:13 -0500 Subject: [PATCH 5/8] fix: lint --- test/model.hydrate.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/model.hydrate.test.js b/test/model.hydrate.test.js index 51358f08aa9..bc6632f5b15 100644 --- a/test/model.hydrate.test.js +++ b/test/model.hydrate.test.js @@ -108,10 +108,10 @@ describe('model', function() { users: [{ ref: 'User', type: Schema.Types.ObjectId }] }); - const User = db.model('UserTestHydrate', userSchema); + db.model('UserTestHydrate', userSchema); const Company = db.model('CompanyTestHyrdrate', companySchema); - const users = [{ _id: new mongoose.Types.ObjectId(), name: 'Val'}]; + const users = [{ _id: new mongoose.Types.ObjectId(), name: 'Val' }]; const company = { _id: new mongoose.Types.ObjectId(), name: 'Booster', users: [users[0]] }; const C = Company.hydrate(company, null, { hydratedPopulatedDocs: true }); From 648239d7853bffe134a213e9bc5140acb5c58b45 Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Wed, 14 Feb 2024 12:10:03 -0500 Subject: [PATCH 6/8] add typescript types, begin typescript test --- test/types/models.test.ts | 22 ++++++++++++++++++++++ types/models.d.ts | 7 ++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/test/types/models.test.ts b/test/types/models.test.ts index 9bf423f136f..dd92a46f602 100644 --- a/test/types/models.test.ts +++ b/test/types/models.test.ts @@ -880,3 +880,25 @@ async function gh13999() { } } } + +function gh4727() { + const userSchema = new mongoose.Schema({ + name: String + }); + const companySchema = new mongoose.Schema({ + name: String, + users: [{ ref: 'User', type: mongoose.Schema.Types.ObjectId }] + }); + + mongoose.model('UserTestHydrate', userSchema); + const Company = mongoose.model('CompanyTestHyrdrate', companySchema); + + const users = [{ _id: new mongoose.Types.ObjectId(), name: 'Val' }]; + const company = { _id: new mongoose.Types.ObjectId(), name: 'Booster', users: [users[0]] }; + + expectType | null>( + Company.hydrate(company, null, { hydratedPopulatedDocs: true }) + ); + + +} diff --git a/types/models.d.ts b/types/models.d.ts index 67731cc3eb0..a77aa25525e 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -37,6 +37,11 @@ declare module 'mongoose' { skipValidation?: boolean; } + interface HydrateOptions { + setters?: boolean; + hydratedPopulatedDocs?: boolean; + } + interface InsertManyOptions extends PopulateOption, SessionOption { @@ -371,7 +376,7 @@ declare module 'mongoose' { * Shortcut for creating a new Document from existing raw data, pre-saved in the DB. * The document returned has no paths marked as modified initially. */ - hydrate(obj: any, projection?: AnyObject, options?: { setters?: boolean }): THydratedDocumentType; + hydrate(obj: any, projection?: AnyObject, options?: HydrateOptions): THydratedDocumentType; /** * This function is responsible for building [indexes](https://www.mongodb.com/docs/manual/indexes/), From 46756713ffde1f0bbfe09f9de50c66019c5c9029 Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Wed, 14 Feb 2024 14:33:43 -0500 Subject: [PATCH 7/8] Update models.test.ts --- test/types/models.test.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/types/models.test.ts b/test/types/models.test.ts index dd92a46f602..6761f031456 100644 --- a/test/types/models.test.ts +++ b/test/types/models.test.ts @@ -896,9 +896,7 @@ function gh4727() { const users = [{ _id: new mongoose.Types.ObjectId(), name: 'Val' }]; const company = { _id: new mongoose.Types.ObjectId(), name: 'Booster', users: [users[0]] }; - expectType | null>( - Company.hydrate(company, null, { hydratedPopulatedDocs: true }) - ); + return Company.hydrate(company, {}, { hydratedPopulatedDocs: true }) } From 6dafa54f57341f44af13ff0451decf435cd1845d Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Wed, 14 Feb 2024 14:34:21 -0500 Subject: [PATCH 8/8] fix: lint --- test/types/models.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/types/models.test.ts b/test/types/models.test.ts index 6761f031456..61298be836d 100644 --- a/test/types/models.test.ts +++ b/test/types/models.test.ts @@ -896,7 +896,7 @@ function gh4727() { const users = [{ _id: new mongoose.Types.ObjectId(), name: 'Val' }]; const company = { _id: new mongoose.Types.ObjectId(), name: 'Booster', users: [users[0]] }; - return Company.hydrate(company, {}, { hydratedPopulatedDocs: true }) + return Company.hydrate(company, {}, { hydratedPopulatedDocs: true }); }