# card-query **Repository Path**: wbenxin/card-query ## Basic Information - **Project Name**: card-query - **Description**: 基于卡片元数据模型的数据查询方案 - **Primary Language**: JavaScript - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2021-12-24 - **Last Updated**: 2025-07-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # card-query 基于卡片元数据模型的数据查询方案 ## 安装 ``` npm i card-query ``` ## 使用 ``` const query = require('card-query'); query.configure(config); async function test() { let data = await query.cardr('卡片名称', ['名称', '编码'], { filter, order }); } ``` ## 功能列表 * [configure(config)](#configure) * [getDatabase(name)](#getDatabase) * [middleware()](#middleware) * [async cardr(name, model, options)](#cardr) * [async cardw(name, model, records)](#cardw) * [async cardd(name, ids)](#cardd) * [async treer(name, model, parent, options)](#treer) * [async treed(name, ids)](#treed) ## configure(config) 配置数据库连接参数. 支持Oracle和Mysql两种数据库. ### @ config参数 ``` { [name]: { driver: '', // 'oracle'或者'mysql' connectString: 'localhost/orcl', // oracle专用 host: '', // mysql专用 database: '', // mysql专用 user: '', // 用户名 password: '', // 密码 } } ``` > 至少要有一个name为'default'的连接. 加载元数据会使用该连接 更多配置项请参考[oracledb](https://oracle.github.io/node-oracledb/doc/api.html)或[mysql2](https://github.com/sidorares/node-mysql2/blob/master/README.md) ## getDatabase(name) 获取指定名称的数据库连接对象 ### @ name参数 对应出现在config中的key值 ### @ 返回值 根据连接的数据库类型, 可能为Oracle或者Mysql对象的实例. 两者都具有如下属性: * driver * 只读属性, 取值为`'oracle'`或者`'mysql'` * 可以用来判断连接的数据库类型 * async getConnection() * 这是用来访问`oracledb`和`mysql2`的连接对象的, 一般不推荐使用 * 这个方法从连接池中获取一个连接对象. 如果是Oracle数据库, 连接对象是`oracledb`提供的. 如果是MySQL数据库, 连接对象是`mysql2`提供的. * 获取的连接, 用完后需要归还给连接池. 两个数据库的归还接口不一样, Oracle数据库需要调用`conn.close()`方法, MySQL数据库需要调用`conn.release()`方法. ``` let db = getDatabase('AmyDB'); let conn = await db.getConnection(); conn.execute('select 1 from dual'); conn.close(); // 如果是mysql则需要用conn.release() ``` * async execute(sql, values = []) * 执行一个SQL语句 * 支持以 `?` 表达的参数化查询. 比如: `execute('select ? from dual', [1])` . 如果是Oracle数据库, 会自动将 `?` 转为 `:` 格式 * 参数是数组格式. 顺序与sql文本中 `?` 出现的顺序一致 * async trans(callback) * 启动一个事务, 然后执行 `callback` 指定的回调函数. 如果没有错误发生, 则提交事务. 如果抛出了异常, 则回滚事务. * 如果成功提交了, 则返回`Promise`, 如果失败回滚了, 则返回`Promise` * format_result(result) * 用来将`oracledb`和`mysql2`的SQL结果集规范化到统一的格式: { rows, fields, affected, insertId } * rows: 返回的行数组 * fields: 每个列的类型描述 * affected: 影响的行数 * insertId: 插入的ID. 如果是自增ID, 可以从这里拿到插入后生成的ID ## middleware() 提供koa中间件支持. 调用后会返回一个koa中间件, 用来为ctx对象增加db属性. 可以通过db属性访问所有的功能. ``` const App = require('koa'); const query = require('card-query'); // 初始化数据库连接配置 query.configure(config); const app = new App(); // 引入中间件 app.use(query.middleware()); app.use(async (ctx, next) => { // 执行SQL let d = await ctx.db('conn_name').execute('select * from user where id=?', [123]); // 调用cardr接口获取卡片的数据 let data = await ctx.db.cardr('some card', [], 'some id'); // 输出到浏览器 ctx.body = JSON.stringify(data); }); app.listen(3000); ``` ## async cardr(name, model, options) 提供基于卡片的数据查询功能 ### @ name参数: string 卡片名称 ### @ model参数: string[] 字段的数组. 可以用 `.` 多级连接引用的字段. 以下都是合法的写法: ``` ['名称', '描述', '上级.名称', '持有人.所属部门.名称'] ``` > 不需要显式添加 'ID' 字段 > > 特殊地, 如果model参数传递 `'*'`, `[]`, `null`, `undefined` 表示使用卡片的全部字段(卡片数据+卡片引用). ### @ options参数: string | number | object 可以传递一个ID值, 来直接查询对应ID的单条记录. 支持以下属性 * filter - 筛选条件 * order - 排序 * rows - 每页行数 * page - 页码 #### filter > 格式为 { 'and/or': [ [三元组], { 'and/or': [] }, ... ] } > 特殊地, 也支持 [ [三元组], { 'and/or': [] } ... ] 格式. 默认为 `and` 连接 其中, `[三元组]` 表示一个形如 `[a,b,c]` 由三个元素组成的数组. 第一个元素是字段名(model数组中的元素), 第二个元素是比较运算符, 第三个元素是值. 支持的比较运算符有: ``` =, <>, >, >=, <, <= IS IN, NOT IN LIKE, NOT LIKE BETWEEN, NOT BETWEEN ``` 如果是 `IN`, `BETWEEN` 这样需要多个参数值的情况, 第三个元素要传递一个数组类型. 比如 `['序号', 'BETWEEN', [100,200]]` #### order > 格式为 { '字段名': 'asc/desc', ... } 字段名是model数组中的元素 如果未提供此参数, 则默认为model中的 `'记录时间'` 降序. 如果model中没有 `'记录时间'`, 就默认为 `'ID'` 升序 #### rows 如果不指定该参数, 或者指定为0, 都会被强制覆盖为默认的100. 这是为了避免一次性加载海量数据从而导致node因达到内存上限而崩溃. 如果确实需要一次性加载大量数据, 可以指定一个足够大的数值. > 最佳实践是少量多次加载数据, 并且每次加载后根据rows和返回的行数是否相等来判断还有没有下一页数据等待加载. #### page 请求的页码, 默认为 1 ### @ 返回值 数组类型, 查询到的所有记录 ## async cardw(name, model, records) 提供基于卡片的数据保存功能 ### @ name参数 卡片名称 ### @ model参数 请参考 `cardr` 中的model参数 ### @ records参数 要保存到数据库的记录数组 所有记录都会在一个事务中处理, 要么都成功, 要么都失败. ### @ 返回值 返回经过修改的records参数: > 如果记录没有指定ID, 则自动在插入数据库时生成新的ID并更新到记录上 ## async cardd(name, ids) 提供基于卡片的数据删除功能 ### @ name参数 卡片名称 ### @ ids参数 类型为 string[] 要删除记录的ID数组 ### @ 返回值 类型为 string[] 删除成功了的ID数组 ## async treer(name, model, parent, options) 基于cardr的结果集, 根据praent参数指定的上级字段名, 将结果集重组为children树的形式. 默认情况下, 上级记录不在cardr结果集中的记录都将作为树的顶级节点. 这在某些情况下这可能有问题, 可以通过指定options.root参数, 来指定成为顶级记录的必要条件. ## async treed(name, ids) 提供基于卡片的数据树删除功能 参数与返回值都与cardd兼容. 相比cardd, treed可以自动删除ids对应记录的所有子记录.