thinkjs sql生成原理
Posted 章鱼樟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了thinkjs sql生成原理相关的知识,希望对你有一定的参考价值。
think.model 提供了丰富的CRUD方法,下面说明一下这些组合方法转为 sql 的实现原理
以 select 为例
const userData = await this.model(‘user‘).where({ token, phone}).find();
think-model model.js 部分代码; where、find 方法会处理options 然后返回 this 供下一个方法继续使用,其他方法同理
/**
* set where options
* @return {} []
*/
where(where) {
if (!where) return this;
if (helper.isString(where)) {
where = { _string: where };
}
const options = this.options;
if (options.where && helper.isString(options.where)) {
options.where = { _string: options.where }; // 给 options 加上 where 属性
}
options.where = helper.extend({}, options.where, where);
return this;
}
/**
* find data
* @return Promise
*/
find(options) {
var _this8 = this;
return _asyncToGenerator(function* () {
options = yield _this8.parseOptions(options); // 最后处理options
options.limit = 1;
options = yield _this8.beforeFind(options);
const data = yield _this8.db().select(options); // 调用 think-model-abstract selece()
return _this8.afterFind(data[0] || {}, options);
})();
}
/**
* before find
*/
beforeFind(options) {
return options;
}
/**
* after find
* @return {} []
*/
afterFind(data) {
return this[RELATION].afterFind(data);
}
/**
* parse options, reset this.options to {}
* @param {Object} options
*/
parseOptions(options) {
var _this = this;
return _asyncToGenerator(function* () {
if (helper.isNumber(options) || helper.isString(options)) {
options += ‘‘;
const where = {
[_this.pk]: options.indexOf(‘,‘) > -1 ? { IN: options } : options
};
options = { where };
}
options = helper.extend({}, _this.options, options);
_this.options = {};
options.table = options.table || _this.tableName;
options.tablePrefix = _this.tablePrefix;
options.pk = _this.pk; // add primary key for options
if (options.field && options.fieldReverse) {
options.field = yield _this.db().getReverseFields(options.field);
delete options.fieldReverse;
}
return options;
})();
}
上方处理完 options 调用 select() 方法。。。
think-model-abstract query.js 部分代码
/**
* 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); // options 是否已有sql,没有先拼接 sql,具体代码在下方
cache = cache || options.cache;
} else {
sql = options;
}
if (!cache) return this.query(sql);
cache.key = cache.key || helper.md5(sql);
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).then(() => {
return data;
});
});
});
}
生成 sql 代码:
think-model-abstract parse.js 部分代码
/**
* get select sql
* @param {Object} options []
* @return {String} [sql string]
*/
buildSelectSql(options) {
// 这是 select sql的完整模板
const sql = ‘%EXPLAIN%SELECT%DISTINCT% %FIELD% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%%ORDER%%LIMIT%%UNION%%LOCK%%COMMENT%‘;
return this.parseSql(sql, options);
}
/**
* parse sql
* @param {String} sql []
* @param {Object} options []
* @return {String} []
*/
parseSql(sql, options) {
// 把模板中需要填充的位置(%。。。%)用 options 的数据转为相应 sql ;
return sql.replace(/%([A-Z]+)%/g, (a, type) => {
// 以模板填充位置 WHERE 为例;
type = type.toLowerCase(); // type = "where";
const ucfirst = type[0].toUpperCase() + type.slice(1); // ucfirst = "Where"
if (helper.isFunction(this[‘parse‘ + ucfirst])) { // 此处存在 parseWhere 函数
return this[‘parse‘ + ucfirst](options[type] || ‘‘, options); // 把 where 属性转为 sql
}
return a;
}).replace(/s__([A-Z_-]+)__s?/g, (a, b) => {
return ‘ `‘ + this.config.prefix + b.toLowerCase() + ‘` ‘;
});
}
/**
* parse where
* @param {Mixed} where []
* @return {String} []
*/
parseWhere(where) {
if (helper.isEmpty(where)) {
return ‘‘;
} else if (helper.isString(where)) {
return ` WHERE ${where}`;
}
const logic = this.getLogic(where);
// safe key regexp
const keySafeRegExp = /^[w|&-.(),]+$/;
const multi = where._multi;
delete where._multi;
// eslint-disable-next-line one-var
let key, val, result = [], str = ‘‘;
const fn = (item, i) => {
const v = multi ? val[i] : val;
return ‘(‘ + this.parseWhereItem(this.parseKey(item), v) + ‘)‘;
};
for (key in where) {
val = where[key];
str = ‘( ‘;
// _string: ‘‘
if ([‘_string‘, ‘_complex‘, ‘_query‘].indexOf(key) > -1) {
str += this.parseThinkWhere(key, val);
} else if (!keySafeRegExp.test(key)) {
throw new Error(‘INVALID_WHERE_CONDITION_KEY‘);
// title|content
} else if (key.indexOf(‘|‘) > -1) {
str += key.split(‘|‘).map(fn).join(‘ OR ‘);
// title&content
} else if (key.indexOf(‘&‘) > -1) {
str += key.split(‘&‘).map(fn).join(‘ AND ‘);
} else {
str += this.parseWhereItem(this.parseKey(key), val);
}
str += ‘ )‘;
result.push(str);
}
result = result.join(` ${logic} `);
return result ? ` WHERE ${result}` : ‘‘;
}
至此 就是查询数据的完整逻辑,欢迎大佬指正;
以上是关于thinkjs sql生成原理的主要内容,如果未能解决你的问题,请参考以下文章