From 30a54625f27e76bc4b90ef519011585e7e3bd103 Mon Sep 17 00:00:00 2001
From: Rockafella <eugene.kostrikov@gmail.com>
Date: Fri, 6 Jun 2014 15:32:04 +0400
Subject: [PATCH] Extended filters

---
 lib/adapters/mongodb.js              |  26 +++++-
 test/app.js                          |   1 +
 test/fixtures.json                   |   3 +
 test/fortune-mongodb/mongodb.spec.js | 119 ++++++++++++++++++++++++++-
 4 files changed, 145 insertions(+), 4 deletions(-)

diff --git a/lib/adapters/mongodb.js b/lib/adapters/mongodb.js
index f2f255351..7efc6aef3 100644
--- a/lib/adapters/mongodb.js
+++ b/lib/adapters/mongodb.js
@@ -230,9 +230,11 @@ adapter.findMany = function(model, query, projection) {
 
   var pk = model.pk || "_id";
 
+  if (_.isObject(query) && !_.isArray(query)){
   _.each(query, function(val, key){
     var m;
     if(model.schema.tree[key] === Date && _.isString(val)){
+      //Strict date equality
       m = moment(val);
       
       if(m.format("YYYY-MM-DD") === val){
@@ -241,8 +243,30 @@ adapter.findMany = function(model, query, projection) {
           $lte: moment(val).add("days", 1).format("YYYY-MM-DD")
         };
       }
-    } 
+    }else if ((model.schema.tree[key] === Date || model.schema.tree[key] === Number) && _.isObject(val)){
+      //gt/gte/lt/lte for dates and numbers
+      query[key] = {};
+      if (!_.isUndefined(val.gt)){
+        query[key].$gt = val.gt;
+      }
+      if (!_.isUndefined(val.gte)){
+        query[key].$gte = val.gte;
+      }
+      if (!_.isUndefined(val.lt)){
+        query[key].$lt = val.lt;
+      }
+      if (!_.isUndefined(val.lte)){
+        query[key].$lte = val.lte;
+      }
+    }else if ((model.schema.tree[key] === String || model.schema.tree[key].type === String) && _.isObject(val)){
+      //regex for strings
+      query[key] = {
+        $regex: val.regex ? val.regex : '',
+        $options: val.options ? val.options : ''
+      };
+    }
   });
+  }
 
   if(_.isObject(query)){
     if(_.isArray(query)) {
diff --git a/test/app.js b/test/app.js
index c6e204c35..5745e2a15 100644
--- a/test/app.js
+++ b/test/app.js
@@ -66,6 +66,7 @@ module.exports = function(options, port) {
       official: String,
       password: String,
       appearances: Number,
+      birthday: Date,
       email: String,
       pets: ['pet'],
       soulmate: {ref: 'person', inverse: 'soulmate', type: String},
diff --git a/test/fixtures.json b/test/fixtures.json
index 43c7117bc..5cb0cd0d2 100644
--- a/test/fixtures.json
+++ b/test/fixtures.json
@@ -3,16 +3,19 @@
     {
       "name": "Dilbert",
       "appearances": 3457,
+      "birthday": "1990-01-01",
       "email": "dilbert@mailbert.com"
     },
     {
       "name": "Wally",
       "appearances": 1934,
+      "birthday": "1995-01-01",
       "email": "wally@mailbert.com"
     },
     {
       "name": "Robert",
       "appearances": 0,
+      "birthday": "2000-01-01",
       "email": "robert@mailbert.com"
     }
   ],
diff --git a/test/fortune-mongodb/mongodb.spec.js b/test/fortune-mongodb/mongodb.spec.js
index e18674f46..f43283011 100644
--- a/test/fortune-mongodb/mongodb.spec.js
+++ b/test/fortune-mongodb/mongodb.spec.js
@@ -11,7 +11,7 @@ RSVP.on('error', function(err){
 });
 
 module.exports = function(options){
-  describe('MongoDB adapter', function(){
+  describe.only('MongoDB adapter', function(){
     var ids;
 
     beforeEach(function(){
@@ -115,7 +115,7 @@ module.exports = function(options){
               //hooks add their black magic here.
               //See what you have in fixtures + what beforeWrite hooks assign in addiction
               var keys = Object.keys(docs[0]).length;
-              (keys).should.equal(6);
+              (keys).should.equal(7);
               done();
             });
         });
@@ -172,7 +172,7 @@ module.exports = function(options){
             .then(function(doc){
               //hooks add their black magic here.
               //See what you have in fixtures + what beforeWrite hooks assign in addiction
-              (Object.keys(doc).length).should.equal(6);
+              (Object.keys(doc).length).should.equal(7);
               done();
             });
         });
@@ -192,6 +192,119 @@ module.exports = function(options){
         });
       });
     });
+    describe('Filtering', function(){
+      it('should be able to filter date by exact value', function(done){
+        adapter.findMany('person', {birthday: '2000-01-01'})
+          .then(function(docs){
+            (docs.length).should.equal(1);
+            (docs[0].name).should.equal('Robert');
+            done();
+          });
+      });
+      it('should be able to filter date range: exclusive', function(done){
+        var query = {
+          birthday: {
+            lt: '2000-02-02',
+            gt: '1990-01-01'
+          }
+        };
+        adapter.findMany('person', query)
+          .then(function(docs){
+            (docs.length).should.equal(2);
+            done();
+          });
+      });
+      it('should be able to filter date range: inclusive', function(done){
+        var query = {
+          birthday: {
+            gte: '1995-01-01',
+            lte: '2000-01-01'
+          }
+        };
+        adapter.findMany('person', query)
+          .then(function(docs){
+            (docs.length).should.equal(2);
+            done();
+          });
+      });
+      it('should be able to filter number range: exclusive', function(done){
+        var query = {
+          appearances: {
+            gt: 1934,
+            lt: 4000
+          }
+        };
+        adapter.findMany('person', query)
+          .then(function(docs){
+            (docs.length).should.equal(1);
+            done();
+          });
+      });
+      it('should be able to filter number range: inclusive', function(done){
+        var query = {
+          appearances: {
+            gte: 1934,
+            lte: 3457
+          }
+        };
+        adapter.findMany('person', query)
+          .then(function(docs){
+            (docs.length).should.equal(2);
+            done();
+          });
+      });
+      it('should be able to run regex query with default options', function(done){
+        var queryLowercase = {
+          email: {
+            regex: 'bert@'
+          }
+        };
+        var queryUppercase = {
+          email: {
+            regex: 'Bert@'
+          }
+        };
+        new Promise(function(resolve){
+          adapter.findMany('person', queryLowercase)
+            .then(function(docs){
+              (docs.length).should.equal(2);
+              resolve();
+            });
+        }).then(function(){
+            adapter.findMany('person',queryUppercase)
+              .then(function(docs){
+                (docs.length).should.equal(0);
+                done();
+              });
+          });
+      });
+      it('should be possible to specify custom options', function(done){
+        var query = {
+          name: {
+            regex: 'WALLY',
+            options: 'i'
+          }
+        };
+        adapter.findMany('person', query)
+          .then(function(docs){
+            (docs.length).should.equal(1);
+            (docs[0].name).should.equal('Wally');
+            done();
+          });
+      });
+      it('should treat empty regex as find all', function(done){
+        var query = {
+          email: {
+            regex: ''
+          }
+        };
+        adapter.findMany('person', query)
+          .then(function(docs){
+            (docs.length).should.equal(3);
+            done();
+          });
+      });
+    });
   });
 
 };
\ No newline at end of file