From f3af2bf190c2001b5ea3bb11bcca74857f71a7e1 Mon Sep 17 00:00:00 2001 From: Dimitris Halatsis Date: Sat, 30 Jan 2016 13:16:56 +0200 Subject: [PATCH 1/5] added .idea folder to .gitignore file --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8c2aa81..9bd15e5 100755 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,7 @@ lib-cov *.out *.pid *.gz - +.idea pids logs results From dc4c979d340ba5ccedc68cedef0b7195ad998ac6 Mon Sep 17 00:00:00 2001 From: Dimitris Halatsis Date: Sat, 30 Jan 2016 13:21:40 +0200 Subject: [PATCH 2/5] added $elemMatch query operator support --- lib/model.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/model.js b/lib/model.js index 89aec96..6d53d87 100755 --- a/lib/model.js +++ b/lib/model.js @@ -622,7 +622,20 @@ comparisonFunctions.$size = function (obj, value) { return (obj.length == value); }; +comparisonFunctions.$elemMatch = function(obj,value){ + if (!util.isArray(obj)) { return false; } + var i = obj.length; + var result = false; // Initialize result + while(i--){ + if (match(obj[i],value)){ // If match for array element, return true + result = true; + break; + } + } + return result; +}; arrayComparisonFunctions.$size = true; +arrayComparisonFunctions.$elemMatch = true; /** From 5db8ca7480df1f5f3f6433ea7f368a9640b4d69d Mon Sep 17 00:00:00 2001 From: Dimitris Halatsis Date: Sat, 30 Jan 2016 13:48:54 +0200 Subject: [PATCH 3/5] Added tests for $elemMatch --- lib/model.js | 6 +++--- test/model.test.js | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/lib/model.js b/lib/model.js index 6d53d87..0a289ab 100755 --- a/lib/model.js +++ b/lib/model.js @@ -622,12 +622,12 @@ comparisonFunctions.$size = function (obj, value) { return (obj.length == value); }; -comparisonFunctions.$elemMatch = function(obj,value){ +comparisonFunctions.$elemMatch = function (obj,value) { if (!util.isArray(obj)) { return false; } var i = obj.length; var result = false; // Initialize result - while(i--){ - if (match(obj[i],value)){ // If match for array element, return true + while (i--) { + if (match(obj[i],value)) { // If match for array element, return true result = true; break; } diff --git a/test/model.test.js b/test/model.test.js index 8990276..5f81723 100755 --- a/test/model.test.js +++ b/test/model.test.js @@ -1216,6 +1216,30 @@ describe('Model', function () { model.match({ childrens: [ 'Riri', 'Fifi', 'Loulou' ] }, { "childrens": { $size: 3, $size: 4 } }).should.equal(false); // Of course this can never be true }); + it('Can query array documents with multiple simultaneous conditions', function () { + // Non nested documents + model.match({ childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] }, { "childrens": { $elemMatch: { name: "Dewey", age: 7 } } }).should.equal(true); + model.match({ childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] }, { "childrens": { $elemMatch: { name: "Dewey", age: 12 } } }).should.equal(false); + model.match({ childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] }, { "childrens": { $elemMatch: { name: "Louie", age: 3 } } }).should.equal(false); + + // Nested documents + model.match({ outer: { childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] } }, { "outer.childrens": { $elemMatch: { name: "Dewey", age: 7 } } }).should.equal(true); + model.match({ outer: { childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] } }, { "outer.childrens": { $elemMatch: { name: "Dewey", age: 12 } } }).should.equal(false); + model.match({ outer: { childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] } }, { "outer.childrens": { $elemMatch: { name: "Louie", age: 3 } } }).should.equal(false); + + }); + + it('$elemMatch operator works with empty arrays', function () { + model.match({ childrens: [] }, { "childrens": { $elemMatch: { name: "Mitsos" } } }).should.equal(false); + model.match({ childrens: [] }, { "childrens": { $elemMatch: {} } }).should.equal(false); + }); + + it('Can use more complex comparisons inside nested query documents', function () { + model.match({ childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] }, { "childrens": { $elemMatch: { name: "Dewey", age: { $gt: 6, $lt: 8 } } } }).should.equal(true); + model.match({ childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] }, { "childrens": { $elemMatch: { name: "Dewey", age: { $in: [ 6, 7, 8 ] } } } } ).should.equal(true); + model.match({ childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] }, { "childrens": { $elemMatch: { name: "Dewey", age: { $gt: 6, $lt: 7 } } } }).should.equal(false); + model.match({ childrens: [ { name: "Huey", age: 3 }, { name: "Dewey", age: 7 }, { name: "Louie", age: 12 } ] }, { "childrens": { $elemMatch: { name: "Louie", age: { $gt: 6, $lte: 7 } } } }).should.equal(false); + }); }); From 05e6b45124906028c4d87eeb0233235f61be6635 Mon Sep 17 00:00:00 2001 From: Dimitris Halatsis Date: Sat, 30 Jan 2016 14:04:52 +0200 Subject: [PATCH 4/5] Added $elemMatch to README --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a269e97..4103949 100755 --- a/README.md +++ b/README.md @@ -271,7 +271,7 @@ db.find({ planet: { $regex: /ar/, $nin: ['Jupiter', 'Earth'] } }, function (err, ``` #### Array fields -When a field in a document is an array, NeDB first tries to see if the query value is an array to perform an exact match, then whether there is an array-specific comparison function (for now there is only `$size`) being used. If not, the query is treated as a query on every element and there is a match if at least one element matches. +When a field in a document is an array, NeDB first tries to see if the query value is an array to perform an exact match, then whether there is an array-specific comparison function (for now there is only `$size` and `$elemMatch`) being used. If not, the query is treated as a query on every element and there is a match if at least one element matches. ```javascript // Exact match @@ -283,6 +283,20 @@ db.find({ satellites: ['Deimos', 'Phobos'] }, function (err, docs) { }) // Using an array-specific comparison function +// $elemMatch operator will provide match for a document, if an element from the array field satisfies all the conditions specified with the `$elemMatch` operator +db.find({ completeData: { planets: { $elemMatch: { name: 'Earth', number: 3 } } } }, function (err, docs) { + // docs contains documents with id 5 (completeData) +}); + +db.find({ completeData: { planets: { $elemMatch: { name: 'Earth', number: 5 } } } }, function (err, docs) { + // docs is empty +}); + +// You can use inside #elemMatch query any known document query operator +db.find({ completeData: { planets: { $elemMatch: { name: 'Earth', number: { $gt: 2 } } } } }, function (err, docs) { + // docs contains documents with id 5 (completeData) +}); + // Note: you can't use nested comparison functions, e.g. { $size: { $lt: 5 } } will throw an error db.find({ satellites: { $size: 2 } }, function (err, docs) { // docs contains Mars From 310e881906cb2c94fbcb9573885c41aeea211afb Mon Sep 17 00:00:00 2001 From: Dimitris Halatsis Date: Sat, 30 Jan 2016 14:08:30 +0200 Subject: [PATCH 5/5] styling correction --- lib/model.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/model.js b/lib/model.js index 0a289ab..8845bf8 100755 --- a/lib/model.js +++ b/lib/model.js @@ -622,12 +622,12 @@ comparisonFunctions.$size = function (obj, value) { return (obj.length == value); }; -comparisonFunctions.$elemMatch = function (obj,value) { +comparisonFunctions.$elemMatch = function (obj, value) { if (!util.isArray(obj)) { return false; } var i = obj.length; var result = false; // Initialize result while (i--) { - if (match(obj[i],value)) { // If match for array element, return true + if (match(obj[i], value)) { // If match for array element, return true result = true; break; }