Skip to content

Commit

Permalink
Merge pull request #43 from DesignByOnyx/42-nested-discriminator-props
Browse files Browse the repository at this point in the history
fix: allow nested schemas on discriminated models to work
  • Loading branch information
vkarpov15 authored Feb 21, 2025
2 parents 5f7065d + d88c67e commit cefc3e6
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 47 deletions.
42 changes: 13 additions & 29 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ function applyGettersMiddleware(schema, options) {
};
}

function applyGetters(schema, res, path) {
function applyGetters(schema, res) {
if (res == null) {
return;
}
Expand All @@ -49,22 +49,11 @@ function applyGetters(schema, res, path) {
if (Array.isArray(res)) {
const len = res.length;
for (let i = 0; i < len; ++i) {
applyGettersToDoc.call(this, schema, res[i], this._fields, path);
applyGettersToDoc.call(this, schema, res[i]);
}
} else {
applyGettersToDoc.call(this, schema, res, this._fields, path);
applyGettersToDoc.call(this, schema, res);
}

for (let i = 0; i < schema.childSchemas.length; ++i) {
const childPath = path ? path + '.' + schema.childSchemas[i].model.path : schema.childSchemas[i].model.path;
const _schema = schema.childSchemas[i].schema;
const doc = mpath.get(schema.childSchemas[i].model.path, res);
if (doc == null) {
continue;
}
applyGetters.call(this, _schema, doc, childPath);
}

return res;
} else {
return res;
Expand All @@ -90,7 +79,7 @@ function getSchemaForDoc(schema, res) {
return childSchema || schema;
}

function applyGettersToDoc(schema, doc, fields, prefix) {
function applyGettersToDoc(schema, doc) {
if (doc == null) {
return;
}
Expand All @@ -102,32 +91,23 @@ function applyGettersToDoc(schema, doc, fields, prefix) {
if (currentDoc == null) continue;
// If it is a nested array, apply getters to each subdocument (otherwise it would attempt to apply getters to the array itself)
if (Array.isArray(currentDoc)) {
applyGettersToDoc.call(this, schema, currentDoc, fields, prefix);
applyGettersToDoc.call(this, schema, currentDoc);
continue;
}
const schemaForDoc = getSchemaForDoc(schema, currentDoc);
applyGettersToDoc.call(this, schemaForDoc, currentDoc, fields, prefix);
applyGettersToDoc.call(this, schemaForDoc, currentDoc);
}
return;
}

const schemaForDoc = getSchemaForDoc(schema, doc);

schemaForDoc.eachPath((path, schematype) => {
const pathWithPrefix = prefix ? prefix + '.' + path : path;
if (this.selectedInclusively() &&
fields &&
fields[pathWithPrefix] == null &&
!this.isPathSelectedInclusive(pathWithPrefix)) { // fields[pathWithPrefix] should return false
if (!mpath.has(path, doc)) {
// The path is not present (likely from projection)
return;
}
if (this.selectedExclusively() &&
fields &&
fields[pathWithPrefix] != null &&
!this.isPathSelectedInclusive(pathWithPrefix)) {
return;
}


const pathExists = mpath.has(path, doc);
if (pathExists) {
const val = schematype.applyGetters(mpath.get(path, doc), doc, true);
Expand All @@ -139,10 +119,14 @@ function applyGettersToDoc(schema, doc, fields, prefix) {
}),
doc
);
} if (val && schematype.$isSingleNested) {
applyGettersToDoc.call(this, schematype.schema, pathVal);
} else {
mpath.set(path, val, doc);
}
}

mpath.set(path, schematype.applyGetters(pathVal, doc, true), doc);
});
}

Expand Down
7 changes: 2 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"mongodb"
],
"dependencies": {
"mongoose": "^8.5.1",
"mpath": "0.9.x"
},
"engines": {
Expand All @@ -33,11 +34,7 @@
"co": "4.6.0",
"eslint": "5.16.0",
"istanbul": "0.4.5",
"mocha": "5.2.x",
"mongoose": ">= 7.5.0"
},
"peerDependencies": {
"mongoose": ">= 7.5.0"
"mocha": "5.2.x"
},
"author": "Valeri Karpov <val@karpov.io>",
"license": "Apache 2.0",
Expand Down
67 changes: 54 additions & 13 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('mongoose-lean-getters', function() {
after(() => mongoose.disconnect());

it('with different types', async function() {
const schema = mongoose.Schema({
const schema = new mongoose.Schema({
name: {
type: String,
get: v => v != null ? v.toLowerCase() : v
Expand Down Expand Up @@ -62,7 +62,7 @@ describe('mongoose-lean-getters', function() {
});

it('only calls getters once with find() (gh-1)', async function() {
const schema = mongoose.Schema({
const schema = new mongoose.Schema({
name: {
type: String,
get: v => v + '123'
Expand All @@ -81,14 +81,14 @@ describe('mongoose-lean-getters', function() {
});

it('avoids running getters on fields that are projected out (gh-9)', async function() {
const childSchema = mongoose.Schema({
const childSchema = new mongoose.Schema({
name: {
type: String,
get: v => v + '456'
}
});

const schema = mongoose.Schema({
const schema = new mongoose.Schema({
name: {
type: String,
get: v => v + '123'
Expand All @@ -114,22 +114,22 @@ describe('mongoose-lean-getters', function() {
});

it('should call nested getters', async function() {
const subChildSchema = mongoose.Schema({
const subChildSchema = new mongoose.Schema({
name: {
type: String,
get: v => v + ' nested child'
}
});

const childSchema = mongoose.Schema({
const childSchema = new mongoose.Schema({
name: {
type: String,
get: v => v + ' child'
},
subChilren: [subChildSchema]
});

const schema = mongoose.Schema({
const schema = new mongoose.Schema({
name: {
type: String,
get: v => v + ' root'
Expand Down Expand Up @@ -209,12 +209,13 @@ describe('mongoose-lean-getters', function() {
});

it('should work with arrays gh-22', async function() {
const schema = mongoose.Schema({
const schema = new mongoose.Schema({
name: {
type: String
},
items: [{
text: { type: String, default: null, get: v => v.slice(-6) }
text: { type: String, default: null, get: v => v.slice(-6) },
num: Number,
}]
});

Expand All @@ -231,18 +232,21 @@ describe('mongoose-lean-getters', function() {
}, {
$push: {
items: {
text: 'Lorem ipsum dolor sit amet'
text: 'Lorem ipsum dolor sit amet',
num: 1234,
}
}
}, { new: true, projection: 'name items'}).lean({ getters: true });
}, { new: true, projection: 'name items.text'}).lean({ getters: true });

const success = await Test.findOneAndUpdate({
name: 'Captain Jean-Luc Picard'
}).lean({ getters: true });

await Test.deleteMany({});
assert.equal(success.items[0].text, 't amet');
assert.equal(res.items[0].text, 't amet');
assert.equal(success.items[0].text, 't amet', 'Success text is wrong');
assert.equal(success.items[0].num, 1234);
assert.equal(res.items[0].text, 't amet', 'Projected result is wrong');
assert.equal(res.items[0].num, undefined, 'num should be undefined');
});

it('should call getters on schemas with discriminator', async function() {
Expand Down Expand Up @@ -401,6 +405,43 @@ describe('mongoose-lean-getters', function() {
assert.strictEqual(doc.field, '1337');
});

it('should call getters on nested schemas within discriminated models', async() => {
const nestedSchema = new mongoose.Schema({
num: {
type: mongoose.Types.Decimal128,
get: (val) => `${val}`
}
});

const rootSchema = new mongoose.Schema({
// These properties are here as a control (these always worked as expected)
rootProp: { type: nestedSchema },
rootArray: { type: [nestedSchema] },
});
rootSchema.plugin(mongooseLeanGetters);

const discriminatedSchema = new mongoose.Schema({
// These props on the discriminated schemas were not having getters called
discriminatedProp: { type: nestedSchema },
discriminatedArray: { type: [nestedSchema] },
});

const RootModel = mongoose.model('Root', rootSchema);
const DiscriminatedModel = RootModel.discriminator('Discriminated', discriminatedSchema);

const entry = await DiscriminatedModel.create({
rootProp: { num: -0.1111111111111111111 },
rootArray: [{ num: -0.1111111111111111111 }],
discriminatedProp: { num: -0.222222222222222222 },
discriminatedArray: [{ num: -0.333333333333333333 }],
});

const found = await DiscriminatedModel.findById(entry._id).lean({ getters: true }).exec();
assert.equal(typeof found.rootProp.num, 'string', 'Root prop is not a string');
assert.equal(typeof found.rootArray[0].num, 'string', 'Root array is not a string');
assert.equal(typeof found.discriminatedProp.num, 'string', 'Discriminated prop is not a string');
assert.equal(typeof found.discriminatedArray[0].num, 'string', 'Discriminated array is not a string');
});
it('allows defaultLeanOptions to be set and overridden at call time (#33)', async() => {
const testSchema = new mongoose.Schema({
field: {
Expand Down

0 comments on commit cefc3e6

Please sign in to comment.