diff --git a/history.md b/history.md index c226af2..06d668b 100644 --- a/history.md +++ b/history.md @@ -1,3 +1,8 @@ +# v0.2.16 / 2016-03-07 + +* mondern versions of mongoose expect the skip and limit parameter to be an int. +* remove ability to specify gt,gte,lt,lte and ne parameters with an optional filter + # v0.2.15 / 2016-03-04 * Fixed issue where there was an incompatibility with mquery module in mongoose diff --git a/lib/filter.js b/lib/filter.js index 7095ccf..4e14332 100644 --- a/lib/filter.js +++ b/lib/filter.js @@ -38,79 +38,50 @@ module.exports = function(mongoose) { return val; }; - var applyGreaterThan = function (spec, isOptional) { - if (typeof isOptional === 'undefined') { - isOptional = false; - } - + var applyGreaterThan = function (spec) { for (var key in spec) { if (spec.hasOwnProperty(key)) { - (isOptional ? self.or(key) : self.where(key)).gt(spec[key]); + self.where(key).gt(spec[key]); } } }; - var applyGreaterThanEqual = function (spec, isOptional) { - if (typeof isOptional === 'undefined') { - isOptional = false; - } - + var applyGreaterThanEqual = function (spec) { for (var key in spec) { if (spec.hasOwnProperty(key)) { - (isOptional ? self.or(key) : self.where(key)).gte(spec[key]); + self.where(key).gte(spec[key]); } } }; - var applyLesserThan = function (spec, isOptional) { - if (typeof isOptional === 'undefined') { - isOptional = false; - } - + var applyLesserThan = function (spec) { for (var key in spec) { if (spec.hasOwnProperty(key)) { - (isOptional ? self.or(key) : self.where(key)).lt(spec[key]); + self.where(key).lt(spec[key]); } } }; - var applyLesserThanEqual = function (spec, isOptional) { - if (typeof isOptional === 'undefined') { - isOptional = false; - } - + var applyLesserThanEqual = function (spec) { for (var key in spec) { if (spec.hasOwnProperty(key)) { - (isOptional ? self.or(key) : self.where(key)).lte(spec[key]); + self.where(key).lte(spec[key]); } } }; - var applyNotEqual = function (spec, isOptional) { - if (typeof isOptional === 'undefined') { - isOptional = false; - } - + var applyNotEqual = function (spec) { for (var key in spec) { if (spec.hasOwnProperty(key)) { - (isOptional ? self.or(key) : self.where(key)) - .ne(analyzeWhereSpec(spec[key])); + self.where(key).ne(analyzeWhereSpec(spec[key])); } } }; - var applyRegex = function (spec, buildRegex, isOptional) { - if (typeof isOptional === 'undefined') { - isOptional = false; - } - + var applyRegex = function (spec, buildRegex) { var bulkApply = function (key, val) { val.forEach(function (term) { - if (isOptional) { - self.or(key, term); - } else { - self.where(key, term); - } + self.where(key, term); }); }; @@ -121,14 +92,44 @@ module.exports = function(mongoose) { if (Array.isArray(val)) { bulkApply(key, val); } else { - if (isOptional) { - self.or(key, val); - } else { - self.where(key, val); - } + self.where(key, val); + } + } + } + }, + // mongoose.Query.prototype.or handles or clauses differently than + // before. time was you could pass in a key value pair, now it looks + // like it expects array of objects + applyRegexAsOptional = function (spec, buildRegex) { + function bulkApply(key, val) { + var node = {}, + nodeOptions = []; + + val.forEach(function (term) { + node = {}; + node[key] = term; + nodeOptions.push(node); + }); + + return nodeOptions; + } + var orOptions = [], + orOptionsNode = {}; + + for (var key in spec) { + if (spec.hasOwnProperty(key)) { + var val = buildRegex(spec[key]); + + if (Array.isArray(val)) { + orOptions = orOptions.concat(bulkApply(key, val)); + } else { + orOptionsNode = {}; + orOptionsNode[key] = val; + orOptions.push(orOptionsNode); } } } + self.or(orOptions); }; var regexContains = function (val) { @@ -199,26 +200,10 @@ module.exports = function(mongoose) { mandatory.notEqual || mandatory.notEqualTo || mandatory.ne || {}); // OPTIONAL - applyRegex(optional.contains, regexContains, true); - applyRegex(optional.endsWith, regexEndsWith, true); - applyRegex(optional.startsWith, regexStartsWith, true); - applyRegex(optional.exact, regexExact, true); - - applyGreaterThan( - optional.greaterThan || optional.gt || {}, - true); - applyGreaterThanEqual( - optional.greaterThanEqual || optional.gte || {}, - true); - applyLesserThan( - optional.lessThan || optional.lt || {}, - true); - applyLesserThanEqual( - optional.lessThanEqual || optional.lte || {}, - true); - applyNotEqual( - optional.notEqual || optional.notEqualTo || optional.ne || {}, - true); + applyRegexAsOptional(optional.contains, regexContains); + applyRegexAsOptional(optional.endsWith, regexEndsWith); + applyRegexAsOptional(optional.startsWith, regexStartsWith); + applyRegexAsOptional(optional.exact, regexExact); return self; }; diff --git a/lib/page.js b/lib/page.js index 14ad161..1f8f262 100644 --- a/lib/page.js +++ b/lib/page.js @@ -18,8 +18,9 @@ module.exports = function(mongoose) { wrap = {}; options = options || defaults; - options.start = (options && options.start ? options.start : defaults.start); - options.count = (options && options.count ? options.count : defaults.count); + // this might be getting a little long; + options.start = (options && options.start && parseInt(options.start, 10) ? parseInt(options.start, 10) : defaults.start); + options.count = (options && options.count && parseInt(options.count, 10) ? parseInt(options.count, 10) : defaults.count); if (maxDocs > 0 && (options.count > maxDocs || options.count === 0)) { options.count = maxDocs; diff --git a/package.json b/package.json index 056e148..a829e11 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mongoose-middleware", "description": "Middleware for mongoose that makes filtering, sorting, pagination and projection chainable and simple to apply", - "version": "0.2.15", + "version": "0.2.16", "scripts": { "test": "gulp test-all" }, diff --git a/readme.md b/readme.md index 2c7a7f6..b84f866 100644 --- a/readme.md +++ b/readme.md @@ -193,10 +193,14 @@ KittehModel Filters can be used in three ways: mandatory, optional and keyword searches. Additionally, for mandatory and optional searches, exact, contains and startsWith string pattern matches may be used. +The following filters can be used for *mandatory*, *optional*, and *keyword* searches. + * `exact` - Matches the string letter for letter, but is not case sensitive * `contains` - Matches documents where the string exists as a substring of the field (similar to a where field like '%term%' query in a relational datastore) * `startsWith` - Matches documents where field begins with the string supplied (similar to a where field like 'term%' query in a relational datastore) * `endsWith` - Matches documents where field ends with the string supplied (similar to a where field like '%term' query in a relational datastore) + +The following filters can *ONLY* be used for *mandatory* and *keyword* searches. * `greaterThan` (or `gt`) - Matches documents where field value is greater than supplied number or Date value in query * `greaterThanEqual` (or `gte`) - Matches documents where field value is greater than or equal to supplied number or Date value in query * `lessThan` (or `lt`) - Matches documents where field value is less than supplied number or Date value in query diff --git a/test/lib/filter.js b/test/lib/filter.js index b717a70..930c2b6 100644 --- a/test/lib/filter.js +++ b/test/lib/filter.js @@ -28,38 +28,48 @@ describe('filter', function () { before(function () { filterLib = require('../../lib/filter')(mongoose); - mongoose.Query.prototype.or = function (key, val) { - if (typeof val === 'undefined') { - val = { expr : '', val : null }; - } - - if (orClause[key]) { - var newVal = [orClause[key], val]; - orClause[key] = newVal; - } else { - orClause[key] = val; + mongoose.Query.prototype.or = function (orOptions) { + + if (Array.isArray(orOptions)) { + orOptions.forEach(function (elem) { + for (var x in elem) { + if (elem.hasOwnProperty(x)) { + if (orClause[x]) { + var newVal = [orClause[x], elem[x]]; + orClause[x] = newVal; + } else { + orClause[x] = elem[x]; + } + } + } + }); } + // it doesn't seem the mquery/mongoose supports subsequent gt,lt, + // gte,lte,ne filtering for or queries, however prior to v0.2.16 of + // mongoose-middleware some features were built as though it was + // supported. this will give us some indication if any code remains + // that tries to use these filtering options return { - gt : function (v) { - orClause[key].expr = 'gt'; - orClause[key].val = v; + gt : function () { + throw new Error( + 'mongoose.Query.prototype.or does not support gt'); }, - gte : function (v) { - orClause[key].expr = 'gte'; - orClause[key].val = v; + gte : function () { + throw new Error( + 'mongoose.Query.prototype.or does not support gte'); }, - lt : function (v) { - orClause[key].expr = 'lt'; - orClause[key].val = v; + lt : function () { + throw new Error( + 'mongoose.Query.prototype.or does not support lt'); }, - lte : function (v) { - orClause[key].expr = 'lte'; - orClause[key].val = v; + lte : function () { + throw new Error( + 'mongoose.Query.prototype.or does not support lte'); }, - ne : function (v) { - orClause[key].expr = 'ne'; - orClause[key].val = v; + ne : function () { + throw new Error( + 'mongoose.Query.prototype.or does not support ne'); } }; }; diff --git a/test/lib/page.js b/test/lib/page.js index e465043..53c8d36 100644 --- a/test/lib/page.js +++ b/test/lib/page.js @@ -175,4 +175,78 @@ describe('page', function () { return done(); }); }); + + it('should return results when start is a string', function (done) { + var options = { + start : "0", + count : 100 + }; + + pageLib.initialize({ maxDocs: 50 }); + + Kitteh + .find() + .page(options, function (err, data) { + should.not.exist(err); + should.exist(data); + + return done(); + }); + }); + + it('should return results when start is NaN', function (done) { + var options = { + start : "start", + count : 100 + }; + + pageLib.initialize({ maxDocs: 50 }); + + Kitteh + .find() + .page(options, function (err, data) { + should.not.exist(err); + should.exist(data); + + return done(); + }); + }); + + it('should return results when count is a string', function (done) { + var options = { + start : 0, + count : "100" + }; + + pageLib.initialize({ maxDocs: 50 }); + + Kitteh + .find() + .page(options, function (err, data) { + should.not.exist(err); + should.exist(data); + + return done(); + }); + }); + + it('should return results when count is NaN', function (done) { + var options = { + start : 0, + count : "count" + }; + + pageLib.initialize({ maxDocs: 50 }); + + Kitteh + .find() + .page(options, function (err, data) { + should.not.exist(err); + should.exist(data); + + data.options.count.should.equals(50); + + return done(); + }); + }); });