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生成原理的主要内容,如果未能解决你的问题,请参考以下文章

Apollo Codegen 没有找到生成代码的操作或片段

ThinkJS连接MongoDB

奇舞团开源thinkjs - 基于Promise的Node.js MVC框架

thinkjs中updateMany的BUG问题

thinkjs中updateMany的BUG问题

VUE3前端开发入门系列教程五:Nginx+ThinkJS后端无缝接入