Skip to content

Commit

Permalink
fix: allow calling parent() on parent, allow accessing parent's vir…
Browse files Browse the repository at this point in the history
…tual from child subdoc

Fix #65
Fix #64
  • Loading branch information
vkarpov15 committed Jun 7, 2024
1 parent 1311304 commit 8fcdc13
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 12 deletions.
10 changes: 6 additions & 4 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
declare module "mongoose-lean-virtuals" {
import mongoose = require('mongoose');
export default function mongooseLeanVirtuals(schema: mongoose.Schema<any, any, any, any>, opts?: any): void;
export function mongooseLeanVirtuals(schema: mongoose.Schema<any, any, any, any>, opts?: any): void;
}
import mongoose = require('mongoose');
export default function mongooseLeanVirtuals(schema: mongoose.Schema<any, any, any, any>, opts?: any): void;
export function mongooseLeanVirtuals(schema: mongoose.Schema<any, any, any, any>, opts?: any): void;

export function parent(value: any): any;
}
30 changes: 25 additions & 5 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const flat = require('array.prototype.flat');
const mpath = require('mpath');

const documentParentsMap = new WeakMap();
const attachVirtualsFnMap = new WeakMap();

module.exports = function mongooseLeanVirtuals(schema, options) {
const fn = attachVirtualsMiddleware(schema, options);
Expand Down Expand Up @@ -32,7 +33,14 @@ module.exports.parent = function(obj) {
if (obj == null) {
return void 0;
}
return documentParentsMap.get(obj);
const res = documentParentsMap.get(obj);
// Since we do we apply virtuals to children before parents,
// we may need to call `applyVirtuals()` on the parent if we're
// accessing the parent from the child.
if (attachVirtualsFnMap.get(res)) {
attachVirtualsFnMap.get(res)();
}
return res;
};

function attachVirtualsMiddleware(schema, options = {}) {
Expand Down Expand Up @@ -75,9 +83,19 @@ function attachVirtuals(schema, res, virtuals, parent) {
}
}

let called = false;
const applyVirtualsToResultOnce = () => {
if (called) {
return;
}
called = true;
applyVirtualsToResult(schema, res, toApply);
};

addToParentMap(res, parent, applyVirtualsToResultOnce);

applyVirtualsToChildren(this, schema, res, virtualsForChildren, parent);
addToParentMap(res, parent);
return applyVirtualsToResult(schema, res, toApply);
return applyVirtualsToResultOnce();
}

function applyVirtualsToResult(schema, res, toApply) {
Expand All @@ -92,8 +110,8 @@ function applyVirtualsToResult(schema, res, toApply) {
}
}

function addToParentMap(res, parent) {
if (parent == null || res == null) {
function addToParentMap(res, parent, attachVirtualsToResult) {
if (res == null) {
return;
}

Expand All @@ -108,13 +126,15 @@ function addToParentMap(res, parent) {
for (const _res of res) {
if (_res != null && typeof _res === 'object') {
documentParentsMap.set(_res, parent);
attachVirtualsFnMap.set(_res, attachVirtualsToResult);
}
}
return;
}

if (typeof res === 'object') {
documentParentsMap.set(res, parent);
attachVirtualsFnMap.set(res, attachVirtualsToResult);
}
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"acquit-ignore": "0.1.0",
"acquit-markdown": "0.1.0",
"co": "4.6.0",
"eslint": "6.8.0",
"eslint": "7.x",
"istanbul": "0.4.5",
"mocha": "5.2.x",
"mongoose": "7.0.0-rc0"
Expand Down
48 changes: 46 additions & 2 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ describe('Discriminators work', () => {

it('sets empty array if no result and justOne: false', async function() {
const childSchema = new mongoose.Schema({ name: String, parentId: 'ObjectId' });
const Child = mongoose.model('C2', childSchema);
mongoose.model('C2', childSchema);

const parentSchema = new mongoose.Schema({ name: String });
parentSchema.virtual('children', {
Expand Down Expand Up @@ -688,7 +688,7 @@ describe('Discriminators work', () => {
const Parent = mongoose.model('Parent2', parentSchema);

const child = await Child.create({ name: 'Luke', surname: { name: 'Skywalker' } });
const parent = await Parent.create({ role: 'Father', surname: { name: 'Vader' }, allegiance: { name: 'Empire' }, child: child });
await Parent.create({ role: 'Father', surname: { name: 'Vader' }, allegiance: { name: 'Empire' }, child: child });
let doc = await Parent.findOne().populate('child').lean({ virtuals: true });
assert.ok(childGetterCalled);
assert.ok(parentGetterCalled);
Expand Down Expand Up @@ -744,4 +744,48 @@ describe('Discriminators work', () => {
assert.equal(doc.lowercase, 'test testerson');
});
});

it('supports parent() on deeply nested docs (gh-65)', function() {
const getParent = (doc) => {
if (doc instanceof mongoose.Document) {
return doc.parent();
}
return mongooseLeanVirtuals.parent(doc);
};

const grandchildSchema = new mongoose.Schema({ firstName: String });
grandchildSchema.virtual('fullName').get(function() {
return `${this.firstName} ${getParent(getParent(this)).lastName}`;
});

const childSchema = new mongoose.Schema({ firstName: String, child: grandchildSchema });
childSchema.virtual('fullName').get(function() {
return `${this.firstName} ${getParent(this).lastName}`;
});

const parentSchema = new mongoose.Schema({
firstName: String,
lastName: String,
child: childSchema
}, { id: false });

parentSchema.plugin(mongooseLeanVirtuals);
const Parent = mongoose.model('gh65', parentSchema);

return co(function*() {
const { _id } = yield Parent.create({
firstName: 'Anakin',
lastName: 'Skywalker',
child: {
firstName: 'Luke',
child: {
firstName: 'Ben'
}
}
});
const doc = yield Parent.findById(_id).lean({ virtuals: true }).orFail();
assert.equal(doc.child.fullName, 'Luke Skywalker');
assert.equal(doc.child.child.fullName, 'Ben Skywalker');
});
});
});

0 comments on commit 8fcdc13

Please sign in to comment.