# 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对应记录的所有子记录.