From 6e41c82dfd17f93b59982573f72a0ed7eaf9e476 Mon Sep 17 00:00:00 2001 From: Leandro Ferrari Thomaz Date: Sun, 17 Nov 2013 00:18:34 +0100 Subject: [PATCH] New operator and extended dot operator subdocument search --- lib/model.js | 22 ++++++++++++---------- test/model.test.js | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/lib/model.js b/lib/model.js index 53c7979..e10b535 100644 --- a/lib/model.js +++ b/lib/model.js @@ -12,7 +12,7 @@ var dateToJSON = function () { return { $$date: this.getTime() }; } , lastStepModifierFunctions = {} , comparisonFunctions = {} , logicalOperators = {} - , queryOperators = {} + , queryOperatorArray = {} ; @@ -573,8 +573,11 @@ comparisonFunctions.$exists = function (value, exists) { } }; -queryOperators.$size = function (obj, value) { - return (util.isArray(obj) && obj.length == value); +queryOperatorArray.$size = function (obj, value) { + if (!util.isArray(obj)) { throw "$size operator not being applied to an array"; } + if (value % 1 !== 0) { throw "$size operator called without an integer"; } + + return (obj.length == value); }; /** @@ -663,23 +666,22 @@ function matchQueryPart (obj, queryKey, queryValue) { var objValue = getDotValue(obj, queryKey) , i, keys, firstChars, dollarFirstChars; - // Check if the object value is an array treat it as an array of { obj, query } - // Where there needs to be at least one match + // Check if the object value is an array if (util.isArray(objValue)) { - // Check if it's a query operator + // Check if it's a query operator array // TODO REFACTOR!!! keys[0]!!! if (queryValue !== null && typeof queryValue === 'object' && !util.isRegExp(queryValue)) { keys = Object.keys(queryValue); firstChars = _.map(keys, function (item) { return item[0]; }); dollarFirstChars = _.filter(firstChars, function (c) { return c === '$'; }); - // queryValue is an object of this form: { : queryOperator } - if (dollarFirstChars.length > 0 && queryOperators[keys[0]]) { - if (!queryOperators[keys[0]](objValue, queryValue[keys[0]])) { return false; } + if (keys.length == 1 && dollarFirstChars.length > 0 && queryOperatorArray[keys[0]]) { + if (!queryOperatorArray[keys[0]](objValue, queryValue[keys[0]])) { return false; } return true; } } - // Else, process every array value + + // Else, treat it as an array of { obj, query } where there needs to be at least one match for (i = 0; i < objValue.length; i += 1) { if (matchQueryPart({ k: objValue[i] }, 'k', queryValue)) { return true; } // k here could be any string } diff --git a/test/model.test.js b/test/model.test.js index a96fe3c..a770d0e 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -1048,7 +1048,30 @@ describe('Model', function () { }); - describe('Array fields', function () { + describe('Query operator array $size', function () { + + it('$size', function () { + model.match({ childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] }, { "childrens": { $size: 0 } }).should.equal(false); + model.match({ childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] }, { "childrens": { $size: 2 } }).should.equal(false); + model.match({ childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] }, { "childrens": { $size: 3 } }).should.equal(true); + }); + + it('$size operator works with empty arrays', function () { + model.match({ childrens: [ ] }, { "childrens": { $size: 0 } }).should.equal(true); + model.match({ childrens: [ ] }, { "childrens": { $size: 2 } }).should.equal(false); + model.match({ childrens: [ ] }, { "childrens": { $size: 3 } }).should.equal(false); + }); + + it('Should throw an error if a query operator is used without being applied to an array and comparing to an integer', function () { + (function () { model.match({ a: 5 }, { a: { $size: 1 } }); }).should.throw(); + (function () { model.match({ a: [1, 5] }, { a: { $size: 1.4 } }); }).should.throw(); + (function () { model.match({ a: [1, 5] }, { a: { $size: 'fdf' } }); }).should.throw(); + }); + + }); + + + describe('Array fields', function () { it('Field equality', function () { model.match({ tags: ['node', 'js', 'db'] }, { tags: 'python' }).should.equal(false); @@ -1080,6 +1103,14 @@ describe('Model', function () { model.match({ children: { ages: [3, 7, 12] } }, { "children.ages": { $lt: 13 } }).should.equal(true); }); + it('Works also with arrays of objects that are in subdocuments', function () { + model.match({ childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] }, { "childrens.age": { $lt: 2 } }).should.equal(false); + model.match({ childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] }, { "childrens.age": { $lt: 3 } }).should.equal(false); + model.match({ childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] }, { "childrens.age": { $lt: 4 } }).should.equal(true); + model.match({ childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] }, { "childrens.age": { $lt: 8 } }).should.equal(true); + model.match({ childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] }, { "childrens.age": { $lt: 13 } }).should.equal(true); + }); + }); }); // ==== End of 'Finding documents' ==== //