From d4ca20c01a7981820571fa962e170a6280ebb8d4 Mon Sep 17 00:00:00 2001 From: Ritchie Martori Date: Tue, 16 Jul 2013 17:53:52 -0700 Subject: [PATCH] Add filter.fields support to dao and memory connector --- lib/adapters/memory.js | 7 ++++- lib/dao.js | 12 +++++--- lib/utils.js | 58 +++++++++++++++++++++++++++++++++++++ test/basic-querying.test.js | 47 ++++++++++++++++++++++++++++++ test/util.test.js | 27 +++++++++++++++++ 5 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 test/util.test.js diff --git a/lib/adapters/memory.js b/lib/adapters/memory.js index 1ed68c8f..62f30c0d 100644 --- a/lib/adapters/memory.js +++ b/lib/adapters/memory.js @@ -1,4 +1,5 @@ var geo = require('../geo'); +var utils = require('../utils'); exports.initialize = function initializeSchema(schema, callback) { schema.adapter = new Memory(); @@ -145,12 +146,16 @@ Memory.prototype.all = function all(model, filter, callback) { if (filter.where) { nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes; } + + // field selection + if(filter.fields) { + nodes = nodes.map(utils.selectFields(filter.fields)) + } // limit/skip filter.skip = filter.skip || 0; filter.limit = filter.limit || nodes.length; nodes = nodes.slice(filter.skip, filter.skip + filter.limit); - } process.nextTick(function () { diff --git a/lib/dao.js b/lib/dao.js index 78a78205..d2b5f73d 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -16,6 +16,7 @@ var Inclusion = require('./include.js'); var Relation = require('./relations.js'); var geo = require('./geo'); var Memory = require('./adapters/memory').Memory; +var fieldsToArray = require('./utils').fieldsToArray; /** * DAO class - base class for all persist objects @@ -321,9 +322,16 @@ DataAccessObject.find = function find(params, cb) { } var constr = this; + + var fields = params && params.fields; var near = params && geo.nearFilter(params.where); var supportsGeo = !!this.schema.adapter.buildNearFilter; + // normalize fields as array of included property names + if(fields) { + params.fields = fieldsToArray(fields, Object.keys(this.properties)); + } + if(near) { if(supportsGeo) { // convert it @@ -331,10 +339,6 @@ DataAccessObject.find = function find(params, cb) { } else if(params.where) { // do in memory query // using all documents - - - - this.schema.adapter.all(this.modelName, {}, function (err, data) { var memory = new Memory(); var modelName = constr.modelName; diff --git a/lib/utils.js b/lib/utils.js index 820a21e8..4933a796 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,4 +1,6 @@ exports.safeRequire = safeRequire; +exports.fieldsToArray = fieldsToArray; +exports.selectFields = selectFields; function safeRequire(module) { try { @@ -9,3 +11,59 @@ function safeRequire(module) { } } +function fieldsToArray(fields, properties) { + if(!fields) return; + + // include all properties by default + var result = properties; + + if(typeof fields === 'string') { + return [fields]; + } + + if (Array.isArray(fields) && fields.length > 0) { + // No empty array, including all the fields + return fields; + } + + 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) { + 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); + result.splice(index, 1); + }); + } + } + + return result; +} + +function selectFields(fields) { + // map function + return function (obj) { + var result = {}; + var key; + + for (var i = 0; i < fields.length; i++) { + key = fields[i]; + + result[key] = obj[key]; + } + return result; + } +} \ No newline at end of file diff --git a/test/basic-querying.test.js b/test/basic-querying.test.js index 5a296a71..a9c7e6c3 100644 --- a/test/basic-querying.test.js +++ b/test/basic-querying.test.js @@ -131,6 +131,53 @@ describe('basic-querying', function() { done(); }); }); + + it('should only include fields as specified', function(done) { + var remaining = 0; + + function sample(fields) { + + return { + expect: function (arr) { + remaining++; + User.find({fields: fields}, function(err, users) { + + remaining--; + if(err) return done(err); + + should.exists(users); + + if(remaining === 0) { + done(); + } + + users.forEach(function (user) { + var obj = user.toObject(); + + Object.keys(obj) + .forEach(function (key) { + // if the obj has an unexpected value + if(obj[key] !== undefined && arr.indexOf(key) === -1) { + console.log('Given fields:', fields); + console.log('Got:', key, obj[key]); + console.log('Expected:', arr); + throw new Error('should not include data for key: '+ key); + } + }); + }); + }); + } + } + } + + sample({name: true}).expect(['name']); + sample({name: false}).expect(['id', 'email', 'role', 'order']); + sample({name: false, id: true}).expect(['id']); + sample({id: true}).expect(['id']); + sample('id').expect(['id']); + sample(['id']).expect(['id']); + sample(['email']).expect(['email']); + }); }); diff --git a/test/util.test.js b/test/util.test.js new file mode 100644 index 00000000..6a4c9eb6 --- /dev/null +++ b/test/util.test.js @@ -0,0 +1,27 @@ +var should = require('./init.js'); +var fieldsToArray = require('../lib/utils').fieldsToArray; + +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); + } + } + } + + sample(false).expect(undefined); + sample(null).expect(undefined); + sample({}).expect(undefined); + sample('foo').expect(['foo']); + sample(['foo']).expect(['foo']); + sample({'foo': 1}).expect(['foo']); + sample({'bat': true}).expect(['bat']); + sample({'bat': 0}).expect(['foo', 'bar', 'baz']); + sample({'bat': false}).expect(['foo', 'bar', 'baz']); + }); +}); \ No newline at end of file