用户:
Nomen查看:2 回复:6 评论:2 创建时间:2023-07-07T11:23:33
这里是没有开场白的Nomen,NomenDBCore是NomenDatabase核心组件,作为其前置模块,但是也可单独使用,或进行扩展,开发一个更好的框架
使用方法如下:
(constructor)NomenDBCore(NomenDBCoreConfig)
NomenDBCoreConfig {
executor?: function, 命令执行者,在执行语句时会传入:string[],...any[],默认为db.sql
name: string,
db: "postgre"|"sqlite",
debug?: boolean 如果为真,则会输出信息和警告
}
(instance)NomenDBCore.where(string,string,string | {})
如果为一个长度为3的数组,则第1项是字段名,第2项是运算符,第3项是值
例如:where("awa","=","qwq") => awa="qwq"
或者传入一个对象,例如 { awa: "qwq", Nomen: 114 } => awa="qwq" AND Nomen=114
(instance)NomenDBCore.insert([] | {}) => Promise<NomenDBCore>
传入一个数组时,则按照该顺序插入值
传入对象时,则按照键值对应插入值
请注意,你不应该让用户引用和操作你的字段名,这可能会存在注入风险
(instance)NomenDBCore.select(string | string[]) => Promise
传入字符串时,则返回此字符串列上的数据,如果为列表,则返回列表中字符串的数据
例如: select("*") => SELECT * FROM
select(["qwq","awa"]) => SELECT qwq,awa FROM
(instance)NomenDBCore.limit(number)
传入一个数字,限制返回的数据量
(instance)NomenDBCore.offset(number)
传入一个数字,指定返回数据的偏移量,例如执行翻页操作
(instance)NomenDBCore.del() => Promise
删除数据
警告:如果实例上没有where作为限制,则删除全部内容
(instance)NomenDBCore.update(value) => Promise
更新数据,如果传入三个字符串,则解析字符串为表达式如where所示并且更新
如果为对象则解析为多表达式并且更新
警告:如果实例上没有where作为限制,则更新全部内容
(instance)NomenDBCore.order(string,"ACS"|"DESC" | {})
如果传入两个字符串,则第一个字符串为键名,第二个字符串为排序方式,只能是"ACS"升序或"DESC"降序
如果传入对象,则分别按照键值排序
例如: {awa:"ACS",qwq:"DESC"} => ORDER BY awa ACS qwq DESC
(instance)NomenDBCore.addStringQuery(string)
请尽量不要使用该方法,该方喵将字符串作为搜索参数直接插入,具有被注入的风险
例如: addStringQuery("userKey=123") => WHERW userKey=123
NomenDBCore @1.0.0:
class NomenDBCore {
constructor(config) {
if (!config) throw new Error("The config is required");
if (!config.name) throw new Error("The table name is required");
if (!["sqlite", "postgre"].includes(config.db)) throw new Error("The database type should be \"postgre\" or \"sqlite\"");
let { executor, name, db, query, debug } = config;
this._sql = typeof executor == "function" ? executor : db.sql;
this._name = name;
this.db = db;
this.query = query || [];
this.debug = debug || true;
this._limit = undefined;
this._offset = undefined;
this._order = [];
}
where(...arg) {
let operators = {
"postgre": ["=", "!=", "<>", "<", ">", "<=", ">="],
"sqlite": ["==", "=", "!=", "<>", "<", ">", "<=", ">=", "!<", "!>"]
}
if (arg.length == 3 && operators[this.db].includes(arg[1]) && typeof arg[0] == "string") {
this.query.push(arg);
} else if (arg.length == 1) {
for (let n of Object.keys(arg[0])) {
this.query.push([n, ({
"postgre": "=",
"sqlite": "="
})[this.db], arg[0][n]]);
};
}
return this;
}
insert(value) {
let strs = [], params = [], k = this;
if (Array.isArray(value)) {
strs = [`INSERT INTO ${this._name} VALUES (`];
params = value;
for (let i = 0; i < Object.values(value).length - 1; i++) {
strs.push(",")
}
strs.push(")");
} else {
strs = [`INSERT INTO ${this._name} (${Object.keys(value).join(",")}) VALUES (`];
params = Object.values(value);
for (let i = 0; i < Object.values(value).length - 1; i++) {
strs.push(",")
}
strs.push(")");
}
return new Promise(r => {
this._exec(strs.join("?"), params)
.then(() => r(k));
});
}
select(query) {
let req = Array.isArray(query) ? query.join(",") : query;
let { params, strs } = this._genSelector(this.query);
console.log(JSON.stringify({ params, strs }))
let output = `SELECT ${req} FROM ${this._name} ${this.query.length ? "WHERE " + (strs[0] ? strs.join("") : "") : ""}`
output = output.concat(` ${this._order.length ? `ORDER BY ` + this._order.map(v => v.key + " " + v.type).join(",") : ""} ` +
`${this._limit ? "LIMIT " + Number(this._limit) : ""} ${(this._offset && this._limit) ? "OFFSET " + Number(this._offset) : ""}`)
console.log(JSON.stringify(strs))
return this._exec(output, params);
}
update(...value) {
let exc = [], mparams = [], k = this;
if (value.length == 2) {
exc.push(`UPDATE ${this._name} SET ${value[0]} ${value[1]}`);
mparams.push(value[2]);
} else {
for (let i = 0; i < Object.keys(value[0]).length; i++) {
exc.push((i == 0 ? `UPDATE ${this._name} SET ` : ",") + `${Object.keys(value[0])[i]}=`);
mparams.push(value[0][Object.keys(value[0])[i]]);
}
}
if (this.query.length) {
let { params, strs } = this._genSelector(this.query);
strs[0] = (` WHERE `).concat(strs[0]);
exc = exc.concat(strs);
mparams = mparams.concat(params);
}
return new Promise(r => {
k._exec(exc.join("?"), mparams).then(() => r(k))
})
}
del() {
let k = this;
if (this.query.length) {
let { params, strs } = this._genSelector(this.query);
strs[0] = (`DELETE FROM ${this._name} WHERE `).concat(strs[0]);
return new Promise(r => {
k._exec(strs.join("?"), params).then(() => r(k))
})
} else {
return new Promise(r => {
k._exec(`DELETE FROM ${k._name}`).then(() => r(k));
})
}
}
_genSelector(query) {
let params = [], strs = [], f = false;
query.forEach(v => {
if (v.length == 1) {
strs[strs.length - 1] = strs[strs.length - 1].concat(" AND ", v[0]);
} else if (v.length > 1) {
strs.push((f ? " AND " : (f = true, "")) + v[0] + v[1] + "?");
params.push(v[2]);
}
});
return { params, strs }
}
order(...order) {
if (order.length == 2 && ["ASC", "DESC"].includes(order[1])) {
this._order.push({ key: order[0], type: order[1] })
} else if (order.length == 1) {
Object.keys(order[0]).filter(v => ["ASC", "DESC"].includes(order[0][v])).forEach(k => {
this._order.push({ key: k, type: order[0][k] });
});
}
return this;
}
limit(limit) {
this._limit = limit;
return this;
}
offset(offset) {
this._offset = offset;
return this;
}
addStringQuery(query) {
this.log(1, `Do not use method addStringQuery to add query, it is unsafe`);
this.query.push(query.toString ? query.toString() : query);
return this;
}
async _exec(ex, params) {
console.log(ex)
let result = await this._sql([...ex.split(/(?<!\\)[\?](?=(?:[^"]*"[^"]*")*[^"]*$)/g)], ...params || []);
if (this.handler) this.handler(result);
return this.hook ? this.hook(result) : result;
}
log(level, message) {
let levelsMessage = ["INFO", "WARN", "ERROR"];
if (this.debug || level == 2) {
console[["log", "warn", "error"][level]](`NomenDBCore [${levelsMessage[level]}] ${message}`);
}
}
}
例子:
let MyFirstNomenDBCore = new NomenDBCore({
executor: db.sql,
name: "Nomen",
db: "sqlite"
});
MyFirstNomenDBCore.where({
awa: 1, // 筛选awa为1的数据
qwq: "qwq" // 的同时筛选qwq为“qwq”的数据
})
.limit(1) // 限制为仅获取1条数据
.offset(2) // 偏移量2
.order({
awa: "ASC" // 按照awa字段排序,使用ASC升序,这里可以指定多个字段
})
.select("*") // 选择所有字段,返回Promise
let MySecondNomenDBCore = new NomenDBCore({
executor: db.sql,
name: "Nomen",
db: "sqlite"
});
MySecondNomenDBCore.where({
awa:2 // 筛选awa值为2的数据
})
.update({
awa:114, // 更新awa的值为114
qwq:514
}) // 返回Promise<NomenDBCore>
同时,开放了两个接口用语扩展,设置实例的handler和hook属性,handler会在每次搜寻数据时获得数据,hook会改变数据的输出,例如:
MyFirstNomenDBCore.hook = function (data) {
return data.filter(v => v.coin >= 10); // 将结果筛选为coin大于10的数据
}