think-model-abstract
Version:
Abstract adapter for ThinkJS 3.x
243 lines (232 loc) • 6.56 kB
JavaScript
const helper = require('think-helper');
const debug = require('debug')('think-model');
const SOCKET = Symbol('think-model-socket');
module.exports = class AbstractQuery {
/**
* abstract query class
* @param {object} config
*/
constructor(config = {}) {
this.config = config;
this.lastSql = '';
this.lastInsertId = 0;
}
/**
* query sql
*/
query(sqlOptions, connection = this.connection) {
const sql = helper.isString(sqlOptions) ? sqlOptions : sqlOptions.sql;
this.lastSql = sql;
return this.socket(sql).query(sqlOptions, connection);
}
/**
* execute, override in sub class
*/
execute(sqlOptions, connection = this.connection) {
const sql = helper.isString(sqlOptions) ? sqlOptions : sqlOptions.sql;
this.lastSql = sql;
return this.socket(sql).execute(sqlOptions, connection);
}
/**
* get socket, invoked in sub class
* @param {String|Object} sql
*/
socket(sql, Cls) {
if (sql && this.config.parser) {
const config = Object.assign({}, this.config, this.config.parser(sql));
return Cls.getInstance(config);
}
if (this[SOCKET]) return this[SOCKET];
this[SOCKET] = Cls.getInstance(this.config);
return this[SOCKET];
}
/**
* insert data
* @param {Object} data
* @param {Object} options
*/
add(data, options = {}) {
const values = [];
const fields = [];
const parser = this.parser;
for (const key in data) {
let val = data[key];
val = parser.parseValue(val);
if (helper.isString(val) || helper.isBoolean(val) || helper.isNumber(val)) {
values.push(val);
fields.push(parser.parseKey(key));
}
}
// compatiable with boolean and array update property value
if (options.update === true) {
options.update = fields;
} else if (helper.isArray(options.update)) {
options.update = options.update.filter(field =>
fields.indexOf(field) > -1
);
} else {
for (const key in options.update) {
if (fields.indexOf(key) > -1) {
continue;
}
delete options.update[key];
}
}
options.fields = fields.join(',');
options.values = values.join(',');
const sql = this.parser.buildInsertSql(options);
return this.execute(sql).then(() => this.lastInsertId);
}
/**
* insert multi data
* @param {Array} data [data list]
* @param {Object} options []
* @return {Promise} []
*/
addMany(data, options = {}) {
const parser = this.parser;
let fields = Object.keys(data[0]);
const values = data.map(item => {
const value = [];
fields.forEach(key => {
const val = parser.parseValue(item[key]);
if (helper.isString(val) || helper.isBoolean(val) || helper.isNumber(val)) {
value.push(val);
}
});
return `(${value.join(',')})`;
}).join(',');
fields = fields.map(field => parser.parseKey(field));
// compatiable with boolean and array update property value
if (options.update === true) {
options.update = fields;
} else if (helper.isArray(options.update)) {
options.update = options.update.filter(field =>
fields.indexOf(field) > -1
);
} else {
for (const key in options.update) {
if (fields.indexOf(key) > -1) {
continue;
}
delete options.update[key];
}
}
options.fields = fields.join(',');
options.values = values;
const sql = this.parser.buildInsertSql(options);
return this.execute(sql).then(() => {
return data.map((item, index) => {
return this.lastInsertId ? this.lastInsertId + index : 0;
});
});
}
/**
* select data
* @param {String} fields []
* @param {String} table []
* @param {Object} options []
* @return {Promise} []
*/
selectAdd(fields, table, options = {}) {
if (helper.isString(fields)) {
fields = fields.split(/\s*,\s*/);
}
const parser = this.parser;
fields = fields.map(item => parser.parseKey(item));
const sql = this.parser.buildInsertSql({
table,
fields: fields.join(','),
values: options,
replace: options.replace,
ignore: options.ignore
});
return this.execute(sql);
}
/**
* delete data
* @param {Object} options []
* @return {Promise} []
*/
delete(options) {
const sql = this.parser.buildDeleteSql(options);
return this.execute(sql);
}
/**
* update data
* @param {Object} data []
* @param {Object} options []
* @return {Promise} []
*/
update(data, options) {
const sql = this.parser.buildUpdateSql(data, options);
return this.execute(sql);
}
/**
* select
* @param {Object} options []
* @return {Promise} []
*/
select(options, cache) {
const parser = this.parser;
let sql;
if (helper.isObject(options)) {
sql = options.sql ? options.sql : parser.buildSelectSql(options);
cache = cache || options.cache;
} else {
sql = options;
}
if (!cache) return this.query(sql);
cache.key = cache.key || helper.md5(sql);
const timeout = cache._keyTimeout;
delete cache._keyTimeout;
const Handle = cache.handle;
const instance = new Handle(cache);
return instance.get(cache.key).then(data => {
if (data !== undefined) {
debug(`get data from cache: ${JSON.stringify(cache)}`);
return data;
}
return this.query(sql).then(data => {
return instance.set(cache.key, data, timeout).then(() => {
return data;
});
});
});
}
/**
* start transaction
* @param {Object} connection
*/
startTrans(connection) {
return this.socket('START TRANSACTION').startTrans(connection).then(connection => {
this.connection = connection;
return connection;
});
}
/**
* commit transaction
* @param {Object} connection
*/
commit(connection = this.connection) {
return this.socket('COMMIT').commit(connection);
}
/**
* rollback transaction
* @param {Object} connection
*/
rollback(connection = this.connection) {
return this.socket('ROLLBACK').rollback(connection);
}
/**
* wrap transaction
* @param {Function} fn
* @param {Object} connection
*/
transaction(fn, connection) {
return this.socket('START TRANSACTION').transaction(connection => {
this.connection = connection;
return fn(connection);
}, connection);
}
};