Permalink
Browse files

feat: update data by query

  • Loading branch information...
XadillaX committed Sep 26, 2016
1 parent 30f47e8 commit 684fda828a7584be998a76ae04c99dad89c1cbf3
Showing with 107 additions and 3 deletions.
  1. +95 −3 lib/adapters/mysql.js
  2. +12 −0 lib/query.js
@@ -7,10 +7,12 @@
"use strict";

const _ = require("lodash");
const async = require("async");
const cu = require("config.util");
const debug = require("debug")("toshihiko:adapter:mysql");
const Scarlet = require("scarlet-task");
const SqlString = require("sqlstring");
const SqlParser = require("toshihiko-mysqlparser");

const Adapter = require("./base");
const escaper = require("../../util/escaper");
@@ -371,6 +373,39 @@ class MySQLAdapter extends Adapter {
return limit.map(l => parseInt(l) || 0).join(", ");
}

makeSet(model, update) {
let pattern = "";
let params = [];

for(let key in update) {
if(!update.hasOwnProperty(key)) continue;
if(pattern !== "") pattern += ", ";

const value = update[key];
const field = model.fieldNamesMap[key];
if(!field) continue;

pattern += `\`${field.column}\` = `;

// judge raw data, eg.
// {{i + 1}}
if(typeof value === "string" && value.length >= 4 &&
value[0] === "{" && value[1] === "{" &&
value[value.length - 1] === "}" && value[value.length - 2] === "}") {
pattern += SqlParser.sqlNameToColumn(value.substr(2, value.length - 4), model.nameToColumn);
} else {
if(!field.needQuotes) {
pattern += `${escaper.escape(field.restore(value))}`;
} else {
pattern += "?";
params.push(field.restore(value));
}
}
}

return this.format(pattern, params);
}

makeFind(model, options) {
options = options || {};

@@ -411,9 +446,26 @@ class MySQLAdapter extends Adapter {
return sql;
}

makeUpdate(model, options) {
options = options || {};
const set = this.makeSet(model, options.update);
if(!set) throw new Error("no set data.");
let sql = `UPDATE \`${model.name}\` SET ${set}`;

if(options.where && Object.keys(options.where).length) {
const where = this.makeWhere(model, options.where);
if(where) {
sql += ` WHERE ${where}`;
}
}

return sql;
}

makeSql(type, model, options) {
switch(type) {
case "find": return this.makeFind(model, options);
case "update": return this.makeUpdate(model, options);
default: return this.makeFind(model, options);
}
}
@@ -541,10 +593,11 @@ class MySQLAdapter extends Adapter {
fields: query._fields,
where: query._where,
order: query._order,
limit: query._limit
}, options);
limit: query._limit,
update: query._updateData
}, options || {});

if(options.single) {
if(_options.single) {
if(!_options.limit || !_options.limit.length) {
_options.limit = [ 0, 1 ];
} else if(_options.limit.length === 1) {
@@ -566,6 +619,45 @@ class MySQLAdapter extends Adapter {
}
}

updateByQuery(query, callback) {
const self = this;
const options = this.queryToOptions(query);
const sql = this.makeSql("update", query.model, options);
const model = query.model;

let primaryKeys;
async.waterfall([
function(callback) {
if(!model.cache) {
return callback();
}

// delete related rows if using cache
const tempFields = options.fields;
primaryKeys = model.primaryKeys.map(key => key.name);
options.fields = primaryKeys;

const relatedSql = self.makeSql("find", query.model, options);
debug("find related rows when updating", relatedSql);
self.execute(relatedSql, function(err, result) {
if(err) return callback(err);
model.cache.deleteKeys(self.getDBName(), model.name, result, function(err) {
options.fields = tempFields;
callback(err);
});
});
},

function(callback) {
debug("update data by query", sql);
self.execute(sql, callback);
}
], function(err, result) {
if(err === null) err = undefined;
callback(err, result, sql);
});
}

insert(model, _data, callback) {
// let it be
// [ { field: foo, value: bar }, ... ]
@@ -10,6 +10,8 @@ const _ = require("lodash");

const Yukari = require("./yukari");

const emptyFunc = function() {};

class ToshihikoQuery {
constructor(model) {
Object.defineProperties(this, {
@@ -148,6 +150,7 @@ class ToshihikoQuery {
toJSON = false;
}
options = options || {};
if(undefined === callback) callback = emptyFunc;

const self = this;
this.adapter.find(this, function(err, row, extra) {
@@ -181,6 +184,15 @@ class ToshihikoQuery {
findOne(callback, toJSON) {
this.find(callback, toJSON, { single: true });
}

update(data, callback) {
if(undefined === callback) callback = emptyFunc;
this._updateData = data;

this.adapter.updateByQuery(this, function(err, result, extra) {
return callback(err, result, extra);
});
}
}

module.exports = ToshihikoQuery;

0 comments on commit 684fda8

Please sign in to comment.