diff --git a/lib/dao.js b/lib/dao.js index 9bf0467a..6dd5594d 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -947,7 +947,7 @@ DataAccessObject._normalize = function (filter) { // normalize fields as array of included property names if (filter.fields) { filter.fields = fieldsToArray(filter.fields, - Object.keys(this.definition.properties)); + Object.keys(this.definition.properties), this.settings.strict); } filter = removeUndefined(filter); diff --git a/lib/model.js b/lib/model.js index 17d61453..d595988d 100644 --- a/lib/model.js +++ b/lib/model.js @@ -181,8 +181,9 @@ ModelBaseClass.prototype._initProperties = function (data, options) { } else if (ctor.relations[p]) { var relationType = ctor.relations[p].type; + var modelTo; if (!properties[p]) { - var modelTo = ctor.relations[p].modelTo || ModelBaseClass; + modelTo = ctor.relations[p].modelTo || ModelBaseClass; var multiple = ctor.relations[p].multiple; var typeName = multiple ? 'Array' : modelTo.modelName; var propType = multiple ? [modelTo] : modelTo; @@ -196,7 +197,8 @@ ModelBaseClass.prototype._initProperties = function (data, options) { self.__data[ctor.relations[p].keyFrom] = propVal[ctor.relations[p].keyTo]; if (ctor.relations[p].options.embedsProperties) { - var fields = fieldsToArray(ctor.relations[p].properties, modelTo.definition.properties); + var fields = fieldsToArray(ctor.relations[p].properties, + modelTo.definition.properties, modelTo.setting.strict); if (!~fields.indexOf(ctor.relations[p].keyTo)) { fields.push(ctor.relations[p].keyTo); } diff --git a/lib/utils.js b/lib/utils.js index 31acfb4b..cb702cec 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -149,7 +149,7 @@ function mergeQuery(base, update, spec) { } spec = spec || {}; base = base || {}; - + if (update.where && Object.keys(update.where).length > 0) { if (base.where && Object.keys(base.where).length > 0) { base.where = {and: [base.where, update.where]}; @@ -178,81 +178,96 @@ function mergeQuery(base, update, spec) { } } } - + if (spec.collect !== false && update.collect) { base.collect = update.collect; } - + // Overwrite fields if (spec.fields !== false && update.fields !== undefined) { base.fields = update.fields; } else if (update.fields !== undefined) { base.fields = [].concat(base.fields).concat(update.fields); } - + // set order if ((!base.order || spec.order === false) && update.order) { base.order = update.order; } - + // overwrite pagination if (spec.limit !== false && update.limit !== undefined) { base.limit = update.limit; } - + var skip = spec.skip !== false && spec.offset !== false; - + if (skip && update.skip !== undefined) { base.skip = update.skip; } - + if (skip && update.offset !== undefined) { base.offset = update.offset; } - + return base; } -function fieldsToArray(fields, properties) { +/** + * Normalize fields to an array of included properties + * @param {String|String[]|Object} fields Fields filter + * @param {String[]} properties Property names + * @param {Boolean} excludeUnknown To exclude fields that are unknown properties + * @returns {String[]} An array of included property names + */ +function fieldsToArray(fields, properties, excludeUnknown) { if (!fields) return; // include all properties by default var result = properties; + var i, n; if (typeof fields === 'string') { - return [fields]; - } - - if (Array.isArray(fields) && fields.length > 0) { + result = [fields]; + } else if (Array.isArray(fields) && fields.length > 0) { // No empty array, including all the fields - return fields; - } - - if ('object' === typeof fields) { + result = fields; + } else if ('object' === typeof fields) { // { field1: boolean, field2: boolean ... } var included = []; var excluded = []; var keys = Object.keys(fields); if (!keys.length) return; - keys.forEach(function (k) { + for (i = 0, n = keys.length; i < n; i++) { + var k = keys[i]; if (fields[k]) { included.push(k); } else if ((k in fields) && !fields[k]) { excluded.push(k); } - }); + } if (included.length > 0) { result = included; } else if (excluded.length > 0) { - excluded.forEach(function (e) { - var index = result.indexOf(e); + for (i = 0, n = excluded.length; i < n; i++) { + var index = result.indexOf(excluded[i]); result.splice(index, 1); - }); + } } } - return result; + var fieldArray = []; + if (excludeUnknown) { + for (i = 0, n = result.length; i < n; i++) { + if (properties.indexOf(result[i]) !== -1) { + fieldArray.push(result[i]); + } + } + } else { + fieldArray = result; + } + return fieldArray; } function selectFields(fields) { @@ -404,16 +419,16 @@ function sortObjectsByIds(idName, ids, objects, strict) { ids = ids.map(function(id) { return (typeof id === 'object') ? String(id) : id; }); - + var indexOf = function(x) { var isObj = (typeof x[idName] === 'object'); // ObjectID var id = isObj ? String(x[idName]) : x[idName]; return ids.indexOf(id); }; - + var heading = []; var tailing = []; - + objects.forEach(function(x) { if (typeof x === 'object') { var idx = indexOf(x); @@ -421,7 +436,7 @@ function sortObjectsByIds(idName, ids, objects, strict) { idx === -1 ? tailing.push(x) : heading.push(x); } }); - + heading.sort(function(x, y) { var a = indexOf(x); var b = indexOf(y); @@ -430,7 +445,7 @@ function sortObjectsByIds(idName, ids, objects, strict) { if (a > b) return 1; if (a < b) return -1; }); - + return heading.concat(tailing); }; diff --git a/test/util.test.js b/test/util.test.js index 24b9451d..369ae297 100644 --- a/test/util.test.js +++ b/test/util.test.js @@ -7,16 +7,17 @@ var mergeIncludes = utils.mergeIncludes; var sortObjectsByIds = utils.sortObjectsByIds; describe('util.fieldsToArray', function () { - it('Turn objects and strings into an array of fields to include when finding models', function () { - - function sample(fields) { - var properties = ['foo', 'bar', 'bat', 'baz']; - return { - expect: function (arr) { - should.deepEqual(fieldsToArray(fields, properties), arr); - } + function sample(fields, excludeUnknown) { + var properties = ['foo', 'bar', 'bat', 'baz']; + return { + expect: function (arr) { + should.deepEqual(fieldsToArray(fields, properties, excludeUnknown), arr); } - } + }; + } + + it('Turn objects and strings into an array of fields' + + ' to include when finding models', function () { sample(false).expect(undefined); sample(null).expect(undefined); @@ -28,6 +29,19 @@ describe('util.fieldsToArray', function () { sample({'bat': 0}).expect(['foo', 'bar', 'baz']); sample({'bat': false}).expect(['foo', 'bar', 'baz']); }); + + it('should exclude unknown properties', function () { + + sample(false, true).expect(undefined); + sample(null, true).expect(undefined); + sample({}, true).expect(undefined); + sample('foo', true).expect(['foo']); + sample(['foo', 'unknown'], true).expect(['foo']); + sample({'foo': 1, unknown: 1}, true).expect(['foo']); + sample({'bat': true, unknown: true}, true).expect(['bat']); + sample({'bat': 0}, true).expect(['foo', 'bar', 'baz']); + sample({'bat': false}, true).expect(['foo', 'bar', 'baz']); + }); }); describe('util.removeUndefined', function () { @@ -190,7 +204,7 @@ describe('mergeSettings', function () { }); describe('sortObjectsByIds', function () { - + var items = [ { id: 1, name: 'a' }, { id: 2, name: 'b' }, @@ -211,7 +225,7 @@ describe('sortObjectsByIds', function () { var names = sorted.map(function(u) { return u.name; }); should.deepEqual(names, ['e', 'c', 'b', 'a', 'd', 'f']); }); - + it('should sort - strict', function() { var sorted = sortObjectsByIds('id', [5, 3, 2], items, true); var names = sorted.map(function(u) { return u.name; });