Skip to content

Commit c233c62

Browse files
committed
Merge pull request #17 from holidayextras/improved-non-string-filtering
Improved non-string filtering
2 parents b9d1f25 + ee96a3e commit c233c62

File tree

3 files changed

+77
-32
lines changed

3 files changed

+77
-32
lines changed

CHANGELOG.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
- 2015-06-29 - Initial release
2-
- 2015-12-21 - Pagination support
3-
- 2015-12-21 - Sort support
4-
- 2015-12-21 - v1.0.0
5-
- 2015-12-30 - Offload queries to Mongo
6-
- 2015-12-30 - v1.1.0
7-
- 2016-01-21 - Robust MongoDB connection management
1+
- 2016-02-17 - v1.2.1
2+
- 2016-02-17 - Improved filtering by non-string attributes
83
- 2016-01-21 - v1.2.0
4+
- 2016-01-21 - Robust MongoDB connection management
5+
- 2015-12-30 - v1.1.0
6+
- 2015-12-30 - Offload queries to Mongo
7+
- 2015-12-21 - v1.0.0
8+
- 2015-12-21 - Sort support
9+
- 2015-12-21 - Pagination support
10+
- 2015-06-29 - Initial release

lib/mongoHandler.js

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ var _ = {
55
var async = require("async");
66
var debug = require("./debugging");
77
var mongodb = require("mongodb");
8-
8+
var Joi = require("joi");
99

1010
var MongoStore = module.exports = function MongoStore(config) {
1111
this._config = config;
1212
};
1313

1414

15+
var FILTER_OPERATORS = ["<", ">", "~", ":"];
16+
17+
1518
/**
1619
Handlers readiness status. This should be set to `true` once all handlers are ready to process requests.
1720
*/
@@ -48,13 +51,43 @@ MongoStore._getRelationshipAttributeNames = function(attributes) {
4851
};
4952

5053

54+
MongoStore._splitFilterElement = function(filterElementStr) {
55+
if (FILTER_OPERATORS.indexOf(filterElementStr[0]) !== -1) {
56+
return { operator: filterElementStr[0], value: filterElementStr.substring(1) };
57+
}
58+
return { operator: null, value: filterElementStr };
59+
};
60+
61+
62+
MongoStore._filterElementToMongoExpr = function(attributeConfig, filterElementStr) {
63+
var filterElement = MongoStore._splitFilterElement(filterElementStr);
64+
var value = filterElement.value;
65+
if (!attributeConfig._settings) { // not a relationship attribute
66+
var validationResult = attributeConfig.validate(filterElement.value);
67+
if (validationResult.error) return null;
68+
value = validationResult.value;
69+
}
70+
if (!filterElement.operator) return value;
71+
if (["~", ":"].indexOf(filterElement.operator) !== -1 && typeof value !== "string") {
72+
return null;
73+
}
74+
var mongoExpr = {
75+
">": { $gt: value },
76+
"<": { $lt: value },
77+
"~": new RegExp("^" + value + "$", "i"),
78+
":": new RegExp(value)
79+
}[filterElement.operator];
80+
return mongoExpr;
81+
};
82+
83+
5184
MongoStore.prototype._getSearchCriteria = function(request) {
5285
var self = this;
5386
if (!request.params.filter) return { };
5487

5588
var criteria = Object.keys(request.params.filter).map(function(attribute) {
5689
var attributeConfig = self.resourceConfig.attributes[attribute];
57-
// If the filter attribute doens't exist, skip it
90+
// If the filter attribute doesn't exist, skip it
5891
if (!attributeConfig) return null;
5992

6093
var values = request.params.filter[attribute];
@@ -65,28 +98,33 @@ MongoStore.prototype._getSearchCriteria = function(request) {
6598
if (values instanceof Object) return null;
6699
}
67100

68-
// Coerce values to an array to simplify the logic
69-
if (!(values instanceof Array)) values = [ values ];
70-
values = values.map(function(value) {
71-
if (value[0] === "<") return { $lt: value.substring(1) };
72-
if (value[0] === ">") return { $gt: value.substring(1) };
73-
if (value[0] === "~") return new RegExp("^" + value.substring(1) + "$", "i");
74-
if (value[0] === ":") return new RegExp(value.substring(1));
75-
return value;
76-
}).map(function(value) {
77-
var tmp = { };
78-
tmp[attribute] = value;
79-
return tmp;
80-
});
81-
101+
values = [].concat(values); // Coerce values to an array to simplify the logic
102+
var filterElemForAttrToMongoExpr = MongoStore._filterElementToMongoExpr.bind(null, attributeConfig);
103+
values = values.map(filterElemForAttrToMongoExpr);
104+
values = values.reduce(function(mongoExpressions, mongoExpr) {
105+
if (mongoExpr !== null) {
106+
var mongoExprForAttr = { };
107+
mongoExprForAttr[attribute] = mongoExpr;
108+
mongoExpressions.push(mongoExprForAttr);
109+
}
110+
return mongoExpressions;
111+
}, []);
112+
if (values.length === 0) {
113+
return null;
114+
}
115+
if (values.length === 1) {
116+
return values[0];
117+
}
82118
return { $or: values };
83119
}).filter(function(value) {
84120
return value !== null;
85121
});
86-
87122
if (criteria.length === 0) {
88123
return { };
89124
}
125+
if (criteria.length === 1) {
126+
return criteria[0];
127+
}
90128
return { $and: criteria };
91129
};
92130

@@ -192,7 +230,11 @@ MongoStore.prototype.populate = function(callback) {
192230
self._db.dropDatabase(function(err) {
193231
if (err) return console.error("error dropping database", err.message);
194232
async.each(self.resourceConfig.examples, function(document, cb) {
195-
self.create({ params: {} }, document, cb);
233+
var validationResult = Joi.validate(document, self.resourceConfig.attributes);
234+
if (validationResult.error) {
235+
return cb(validationResult.error);
236+
}
237+
self.create({ params: {} }, validationResult.value, cb);
196238
}, function(error) {
197239
if (error) console.error("error creating example document:", error);
198240
return callback();

package.json

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jsonapi-store-mongodb",
3-
"version": "1.2.0",
3+
"version": "1.2.1",
44
"description": "MongoDB data store for jsonapi-server.",
55
"main": "lib/mongoHandler.js",
66
"repository": {
@@ -27,19 +27,20 @@
2727
"dependencies": {
2828
"async": "1.5.0",
2929
"debug": "2.2.0",
30+
"joi": "6.10.1",
3031
"lodash.omit": "3.1.0",
3132
"mongodb": "2.0.48"
3233
},
3334
"devDependencies": {
34-
"mocha": "2.2.5",
35-
"mysql": "2.9.0",
36-
"eslint": "0.24.1",
3735
"blanket": "1.1.7",
38-
"mocha-lcov-reporter": "0.0.2",
3936
"coveralls": "2.11.2",
40-
"plato": "1.5.0",
37+
"eslint": "0.24.1",
38+
"jsonapi-server": "1.3.2",
39+
"mocha": "2.2.5",
40+
"mocha-lcov-reporter": "0.0.2",
4141
"mocha-performance": "0.1.0",
42-
"jsonapi-server": "1.0.3"
42+
"plato": "1.5.0",
43+
"v8-profiler": "^5.6.0"
4344
},
4445
"scripts": {
4546
"test": "./node_modules/mocha/bin/mocha --timeout 20000 -R spec ./test/*.js",

0 commit comments

Comments
 (0)