Merge pull request #144 from strongloop/feature/add-update
Feature/add update
This commit is contained in:
commit
86073cb480
|
@ -363,8 +363,40 @@ function applyFilter(filter) {
|
||||||
return pass;
|
return pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toRegExp(pattern) {
|
||||||
|
if (pattern instanceof RegExp) {
|
||||||
|
return pattern;
|
||||||
|
}
|
||||||
|
var regex = '';
|
||||||
|
// Escaping user input to be treated as a literal string within a regular expression
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Writing_a_Regular_Expression_Pattern
|
||||||
|
pattern = pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
|
||||||
|
for (var i = 0, n = pattern.length; i < n; i++) {
|
||||||
|
var char = pattern.charAt(i);
|
||||||
|
if (char === '\\') {
|
||||||
|
i++; // Skip to next char
|
||||||
|
if (i < n) {
|
||||||
|
regex += pattern.charAt(i);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if (char === '%') {
|
||||||
|
regex += '.*';
|
||||||
|
} else if (char === '_') {
|
||||||
|
regex += '.';
|
||||||
|
} else if (char === '.') {
|
||||||
|
regex += '\\.';
|
||||||
|
} else if (char === '*') {
|
||||||
|
regex += '\\*';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
regex += char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return regex;
|
||||||
|
}
|
||||||
|
|
||||||
function test(example, value) {
|
function test(example, value) {
|
||||||
if (typeof value === 'string' && example && example.constructor.name === 'RegExp') {
|
if (typeof value === 'string' && (example instanceof RegExp)) {
|
||||||
return value.match(example);
|
return value.match(example);
|
||||||
}
|
}
|
||||||
if (example === undefined || value === undefined) {
|
if (example === undefined || value === undefined) {
|
||||||
|
@ -386,6 +418,21 @@ function applyFilter(filter) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (example.like || example.nlike) {
|
||||||
|
|
||||||
|
var like = example.like || example.nlike;
|
||||||
|
if (typeof like === 'string') {
|
||||||
|
like = toRegExp(like);
|
||||||
|
}
|
||||||
|
if (example.like) {
|
||||||
|
return !!new RegExp(like).test(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (example.nlike) {
|
||||||
|
return !new RegExp(like).test(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (testInEquality(example, value)) {
|
if (testInEquality(example, value)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -476,6 +523,29 @@ Memory.prototype.count = function count(model, callback, where) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Memory.prototype.update =
|
||||||
|
Memory.prototype.updateAll = function updateAll(model, where, data, cb) {
|
||||||
|
var self = this;
|
||||||
|
var cache = this.cache[model];
|
||||||
|
var filter = null;
|
||||||
|
where = where || {};
|
||||||
|
filter = applyFilter({where: where});
|
||||||
|
|
||||||
|
var ids = Object.keys(cache);
|
||||||
|
async.each(ids, function (id, done) {
|
||||||
|
var inst = self.fromDb(model, cache[id]);
|
||||||
|
if (!filter || filter(inst)) {
|
||||||
|
self.updateAttributes(model, id, data, done);
|
||||||
|
} else {
|
||||||
|
process.nextTick(done);
|
||||||
|
}
|
||||||
|
}, function (err) {
|
||||||
|
if (!err) {
|
||||||
|
self.saveToFile(null, cb);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Memory.prototype.updateAttributes = function updateAttributes(model, id, data, cb) {
|
Memory.prototype.updateAttributes = function updateAttributes(model, id, data, cb) {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
var err = new Error('You must provide an id when updating attributes!');
|
var err = new Error('You must provide an id when updating attributes!');
|
||||||
|
|
65
lib/dao.js
65
lib/dao.js
|
@ -19,6 +19,7 @@ var utils = require('./utils');
|
||||||
var fieldsToArray = utils.fieldsToArray;
|
var fieldsToArray = utils.fieldsToArray;
|
||||||
var removeUndefined = utils.removeUndefined;
|
var removeUndefined = utils.removeUndefined;
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
var assert = require('assert');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for all persistent objects.
|
* Base class for all persistent objects.
|
||||||
|
@ -958,6 +959,70 @@ DataAccessObject.prototype.save = function (options, callback) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update multiple instances that match the where clause
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
*```js
|
||||||
|
* Employee.update({managerId: 'x001'}, {managerId: 'x002'}, function(err) {
|
||||||
|
* ...
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param {Object} [where] Search conditions (optional)
|
||||||
|
* @param {Object} data Changes to be made
|
||||||
|
* @param {Function} cb Callback, called with (err, count)
|
||||||
|
*/
|
||||||
|
DataAccessObject.update =
|
||||||
|
DataAccessObject.updateAll = function (where, data, cb) {
|
||||||
|
if (stillConnecting(this.getDataSource(), this, arguments)) return;
|
||||||
|
|
||||||
|
if (arguments.length === 1) {
|
||||||
|
// update(data) is being called
|
||||||
|
data = where;
|
||||||
|
where = null;
|
||||||
|
cb = null;
|
||||||
|
} else if (arguments.length === 2) {
|
||||||
|
if (typeof data === 'function') {
|
||||||
|
// update(data, cb) is being called
|
||||||
|
cb = data;
|
||||||
|
data = where;
|
||||||
|
where = null;
|
||||||
|
} else {
|
||||||
|
// update(where, data) is being called
|
||||||
|
cb = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(typeof where === 'object', 'The where argument should be an object');
|
||||||
|
assert(typeof data === 'object', 'The data argument should be an object');
|
||||||
|
assert(cb === null || typeof cb === 'function', 'The cb argument should be a function');
|
||||||
|
|
||||||
|
try {
|
||||||
|
where = removeUndefined(where);
|
||||||
|
where = this._coerce(where);
|
||||||
|
} catch (err) {
|
||||||
|
return process.nextTick(function () {
|
||||||
|
cb && cb(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var connector = this.getDataSource().connector;
|
||||||
|
connector.update(this.modelName, where, data, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
// updateAll ~ remoting attributes
|
||||||
|
setRemoting(DataAccessObject.updateAll, {
|
||||||
|
description: 'Update instances of the model matched by where from the data source',
|
||||||
|
accepts: [
|
||||||
|
{arg: 'where', type: 'object', http: {source: 'query'},
|
||||||
|
description: 'Criteria to match model instances'},
|
||||||
|
{arg: 'data', type: 'object', http: {source: 'body'},
|
||||||
|
description: 'An object of model property name/value pairs'},
|
||||||
|
],
|
||||||
|
http: {verb: 'post', path: '/update'}
|
||||||
|
});
|
||||||
|
|
||||||
DataAccessObject.prototype.isNewRecord = function () {
|
DataAccessObject.prototype.isNewRecord = function () {
|
||||||
return !getIdValue(this.constructor, this);
|
return !getIdValue(this.constructor, this);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// This test written in mocha+should.js
|
// This test written in mocha+should.js
|
||||||
var should = require('./init.js');
|
var should = require('./init.js');
|
||||||
|
var async = require('async');
|
||||||
var db, User;
|
var db, User;
|
||||||
|
|
||||||
describe('basic-querying', function () {
|
describe('basic-querying', function () {
|
||||||
|
@ -552,10 +553,43 @@ describe('basic-querying', function () {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('updateAll ', function () {
|
||||||
|
|
||||||
|
beforeEach(seed);
|
||||||
|
|
||||||
|
it('should only update instances that satisfy the where condition', function (done) {
|
||||||
|
User.update({name: 'John Lennon'}, {name: 'John Smith'}, function () {
|
||||||
|
User.find({where: {name: 'John Lennon'}}, function (err, data) {
|
||||||
|
should.not.exist(err);
|
||||||
|
data.length.should.equal(0);
|
||||||
|
User.find({where: {name: 'John Smith'}}, function (err, data) {
|
||||||
|
should.not.exist(err);
|
||||||
|
data.length.should.equal(1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update all instances without where', function (done) {
|
||||||
|
User.update({name: 'John Smith'}, function () {
|
||||||
|
User.find({where: {name: 'John Lennon'}}, function (err, data) {
|
||||||
|
should.not.exist(err);
|
||||||
|
data.length.should.equal(0);
|
||||||
|
User.find({where: {name: 'John Smith'}}, function (err, data) {
|
||||||
|
should.not.exist(err);
|
||||||
|
data.length.should.equal(6);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function seed(done) {
|
function seed(done) {
|
||||||
var count = 0;
|
|
||||||
var beatles = [
|
var beatles = [
|
||||||
{
|
{
|
||||||
seq: 0,
|
seq: 0,
|
||||||
|
@ -580,15 +614,11 @@ function seed(done) {
|
||||||
{seq: 4, name: 'Pete Best', order: 4},
|
{seq: 4, name: 'Pete Best', order: 4},
|
||||||
{seq: 5, name: 'Stuart Sutcliffe', order: 3, vip: true}
|
{seq: 5, name: 'Stuart Sutcliffe', order: 3, vip: true}
|
||||||
];
|
];
|
||||||
User.destroyAll(function () {
|
|
||||||
beatles.forEach(function (beatle) {
|
|
||||||
User.create(beatle, ok);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function ok() {
|
async.series([
|
||||||
if (++count === beatles.length) {
|
User.destroyAll.bind(User),
|
||||||
done();
|
function(cb) {
|
||||||
}
|
async.each(beatles, User.create.bind(User), cb);
|
||||||
}
|
}
|
||||||
|
], done);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ var path = require('path');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
var should = require('./init.js');
|
||||||
|
|
||||||
describe('Memory connector', function () {
|
describe('Memory connector', function () {
|
||||||
var file = path.join(__dirname, 'memory.json');
|
var file = path.join(__dirname, 'memory.json');
|
||||||
|
@ -91,5 +92,92 @@ describe('Memory connector', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Query for memory connector', function () {
|
||||||
|
var ds = new DataSource({
|
||||||
|
connector: 'memory'
|
||||||
|
});
|
||||||
|
|
||||||
|
var User = ds.define('User', {
|
||||||
|
seq: {type: Number, index: true},
|
||||||
|
name: {type: String, index: true, sort: true},
|
||||||
|
email: {type: String, index: true},
|
||||||
|
birthday: {type: Date, index: true},
|
||||||
|
role: {type: String, index: true},
|
||||||
|
order: {type: Number, index: true, sort: true},
|
||||||
|
vip: {type: Boolean}
|
||||||
|
});
|
||||||
|
|
||||||
|
before(seed);
|
||||||
|
it('should allow to find using like', function (done) {
|
||||||
|
User.find({where: {name: {like: '%St%'}}}, function (err, posts) {
|
||||||
|
should.not.exist(err);
|
||||||
|
posts.should.have.property('length', 2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support like for no match', function (done) {
|
||||||
|
User.find({where: {name: {like: 'M%XY'}}}, function (err, posts) {
|
||||||
|
should.not.exist(err);
|
||||||
|
posts.should.have.property('length', 0);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow to find using nlike', function (done) {
|
||||||
|
User.find({where: {name: {nlike: '%St%'}}}, function (err, posts) {
|
||||||
|
should.not.exist(err);
|
||||||
|
posts.should.have.property('length', 4);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support nlike for no match', function (done) {
|
||||||
|
User.find({where: {name: {nlike: 'M%XY'}}}, function (err, posts) {
|
||||||
|
should.not.exist(err);
|
||||||
|
posts.should.have.property('length', 6);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function seed(done) {
|
||||||
|
var beatles = [
|
||||||
|
{
|
||||||
|
seq: 0,
|
||||||
|
name: 'John Lennon',
|
||||||
|
email: 'john@b3atl3s.co.uk',
|
||||||
|
role: 'lead',
|
||||||
|
birthday: new Date('1980-12-08'),
|
||||||
|
order: 2,
|
||||||
|
vip: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
seq: 1,
|
||||||
|
name: 'Paul McCartney',
|
||||||
|
email: 'paul@b3atl3s.co.uk',
|
||||||
|
role: 'lead',
|
||||||
|
birthday: new Date('1942-06-18'),
|
||||||
|
order: 1,
|
||||||
|
vip: true
|
||||||
|
},
|
||||||
|
{seq: 2, name: 'George Harrison', order: 5, vip: false},
|
||||||
|
{seq: 3, name: 'Ringo Starr', order: 6, vip: false},
|
||||||
|
{seq: 4, name: 'Pete Best', order: 4},
|
||||||
|
{seq: 5, name: 'Stuart Sutcliffe', order: 3, vip: true}
|
||||||
|
];
|
||||||
|
|
||||||
|
async.series([
|
||||||
|
User.destroyAll.bind(User),
|
||||||
|
function(cb) {
|
||||||
|
async.each(beatles, User.create.bind(User), cb);
|
||||||
|
}
|
||||||
|
], done);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue