export interface Field<T> {
  type: string
  length?: number
  nullable?: boolean
  initial?: T
}
数据库 (Database)
内置表
User
- id: 
string内部编号 - name: 
string用户昵称 - flag: 
number状态标签 - authority: 
number用户权限 - usage: 
Record<string, number>指令调用次数 - timers: 
Record<string, number>指令调用时间 
Channel
- id: 
string频道标识符 - flag: 
number状态标签 - assignee: 
string代理者 
全局接口
User.Flag, Channel.Flag
所有用户 / 频道状态标签构成的枚举类型。参见 状态标签。
User.fields, Channel.fields
所有用户 / 频道字段构成的列表。
User.extend(getter), Channel.extend(getter) deprecated
- getter: 
(type: string, id: string) => object新字段的初始化函数,返回值应该是一个由要扩展的字段和它们的默认值构成的键值对 - 返回值: 
void 
WARNING
此接口已被弃用,请使用 Tables.extend()
扩展用户 / 频道字段。
User.create(type, id), Channel.create(type, id)
- type: 
string平台名 - id: 
string用户 / 频道标识符 - 返回值: 
User/Channel 
创建一个新用户 / 频道数据对象。
Database.extend(database, extension)
- database: 
string | (new () => Database)要扩展的数据库类- 如果传入一个字符串,则会将这个模块的默认导出作为目标类
 
 - extension: 
Partial<Database> | ((Database: T) => void)- 如果传入一个对象,则表示要添加到原型链的方法
 - 如果传入一个函数,则会传入上述类构造器并立即执行
 
 
扩展数据库的功能。
一个 Database 对象代理了 Koishi 上下文绑定的应用实例有关的所有数据库访问。同时它具有注入特性,任何插件都可以自己定义数据库上的方法。本章主要介绍数据库的官方接口。注意:它们并不由 Koishi 自身实现,而是由每个数据库分别实现的。Koishi 只是提供了一套标准。
Tables.extend(name, meta?)
- name: 
string数据表名 - meta: 
Table.Extension表的基本配置- meta.primary: 
string主键名,默认为'id' - meta.unique: 
string[]值唯一的键名列表 - meta.type: 
string主键产生的方式,目前支持:incremental: 检测目前最大的主键值,并增加 1 作为新的主键值,适用于数值类型的主键random: 产生一个随机字符串作为新的主键值,适用于字符串类型的主键- 注意:如果你的主键值是用户指定的,那么你将不需要此项配置
 
 - meta.fields: 
Record<string, string | Field>: 字段信息,格式参照 指南,类型Field见下文 
 - meta.primary: 
 
扩展既有的数据表,或者定义一个新的数据表。
数据类型
数值类型会被用于 Tables.extend(),其定义如下:
TIP
- 默认情况下 
nullable为true - 使用简写定义时,
nullable为false - 使用完整定义时,若 
initial不为null,则nullable默认为false - 如果希望覆盖默认初始值的以上行为,可以将 
initial手动设置为null - 在扩展表时,由于 “不可为空”问题,推荐手动将扩展的字段设置为 
nullable: true 
数值类型
| 名称 | TS 类型 | 默认长度 | 默认初始值 | 说明 | 
|---|---|---|---|---|
| integer | number | 10 | 0 | 有符号整型数,长度决定了数据的范围 | 
| unsigned | number | 10 | 0 | 无符号整型数,长度决定了数据的范围 | 
| float | number | 固定长度 | 0 | 单精度浮点数 | 
| double | number | 固定长度 | 0 | 双精度浮点数 | 
字符串类型
| 名称 | TS 类型 | 默认长度 | 默认初始值 | 说明 | 
|---|---|---|---|---|
| char | string | 64 | '' | 定长的字符串 | 
| string | string | 256 | '' | 变长的字符串 | 
| text | string | 65535 | '' | 变长的字符串 | 
时间类型
| 名称 | TS 类型 | 默认长度 | 默认初始值 | 说明 | 
|---|---|---|---|---|
| date | Date | 固定长度 | null | 日期值 | 
| time | Date | 固定长度 | null | 时间值 | 
| timestamp | Date | 固定长度 | null | 时间戳 | 
其他类型
| 名称 | TS 类型 | 默认长度 | 默认初始值 | 说明 | 
|---|---|---|---|---|
| json | object | 65535 | null | 可被序列化为 json 的结构化数据 | 
| list | string[] | 65535 | [] | 字符串构成的列表,序列化时以逗号分隔 | 
ORM API
db.get(table, query, modifier?)
- table: 
keyof Tables注册在 ORM 中的表名 - query: 
QueryExpr<Tables[T]> | QueryShorthand搜索表达式 - modifier: 
QueryModifier<keyof Tables[T]>请求修饰符 - 返回值: 
Promise<Tables[T][]>用户数据 
获取 数据库中的数据。
参数 query 支持正则以及表达式,你可以使用复杂的嵌套更细致化的去完成你对数据库的查找服务。实现上与 mongo 近似,如果你有使用过 mongodb 经验,那么使用 Koishi ORM 对你来说便不是一件难事。
interface FieldQueryExpr<T> {
  $regex?: RegExp
  $in?: T[]
  $nin?: T[]
  $eq?: T
  $ne?: T
  $gt?: T
  $gte?: T
  $lt?: T
  $lte?: T
}
interface LogicalQueryExpr<T> {
  $or?: QueryExpr<T>[]
  $and?: QueryExpr<T>[]
  $not?: QueryExpr<T>
}
type QueryShorthand<T> = T[] | RegExp
type FieldQuery<T> = FieldQueryExpr<T> | QueryShorthand<T>
type QueryExpr<T> = LogicalQueryExpr<T> & {
  [K in keyof T]?: FieldQuery<T[K]>
}
interface QueryOptions<T extends string> {
  limit?: number
  offset?: number
  fields?: T[]
}
type QueryModifier<T extends string> = T[] | QueryOptions<T>
下面是一些简单的示例
// 获取名为 schedule 的表中 id 为 1 或者 2 的数据行
// Koishi ORM 自动解析你的 primary key
const rows = await ctx.database.get('schedule', [1, 2])
const rows = await ctx.database.get('schedule', { id: [1, 2] })
// 当然 Koishi ORM 也支持了 mongo 的正则写法
const rows = await ctx.database.get('schedule', { command: /echo.*/ })
// 获取名为 schedule 的表中 id 大于 2 但是小于等于 5 的数据行
const rows = await ctx.database.get('schedule', { id: { $gt: 2, $lte: 5 } })
// 获取名为 schedule 的表中
// id 大于 2 但是小于等于 5 或者 id 大于 100 的数据行
const rows = await ctx.database.get('schedule', {
  id: { $gt: 2, $lte: 5 },
  $or: [{ $id: { $gt: 100 } }],
})
// 只获取 id 和 command 字段(默认情况下将获取全部字段)
const rows = await ctx.database.get('schedule', 1, ['id', 'command'])
db.remove(table, query)
- table: 
keyof Tables注册在 ORM 中的表名 - query: 
QueryExpr<Tables[T]> | QueryShorthand搜索表达式 - 返回值: 
Promise<void> 
删除 数据库中的数据。
db.create(table, data)
- table: 
keyof Tables注册在 ORM 中的表名 - data: 
Partial<TableType>前一个参数table所对应的数据库的数据结构 - 返回值: 
Promise<TableType>添加行的完整数据 
向数据库中 添加 数据。
db.update(table, data, key?)
- table: 
keyof Tables注册在 ORM 中的表名 - data: 
Partial<TableType>[]前一个参数table所对应的数据库的数据结构所构成的数组 - key: 
Query.Index<table>以非主键来索引要修改的数据作为参考的字段 - 返回值: 
Promise<void> 
向数据库中 更新 数据。
TIP
- 不提供 
key参数时,data数组中的每个元素都需要包含主键(一般为id) - 提供 
key参数时,data数组中的每个元素都需要包含key字段。 
数据库方法
db.getUser(type, id, modifier?)
- type: 
string平台名 - id: 
string | string[]用户标识符 - modifier: 
QueryModifier<User.Field>请求修饰符 - 返回值: 
Promise<User | User[]>用户数据 
向数据库请求用户数据。如果传入的 id 是一个列表,则返回值也应当是一个列表。
提示
尽管这里我们提供了 fields 参数用于对特定的数据库进行优化,但是如果你是数据库开发者,也完全可以忽略这个参数。只需要保证返回的数据满足用户数据格式,且包含在 fields 中的字段都存在即可。
db.setUser(type, id, data)
- type: 
string平台名 - id: 
string用户标识符 - data: 
User要修改 / 添加的数据 - 返回值: 
Promise<void> 
向数据库修改或添加用户数据。
db.getChannel(type, id, modifier?)
- type: 
string平台名 - id: 
string | string[]频道标识符 - modifier: 
QueryModifier<keyof Channel>请求修饰符 - 返回值: 
Promise<Channel | Channel[]>频道数据 
向数据库请求频道数据。如果传入的 id 是一个列表,则返回值也应当是一个列表。
db.getAssignedChannels(fields?, type?, assignees?) deprecated
- fields: 
ChannelField[]请求的字段,默认为全部字段 - type: 
string平台名,默认为全平台 - assignees: 
string[]代理者列表,默认为当前运行的全部机器人 - 返回值: 
Promise<Channel[]>频道数据列表 
向数据库请求被特定机器人管理的所有频道数据。这里的两个参数可以写任意一个,都可以识别。
db.setChannel(type, id, data)
- type: 
string平台名 - id: 
number频道标识符 - data: 
Channel要修改 / 添加的数据 - 返回值: 
Promise<void> 
向数据库修改或添加频道数据。