Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: allow nested schemas on discriminated models to work #43

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
67 changes: 28 additions & 39 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,30 +37,19 @@ function applyGettersMiddleware(schema) {
};
}

function applyGetters(schema, res, path) {
function applyGetters(schema, res) {
if (res == null) {
return;
}
if (this._mongooseOptions.lean && this._mongooseOptions.lean.getters) {
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 @@ -84,7 +73,7 @@ function getSchemaForDoc(schema, res) {
return childSchema;
}

function applyGettersToDoc(schema, doc, fields, prefix) {
function applyGettersToDoc(schema, doc) {
if (doc == null) {
return;
}
Expand All @@ -96,46 +85,46 @@ 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)) {

const pathVal = mpath.get(path, doc);
if (schematype.$isMongooseArray) {
if (schematype.$isMongooseDocumentArray) {
pathVal.forEach((subdoc) => applyGettersToDoc.call(this, schematype.schema, subdoc));
return;
}

mpath.set(
path,
schematype.applyGetters(pathVal, doc, true).map(subdoc => {
return schematype.caster.applyGetters(subdoc, doc);
}),
doc
);
return;
}

const pathExists = mpath.has(path, doc);
if (pathExists) {
if (schematype.$isMongooseArray && !schematype.$isMongooseDocumentArray) {
mpath.set(
path,
schematype.applyGetters(mpath.get(path, doc), doc, true).map(subdoc => {
return schematype.caster.applyGetters(subdoc, doc);
}),
doc
);
} else {
mpath.set(path, schematype.applyGetters(mpath.get(path, doc), doc, true), doc);
}
if (schematype.$isSingleNested) {
applyGettersToDoc.call(this, schematype.schema, pathVal);
return;
}

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 <[email protected]>",
"license": "Apache 2.0",
Expand Down
68 changes: 55 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 @@ -46,7 +46,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 @@ -65,14 +65,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 @@ -98,22 +98,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 @@ -193,12 +193,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 @@ -215,18 +216,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 @@ -384,4 +388,42 @@ describe('mongoose-lean-getters', function() {
assert.equal(typeof doc.field, 'string');
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');
});
});
Loading