New operator and extended dot operator subdocument search

pull/2/head
Leandro Ferrari Thomaz 11 years ago
parent bbc1ff6eb7
commit 6e41c82dfd
  1. 22
      lib/model.js
  2. 33
      test/model.test.js

@ -12,7 +12,7 @@ var dateToJSON = function () { return { $$date: this.getTime() }; }
, lastStepModifierFunctions = {} , lastStepModifierFunctions = {}
, comparisonFunctions = {} , comparisonFunctions = {}
, logicalOperators = {} , logicalOperators = {}
, queryOperators = {} , queryOperatorArray = {}
; ;
@ -573,8 +573,11 @@ comparisonFunctions.$exists = function (value, exists) {
} }
}; };
queryOperators.$size = function (obj, value) { queryOperatorArray.$size = function (obj, value) {
return (util.isArray(obj) && obj.length == 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) var objValue = getDotValue(obj, queryKey)
, i, keys, firstChars, dollarFirstChars; , i, keys, firstChars, dollarFirstChars;
// Check if the object value is an array treat it as an array of { obj, query } // Check if the object value is an array
// Where there needs to be at least one match
if (util.isArray(objValue)) { if (util.isArray(objValue)) {
// Check if it's a query operator // Check if it's a query operator array
// TODO REFACTOR!!! keys[0]!!! // TODO REFACTOR!!! keys[0]!!!
if (queryValue !== null && typeof queryValue === 'object' && !util.isRegExp(queryValue)) { if (queryValue !== null && typeof queryValue === 'object' && !util.isRegExp(queryValue)) {
keys = Object.keys(queryValue); keys = Object.keys(queryValue);
firstChars = _.map(keys, function (item) { return item[0]; }); firstChars = _.map(keys, function (item) { return item[0]; });
dollarFirstChars = _.filter(firstChars, function (c) { return c === '$'; }); dollarFirstChars = _.filter(firstChars, function (c) { return c === '$'; });
// queryValue is an object of this form: { <array>: queryOperator } if (keys.length == 1 && dollarFirstChars.length > 0 && queryOperatorArray[keys[0]]) {
if (dollarFirstChars.length > 0 && queryOperators[keys[0]]) { if (!queryOperatorArray[keys[0]](objValue, queryValue[keys[0]])) { return false; }
if (!queryOperators[keys[0]](objValue, queryValue[keys[0]])) { return false; }
return true; 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) { for (i = 0; i < objValue.length; i += 1) {
if (matchQueryPart({ k: objValue[i] }, 'k', queryValue)) { return true; } // k here could be any string if (matchQueryPart({ k: objValue[i] }, 'k', queryValue)) { return true; } // k here could be any string
} }

@ -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 () { it('Field equality', function () {
model.match({ tags: ['node', 'js', 'db'] }, { tags: 'python' }).should.equal(false); 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); 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' ==== // }); // ==== End of 'Finding documents' ==== //

Loading…
Cancel
Save