猫史档案馆


NomenDBCore: 真正轻量级链式易于理解的通用SQL表格数据管理器

用户:NomenNomen查看: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的数据
}


回复

上一页1 页 / 共 1下一页
NomenNomen

照例沙发

点赞1


评论


NomenNomen

注:教程中的排序模式ACS更改为ASC(升序),实际代码没有出错,但是使用需要注意,如果写错,会导致静默错误,知晓

点赞1


评论


Function函数Function函数

好耶

点赞0


评论


神岛吉吉喵神岛吉吉喵

emotion_编程猫_点赞

点赞0


评论


FML饭米粒FML饭米粒

这是把sql的多个子句用js拼凑在一起了吗)

点赞0


评论


伴只狗头伴只狗头

前排

点赞0


评论