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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -842,7 +842,7 @@ function init(self, obj, doc, opts, prefix) {
*/

Document.prototype.updateOne = function updateOne(doc, options, callback) {
const query = this.constructor.updateOne({ _id: this._id }, doc, options);
const query = this.constructor.updateOne({ _id: this._doc._id }, doc, options);
const self = this;
query.pre(function queryPreUpdateOne(cb) {
self.constructor._middleware.execPre('updateOne', self, [self], cb);
Expand Down Expand Up @@ -883,7 +883,7 @@ Document.prototype.updateOne = function updateOne(doc, options, callback) {

Document.prototype.replaceOne = function replaceOne() {
const args = [...arguments];
args.unshift({ _id: this._id });
args.unshift({ _id: this._doc._id });
return this.constructor.replaceOne.apply(this.constructor, args);
};

Expand Down Expand Up @@ -3050,7 +3050,7 @@ Document.prototype.$__validate = function(pathsToValidate, options, callback) {
} else if (val != null && val.$__ != null && val.$__.wasPopulated) {
// Array paths, like `somearray.1`, do not show up as populated with `$populated()`,
// so in that case pull out the document's id
val = val._id;
val = val._doc._id;
}
const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ?
_this.$__.pathsToScopes[path] :
Expand Down
2 changes: 1 addition & 1 deletion lib/error/parallelSave.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ParallelSaveError extends MongooseError {
*/
constructor(doc) {
const msg = 'Can\'t save() the same doc multiple times in parallel. Document: ';
super(msg + doc._id);
super(msg + doc._doc._id);
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/error/parallelValidate.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class ParallelValidateError extends MongooseError {
*/
constructor(doc) {
const msg = 'Can\'t validate() the same doc multiple times in parallel. Document: ';
super(msg + doc._id);
super(msg + doc._doc._id);
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/error/version.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class VersionError extends MongooseError {
*/
constructor(doc, currentVersion, modifiedPaths) {
const modifiedPathsStr = modifiedPaths.join(', ');
super('No matching document found for id "' + doc._id +
super('No matching document found for id "' + doc._doc._id +
'" version ' + currentVersion + ' modifiedPaths "' + modifiedPathsStr + '"');
this.version = currentVersion;
this.modifiedPaths = modifiedPaths;
Expand Down
3 changes: 3 additions & 0 deletions lib/helpers/model/discriminator.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu
}
}

// Shallow clone `obj` so we can add additional properties without modifying original
// schema. `Schema.prototype.clone()` copies `obj` by reference, no cloning.
schema.obj = { ...schema.obj };
mergeDiscriminatorSchema(schema, baseSchema);

// Clean up conflicting paths _after_ merging re: gh-6076
Expand Down
4 changes: 2 additions & 2 deletions lib/helpers/populate/getModelsMapForPopulate.js
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ function _getLocalFieldValues(doc, localField, model, options, virtual, schema)

function convertTo_id(val, schema) {
if (val != null && val.$__ != null) {
return val._id;
return val._doc._id;
}
if (val != null && val._id != null && (schema == null || !schema.$isSchemaMap)) {
return val._id;
Expand All @@ -636,7 +636,7 @@ function convertTo_id(val, schema) {
const rawVal = val.__array != null ? val.__array : val;
for (let i = 0; i < rawVal.length; ++i) {
if (rawVal[i] != null && rawVal[i].$__ != null) {
rawVal[i] = rawVal[i]._id;
rawVal[i] = rawVal[i]._doc._id;
}
}
if (utils.isMongooseArray(val) && val.$schema()) {
Expand Down
4 changes: 2 additions & 2 deletions lib/helpers/populate/markArraySubdocsPopulated.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ const utils = require('../../utils');
*/

module.exports = function markArraySubdocsPopulated(doc, populated) {
if (doc._id == null || populated == null || populated.length === 0) {
if (doc._doc._id == null || populated == null || populated.length === 0) {
return;
}

const id = String(doc._id);
const id = String(doc._doc._id);
for (const item of populated) {
if (item.isVirtual) {
continue;
Expand Down
6 changes: 3 additions & 3 deletions lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -2345,7 +2345,7 @@ Model.findByIdAndUpdate = function(id, update, options) {

// if a model is passed in instead of an id
if (id instanceof Document) {
id = id._id;
id = id._doc._id;
}

return this.findOneAndUpdate.call(this, { _id: id }, update, options);
Expand Down Expand Up @@ -3408,7 +3408,7 @@ Model.bulkSave = async function bulkSave(documents, options) {
documents.map(async(document) => {
const documentError = bulkWriteError && bulkWriteError.writeErrors.find(writeError => {
const writeErrorDocumentId = writeError.err.op._id || writeError.err.op.q._id;
return writeErrorDocumentId.toString() === document._id.toString();
return writeErrorDocumentId.toString() === document._doc._id.toString();
});

if (documentError == null) {
Expand Down Expand Up @@ -4436,7 +4436,7 @@ function _assign(model, vals, mod, assignmentOpts) {

for (let __val of _val) {
if (__val instanceof Document) {
__val = __val._id;
__val = __val._doc._id;
}
key = String(__val);
if (rawDocs[key]) {
Expand Down
6 changes: 3 additions & 3 deletions lib/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -3380,9 +3380,9 @@ Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() {
if (!this._update || Object.keys(this._update).length === 0) {
if (options.upsert) {
// still need to do the upsert to empty doc
const doc = clone(this._update);
delete doc._id;
this._update = { $set: doc };
const $set = clone(this._update);
delete $set._id;
this._update = { $set };
} else {
this._executionStack = null;
const res = await this._findOne();
Expand Down
17 changes: 17 additions & 0 deletions test/model.discriminator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,23 @@ describe('model', function() {
Person.discriminator('Parent2', parentSchema.clone());
});

it('clone() does not modify original schema `obj` (gh-14821)', function() {
const personSchema = new Schema({
name: String
}, { discriminatorKey: 'kind' });

const parentSchema = new Schema({
child: String
});

const Person = db.model('Person', personSchema);
const Parent = Person.discriminator('Parent', parentSchema.clone());

assert.ok(!Person.schema.obj.child);
assert.ok(!personSchema.obj.child);
assert.ok(Parent.schema.obj.child);
});

it('clone() allows reusing with different models (gh-5721)', async function() {
const schema = new mongoose.Schema({
name: String
Expand Down
35 changes: 35 additions & 0 deletions test/model.populate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11049,6 +11049,41 @@ describe('model: populate:', function() {
assert.equal(pet2.owner.name, 'Alice');
});

it('avoids populating manually populated doc as getter value (gh-14827)', async function() {
const ownerSchema = new mongoose.Schema({
_id: {
type: 'ObjectId',
get(value) {
return value == null ? value : value.toString();
}
},
name: 'String'
});
const petSchema = new mongoose.Schema({
name: 'String',
owner: { type: 'ObjectId', ref: 'Owner' }
});

const Owner = db.model('Owner', ownerSchema);
const Pet = db.model('Pet', petSchema);

const _id = new mongoose.Types.ObjectId();
const owner = new Owner({ _id, name: 'Alice' });
const pet = new Pet({ name: 'Kitty', owner: owner });

await owner.save();

assert.equal(typeof pet._doc.owner.$__.wasPopulated.value, 'object');
await pet.populate('owner');
assert.equal(typeof pet._doc.owner.$__.wasPopulated.value, 'object');

await pet.save();


const fromDb = await Pet.findOne({ owner: _id }).lean().orFail();
assert.ok(fromDb.owner instanceof mongoose.Types.ObjectId);
});

it('makes sure that populate works correctly with duplicate foreignField with lean(); (gh-14794)', async function() {
const authorSchema = new mongoose.Schema({
group: String,
Expand Down