Skip to content

Commit

Permalink
feat(orm): export Field API
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Aug 8, 2021
1 parent 95beb17 commit a021b05
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 75 deletions.
77 changes: 18 additions & 59 deletions docs/api/database.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ sidebarDepth: 2
- **config:** `Table.Meta` 表的基本配置
- **config.primary:** `string` 主键名,默认为 `'id'`
- **config.unique:** `string[]` 值唯一的键名列表
- **config.fields:** `Record<string, Table.Field>` 字段信息
- **config.type:** `string` 主键产生的方式,目前支持:
- `incremental`: 检测目前最大的主键值,并增加 1 作为新的主键值,适用于数值类型的主键
- `random`: 产生一个随机字符串作为新的主键值,适用于字符串类型的主键
Expand All @@ -70,66 +69,15 @@ sidebarDepth: 2

扩展数据库的功能。

## 数据类型

数值类型会被用于 [`Tables.extend()`](#tables-extend-name-config),其定义如下:

```ts
export interface Field<T> {
type: string
length?: number
nullable?: boolean
initial?: T
comment?: string
}
```

::: tip
- 默认情况下 `nullable``true`
- 如果 `initial` 或默认初始值不为 `null`,则 `nullable` 默认为 `false`
- 如果希望覆盖默认初始值的以上行为,可以将 `initial` 手动设置为 `null`
:::

### 数值类型

| 名称 | TS 类型 | 默认长度 | 默认初始值 | 说明 |
| :-: | :-: | :-: | :-: | :-: |
| integer | `number` | 10 | `0` | 有符号整型数,长度决定了数据的范围 |
| unsigned | `number` | 10 | `0` | 无符号整型数,长度决定了数据的范围 |
| float | `number` | 固定长度 | `0` | 单精度浮点数 |
| double | `number` | 固定长度 | `0` | 双精度浮点数 |

### 字符串类型

| 名称 | TS 类型 | 默认长度 | 默认初始值 | 说明 |
| :-: | :-: | :-: | :-: | :-: |
| char | `string` | 需手动设置 | `''` | 定长的字符串 |
| string | `string` | 65536 | `''` | 变长的字符串 |

### 时间类型

| 名称 | TS 类型 | 默认长度 | 默认初始值 | 说明 |
| :-: | :-: | :-: | :-: | :-: |
| date | `Date` | 固定长度 | `null` | 日期值 |
| time | `Date` | 固定长度 | `null` | 时间值 |
| timestamp | `Date` | 固定长度 | `null` | 时间戳 |

### 其他类型

| 名称 | TS 类型 | 默认长度 | 默认初始值 | 说明 |
| :-: | :-: | :-: | :-: | :-: |
| json | `object` | 65536 | `null` | 可被序列化为 json 的结构化数据 |
| list | `string[]` | 65536 | `[]` | 字符串构成的列表,序列化时以逗号分隔 |

## ORM API

一个 Database 对象代理了 Koishi 上下文绑定的应用实例有关的所有数据库访问。同时它具有注入特性,任何插件都可以自己定义数据库上的方法。本章主要介绍数据库的官方接口。注意:**它们并不由 Koishi 自身实现,而是由每个数据库分别实现的**。Koishi 只是提供了一套标准。

### db.get(table, query, fields?)
### db.get(table, query, modifier?)

- **table:** `keyof Tables` 注册在 ORM 中的表名
- **query:** `QueryExpr<Tables[T]> | QueryShorthand` 搜索表达式
- **fields:** `Tables.Field<T>[]` 请求的字段,默认为全部字段
- **modifier:** `QueryModifier<keyof Tables[T]>` 请求修饰符
- 返回值: `Promise<Tables[T][]>` 用户数据

参数 query 支持正则以及表达式,你可以使用复杂的嵌套更细致化的去完成你对数据库的查找服务。实现上与 mongo 近似,如果你有使用过 mongodb 经验,那么使用 Koishi ORM 对你来说便不是一件难事。
Expand All @@ -153,11 +101,19 @@ interface LogicalQueryExpr<T> {
$not?: QueryExpr<T>
}

type QueryShorthand<T = IndexType> = T[] | RegExp
type QueryShorthand<T> = T[] | RegExp
type FieldQuery<T> = FieldQueryExpr<T> | QueryShorthand<T>
type QueryExpr<T = any> = LogicalQueryExpr<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>
```
下面是一些简单的示例
Expand All @@ -180,6 +136,9 @@ 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)
Expand All @@ -190,11 +149,11 @@ const rows = await ctx.database.get('schedule', {

## 数据库方法

### db.getUser(type, id, fields?)
### db.getUser(type, id, modifier?)

- **type:** `string` 平台名
- **id:** `string | string[]` 用户标识符
- **fields:** `User.Field[]` 请求的字段,默认为全部字段
- **modifier:** `QueryModifier<User.Field>` 请求修饰符
- 返回值: `Promise<User | User[]>` 用户数据

向数据库请求用户数据。如果传入的 id 是一个列表,则返回值也应当是一个列表。
Expand All @@ -216,7 +175,7 @@ const rows = await ctx.database.get('schedule', {

- **type:** `string` 平台名
- **id:** `string | string[]` 频道标识符
- **fields:** `ChannelField[]` 请求的字段,默认为全部字段
- **fields:** `QueryModifier<User.Field>` 请求修饰符
- 返回值: `Promise<Channel | Channel[]>` 频道数据

向数据库请求频道数据。如果传入的 id 是一个列表,则返回值也应当是一个列表。
Expand Down
27 changes: 17 additions & 10 deletions packages/koishi-core/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,30 @@ export interface Tables {
channel: Channel
}

export namespace Tables {
type Unique<K> = (K | K[])[]
export interface Field<T = any> {
type: Field.Type<T>
length?: number
nullable?: boolean
initial?: T
}

export namespace Field {
export const numberTypes: Type[] = ['integer', 'unsigned', 'float', 'double']
export const stringTypes: Type[] = ['char', 'string', 'text']
export const dateTypes: Type[] = ['timestamp', 'date', 'time']
export const objectTypes: Type[] = ['list', 'json']

type FieldType<T = any> =
export type Type<T = any> =
| T extends number ? 'integer' | 'unsigned' | 'float' | 'double'
: T extends string ? 'string'
: T extends string ? 'char' | 'string' | 'text'
: T extends Date ? 'timestamp' | 'date' | 'time'
: T extends any[] ? 'list' | 'json'
: T extends object ? 'json'
: never
}

export interface Field<T = any> {
type: FieldType<T>
length?: number
nullable?: boolean
initial?: T
}
export namespace Tables {
type Unique<K> = (K | K[])[]

export interface Meta<O = any> {
type?: 'random' | 'incremental'
Expand Down
4 changes: 2 additions & 2 deletions packages/plugin-mongo/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import MongoDatabase, { Config } from './database'
import { User, Tables, Database, Context, Channel, Random, pick, omit, TableType, Query } from 'koishi-core'
import { User, Tables, Database, Field, Context, Channel, Random, pick, omit, TableType, Query } from 'koishi-core'

export * from './database'
export default MongoDatabase
Expand Down Expand Up @@ -118,7 +118,7 @@ function createFilter<T extends TableType>(name: T, _query: Query<T>) {

function getFallbackType({ fields, primary }: Tables.Meta) {
const { type } = fields[primary]
return type === 'string' ? 'random' : 'incremental'
return Field.stringTypes.includes(type) ? 'random' : 'incremental'
}

Database.extend(MongoDatabase, {
Expand Down
8 changes: 4 additions & 4 deletions packages/plugin-mysql/src/database.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createPool, Pool, PoolConfig, escape as mysqlEscape, escapeId, format, OkPacket, TypeCast } from 'mysql'
import { App, Database } from 'koishi-core'
import { App, Database, Field } from 'koishi-core'
import * as Koishi from 'koishi-core'
import { Logger } from 'koishi-utils'
import { types } from 'util'
Expand Down Expand Up @@ -38,7 +38,7 @@ function escape(value: any, table?: TableType, field?: string) {
return mysqlEscape(stringify(value, table, field))
}

function getTypeDefinition({ type, length }: Koishi.Tables.Field) {
function getTypeDefinition({ type, length }: Field) {
switch (type) {
case 'float':
case 'double':
Expand Down Expand Up @@ -254,7 +254,7 @@ namespace MysqlDatabase {
}

/**
* @deprecated use `Tables.Field` instead
* @deprecated use `import('koishi-core').Field` instead
*/
export const tables: Declarations = {
user: {},
Expand All @@ -270,7 +270,7 @@ namespace MysqlDatabase {
}

/**
* @deprecated use `Tables.Field` instead
* @deprecated use `import('koishi-core').Field` instead
*/
export namespace Domain {
export function definition(domain: string | Domain) {
Expand Down

0 comments on commit a021b05

Please sign in to comment.