Add support for regex operator
This commit is contained in:
parent
01af886c7d
commit
b8f1598723
|
@ -350,9 +350,8 @@ Memory.prototype.all = function all(model, filter, options, callback) {
|
|||
}
|
||||
|
||||
// do we need some filtration?
|
||||
if (filter.where) {
|
||||
nodes = nodes ? nodes.filter(applyFilter(filter)) : nodes;
|
||||
}
|
||||
if (filter.where && nodes)
|
||||
nodes = nodes.filter(applyFilter(filter));
|
||||
|
||||
// field selection
|
||||
if (filter.fields) {
|
||||
|
@ -401,7 +400,7 @@ function applyFilter(filter) {
|
|||
var keys = Object.keys(where);
|
||||
return function (obj) {
|
||||
var pass = true;
|
||||
keys.forEach(function (key) {
|
||||
keys.forEach(function(key) {
|
||||
if(key === 'and' || key === 'or') {
|
||||
if(Array.isArray(where[key])) {
|
||||
if(key === 'and') {
|
||||
|
@ -461,6 +460,10 @@ function applyFilter(filter) {
|
|||
if (typeof value === 'string' && (example instanceof RegExp)) {
|
||||
return value.match(example);
|
||||
}
|
||||
|
||||
if (example.regexp)
|
||||
return value.match(example.regexp);
|
||||
|
||||
if (example === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
|
16
lib/dao.js
16
lib/dao.js
|
@ -939,7 +939,8 @@ var operators = {
|
|||
nin: 'NOT IN',
|
||||
neq: '!=',
|
||||
like: 'LIKE',
|
||||
nlike: 'NOT LIKE'
|
||||
nlike: 'NOT LIKE',
|
||||
regexp: 'REGEXP'
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1169,6 +1170,13 @@ DataAccessObject._coerce = function (where) {
|
|||
throw err;
|
||||
}
|
||||
break;
|
||||
case 'regexp':
|
||||
val = utils.toRegExp(val);
|
||||
if (val instanceof Error) {
|
||||
result.statusCode = 400;
|
||||
throw err;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1183,8 +1191,9 @@ DataAccessObject._coerce = function (where) {
|
|||
}
|
||||
} else {
|
||||
if (val != null) {
|
||||
if (!((operator === 'like' || operator === 'nlike') &&
|
||||
val instanceof RegExp)) {
|
||||
if (operator === 'regexp' && val instanceof RegExp) {
|
||||
// do not coerce regex literals/objects
|
||||
} else if (!((operator === 'like' || operator === 'nlike') && val instanceof RegExp)) {
|
||||
val = DataType(val);
|
||||
}
|
||||
}
|
||||
|
@ -1237,6 +1246,7 @@ DataAccessObject._coerce = function (where) {
|
|||
* - neq: !=
|
||||
* - like: LIKE
|
||||
* - nlike: NOT LIKE
|
||||
* - regexp: REGEXP
|
||||
*
|
||||
* You can also use `and` and `or` operations. See [Querying models](http://docs.strongloop.com/display/DOC/Querying+models) for more information.
|
||||
* @property {String|Object|Array} include Allows you to load relations of several objects and optimize numbers of requests.
|
||||
|
|
52
lib/utils.js
52
lib/utils.js
|
@ -12,6 +12,9 @@ exports.mergeQuery = mergeQuery;
|
|||
exports.mergeIncludes = mergeIncludes;
|
||||
exports.createPromiseCallback = createPromiseCallback;
|
||||
exports.uniq = uniq;
|
||||
exports.toRegExp = toRegExp;
|
||||
exports.hasRegExpFlags = hasRegExpFlags;
|
||||
exports.getRegExpExpression = getRegExpExpression;
|
||||
|
||||
var traverse = require('traverse');
|
||||
var assert = require('assert');
|
||||
|
@ -506,3 +509,52 @@ function uniq(a) {
|
|||
}
|
||||
return uniqArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string, regex literal, or a RegExp object to a RegExp object.
|
||||
* @param {String|Object} The string, regex literal, or RegExp object to convert
|
||||
* @returns {Object} A RegExp object
|
||||
*/
|
||||
function toRegExp(regex) {
|
||||
var isString = typeof regex === 'string';
|
||||
var isRegExp = regex instanceof RegExp;
|
||||
|
||||
if (!(isString || isRegExp))
|
||||
return new Error('Invalid argument, must be a string, regex literal, or ' +
|
||||
'RegExp object');
|
||||
|
||||
if (isRegExp)
|
||||
return regex;
|
||||
|
||||
if (!hasRegExpFlags(regex))
|
||||
return new RegExp(regex);
|
||||
|
||||
// only accept i, g, or m as valid regex flags
|
||||
var flags = regex.split('/').pop().split('');
|
||||
var validFlags = ['i', 'g', 'm'];
|
||||
var invalidFlags = [];
|
||||
flags.forEach(function(flag) {
|
||||
if (validFlags.indexOf(flag) === -1)
|
||||
invalidFlags.push(flag);
|
||||
});
|
||||
|
||||
var hasInvalidFlags = invalidFlags.length > 0;
|
||||
if (hasInvalidFlags)
|
||||
return new Error('Invalid regex flags: ' + invalidFlags);
|
||||
|
||||
// strip regex delimiter forward slashes
|
||||
var expression = regex.substr(1, regex.lastIndexOf('/') - 1);
|
||||
return new RegExp(expression, flags.join(''));
|
||||
}
|
||||
|
||||
function hasRegExpFlags(regex) {
|
||||
return regex instanceof RegExp ?
|
||||
regex.toString().split('/').pop() :
|
||||
!!regex.match(/.*\/.+$/);
|
||||
}
|
||||
|
||||
function getRegExpExpression(regex) {
|
||||
return regex instanceof RegExp ?
|
||||
regex.source :
|
||||
regex.split('/').shift();
|
||||
}
|
||||
|
|
|
@ -600,6 +600,24 @@ describe('basic-querying', function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
context('regexp operator', function() {
|
||||
var invalidDataTypes = [0, true, {}, [], Function, null];
|
||||
|
||||
before(seed);
|
||||
|
||||
it('should return an error for invalid data types', function(done) {
|
||||
// `undefined` is not tested because the `removeUndefined` function
|
||||
// in `lib/dao.js` removes it before coercion
|
||||
invalidDataTypes.forEach(function(invalidDataType) {
|
||||
User.find({where: {name: {regexp: invalidDataType}}}, function(err,
|
||||
users) {
|
||||
should.exist(err);
|
||||
});
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function seed(done) {
|
||||
|
|
|
@ -356,6 +356,34 @@ describe('Memory connector', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should support the regexp operator with regex strings', function(done) {
|
||||
User.find({where: {name: {regexp: '^J'}}}, function(err, users) {
|
||||
should.not.exist(err);
|
||||
users.length.should.equal(1);
|
||||
users[0].name.should.equal('John Lennon');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should support the regexp operator with regex literals', function(done) {
|
||||
User.find({where: {name: {regexp: /^J/}}}, function(err, users) {
|
||||
should.not.exist(err);
|
||||
users.length.should.equal(1);
|
||||
users[0].name.should.equal('John Lennon');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should support the regexp operator with regex objects', function(done) {
|
||||
User.find({where: {name: {regexp: new RegExp(/^J/)}}}, function(err,
|
||||
users) {
|
||||
should.not.exist(err);
|
||||
users.length.should.equal(1);
|
||||
users[0].name.should.equal('John Lennon');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should support nested property in query', function(done) {
|
||||
User.find({where: {'address.city': 'San Jose'}}, function(err, users) {
|
||||
should.not.exist(err);
|
||||
|
|
|
@ -420,3 +420,111 @@ describe('util.uniq', function() {
|
|||
});
|
||||
|
||||
});
|
||||
|
||||
describe('util.toRegExp', function() {
|
||||
var invalidDataTypes;
|
||||
var validDataTypes;
|
||||
|
||||
before(function() {
|
||||
invalidDataTypes = [0, true, {}, [], Function, null];
|
||||
validDataTypes = ['string', /^regex/, new RegExp(/^regex/)];
|
||||
});
|
||||
|
||||
it('should not accept invalid data types', function() {
|
||||
invalidDataTypes.forEach(function(invalid) {
|
||||
utils.toRegExp(invalid).should.be.an.Error;
|
||||
});
|
||||
});
|
||||
|
||||
it('should accept valid data types', function() {
|
||||
validDataTypes.forEach(function(valid) {
|
||||
utils.toRegExp(valid).should.not.be.an.Error;
|
||||
});
|
||||
});
|
||||
|
||||
context('with a regex string', function() {
|
||||
it('should return a RegExp object when no regex flags are provided',
|
||||
function() {
|
||||
utils.toRegExp('^regex$').should.be.an.instanceOf(RegExp);
|
||||
});
|
||||
|
||||
it('should throw an error when invalid regex flags are provided',
|
||||
function() {
|
||||
utils.toRegExp('^regex$/abc').should.be.an.Error;
|
||||
});
|
||||
|
||||
it('should return a RegExp object when valid flags are provided',
|
||||
function() {
|
||||
utils.toRegExp('regex/igm').should.be.an.instanceOf(RegExp);
|
||||
});
|
||||
});
|
||||
|
||||
context('with a regex literal', function() {
|
||||
it('should return a RegExp object', function() {
|
||||
utils.toRegExp(/^regex$/igm).should.be.an.instanceOf(RegExp);
|
||||
});
|
||||
});
|
||||
|
||||
context('with a regex object', function() {
|
||||
it('should return a RegExp object', function() {
|
||||
utils.toRegExp(new RegExp('^regex$', 'igm')).should.be.an.instanceOf(RegExp);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('util.hasRegExpFlags', function() {
|
||||
context('with a regex string', function() {
|
||||
it('should be true when the regex has invalid flags', function() {
|
||||
utils.hasRegExpFlags('^regex$/abc').should.be.ok;
|
||||
});
|
||||
|
||||
it('should be true when the regex has valid flags', function() {
|
||||
utils.hasRegExpFlags('^regex$/igm').should.be.ok;
|
||||
});
|
||||
|
||||
it('should be false when the regex has no flags', function() {
|
||||
utils.hasRegExpFlags('^regex$').should.not.be.ok;
|
||||
utils.hasRegExpFlags('^regex$/').should.not.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
context('with a regex literal', function() {
|
||||
it('should be true when the regex has valid flags', function() {
|
||||
utils.hasRegExpFlags(/^regex$/igm).should.be.ok;
|
||||
});
|
||||
|
||||
it('should be false when the regex has no flags', function() {
|
||||
utils.hasRegExpFlags(/^regex$/).should.not.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
context('with a regex object', function() {
|
||||
it('should be true when the regex has valid flags', function() {
|
||||
utils.hasRegExpFlags(new RegExp(/^regex$/igm)).should.be.ok;
|
||||
});
|
||||
|
||||
it('should be false when the regex has no flags', function() {
|
||||
utils.hasRegExpFlags(new RegExp(/^regex$/)).should.not.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('util.getRegExpExpression', function() {
|
||||
context('with a regex string', function() {
|
||||
it('should return the expression without flags', function() {
|
||||
utils.getRegExpExpression('^regex$/abc').should.equal('^regex$');
|
||||
});
|
||||
});
|
||||
|
||||
context('with a regex literal', function() {
|
||||
it('should return the expression without flags', function() {
|
||||
utils.hasRegExpFlags(/^regex$/igm).should.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
context('with a regex object', function() {
|
||||
it('should return the expression without flags', function() {
|
||||
utils.hasRegExpFlags(new RegExp(/^regex$/igm)).should.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue