From 0a1e8a8b1e05cd09e613dcfb3416934ee16a517f Mon Sep 17 00:00:00 2001 From: chenos Date: Mon, 19 Apr 2021 18:19:21 +0800 Subject: [PATCH] docs: database api --- docs/cores/packages/database.md | 352 ++++++++++++++++++++++++++++++-- docs/guide/getting-started.md | 4 +- 2 files changed, 338 insertions(+), 18 deletions(-) diff --git a/docs/cores/packages/database.md b/docs/cores/packages/database.md index 90cc78a50..df90d18a4 100644 --- a/docs/cores/packages/database.md +++ b/docs/cores/packages/database.md @@ -122,17 +122,21 @@ const post = await Post.create({ 长文本 -### json 待完善 +### date -JSON 是个特殊的数据类型,在不同数据库里有所不同,目前只兼容了 PostgreSQL 的一些情况。 +日期,包括时间 + +### dateonly + +日期,不包括时间 ### time 时间 -### date +### json 待完善 -日期 +JSON 是个特殊的数据类型,在不同数据库里有所不同,目前只兼容了 PostgreSQL 的一些情况。 ### virtual @@ -499,7 +503,10 @@ await Pwd.verify('123456', user.password); // true ## API -### registerField +### registerField(type: string, field: Field) + +- type:字段类型,不区分大小写 +- field:字段类 注册字段 @@ -512,16 +519,49 @@ await Pwd.verify('123456', user.password); // true 获取已注册字段类 -虽然直接 import/require 已导出的字段类型也可以,但是更推荐用 getField 来处理。 +虽然直接通过 import/require 可以引用已导出的字段类型,但是更推荐用 getField 来处理,尤其在插件化的场景下。 +```ts +import CustomField from './CustomField'; + +registerFields({ CustomField }); + +// 以下三种写法最终效果一致,区别在于用 getField 只能获取已注册的字段类型 +import CustomField from './CustomField'; +const CustomField = require('./CustomField'); +// CustomField 只是 key,真实的类由 registerFields 控制 +const CustomField = getField('CustomField'); +``` + ### registerModel -注册模型 +注册 Model + +```ts +import { Model, registerModel } from '@nocobase/database'; + +class Test extends Model { + // 在这个类里可以为 Test Model 扩展其他 API + static hello() { + + } +} + +registerModel('test', Test); + +db.table({ + name: 'tests', +}); + +const Test = db.getModel('tests'); +// Test 可以调用 hello 方法了 +Test.hello(); +``` ### registerModels -批量注册模型,用法同 registerModel +批量注册 Model,用法同 registerModel ### getRegisteredModel @@ -529,7 +569,29 @@ await Pwd.verify('123456', user.password); // true ### extend 实验性 -与 database.import 配合使用 +扩展配置的语法糖,需要与 database.import 配合使用,extend 的用法参考 [database.extend](#databaseextend)。 table 配置只有 extend 时,不处理。 + +如某配置文件 `./path1/foos-extends.ts` + +```ts +import { extend } from '@nocobase/database'; + +export default extend({ + name: 'foos', + fields: [], +}); +``` + +导入配置 + +```ts +database.import({ + directory: '/path1', +}); + +// foos 只有 extend 配置文件,不导入处理 +database.isDefined('foos'); // false +``` ### Operator.register 实验性 @@ -539,17 +601,81 @@ await Pwd.verify('123456', user.password); // true 初始化实例 +```ts +const db = new Database({ + username, + password, + database, + host, + port, + dialect, + dialectOptions, + pool, + logging, + define, + sync, +}); +``` + +更多用法参考 [Sequelize.Options](https://github.com/sequelize/sequelize/blob/5b16b32259f0599a6af2d1eb625622da9054265e/types/lib/sequelize.d.ts#L180) + ### database.import +- directory:文件夹路径 +- extensions:扩展,默认 `['js', 'ts', 'json']` + 批量导入配置 +```ts +database.import({ + directory: '/path1/tables', + extensions: ['js', 'ts', 'json'], +}); +``` + ### database.table +- name:表名 +- fields:字段 +- model:绑定自定义的 Model + +更多参数查看 [TableOptions](https://github.com/nocobase/nocobase/blob/bbcd31016913b1297f258786df97b237d9fbd977/packages/database/src/table.ts#L48)(继承 [Sequelize.ModelOptions](https://github.com/sequelize/sequelize/blob/5b16b32259f0599a6af2d1eb625622da9054265e/types/lib/model.d.ts#L1361)) + 配置数据表 +用例: + +```ts +database.table({ + name: 'demos', + fields: [], +}); +``` + ### database.extend -配置扩展 +配置扩展。更多用法参考 [table.extend](#tableextend) + +```ts +db.table({ + name: 'foos', + fields: [ + {type: 'string', name: 'name'}, + ], +}); + +db.extend({ + name: 'foos', + fields: [ + {type: 'string', name: 'content'}, + ], +}); +``` + + +datatable.table 用于初始化,datatable.extend 用于处理扩展,需要存在 table 已存在。 + + ### database.isDefined @@ -559,26 +685,78 @@ await Pwd.verify('123456', user.password); // true 获取已定义的 Model +```ts +database.table({ + name: 'posts', + fields: [ + {type: 'string', name: 'title'}, + {type: 'string', name: 'content'}, + ], +}); + +const Post = database.getModel('posts'); + +await Post.create({ + title: 't1', + content: 'c1', +}) +``` + ### database.getModels 批量获取已定义的 Models +```ts +const [User, Post] = database.getModels(['users', 'posts']); +``` + ### database.getTable 获取已定义的 Table +```ts +const table = database.getTable('posts'); +``` + ### database.getTables 批量获取已定义的 Tables +```ts +const [user, post] = database.getTables(['users', 'posts']); +``` + ### database.sync +- tables:与数据库结构同步的表 + 数据表配置与数据库表结构同步 +```ts +await db.sync({ + tables: ['users', 'posts'], +}); +``` + +更多参数参考 [Sequelize.sync](https://github.com/sequelize/sequelize/blob/5b16b32259f0599a6af2d1eb625622da9054265e/types/lib/sequelize.d.ts#L45) + + + +- datatable.sync:所有已配置表同步,可以指定 tables 范围 +- table.sync:当前表同步,如果有关系字段,关系表也会处理 +- sequelize.sync:所有 sequelize.models 同步 +- Model.sync 只处理某个 Model.attributes 同步 + + + ### database.close 关闭数据库连接 +```ts +await database.close(); +``` + ### database.addHook 待完善 为数据表配置提供的钩子,目前 HookType 有: @@ -588,6 +766,15 @@ await Pwd.verify('123456', user.password); // true - beforeAddField - afterAddField + + +目前这部分的 api 主要用于弥补数据表配置的事件,长远来看还需要继续完善的有: + +- 统一 hook 接口(Table 和 Model) +- 钩子需要支持优先级(顺序) + + + ### database.runHooks 待完善 运行当前钩子挂载的函数 @@ -596,6 +783,11 @@ await Pwd.verify('123456', user.password); // true 获取数据表配置 +```ts +const options = table.getOptions(); +const fields = table.getOptions('fields'); +``` + ### table.getModel 获取当前配置表对应的 Model @@ -612,6 +804,15 @@ await Pwd.verify('123456', user.password); // true 新增字段(附加操作) +```ts +const table = db.table({ + name: 'foos', +}); + +table.addField({type: 'string', name: 'name'}); +table.addField({type: 'string', name: 'status'}); +``` + ### table.setFields 批量新增字段(替换操作) @@ -628,19 +829,52 @@ await Pwd.verify('123456', user.password); // true 批量建立索引,同 table.addIndex -### table.extend +### table.extend(tableOptions: TableOptions, mergeOptions?: MergeOptions) + +- tableOptions:表配置 +- mergeOptions:自定义合并规则,非必填,参数参考 [deepmerge](https://www.npmjs.com/package/deepmerge#options) 配置扩展 -### table.sync +```ts +table.extend({ + // 与 table.table 一致 +}, { + // 自定义 + arrayMerge, + customMerge, +}) +``` + + + +- table.extend:用于处理当前表扩展 +- database.extend:需要指定 tableName,其他用法同 table.extend +- extend:Markup 函数,只用于标记哪些配置用于扩展,用于 database.import 的配置文件中,用法同 database.extend + + + +### table.sync 待完善 当前表配置与数据库表结构同步 +更多参数参考 [Sequelize.sync](https://github.com/sequelize/sequelize/blob/5b16b32259f0599a6af2d1eb625622da9054265e/types/lib/sequelize.d.ts#L45) + +```ts +await table.sync({}) +``` + + + +因为没有更细度的 field.sync 所以配置有更新时,直接执行 table.sync,如果 table 里存在关系字段,关系表也会执行 sync 可能导致执行时间超时,数据库连接断开。后续需要提供更细度的 field.sync 以减少不必要的性能消耗。 + + + ### Model.database 获取当前 database 实例 -### Model.parseApiJson +### Model.parseApiJson 待完善 将 filter、fields、sort 等参数转换为 where、attributes、include、order 等 @@ -719,14 +953,100 @@ await Pwd.verify('123456', user.password); // true } ``` +#### page + +当前分页 + +#### perPage + +每页多少条数据,当 `perPage=-1` 时,输出所有数据,最多不超过 500 条 + +#### context + +注入上下文,目前主要用于处理 filter 的动态参数 + ### model.database 获取当前 database 实例 -### model.updateAssociations +### model.updateAssociations 待完善 更新关系数据 -### model.getValuesByFieldNames 实验性 +```ts +const user = User.create({}); +const post = Post.create({}); +await post.updateAssociations({ + // 支持直接提供外键值,一般为 Model.primaryKeyAttribute + user: 1, + // 或者是 object 对象,如果数据不存在会直接创建 + user: { + name: 'name1', + }, + // 也可以是 model 对象 + user, + // 如果是 hasMany 或 belongsToMany 关系,可以是上面三种写法,也可以是数组类型 + categories: 1, + tags: [ + 1, + {name: 'tag2'}, // object + tag, // tag 对象 + ], + comments: [ + { content: '' }, + comment, // comment model 对象 + ] +}); +``` -获取当前 model 在 scope 范围内的值 \ No newline at end of file + + +关系数据的新增或更新非常复杂,尤其在表单场景里非常受用。 + +注意:还需要支持仅关联和可新增或更新关系数据两种情况。 + +- 仅关联:只建立关系,不更新关系数据内容 +- 可新增或更新关系数据:建立关联的同时,更新关系数据内容 + + + +### model.getValuesByFieldNames 待完善 + +获取当前 model 在 scope 范围内的值 + +如,某 model 的 dataValues 如下: + +```ts +{ + id: 1, + status: 'publish', + user: { // 关系数据,可能需要查询之后获取 + id: 1, + email: 'admin@example.com' + }, +} +``` + +```ts +await model.getValuesByFieldNames(['status', 'user.email']); + +// 理想化输出的结果为: +{ + status: 'publish', + 'user.email': 'admin@example.com', +} +``` + +可能用于与 filter 进行比较 + +```ts +compareFilterWithScope( + { + and: [{ status: 'publish' }] + }, + { + status: 'publish', + 'user.email': 'admin@example.com', + } +); // => true +``` \ No newline at end of file diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md index 102b0a104..3cf39b27a 100644 --- a/docs/guide/getting-started.md +++ b/docs/guide/getting-started.md @@ -38,7 +38,7 @@ toc: menu ## Npm & Yarn -找个地方创建并进入新目录 +创建并进入新目录 ```bash mkdir my-nocobase-project && cd my-nocobase-project @@ -89,7 +89,7 @@ yarn nocobase start Docker 镜像暂未发布 -为了更方便的安装与部署,NocoBase 也发布了 Docker 镜像 `nocobase/nocobase`,你可以找个地方创建并配置 `docker-compose.yml` 文件,样例如下: +为了更方便的安装与部署,NocoBase 也发布了 Docker 镜像 `nocobase/nocobase`,创建并配置 `docker-compose.yml` 文件,样例如下: ```yaml version: "3"