Skip to content

Commit

Permalink
feat: update for mysql adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
XadillaX committed Oct 10, 2016
1 parent a1f411c commit 3191759
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 1 deletion.
82 changes: 82 additions & 0 deletions lib/adapters/mysql.js
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,88 @@ class MySQLAdapter extends Adapter {
});
}

update(model, pk, data, callback) {
if(!pk || !data) {
return process.nextTick(function() {
callback(new Error("Invalid parameters."));
});
}

const _set = data.reduce((_set, data) => {
if(data.value === null) {
_set.push(`\`${data.field.column}\` = NULL`);
} else if(data.field.needQuotes) {
_set.push(`\`${data.field.column}\` = ${SqlString.escape(data.field.restore(data.value))}`);
} else {
_set.push(`\`${data.field.column}\` = ${data.field.restore(data.value)}`);
}

return _set;
}, []);
const _updateWhere = Object.keys(pk).reduce((where, key) => {
const data = pk[key];
const field = model.fieldNamesMap[key];
if(!field) return where;

if(data === null) {
where.push(`\`${field.column}\` = NULL`);
} else if(field.needQuotes) {
where.push(`\`${field.column}\` = ${SqlString.escape(field.restore(data))}`);
} else {
where.push(`\`${field.column}\` = ${field.restore(data)}`);
}

return where;
}, []);

if(!_updateWhere.length) {
return process.nextTick(function() {
callback(new Error("Broken yukari object."));
});
}

if(!_set.length) {
return process.nextTick(function() {
callback(new Error("Broken update data information."));
});
}

const self = this;
const pkNames = model.primaryKeys.map(key => key.name);
const sql = `UPDATE \`${model.name}\` SET ${_set.join(", ")} WHERE ${_updateWhere.join(" AND ")}`;
async.waterfall([
function(callback) {
if(!model.cache) return callback();

// delete cache if has
const relatedSql = self.makeSql("find", model, {
where: pk,
limit: [ 0, 1 ],
fields: pkNames
});
debug("find related row when updating Yukari", relatedSql);
self.execute(relatedSql, function(err, result) {
if(err) return callback(err);
model.cache.deleteKeys(self.getDBName(), model.name, result, function(err) {
return callback(err);
});
});
},

function(callback) {
self.execute(sql, function(err, results) {
if(err) return callback(err);
if(!results.affectedRows) {
return callback(new Error("Out-dated yukari data."));
}
return callback();
});
}
], function(err) {
return callback(err, sql);
});
}

execute(sql, params, callback) {
if(typeof params === "function") {
callback = params;
Expand Down
78 changes: 77 additions & 1 deletion lib/yukari.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
const _ = require("lodash");
const async = require("async");

// const FieldType = require("./field_type");
const FieldType = require("./field_type");

const _emptyCallback = function() {};

Expand Down Expand Up @@ -204,6 +204,82 @@ class Yukari {
});
}

update(callback) {
if(callback === undefined) callback = _emptyCallback;

if(this.$source === "new") {
return callback(new Error("You must call this function via an old Yukari object."));
}

// get all the data changed and pass to adapter
const model = this.$model;
let data = [];
for(let key in this) {
if(!this.hasOwnProperty(key)) continue;
if(key.length && key[0] === "$") continue;
if(typeof this[key] === "function") continue;
const field = model.fieldNamesMap[key];
if(undefined === field) continue;

// judge if this data changed since read from the data source
const equalFunc = (undefined === field.type.equal) ?
FieldType.$equal.bind(FieldType) :
field.type.equal.bind(field.type);

// allow null and new or old is null
if((null === this[key] || null === this.$origData[key].data) &&
this[key] !== this.$origData[key].data && field.allowNull) {
data.push({ field: field, value: this[key] });
continue;
}

// if data changed
if(!equalFunc(this[key], this.$origData[key].data)) {
data.push({ field: field, value: this[key] });
continue;
}
}

// FIX: if no data changed, change all data
if(!_.size(data)) data = Yukari.extractAdapterData(model, this);

let pk;
if(this.$model.primaryKeys.length) {
// if it has primary key(s), use primary key(s) to query
pk = this.$model.primaryKeys.reduce((pks, field) => {
if(this.$origData[field.name]) {
pks[field.name] = this.$origData[field.name].data;
}
return pks;
}, {});
} else {
// if no primary key, use all original data to query
pk = Object.keys(this.$origData).reduce((pks, key) => {
pks[key] = this.$origData[key].data;
return pks;
}, {});
}

const self = this;
async.waterfall([
self.validateAll.bind(self),
self.$adapter.update.bind(self.$adapter, self.$model, pk, data)
], function(err, extra) {
if(err) return callback(err);

// update this yukari data
for(let key in data) {
if(!data.hasOwnProperty(key)) continue;

const d = data[key];
self.$origData[d.field.name].data = d.value;
}
self.$source = "query";

callback(err, self, extra);
});
}

toJSON(old) {
const obj = old ? this.$origData : this;
const result = {};
Expand Down

0 comments on commit 3191759

Please sign in to comment.