Skip to content

Commit

Permalink
Merge pull request #44 from flyvictor/OR-AND-filters
Browse files Browse the repository at this point in the history
OR and AND filters
  • Loading branch information
mjtodd committed Jul 10, 2014
2 parents e9f8450 + 6dfe11f commit 83c7380
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 49 deletions.
84 changes: 48 additions & 36 deletions lib/adapters/mongodb.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,50 +272,62 @@ adapter.findMany = function(model, query, projection) {

var pk = model.pk || "_id";

_.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){
function parseQuery(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){
query[key] = {
$gte: val,
$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] = _.reduce(val, function(memo, opVal, op){
memo[{ "gt": "$gt", "gte": "$gte", "lt": "$lt", "lte": "$lte" }[op] || op] = opVal;
return memo;
}, {});
}else if (_.isString(val.in || val.$in)){
query[key] = {
$in: (val.in || val.$in).split(',')
};
}else if (_.isObject(val) && _.isString(val.regex)){
//regex
query[key] = {
$gte: val,
$lte: moment(val).add("days", 1).format("YYYY-MM-DD")
$regex: val.regex ? val.regex : '',
$options: val.options ? val.options : ''
};
}else if(key === 'or' || key === 'and'){
query['$' + key] = _.map(val, parseQuery);
delete query[key];
}
}else if ((model.schema.tree[key] === Date || model.schema.tree[key] === Number) && _.isObject(val)){
//gt/gte/lt/lte for dates and numbers
query[key] = _.reduce(val, function(memo, opVal, op){
memo[{ "gt": "$gt", "gte": "$gte", "lt": "$lt", "lte": "$lte" }[op] || op] = opVal;
return memo;
}, {});
}else if (_.isString(val.in || val.$in)){
query[key] = {
$in: (val.in || val.$in).split(',')
};
}else if (_.isObject(val) && _.isString(val.regex)){
//regex
query[key] = {
$regex: val.regex ? val.regex : '',
$options: val.options ? val.options : ''
};
}
});
});

if(_.isObject(query)){
if(_.isArray(query)) {
if(query.length) dbQuery[pk] = {$in: query};
}else{
dbQuery = _.clone(query);

deepReplaceFalsies(dbQuery);
if(_.isObject(query)){
if(_.isArray(query)) {
if(query.length) dbQuery[pk] = {$in: query};
}else{
dbQuery = _.clone(query);

deepReplaceFalsies(dbQuery);

if(query.id){
dbQuery[pk] = query.id;
delete dbQuery.id;
if(query.id){
dbQuery[pk] = query.id;
delete dbQuery.id;
}
}
}

return dbQuery;
}

if (_.isObject(query)){
query = parseQuery(query);
}else if(typeof query === 'number' && arguments.length === 2){
//Just for possible backward compatibility issues
projection = projection || {};
Expand Down
2 changes: 1 addition & 1 deletion lib/fortune.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ Fortune.prototype.resource = function(name, schema, options, schemaCallback) {
* @return {Object}
*/
Fortune.prototype._preprocessSchema = function (schema) {
['id', 'href', 'links', 'in'].forEach(function (reservedKey) {
['id', 'href', 'links', 'in', 'or', 'and'].forEach(function (reservedKey) {
if (schema.hasOwnProperty(reservedKey)) {
delete schema[reservedKey];
console.warn('Reserved key "' + reservedKey + '" is not allowed.');
Expand Down
12 changes: 9 additions & 3 deletions lib/querytree.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,15 @@ function parseQuery(query, requestedResource){
//String ref
freeze[key] = createSubrequest(q, schemaBranch);
}else{
//Plain field
//Do nothing. fetchIds should have this untouched
freeze[key] = q;
if (key === 'or' || key === 'and' || key === '$and' || key === '$or'){
freeze[key] = RSVP.all(_.map(q, function(subq){
return exports.parse(requestedResource, subq);
}));
}else{
//Plain field
//Do nothing. fetchIds should have this untouched
freeze[key] = q;
}
}
});
return RSVP.hash(freeze);
Expand Down
58 changes: 58 additions & 0 deletions test/fortune/fields_and_filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,28 @@ module.exports = function(options){
done();
});
});
it('should support or query', function(done){
request(baseUrl).get('/people?filter[or][0][name]=Dilbert&filter[or][1][email][email protected]&sort=name')
.expect(200)
.end(function(err, res){
should.not.exist(err);
var body = JSON.parse(res.text);
(body.people.length).should.equal(2);
(body.people[0].name).should.equal('Dilbert');
(body.people[1].name).should.equal('Robert');
done();
});
});
it('should have id filter', function(done){
request(baseUrl).get('/cars?filter[id]=' + ids.cars[0])
.expect(200)
.end(function(err, res){
should.not.exist(err);
var body = JSON.parse(res.text);
(body.cars[0].id).should.equal(ids.cars[0]);
done();
});
});
describe('filtering by related objects fields', function(){
beforeEach(function(done){
neighbourhood(adapter, ids).then(function(){
Expand Down Expand Up @@ -362,6 +384,42 @@ module.exports = function(options){
});
});
});
it('should be able to apply OR filter to related resources', function(done){
request(baseUrl).get('/cars?filter[or][0][owner][soulmate]=' + ids.people[0] + '&filter[or][1][id]=' + ids.cars[0])
.expect(200)
.end(function(err, res){
should.not.exist(err);
var body = JSON.parse(res.text);
(body.cars.length).should.equal(2);
var one = _.findWhere(body.cars, {id: ids.cars[0]});
var two = _.findWhere(body.cars, {id: ids.cars[1]});
should.exist(one);
should.exist(two);
done();
});
});
it('should be able to apply AND filter to related resources', function(done){
request(baseUrl).get('/pets?filter[and][0][owner][soulmate][email]=' + ids.people[0] + '&filter[and][1][owner][email]=' + ids.people[1])
.expect(200)
.end(function(err, res){
should.not.exist(err);
var body = JSON.parse(res.text);
(body.pets.length).should.equal(1);
(body.pets[0].id).should.equal(ids.pets[0]);
done();
})
});
it('should be able to nest OR and AND filters', function(done){
request(baseUrl).get('/houses?filter[or][0][and][0][owners][in]=' + ids.people[0])
.expect(200)
.end(function(err, res){
should.not.exist(err);
var body = JSON.parse(res.text);
(body.houses.length).should.equal(1);
(body.houses[0].id).should.equal(ids.houses[0]);
done();
})
});
});
});

Expand Down
32 changes: 24 additions & 8 deletions test/fortune/hooks.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
var request = require('supertest');
var should = require('should');
var RSVP = require('rsvp');

module.exports = function(options){
var ids, app, baseUrl;
Expand Down Expand Up @@ -54,14 +55,29 @@ module.exports = function(options){
});
describe("native mongoose middleware", function(){
it("should be able to expose mongoose api to resources", function(done){
request(baseUrl).get("/houses/" + ids.houses[0])
.expect(200)
.end(function(err, res){
should.not.exist(err);
var body = JSON.parse(res.text);
(body.houses[0].address).should.match(/mongoosed$/);
done();
});
new RSVP.Promise(function(resolve){
request(baseUrl).post("/houses")
.set("content-type", "application/json")
.send(JSON.stringify({
houses: [{
address: "mongoose-"
}]
}))
.end(function(err, res){
should.not.exist(err);
var body = JSON.parse(res.text);
resolve(body.houses[0].id);
});
}).then(function(createdId){
request(baseUrl).get("/houses/" + createdId)
.expect(200)
.end(function(err, res){
should.not.exist(err);
var body = JSON.parse(res.text);
(body.houses[0].address).should.match(/mongoosed$/);
done();
});
});
});
});
};
2 changes: 1 addition & 1 deletion test/mongoose_middleware.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

module.exports = function(schema, options){
var getter = function(v){
return v += 'mongoosed';
return /^mongoose-$/.test(v) ? v += 'mongoosed' : v;
};
var paths = options.paths;
schema.pre('init', function(next, doc){
Expand Down

0 comments on commit 83c7380

Please sign in to comment.