Skip to content

Commit 759f0d4

Browse files
authoredMar 26, 2024··
Merge pull request #14458 from Automattic/vkarpov15/gh-14418
Avoid depopulating populated subdocs underneath document arrays when copying to another document
2 parents 9ff5e42 + bc48379 commit 759f0d4

File tree

5 files changed

+75
-20
lines changed

5 files changed

+75
-20
lines changed
 

‎lib/document.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -1087,7 +1087,11 @@ Document.prototype.$set = function $set(path, val, type, options) {
10871087
if (path.$__isNested) {
10881088
path = path.toObject();
10891089
} else {
1090-
path = path._doc;
1090+
// This ternary is to support gh-7898 (copying virtuals if same schema)
1091+
// while not breaking gh-10819, which for some reason breaks if we use toObject()
1092+
path = path.$__schema === this.$__schema
1093+
? applyVirtuals(path, { ...path._doc })
1094+
: path._doc;
10911095
}
10921096
}
10931097
if (path == null) {
@@ -4012,11 +4016,11 @@ function applyVirtuals(self, json, options, toObjectOptions) {
40124016
? toObjectOptions.aliases
40134017
: true;
40144018

4019+
options = options || {};
40154020
let virtualsToApply = null;
40164021
if (Array.isArray(options.virtuals)) {
40174022
virtualsToApply = new Set(options.virtuals);
4018-
}
4019-
else if (options.virtuals && options.virtuals.pathsToSkip) {
4023+
} else if (options.virtuals && options.virtuals.pathsToSkip) {
40204024
virtualsToApply = new Set(paths);
40214025
for (let i = 0; i < options.virtuals.pathsToSkip.length; i++) {
40224026
if (virtualsToApply.has(options.virtuals.pathsToSkip[i])) {
@@ -4029,7 +4033,6 @@ function applyVirtuals(self, json, options, toObjectOptions) {
40294033
return json;
40304034
}
40314035

4032-
options = options || {};
40334036
for (i = 0; i < numPaths; ++i) {
40344037
path = paths[i];
40354038

‎lib/model.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -5124,7 +5124,7 @@ function _assign(model, vals, mod, assignmentOpts) {
51245124
}
51255125
// flag each as result of population
51265126
if (!lean) {
5127-
val.$__.wasPopulated = val.$__.wasPopulated || true;
5127+
val.$__.wasPopulated = val.$__.wasPopulated || { value: _val };
51285128
}
51295129
}
51305130
}

‎lib/schema/documentarray.js

+3-13
Original file line numberDiff line numberDiff line change
@@ -443,19 +443,9 @@ DocumentArrayPath.prototype.cast = function(value, doc, init, prev, options) {
443443

444444
const Constructor = getConstructor(this.casterConstructor, rawArray[i]);
445445

446-
// Check if the document has a different schema (re gh-3701)
447-
if (rawArray[i].$__ != null && !(rawArray[i] instanceof Constructor)) {
448-
const spreadDoc = handleSpreadDoc(rawArray[i], true);
449-
if (rawArray[i] !== spreadDoc) {
450-
rawArray[i] = spreadDoc;
451-
} else {
452-
rawArray[i] = rawArray[i].toObject({
453-
transform: false,
454-
// Special case: if different model, but same schema, apply virtuals
455-
// re: gh-7898
456-
virtuals: rawArray[i].schema === Constructor.schema
457-
});
458-
}
446+
const spreadDoc = handleSpreadDoc(rawArray[i], true);
447+
if (rawArray[i] !== spreadDoc) {
448+
rawArray[i] = spreadDoc;
459449
}
460450

461451
if (rawArray[i] instanceof Subdocument) {

‎lib/schematype.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1505,7 +1505,7 @@ SchemaType.prototype._castRef = function _castRef(value, doc, init) {
15051505
}
15061506

15071507
if (value.$__ != null) {
1508-
value.$__.wasPopulated = value.$__.wasPopulated || true;
1508+
value.$__.wasPopulated = value.$__.wasPopulated || { value: value._id };
15091509
return value;
15101510
}
15111511

@@ -1531,7 +1531,7 @@ SchemaType.prototype._castRef = function _castRef(value, doc, init) {
15311531
!doc.$__.populated[path].options.options ||
15321532
!doc.$__.populated[path].options.options.lean) {
15331533
ret = new pop.options[populateModelSymbol](value);
1534-
ret.$__.wasPopulated = true;
1534+
ret.$__.wasPopulated = { value: ret._id };
15351535
}
15361536

15371537
return ret;

‎test/document.test.js

+62
Original file line numberDiff line numberDiff line change
@@ -12476,6 +12476,68 @@ describe('document', function() {
1247612476
doc.set({ nested: void 0 });
1247712477
assert.strictEqual(doc.toObject().nested, void 0);
1247812478
});
12479+
12480+
it('avoids depopulating populated subdocs underneath document arrays when copying to another document (gh-14418)', async function() {
12481+
const cartSchema = new mongoose.Schema({
12482+
products: [
12483+
{
12484+
product: {
12485+
type: mongoose.Schema.Types.ObjectId,
12486+
ref: 'Product'
12487+
},
12488+
quantity: Number
12489+
}
12490+
],
12491+
singleProduct: {
12492+
type: mongoose.Schema.Types.ObjectId,
12493+
ref: 'Product'
12494+
}
12495+
});
12496+
const purchaseSchema = new mongoose.Schema({
12497+
products: [
12498+
{
12499+
product: {
12500+
type: mongoose.Schema.Types.ObjectId,
12501+
ref: 'Product'
12502+
},
12503+
quantity: Number
12504+
}
12505+
],
12506+
singleProduct: {
12507+
type: mongoose.Schema.Types.ObjectId,
12508+
ref: 'Product'
12509+
}
12510+
});
12511+
const productSchema = new mongoose.Schema({
12512+
name: String
12513+
});
12514+
12515+
const Cart = db.model('Cart', cartSchema);
12516+
const Purchase = db.model('Purchase', purchaseSchema);
12517+
const Product = db.model('Product', productSchema);
12518+
12519+
const dbProduct = await Product.create({ name: 'Bug' });
12520+
12521+
const dbCart = await Cart.create({
12522+
products: [
12523+
{
12524+
product: dbProduct,
12525+
quantity: 2
12526+
}
12527+
],
12528+
singleProduct: dbProduct
12529+
});
12530+
12531+
const foundCart = await Cart.findById(dbCart._id).
12532+
populate('products.product singleProduct');
12533+
12534+
const purchaseFromDbCart = new Purchase({
12535+
products: foundCart.products,
12536+
singleProduct: foundCart.singleProduct
12537+
});
12538+
assert.equal(purchaseFromDbCart.products[0].product.name, 'Bug');
12539+
assert.equal(purchaseFromDbCart.singleProduct.name, 'Bug');
12540+
});
1247912541
});
1248012542

1248112543
describe('Check if instance function that is supplied in schema option is availabe', function() {

0 commit comments

Comments
 (0)
Please sign in to comment.