nodebatis
Version:
a sql style orm for nodejs
176 lines (166 loc) • 4.94 kB
JavaScript
const Rule = require('./rule')
const path = require('path')
const vm = require('vm')
const fs = require('fs')
const _ = require('lodash')
const keyReg = /\:([\w\._]+)/g
const ddlKeyReg = /\::([\w\._]+)/g
const childKeyReg = /\{{\s*([\w\._]+)\s*}}/g
class SqlContainer {
constructor(dir) {
this.container = new Map()
let files = fs.readdirSync(dir), rule = null
for (let file of files) {
let temp = file.toLowerCase()
if(temp.indexOf('.yml') !== -1 || temp.indexOf('.yaml') !== -1) {
rule = new Rule(path.join(dir, file))
this.container.set(rule.namespace, rule.rawSql)
}
}
}
get(key, data) {
let sqlArray = this.getRaw(key, data)
return this._parseRawSql(sqlArray, data)
}
getRaw(key, data) {
let keys = key.split('.'), sql = null
if (keys.length < 2) {
throw new Error('wrong key, the right key is xxx.xxx')
}
let namespace = keys[0]
let sqlKey = keys.slice(1).join('')
let sqlMap = this.container.get(namespace)
if (sqlMap) {
sql = sqlMap.get(sqlKey)
if (!sql) {
throw new Error('The sql: ' + key + ' not exists!')
} else {
//fill {{key}}
for (let i = 0; i < sql.length; i++) {
let tempSql = new Map()
if (typeof sql[i] == 'string') {
sql[i] = sql[i].replace(childKeyReg, (match, key) => {
let index = `___${tempSql.size}`
tempSql.set(key, this.getRaw(key))
return match
})
}
if (tempSql.size > 0) {
let tempArray = sql[i].split(childKeyReg)
for (let [key, value] of tempSql) {
for (let s=0; s < tempArray.length; s++) {
if (key === tempArray[s]) {
tempArray[s] = value
}
}
}
sql[i] = tempArray
}
}
}
} else {
throw new Error('The namespace: ' + namespace + ' not exists!')
}
// 格式化 sql 数组
return _.filter(_.flattenDeep(sql), s => s !== '')
}
_parseRawSql(sqlArray, data) {
let sqls = [], result = '', condSql = ''
let rawSql = [], params = []
for (let sql of sqlArray) {
if (sql === '') continue;
if (typeof sql === 'string') {
sqls.push(this._fillParams(sql, data))
} else {
// 只判断 test ,验证通过后拼接 sql,但是参数填充统一在后面处理
condSql = this._parseCond(sql, data)
if (condSql) {
sqls.push(condSql)
}
}
}
//combine sql and params
for (let item of sqls) {
rawSql.push(item.sql)
if (item.params) {
params = params.concat(item.params)
}
}
result = rawSql.join(' ')
let lastWhereReg = /\s+where\s*$/i
let whereAndReg = /\s+where\s+and\s+/ig
let whereOtherReg = /\s+where\s+(union\s+|order\s+|group\s+|limit\s+)/gi
result = result.replace(lastWhereReg, '')
result = result.replace(whereAndReg, ' where ')
result = result.replace(whereOtherReg, (match) => {
return match.replace(/\s+where\s+/i, ' ')
})
return {
sql: result,
params
}
}
_parseCond(node, data) {
let sql = null, statements = ''
data = data || {}
const context = new vm.createContext(data)
if (node.name.toLowerCase() === 'if') {
if (node.test && typeof node.test == 'string') {
statements = node.test.replace(keyReg, (match, key) => {
if (data[key] === undefined || data[key] === "") {
data[key] = null;
}
return key
})
let isTrue = false
try {
isTrue = new vm.Script(statements).runInContext(context)
} catch (e) {
isTrue = false
}
if (isTrue) {
sql = this._fillParams(node.sql, data)
}
}
}
if (node.name.toLowerCase() === 'for') {
let arrayKey = node.array.replace(':', '')
if (node.array && data[arrayKey] && data[arrayKey].length > 0) {
let sqlArray = [], rawSql = [], params = []
for (let item of data[arrayKey]) {
sqlArray.push(this._fillParams(node.sql, item))
}
for (let item of sqlArray) {
rawSql.push(item.sql)
params = params.concat(item.params)
}
sql = {
sql: rawSql.join(node.seperator),
params
}
}
}
return sql
}
_fillParams(sql, data) {
let params = [], that = this
//fill ::key
sql = sql.replace(ddlKeyReg, (match, key) => {
return data[key]
})
//fill :key
sql = sql.replace(keyReg, (match, key) => {
if (key === '_') {
params.push(data)
} else {
params.push(data[key])
}
return '?'
})
return {
sql: sql,
params: params.length > 0 ? params : null
}
}
}
module.exports = SqlContainer