From bc626afed640618206a62b209578f64c15fde378 Mon Sep 17 00:00:00 2001 From: Louis Chatriot Date: Thu, 23 May 2013 12:01:54 +0200 Subject: [PATCH] All calls to model.match are wrapped in a try catch now --- lib/datastore.js | 38 ++++++++++++++++++++++------------ lib/model.js | 42 +++++++++++++++----------------------- test/model.test.js | 51 ++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 88 insertions(+), 43 deletions(-) diff --git a/lib/datastore.js b/lib/datastore.js index d446200..5851f08 100644 --- a/lib/datastore.js +++ b/lib/datastore.js @@ -116,10 +116,14 @@ Datastore.prototype.find = function (query, callback) { , i ; - for (i = 0; i < self.data.length; i += 1) { - if (model.match(self.data[i], query)) { - res.push(model.deepCopy(self.data[i])); + try { + for (i = 0; i < self.data.length; i += 1) { + if (model.match(self.data[i], query)) { + res.push(model.deepCopy(self.data[i])); + } } + } catch (err) { + return callback(err); } return callback(null, res); @@ -135,10 +139,14 @@ Datastore.prototype.findOne = function (query, callback) { , i ; - for (i = 0; i < self.data.length; i += 1) { - if (model.match(self.data[i], query)) { - return callback(null, model.deepCopy(self.data[i])); + try { + for (i = 0; i < self.data.length; i += 1) { + if (model.match(self.data[i], query)) { + return callback(null, model.deepCopy(self.data[i])); + } } + } catch (err) { + return callback(err); } return callback(null, null); @@ -264,13 +272,17 @@ Datastore.prototype._remove = function (query, options, cb) { callback = cb || function () {}; multi = options.multi !== undefined ? options.multi : false; - self.data.forEach(function (d) { - if (model.match(d, query) && (multi || numRemoved === 0)) { - numRemoved += 1; - } else { - newData.push(d); - } - }); + try { + self.data.forEach(function (d) { + if (model.match(d, query) && (multi || numRemoved === 0)) { + numRemoved += 1; + } else { + newData.push(d); + } + }); + } catch (err) { + return callback(err); + } self.persistWholeDatabase(newData, function (err) { if (err) { return callback(err); } diff --git a/lib/model.js b/lib/model.js index d68102c..a8427ae 100644 --- a/lib/model.js +++ b/lib/model.js @@ -286,16 +286,6 @@ function getDotValue (obj, field) { } -/** - * Test for field equality - * @param {Object} objValue The value from the object to check - * @param {Model} value The value from the query to check against - */ -matcherFunctions.$eq = function (objValue, value) { - return areThingsEqual(objValue, value); -}; - - /** * Match any of the subconditions */ @@ -323,7 +313,7 @@ function match (obj, query) { ; for (i = 0; i < queryKeys.length; i += 1) { - if (!matchQueryKey(obj, query, queryKeys[i])) { return false; } + if (!matchQueryPart(obj, queryKeys[i], query[queryKeys[i]])) { return false; } } return true; @@ -331,32 +321,32 @@ function match (obj, query) { /** - * Match a specific key of a given query + * Match an object against a specific { key: value } part of a query */ -function matchQueryKey (obj, query, queryKey) { +function matchQueryPart (obj, queryKey, queryValue) { var objValue = getDotValue(obj, queryKey) - , queryValue = query[queryKey] , i ; + // Check if query begins with an operator and we need to rewrite it if (queryKey[0] === '$') { - // We apply an operator like $or, $and if (!matcherFunctions[queryKey]) { throw "Unknown query operator " + queryKey; } return matcherFunctions[queryKey](obj, queryValue); - } else { - // Normal field matching - if (util.isArray(objValue)) { - for (i = 0; i < objValue.length; i += 1) { - if (matcher(objValue[i], queryValue)) { return true; } - } - return false; - } else { - if (!matcherFunctions.$eq(objValue, queryValue)) { return false; } - } + } - return true; + // Check if the object value is an array and we need to rewrite the query + if (util.isArray(objValue)) { + for (i = 0; i < objValue.length; i += 1) { + if (match({ queryKey: objValue[i] }, { queryKey: queryValue })) { return true; } + } + return false; } + + // Normal field matching + if (!areThingsEqual(objValue, queryValue)) { return false; } + + return true; } diff --git a/test/model.test.js b/test/model.test.js index 3782b93..a0f1d7c 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -392,7 +392,7 @@ describe('Model', function () { }); - describe('$eq', function () { + describe('Field equality', function () { it('Can find documents with simple fields', function () { model.match({ test: 'yeah' }, { test: 'yea' }).should.equal(false); @@ -412,7 +412,7 @@ describe('Model', function () { model.match({ test: { pp: undefined } }, { "test.pp": undefined }).should.equal(false); }); - it('For field array, a match means a match on at least one element', function () { + it('For an array field, a match means a match on at least one element', function () { model.match({ tags: ['node', 'js', 'db'] }, { tags: 'python' }).should.equal(false); model.match({ tags: ['node', 'js', 'db'] }, { tagss: 'js' }).should.equal(false); model.match({ tags: ['node', 'js', 'db'] }, { tags: 'js' }).should.equal(true); @@ -429,12 +429,55 @@ describe('Model', function () { }); - describe('$or', function () { + describe('$lt', function () { - it('Any of the subconditions can be used', function () { + it('Cannot compare a field to an object, an array, null or a boolean, it will return false', function () { + model.match({ a: 5 }, { a: { $lt: { a: 6 } } }).should.equal(false); + model.match({ a: 5 }, { a: { $lt: [6, 7] } }).should.equal(false); + model.match({ a: 5 }, { a: { $lt: null } }).should.equal(false); + model.match({ a: 5 }, { a: { $lt: true } }).should.equal(false); + }); + + it('Can compare numbers, with or without dot notation', function () { + model.match({ a: 5 }, { a: { $lt: 6 } }).should.equal(true); + model.match({ a: 5 }, { a: { $lt: 3 } }).should.equal(false); + + model.match({ a: { b: 5 } }, { "a.b": { $lt: 6 } }).should.equal(true); + model.match({ a: { b: 5 } }, { "a.b": { $lt: 3 } }).should.equal(false); + }); + + it('Can compare strings, with or without dot notation', function () { + model.match({ a: "nedb" }, { a: { $lt: "nedc" } }).should.equal(true); + model.match({ a: "nedb" }, { a: { $lt: "neda" } }).should.equal(false); + + model.match({ a: { b: "nedb" } }, { "a.b": { $lt: "nedc" } }).should.equal(true); + model.match({ a: { b: "nedb" } }, { "a.b": { $lt: "neda" } }).should.equal(false); + }); + + it('If field is an array field, a match means a match on at least one element', function () { + model.match({ a: [5, 10] }, { a: { $lt: 4 } }).should.equal(false); + model.match({ a: [5, 10] }, { a: { $lt: 6 } }).should.equal(true); + model.match({ a: [5, 10] }, { a: { $lt: 11 } }).should.equal(true); + }); + + }); + + + describe('Logical operators $or, $and', function () { + + it('Any of the subqueries should match for an $or to match', function () { model.match({ hello: 'world' }, { $or: [ { hello: 'pluton' }, { hello: 'world' } ] }).should.equal(true); model.match({ hello: 'pluton' }, { $or: [ { hello: 'pluton' }, { hello: 'world' } ] }).should.equal(true); model.match({ hello: 'nope' }, { $or: [ { hello: 'pluton' }, { hello: 'world' } ] }).should.equal(false); + model.match({ hello: 'world', age: 15 }, { $or: [ { hello: 'pluton' }, { age: { $lt: 20 } } ] }).should.equal(true); + model.match({ hello: 'world', age: 15 }, { $or: [ { hello: 'pluton' }, { age: { $lt: 10 } } ] }).should.equal(false); + }); + + it('All of the subqueries should match for an $and to match', function () { + model.match({ hello: 'world', age: 15 }, { $and: [ { age: 15 }, { hello: 'world' } ] }).should.equal(true); + model.match({ hello: 'world', age: 15 }, { $and: [ { age: 16 }, { hello: 'world' } ] }).should.equal(false); + model.match({ hello: 'world', age: 15 }, { $and: [ { hello: 'world' }, { age: { $lt: 20 } } ] }).should.equal(true); + model.match({ hello: 'world', age: 15 }, { $and: [ { hello: 'pluton' }, { age: { $lt: 20 } } ] }).should.equal(false); }); });