Skip to content

Commit

Permalink
Add REGEXP to mysql & sqlite3 adapters
Browse files Browse the repository at this point in the history
  • Loading branch information
biggora committed May 3, 2013
1 parent 522f232 commit a4c2e82
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 133 deletions.
218 changes: 114 additions & 104 deletions lib/abstract-class.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ AbstractClass.create = function (data, callback) {
obj.trigger('create', function (done) {

var data = this.toObject(true); // Added this to fix the beforeCreate trigger not fire.
// The fix is per issue #72 and the fix was found by by5739.
// The fix is per issue #72 and the fix was found by by5739.

this._adapter().create(modelName, this.constructor._forDB(data), function (err, id, rev) {
if (id) {
Expand Down Expand Up @@ -271,22 +271,24 @@ AbstractClass.upsert = AbstractClass.updateOrCreate = function upsert(data, call
*/
AbstractClass.findOrCreate = function findOrCreate(query, data, callback) {
if (typeof query === 'undefined') {
query = {where: {}};
}
if (typeof data === 'function' || typeof data === 'undefined') {
callback = data;
data = query && query.where;
}
if (typeof callback === 'undefined') {
callback = function () {};
}
query = {
where: {}
};
}
if (typeof data === 'function' || typeof data === 'undefined') {
callback = data;
data = query && query.where;
}
if (typeof callback === 'undefined') {
callback = function () {};
}

var t = this;
this.findOne(query, function (err, record) {
if (err) return callback(err);
if (record) return callback(null, record);
t.create(data, callback);
});
var t = this;
this.findOne(query, function (err, record) {
if (err) return callback(err);
if (record) return callback(null, record);
t.create(data, callback);
});
};

/**
Expand Down Expand Up @@ -503,69 +505,73 @@ AbstractClass.include = function (objects, include, callback) {
}
var relation = relations[relationName];

var req = {'where': {}};
var req = {
'where': {}
};

if (!keyVals[relation.keyFrom]) {
objsByKeys[relation.keyFrom] = {};
for (var j = 0; j < objs.length; j++) {
if (!objsByKeys[relation.keyFrom][objs[j][relation.keyFrom]]) {
objsByKeys[relation.keyFrom][objs[j][relation.keyFrom]] = [];
}
objsByKeys[relation.keyFrom][objs[j][relation.keyFrom]].push(objs[j]);
if (!keyVals[relation.keyFrom]) {
objsByKeys[relation.keyFrom] = {};
for (var j = 0; j < objs.length; j++) {
if (!objsByKeys[relation.keyFrom][objs[j][relation.keyFrom]]) {
objsByKeys[relation.keyFrom][objs[j][relation.keyFrom]] = [];
}
keyVals[relation.keyFrom] = Object.keys(objsByKeys[relation.keyFrom]);
objsByKeys[relation.keyFrom][objs[j][relation.keyFrom]].push(objs[j]);
}
keyVals[relation.keyFrom] = Object.keys(objsByKeys[relation.keyFrom]);
}

if (keyVals[relation.keyFrom].length > 0) {
// deep clone is necessary since inq seems to change the processed array
var keysToBeProcessed = {};
var inValues = [];
for (var f = 0; f < keyVals[relation.keyFrom].length; f++) {
keysToBeProcessed[keyVals[relation.keyFrom][f]] = true;
if (keyVals[relation.keyFrom][f] !== 'null') {
inValues.push(keyVals[relation.keyFrom][f]);
}
if (keyVals[relation.keyFrom].length > 0) {
// deep clone is necessary since inq seems to change the processed array
var keysToBeProcessed = {};
var inValues = [];
for (var f = 0; f < keyVals[relation.keyFrom].length; f++) {
keysToBeProcessed[keyVals[relation.keyFrom][f]] = true;
if (keyVals[relation.keyFrom][f] !== 'null') {
inValues.push(keyVals[relation.keyFrom][f]);
}
}

req['where'][relation.keyTo] = {inq: inValues};
req['include'] = subInclude;

return function(clbk) {
relation.modelTo.all(req, function(err, objsIncluded) {
for (var i = 0; i < objsIncluded.length; i++) {
delete keysToBeProcessed[objsIncluded[i][relation.keyTo]];
var objectsFrom = objsByKeys[relation.keyFrom][objsIncluded[i][relation.keyTo]];
for (var j = 0; j < objectsFrom.length; j++) {
if (!objectsFrom[j].__cachedRelations) {
objectsFrom[j].__cachedRelations = {};
}
if (relation.multiple) {
if (!objectsFrom[j].__cachedRelations[relationName]) {
objectsFrom[j].__cachedRelations[relationName] = [];
}
objectsFrom[j].__cachedRelations[relationName].push(objsIncluded[i]);
} else {
objectsFrom[j].__cachedRelations[relationName] = objsIncluded[i];
req['where'][relation.keyTo] = {
inq: inValues
};
req['include'] = subInclude;

return function(clbk) {
relation.modelTo.all(req, function(err, objsIncluded) {
for (var i = 0; i < objsIncluded.length; i++) {
delete keysToBeProcessed[objsIncluded[i][relation.keyTo]];
var objectsFrom = objsByKeys[relation.keyFrom][objsIncluded[i][relation.keyTo]];
for (var j = 0; j < objectsFrom.length; j++) {
if (!objectsFrom[j].__cachedRelations) {
objectsFrom[j].__cachedRelations = {};
}
if (relation.multiple) {
if (!objectsFrom[j].__cachedRelations[relationName]) {
objectsFrom[j].__cachedRelations[relationName] = [];
}
objectsFrom[j].__cachedRelations[relationName].push(objsIncluded[i]);
} else {
objectsFrom[j].__cachedRelations[relationName] = objsIncluded[i];
}
}
}

// No relation have been found for these keys
for (var key in keysToBeProcessed) {
var objectsFromRelation = objsByKeys[relation.keyFrom][key];
for (var n = 0; n < objectsFromRelation.length; n++) {
if (!objectsFromRelation[n].__cachedRelations) {
objectsFromRelation[n].__cachedRelations = {};
}
objectsFromRelation[n].__cachedRelations[relationName] = relation.multiple ? [] : null;
// No relation have been found for these keys
for (var key in keysToBeProcessed) {
var objectsFromRelation = objsByKeys[relation.keyFrom][key];
for (var n = 0; n < objectsFromRelation.length; n++) {
if (!objectsFromRelation[n].__cachedRelations) {
objectsFromRelation[n].__cachedRelations = {};
}
objectsFromRelation[n].__cachedRelations[relationName] = relation.multiple ? [] : null;
}
clbk(err, objsIncluded);
});
};
}
return null;
}
clbk(err, objsIncluded);
});
};
}
return null;
}
}

/**
Expand Down Expand Up @@ -900,7 +906,9 @@ AbstractClass.hasMany = function hasMany(anotherClass, params) {
defineScope(this.prototype, anotherClass, methodName, function () {
var x = {};
x[fk] = this.id;
return {where: x};
return {
where: x
};
}, {
find: find,
destroy: destroy
Expand Down Expand Up @@ -1109,55 +1117,57 @@ function defineScope(cls, targetClass, name, params, methods) {

// and it should have create/build methods with binded thisModelNameId param
function build(data) {
return new targetClass(mergeParams(this._scope, {where:data || {}}).where);
}
return new targetClass(mergeParams(this._scope, {
where:data || {}
}).where);
}

function create(data, cb) {
if (typeof data === 'function') {
cb = data;
data = {};
}
this.build(data).save(cb);
function create(data, cb) {
if (typeof data === 'function') {
cb = data;
data = {};
}
this.build(data).save(cb);
}

/*
/*
Callback
- The callback will be called after all elements are destroyed
- For every destroy call which results in an error
- If fetching the Elements on which destroyAll is called results in an error
*/
function destroyAll(callback) {
targetClass.all(this._scope, function (err, data) {
if (err) {
cb(err);
} else {
(function loopOfDestruction (data) {
if(data.length > 0) {
data.shift().destroy(function(err) {
if(err && callback) callback(err);
loopOfDestruction(data);
});
} else {
if(callback) callback();
}
}(data));
}
});
}

function mergeParams(base, update) {
if (update.where) {
base.where = merge(base.where, update.where);
}

// overwrite order
if (update.order) {
base.order = update.order;
function destroyAll(callback) {
targetClass.all(this._scope, function (err, data) {
if (err) {
cb(err);
} else {
(function loopOfDestruction (data) {
if(data.length > 0) {
data.shift().destroy(function(err) {
if(err && callback) callback(err);
loopOfDestruction(data);
});
} else {
if(callback) callback();
}
}(data));
}
});
}

return base;
function mergeParams(base, update) {
if (update.where) {
base.where = merge(base.where, update.where);
}

// overwrite order
if (update.order) {
base.order = update.order;
}

return base;

}
}

AbstractClass.prototype.inspect = function () {
Expand Down
18 changes: 14 additions & 4 deletions lib/adapters/mysql.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,14 @@ MySQL.prototype.all = function all(model, filter, callback) {

if (filter) {

if (filter.fields) {
var fields = [];
filter.fields.forEach(function(field){
fields.push('`' + field + '`');
})
sql = sql.replace(/\*/g,fields.join(', '));
}

if (filter.where) {
sql += ' ' + buildWhere(filter.where);
}
Expand All @@ -231,9 +239,7 @@ MySQL.prototype.all = function all(model, filter, callback) {

return sql;

function buildWhere(conds) {
var cs = [];
Object.keys(conds).forEach(function (key) {
Object.keys(conds).forEach(function (key) {
var keyEscaped = '`' + key.replace(/\./g, '`.`') + '`'
var val = self.toDatabase(props[key], conds[key]);
if (conds[key] === null) {
Expand Down Expand Up @@ -270,11 +276,15 @@ MySQL.prototype.all = function all(model, filter, callback) {
case 'neq':
sqlCond += ' != ';
break;
case 'regexp':
sqlCond += ' REGEXP ';
break;
}
sqlCond += (condType == 'inq' || condType == 'nin') ? '(' + val + ')' : val;
cs.push(sqlCond);
} else if (/^\//gi.test(conds[key])) {
cs.push(keyEscaped + ' REGEXP ' + val.replace(/\//gi,""));
var reg = val.toString().split('/');
cs.push(keyEscaped + ' REGEXP "' + reg[1] + '"');
} else {
cs.push(keyEscaped + ' = ' + val);
}
Expand Down
Loading

0 comments on commit a4c2e82

Please sign in to comment.