schema-dsl 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +130 -113
- package/LICENSE +21 -21
- package/README.md +628 -628
- package/dist/{DslBuilder-DkLaOo9Q.d.ts → DslBuilder-BIgQOAXp.d.ts} +2 -0
- package/dist/{DslBuilder-DQDN0ZxZ.d.cts → DslBuilder-CjHTucNQ.d.cts} +2 -0
- package/dist/{Validator-hFWKGxir.d.ts → Validator-CllRdrY0.d.ts} +1 -1
- package/dist/{Validator-C7GsVQOH.d.cts → Validator-D6okG9tr.d.cts} +1 -1
- package/dist/index.cjs +75 -29
- package/dist/index.d.cts +10 -4
- package/dist/index.d.ts +10 -4
- package/dist/index.js +75 -29
- package/dist/plugins/custom-format.cjs +33 -17
- package/dist/plugins/custom-format.d.cts +1 -1
- package/dist/plugins/custom-format.d.ts +1 -1
- package/dist/plugins/custom-format.js +33 -17
- package/dist/plugins/custom-type-example.cjs +33 -17
- package/dist/plugins/custom-type-example.d.cts +1 -1
- package/dist/plugins/custom-type-example.d.ts +1 -1
- package/dist/plugins/custom-type-example.js +33 -17
- package/dist/plugins/custom-validator.cjs +0 -2
- package/dist/plugins/custom-validator.d.cts +1 -1
- package/dist/plugins/custom-validator.d.ts +1 -1
- package/dist/plugins/custom-validator.js +0 -2
- package/docs/FEATURE-INDEX.md +553 -553
- package/docs/add-custom-locale.md +496 -496
- package/docs/add-keyword.md +24 -24
- package/docs/api-reference.md +1047 -1047
- package/docs/api.md +13 -13
- package/docs/best-practices-project-structure.md +417 -417
- package/docs/best-practices.md +712 -712
- package/docs/cache-manager.md +344 -344
- package/docs/compile.md +45 -45
- package/docs/conditional-api.md +1307 -1307
- package/docs/custom-extensions-guide.md +339 -339
- package/docs/design-philosophy.md +606 -606
- package/docs/doc-index.md +324 -324
- package/docs/dsl-syntax.md +714 -714
- package/docs/dynamic-locale.md +608 -608
- package/docs/enum.md +482 -482
- package/docs/error-handling.md +1975 -1975
- package/docs/export-guide.md +501 -501
- package/docs/export-limitations.md +567 -567
- package/docs/faq.md +596 -596
- package/docs/frontend-i18n-guide.md +307 -307
- package/docs/i18n-user-guide.md +487 -487
- package/docs/i18n.md +476 -476
- package/docs/index.md +48 -48
- package/docs/json-schema-basics.md +40 -40
- package/docs/label-vs-description.md +271 -271
- package/docs/markdown-exporter.md +406 -406
- package/docs/mongodb-exporter.md +302 -302
- package/docs/multi-language.md +26 -26
- package/docs/multi-type-support.md +322 -322
- package/docs/mysql-exporter.md +280 -280
- package/docs/number-operators.md +449 -449
- package/docs/optional-marker-guide.md +326 -326
- package/docs/performance-guide.md +49 -49
- package/docs/plugin-system.md +381 -381
- package/docs/plugin-type-registration.md +34 -34
- package/docs/postgresql-exporter.md +311 -311
- package/docs/public/favicon.svg +4 -4
- package/docs/quick-start.md +435 -435
- package/docs/runtime-locale-support.md +532 -532
- package/docs/schema-helper.md +345 -345
- package/docs/schema-utils-advanced-issues.md +23 -23
- package/docs/schema-utils-best-practices.md +20 -20
- package/docs/schema-utils-chaining.md +150 -150
- package/docs/schema-utils.md +524 -524
- package/docs/security-checklist.md +20 -20
- package/docs/string-extensions.md +488 -488
- package/docs/troubleshooting.md +486 -486
- package/docs/type-converter.md +310 -310
- package/docs/type-reference.md +242 -242
- package/docs/typescript-guide.md +584 -584
- package/docs/union-type-guide.md +157 -157
- package/docs/union-types.md +284 -284
- package/docs/validate-async.md +491 -491
- package/docs/validate-batch.md +49 -49
- package/docs/validate-dsl-object-support.md +578 -578
- package/docs/validate.md +506 -506
- package/docs/validation-guide.md +502 -502
- package/docs/validator.md +39 -39
- package/package.json +131 -131
- package/plugins/custom-format.cjs +8 -8
- package/plugins/custom-type-example.cjs +8 -8
- package/plugins/custom-validator.cjs +8 -8
- package/src/adapters/DslAdapter.ts +111 -111
- package/src/adapters/index.ts +1 -1
- package/src/config/constants.ts +83 -83
- package/src/config/index.ts +2 -2
- package/src/config/patterns.ts +77 -77
- package/src/core/CacheManager.ts +169 -159
- package/src/core/ConditionalBuilder.ts +382 -382
- package/src/core/ConditionalRuntime.ts +27 -27
- package/src/core/ConditionalValidator.ts +254 -254
- package/src/core/DslBuilder.ts +687 -677
- package/src/core/ErrorCodes.ts +38 -38
- package/src/core/ErrorFormatter.ts +271 -271
- package/src/core/JSONSchemaCore.ts +65 -65
- package/src/core/Locale.ts +187 -187
- package/src/core/MessageTemplate.ts +42 -42
- package/src/core/ObjectDslBuilder.ts +64 -64
- package/src/core/PluginManager.ts +326 -326
- package/src/core/StringExtensions.ts +140 -140
- package/src/core/TemplateEngine.ts +44 -44
- package/src/core/Validator.ts +448 -448
- package/src/errors/I18nError.ts +159 -159
- package/src/errors/ValidationError.ts +105 -105
- package/src/exporters/BaseExporter.ts +60 -60
- package/src/exporters/MarkdownExporter.ts +305 -305
- package/src/exporters/MongoDBExporter.ts +126 -126
- package/src/exporters/MySQLExporter.ts +156 -155
- package/src/exporters/PostgreSQLExporter.ts +222 -222
- package/src/exporters/index.ts +18 -18
- package/src/index.ts +651 -633
- package/src/locales/en-US.ts +160 -160
- package/src/locales/es-ES.ts +160 -160
- package/src/locales/fr-FR.ts +160 -160
- package/src/locales/index.ts +103 -103
- package/src/locales/ja-JP.ts +160 -160
- package/src/locales/types.ts +156 -156
- package/src/locales/zh-CN.ts +160 -160
- package/src/parser/ConstraintParser.ts +101 -101
- package/src/parser/DslParser.ts +470 -470
- package/src/parser/SchemaCompiler.ts +66 -66
- package/src/parser/TypeRegistry.ts +250 -250
- package/src/parser/index.ts +6 -6
- package/src/plugins/custom-format.ts +124 -126
- package/src/plugins/custom-type-example.ts +106 -108
- package/src/plugins/custom-validator.ts +138 -140
- package/src/types/conditional.ts +28 -28
- package/src/types/config.ts +59 -59
- package/src/types/dsl.ts +131 -131
- package/src/types/error.ts +60 -60
- package/src/types/index.ts +17 -17
- package/src/types/infer.ts +127 -127
- package/src/types/plugin.ts +58 -58
- package/src/types/safe-regex.d.ts +9 -9
- package/src/types/schema.ts +66 -66
- package/src/types/validate.ts +71 -71
- package/src/utils/SchemaHelper.ts +196 -196
- package/src/utils/SchemaUtils.ts +365 -346
- package/src/utils/TypeConverter.ts +215 -215
- package/src/utils/index.ts +10 -10
- package/src/validators/CustomKeywords.ts +477 -477
package/docs/export-guide.md
CHANGED
|
@@ -1,501 +1,501 @@
|
|
|
1
|
-
# 导出完整指南
|
|
2
|
-
|
|
3
|
-
> **用途**: Schema 到多种输出格式的完整导出指南
|
|
4
|
-
> **阅读时间**: 10分钟
|
|
5
|
-
|
|
6
|
-
> ⚠️ **重要提示**: 并非所有 schema-dsl 特性都能导出到数据库。请先阅读 [导出限制说明](export-limitations.md) 了解哪些特性不支持导出。
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
## 📑 目录
|
|
11
|
-
|
|
12
|
-
- [概述](#概述)
|
|
13
|
-
- [快速开始](#快速开始)
|
|
14
|
-
- [MongoDB 导出](#mongodb-导出)
|
|
15
|
-
- [MySQL 导出](#mysql-导出)
|
|
16
|
-
- [PostgreSQL 导出](#postgresql-导出)
|
|
17
|
-
- [Markdown 导出](#markdown-导出)
|
|
18
|
-
- [导出对比](#导出对比)
|
|
19
|
-
- [最佳实践](#最佳实践)
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## 概述
|
|
24
|
-
|
|
25
|
-
schema-dsl 支持将 JSON Schema 导出为多种数据库结构或文档格式,实现“一次定义,多处使用”。
|
|
26
|
-
|
|
27
|
-
### 支持的导出格式
|
|
28
|
-
|
|
29
|
-
| 类型 | 导出器 | 输出格式 |
|
|
30
|
-
|------|--------|----------|
|
|
31
|
-
| MongoDB | `MongoDBExporter` | `$jsonSchema` 验证文档 |
|
|
32
|
-
| MySQL | `MySQLExporter` | `CREATE TABLE` DDL |
|
|
33
|
-
| PostgreSQL | `PostgreSQLExporter` | `CREATE TABLE` DDL + COMMENT |
|
|
34
|
-
| Markdown | `MarkdownExporter` | 面向人类阅读的 Markdown 文档 |
|
|
35
|
-
|
|
36
|
-
其中 `MarkdownExporter` 更适合生成接口字段说明、表单文档或内部规范文档,完整用法见 [Markdown 导出器](./markdown-exporter.md)。
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## 快速开始
|
|
41
|
-
|
|
42
|
-
```javascript
|
|
43
|
-
const { dsl, exporters } = require('schema-dsl');
|
|
44
|
-
|
|
45
|
-
// 定义统一的 Schema
|
|
46
|
-
const userSchema = dsl({
|
|
47
|
-
id: 'uuid!',
|
|
48
|
-
username: 'string:3-32!'
|
|
49
|
-
.pattern(/^[a-zA-Z0-9_]+$/)
|
|
50
|
-
.description('用户登录名'),
|
|
51
|
-
email: 'email!'
|
|
52
|
-
.description('用户邮箱'),
|
|
53
|
-
age: 'number:18-120',
|
|
54
|
-
status: 'active|inactive|banned',
|
|
55
|
-
createdAt: 'datetime!'
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
// 导出到不同目标
|
|
59
|
-
const mongoSchema = new exporters.MongoDBExporter().export(userSchema);
|
|
60
|
-
const mysqlDdl = new exporters.MySQLExporter().export('users', userSchema);
|
|
61
|
-
const pgDdl = new exporters.PostgreSQLExporter().export('users', userSchema);
|
|
62
|
-
const markdownDoc = exporters.MarkdownExporter.export(userSchema, {
|
|
63
|
-
title: '用户 Schema 文档'
|
|
64
|
-
});
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
---
|
|
68
|
-
|
|
69
|
-
## Markdown 导出
|
|
70
|
-
|
|
71
|
-
如果你的目标不是数据库,而是给研发、测试、产品或接口使用方生成一份可直接阅读的字段说明文档,可以使用 `MarkdownExporter`:
|
|
72
|
-
|
|
73
|
-
```javascript
|
|
74
|
-
const { dsl, exporters } = require('schema-dsl');
|
|
75
|
-
|
|
76
|
-
const schema = dsl({
|
|
77
|
-
username: 'string:3-32!'.description('登录账号'),
|
|
78
|
-
email: 'email!'.description('联系邮箱'),
|
|
79
|
-
age: 'number:18-120'.description('年龄')
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
const markdown = exporters.MarkdownExporter.export(schema, {
|
|
83
|
-
title: '用户注册字段说明',
|
|
84
|
-
locale: 'zh-CN'
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
console.log(markdown);
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
更完整的选项、示例和多语言输出说明见 [Markdown 导出器](./markdown-exporter.md)。
|
|
91
|
-
|
|
92
|
-
---
|
|
93
|
-
|
|
94
|
-
## MongoDB 导出
|
|
95
|
-
|
|
96
|
-
### 基本用法
|
|
97
|
-
|
|
98
|
-
```javascript
|
|
99
|
-
const { dsl, exporters } = require('schema-dsl');
|
|
100
|
-
|
|
101
|
-
const schema = dsl({
|
|
102
|
-
username: 'string:3-32!',
|
|
103
|
-
email: 'email!',
|
|
104
|
-
age: 'number:18-120'
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
const exporter = new exporters.MongoDBExporter();
|
|
108
|
-
const mongoSchema = exporter.export(schema);
|
|
109
|
-
|
|
110
|
-
console.log(JSON.stringify(mongoSchema, null, 2));
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
**输出**:
|
|
114
|
-
|
|
115
|
-
```json
|
|
116
|
-
{
|
|
117
|
-
"$jsonSchema": {
|
|
118
|
-
"bsonType": "object",
|
|
119
|
-
"required": ["username", "email"],
|
|
120
|
-
"properties": {
|
|
121
|
-
"username": {
|
|
122
|
-
"bsonType": "string",
|
|
123
|
-
"minLength": 3,
|
|
124
|
-
"maxLength": 32
|
|
125
|
-
},
|
|
126
|
-
"email": {
|
|
127
|
-
"bsonType": "string"
|
|
128
|
-
},
|
|
129
|
-
"age": {
|
|
130
|
-
"bsonType": "double",
|
|
131
|
-
"minimum": 18,
|
|
132
|
-
"maximum": 120
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
### 生成创建命令
|
|
140
|
-
|
|
141
|
-
```javascript
|
|
142
|
-
const command = exporter.generateCommand('users', schema);
|
|
143
|
-
console.log(command);
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
**输出**:
|
|
147
|
-
|
|
148
|
-
```javascript
|
|
149
|
-
db.createCollection("users", {
|
|
150
|
-
"validator": {
|
|
151
|
-
"$jsonSchema": { ... }
|
|
152
|
-
},
|
|
153
|
-
"validationLevel": "moderate",
|
|
154
|
-
"validationAction": "error"
|
|
155
|
-
})
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### 在 MongoDB 中使用
|
|
159
|
-
|
|
160
|
-
```javascript
|
|
161
|
-
const { MongoClient } = require('mongodb');
|
|
162
|
-
|
|
163
|
-
async function setupCollection() {
|
|
164
|
-
const client = new MongoClient('mongodb://localhost:27017');
|
|
165
|
-
await client.connect();
|
|
166
|
-
|
|
167
|
-
const db = client.db('myapp');
|
|
168
|
-
const exporter = new exporters.MongoDBExporter({ strict: true });
|
|
169
|
-
const { options } = exporter.generateCreateCommand('users', schema);
|
|
170
|
-
|
|
171
|
-
await db.createCollection('users', options);
|
|
172
|
-
console.log('创建带验证的集合成功');
|
|
173
|
-
}
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
## MySQL 导出
|
|
179
|
-
|
|
180
|
-
### 基本用法
|
|
181
|
-
|
|
182
|
-
```javascript
|
|
183
|
-
const { dsl, exporters } = require('schema-dsl');
|
|
184
|
-
|
|
185
|
-
const schema = dsl({
|
|
186
|
-
id: 'string!',
|
|
187
|
-
username: 'string:3-32!',
|
|
188
|
-
email: 'email!',
|
|
189
|
-
age: 'number:0-150',
|
|
190
|
-
status: 'active|inactive'
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
const exporter = new exporters.MySQLExporter();
|
|
194
|
-
const ddl = exporter.export('users', schema);
|
|
195
|
-
|
|
196
|
-
console.log(ddl);
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
**输出**:
|
|
200
|
-
|
|
201
|
-
```sql
|
|
202
|
-
CREATE TABLE `users` (
|
|
203
|
-
`id` VARCHAR(255) NOT NULL,
|
|
204
|
-
`username` VARCHAR(32) NOT NULL,
|
|
205
|
-
`email` VARCHAR(255) NOT NULL,
|
|
206
|
-
`age` DOUBLE NULL,
|
|
207
|
-
`status` VARCHAR(255) NULL,
|
|
208
|
-
PRIMARY KEY (`id`)
|
|
209
|
-
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### 配置选项
|
|
213
|
-
|
|
214
|
-
```javascript
|
|
215
|
-
const exporter = new exporters.MySQLExporter({
|
|
216
|
-
engine: 'InnoDB', // 存储引擎
|
|
217
|
-
charset: 'utf8mb4', // 字符集
|
|
218
|
-
collate: 'utf8mb4_unicode_ci' // 排序规则
|
|
219
|
-
});
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### 生成索引
|
|
223
|
-
|
|
224
|
-
```javascript
|
|
225
|
-
// 唯一索引
|
|
226
|
-
console.log(exporter.generateIndex('users', 'email', { unique: true }));
|
|
227
|
-
// CREATE UNIQUE INDEX `idx_users_email` ON `users` (`email`);
|
|
228
|
-
|
|
229
|
-
// 普通索引
|
|
230
|
-
console.log(exporter.generateIndex('users', 'status'));
|
|
231
|
-
// CREATE INDEX `idx_users_status` ON `users` (`status`);
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
---
|
|
235
|
-
|
|
236
|
-
## PostgreSQL 导出
|
|
237
|
-
|
|
238
|
-
### 基本用法
|
|
239
|
-
|
|
240
|
-
```javascript
|
|
241
|
-
const { dsl, exporters } = require('schema-dsl');
|
|
242
|
-
|
|
243
|
-
const schema = dsl({
|
|
244
|
-
id: 'uuid!',
|
|
245
|
-
username: 'string:3-32!'
|
|
246
|
-
.description('用户登录名'),
|
|
247
|
-
email: 'email!'
|
|
248
|
-
.description('用户邮箱'),
|
|
249
|
-
age: 'number:18-120',
|
|
250
|
-
status: 'active|inactive|banned',
|
|
251
|
-
metadata: {
|
|
252
|
-
lastLogin: 'datetime',
|
|
253
|
-
preferences: 'object'
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
const exporter = new exporters.PostgreSQLExporter();
|
|
258
|
-
const ddl = exporter.export('users', schema);
|
|
259
|
-
|
|
260
|
-
console.log(ddl);
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
**输出**:
|
|
264
|
-
|
|
265
|
-
```sql
|
|
266
|
-
CREATE TABLE public.users (
|
|
267
|
-
id UUID NOT NULL,
|
|
268
|
-
username VARCHAR(32) NOT NULL CHECK (LENGTH(username) BETWEEN 3 AND 32),
|
|
269
|
-
email VARCHAR(255) NOT NULL,
|
|
270
|
-
age DOUBLE PRECISION CHECK (age BETWEEN 18 AND 120),
|
|
271
|
-
status VARCHAR(255) CHECK (status IN ('active', 'inactive', 'banned')),
|
|
272
|
-
metadata JSONB,
|
|
273
|
-
PRIMARY KEY (id)
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
COMMENT ON COLUMN public.users.username IS '用户登录名';
|
|
277
|
-
COMMENT ON COLUMN public.users.email IS '用户邮箱';
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
### 配置选项
|
|
281
|
-
|
|
282
|
-
```javascript
|
|
283
|
-
const exporter = new exporters.PostgreSQLExporter({
|
|
284
|
-
schema: 'myapp' // PostgreSQL schema 名称
|
|
285
|
-
});
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
### 生成索引
|
|
289
|
-
|
|
290
|
-
```javascript
|
|
291
|
-
// B-tree 索引(默认)
|
|
292
|
-
console.log(exporter.generateIndex('users', 'email', { unique: true }));
|
|
293
|
-
// CREATE UNIQUE INDEX idx_users_email ON public.users USING btree (email);
|
|
294
|
-
|
|
295
|
-
// GIN 索引(用于 JSONB)
|
|
296
|
-
console.log(exporter.generateIndex('users', 'metadata', { method: 'gin' }));
|
|
297
|
-
// CREATE INDEX idx_users_metadata ON public.users USING gin (metadata);
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
---
|
|
301
|
-
|
|
302
|
-
## 导出对比
|
|
303
|
-
|
|
304
|
-
### 同一 Schema 的三种导出
|
|
305
|
-
|
|
306
|
-
```javascript
|
|
307
|
-
const schema = dsl({
|
|
308
|
-
id: 'uuid!',
|
|
309
|
-
name: 'string:3-100!',
|
|
310
|
-
score: 'number:0-100',
|
|
311
|
-
tags: 'array<string>',
|
|
312
|
-
active: 'boolean'
|
|
313
|
-
});
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
| 字段 | MongoDB | MySQL | PostgreSQL |
|
|
317
|
-
|------|---------|-------|------------|
|
|
318
|
-
| `id` | `bsonType: 'string'` | `VARCHAR(255) NOT NULL` | `UUID NOT NULL` |
|
|
319
|
-
| `name` | `bsonType: 'string', minLength: 3, maxLength: 100` | `VARCHAR(100) NOT NULL` | `VARCHAR(100) NOT NULL CHECK (...)` |
|
|
320
|
-
| `score` | `bsonType: 'double', minimum: 0, maximum: 100` | `DOUBLE NULL` | `DOUBLE PRECISION CHECK (...)` |
|
|
321
|
-
| `tags` | `bsonType: 'array', items: {...}` | `JSON NULL` | `JSONB` |
|
|
322
|
-
| `active` | `bsonType: 'bool'` | `BOOLEAN NULL` | `BOOLEAN` |
|
|
323
|
-
|
|
324
|
-
### 约束支持对比
|
|
325
|
-
|
|
326
|
-
| 约束类型 | MongoDB | MySQL | PostgreSQL |
|
|
327
|
-
|---------|---------|-------|------------|
|
|
328
|
-
| NOT NULL | ✅ `required` | ✅ `NOT NULL` | ✅ `NOT NULL` |
|
|
329
|
-
| 长度范围 | ✅ `minLength/maxLength` | ❌ | ✅ `CHECK` |
|
|
330
|
-
| 数值范围 | ✅ `minimum/maximum` | ❌ | ✅ `CHECK` |
|
|
331
|
-
| 枚举 | ✅ `enum` | ❌ | ✅ `CHECK` |
|
|
332
|
-
| 正则 | ✅ `pattern` | ❌ | ❌ |
|
|
333
|
-
| 默认值 | ❌ | ✅ `DEFAULT` | ✅ `DEFAULT` |
|
|
334
|
-
| 注释 | ❌ | ✅ `COMMENT` | ✅ `COMMENT ON` |
|
|
335
|
-
|
|
336
|
-
---
|
|
337
|
-
|
|
338
|
-
## 最佳实践
|
|
339
|
-
|
|
340
|
-
### 1. 使用 description 添加注释
|
|
341
|
-
|
|
342
|
-
```javascript
|
|
343
|
-
const schema = dsl({
|
|
344
|
-
username: 'string:3-32!'
|
|
345
|
-
.description('用户登录名,只能包含字母数字下划线'),
|
|
346
|
-
email: 'email!'
|
|
347
|
-
.description('用户邮箱,用于登录和接收通知')
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
// MySQL 和 PostgreSQL 会生成带注释的 DDL
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
### 2. 统一定义,多处导出
|
|
354
|
-
|
|
355
|
-
```javascript
|
|
356
|
-
// schemas/user.js
|
|
357
|
-
const { dsl } = require('schema-dsl');
|
|
358
|
-
|
|
359
|
-
module.exports = dsl({
|
|
360
|
-
id: 'uuid!',
|
|
361
|
-
username: 'string:3-32!',
|
|
362
|
-
email: 'email!',
|
|
363
|
-
createdAt: 'datetime!'
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
// 导出脚本
|
|
367
|
-
const { exporters } = require('schema-dsl');
|
|
368
|
-
const userSchema = require('./schemas/user');
|
|
369
|
-
|
|
370
|
-
// 生成所有数据库的 DDL
|
|
371
|
-
const outputs = {
|
|
372
|
-
mongo: new exporters.MongoDBExporter().generateCommand('users', userSchema),
|
|
373
|
-
mysql: new exporters.MySQLExporter().export('users', userSchema),
|
|
374
|
-
postgres: new exporters.PostgreSQLExporter().export('users', userSchema)
|
|
375
|
-
};
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
### 3. 自动化迁移脚本
|
|
379
|
-
|
|
380
|
-
```javascript
|
|
381
|
-
const fs = require('fs');
|
|
382
|
-
const { dsl, exporters } = require('schema-dsl');
|
|
383
|
-
|
|
384
|
-
function generateMigration(schemaName, schema) {
|
|
385
|
-
const mysql = new exporters.MySQLExporter();
|
|
386
|
-
const pg = new exporters.PostgreSQLExporter();
|
|
387
|
-
|
|
388
|
-
const timestamp = Date.now();
|
|
389
|
-
|
|
390
|
-
// 生成 MySQL 迁移
|
|
391
|
-
fs.writeFileSync(
|
|
392
|
-
`migrations/${timestamp}_create_${schemaName}.mysql.sql`,
|
|
393
|
-
mysql.export(schemaName, schema)
|
|
394
|
-
);
|
|
395
|
-
|
|
396
|
-
// 生成 PostgreSQL 迁移
|
|
397
|
-
fs.writeFileSync(
|
|
398
|
-
`migrations/${timestamp}_create_${schemaName}.pg.sql`,
|
|
399
|
-
pg.export(schemaName, schema)
|
|
400
|
-
);
|
|
401
|
-
|
|
402
|
-
console.log(`生成迁移文件: ${schemaName}`);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
generateMigration('users', userSchema);
|
|
406
|
-
generateMigration('orders', orderSchema);
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
### 4. 版本管理
|
|
410
|
-
|
|
411
|
-
```javascript
|
|
412
|
-
// 在 Schema 中添加版本信息
|
|
413
|
-
const userSchemaV1 = dsl({ username: 'string!' });
|
|
414
|
-
const userSchemaV2 = dsl({ username: 'string:3-32!', email: 'email!' });
|
|
415
|
-
|
|
416
|
-
// 导出时标注版本
|
|
417
|
-
function exportWithVersion(name, schema, version) {
|
|
418
|
-
const ddl = new exporters.MySQLExporter().export(name, schema);
|
|
419
|
-
return `-- Schema Version: ${version}\n-- Generated: ${new Date().toISOString()}\n\n${ddl}`;
|
|
420
|
-
}
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
---
|
|
424
|
-
|
|
425
|
-
## 完整示例
|
|
426
|
-
|
|
427
|
-
### 电商系统 Schema 导出
|
|
428
|
-
|
|
429
|
-
```javascript
|
|
430
|
-
const { dsl, exporters } = require('schema-dsl');
|
|
431
|
-
const fs = require('fs');
|
|
432
|
-
|
|
433
|
-
// 用户 Schema
|
|
434
|
-
const userSchema = dsl({
|
|
435
|
-
id: 'uuid!',
|
|
436
|
-
username: 'string:3-32!'.description('用户名'),
|
|
437
|
-
email: 'email!'.description('邮箱'),
|
|
438
|
-
phone: 'string:11'.phone('cn').description('手机号'),
|
|
439
|
-
status: 'active|inactive|banned',
|
|
440
|
-
createdAt: 'datetime!'
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
// 商品 Schema
|
|
444
|
-
const productSchema = dsl({
|
|
445
|
-
id: 'uuid!',
|
|
446
|
-
name: 'string:3-200!'.description('商品名称'),
|
|
447
|
-
price: 'number:0-'.description('价格'),
|
|
448
|
-
stock: 'integer:0-'.description('库存'),
|
|
449
|
-
category: 'string:2-50!',
|
|
450
|
-
tags: 'array<string>',
|
|
451
|
-
active: 'boolean'
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
// 订单 Schema
|
|
455
|
-
const orderSchema = dsl({
|
|
456
|
-
id: 'uuid!',
|
|
457
|
-
userId: 'uuid!',
|
|
458
|
-
items: 'array!1-100',
|
|
459
|
-
totalAmount: 'number:0-!',
|
|
460
|
-
status: 'pending|paid|shipped|delivered|cancelled',
|
|
461
|
-
createdAt: 'datetime!',
|
|
462
|
-
updatedAt: 'datetime'
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
// 导出所有 Schema
|
|
466
|
-
const schemas = { users: userSchema, products: productSchema, orders: orderSchema };
|
|
467
|
-
const mysqlExporter = new exporters.MySQLExporter();
|
|
468
|
-
const pgExporter = new exporters.PostgreSQLExporter({ schema: 'ecommerce' });
|
|
469
|
-
|
|
470
|
-
let mysqlDdl = '';
|
|
471
|
-
let pgDdl = '';
|
|
472
|
-
|
|
473
|
-
for (const [name, schema] of Object.entries(schemas)) {
|
|
474
|
-
mysqlDdl += mysqlExporter.export(name, schema) + '\n\n';
|
|
475
|
-
pgDdl += pgExporter.export(name, schema) + '\n\n';
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
fs.writeFileSync('schema.mysql.sql', mysqlDdl);
|
|
479
|
-
fs.writeFileSync('schema.pg.sql', pgDdl);
|
|
480
|
-
|
|
481
|
-
console.log('导出完成!');
|
|
482
|
-
```
|
|
483
|
-
|
|
484
|
-
---
|
|
485
|
-
|
|
486
|
-
## 相关文档
|
|
487
|
-
|
|
488
|
-
- [**导出限制说明**](export-limitations.md) ⚠️ **必读**
|
|
489
|
-
- [MongoDB 导出器](mongodb-exporter.md)
|
|
490
|
-
- [MySQL 导出器](mysql-exporter.md)
|
|
491
|
-
- [PostgreSQL 导出器](postgresql-exporter.md)
|
|
492
|
-
- [TypeConverter](type-converter.md)
|
|
493
|
-
- [DSL 语法](dsl-syntax.md)
|
|
494
|
-
|
|
495
|
-
---
|
|
496
|
-
|
|
497
|
-
## 对应示例文件
|
|
498
|
-
|
|
499
|
-
**示例入口**: [export-guide.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/export-guide.ts)
|
|
500
|
-
**说明**: 覆盖同一组 schema 同时导出到 MongoDB、MySQL 和 PostgreSQL 的最小工作流,便于对照多导出器结果。
|
|
501
|
-
|
|
1
|
+
# 导出完整指南
|
|
2
|
+
|
|
3
|
+
> **用途**: Schema 到多种输出格式的完整导出指南
|
|
4
|
+
> **阅读时间**: 10分钟
|
|
5
|
+
|
|
6
|
+
> ⚠️ **重要提示**: 并非所有 schema-dsl 特性都能导出到数据库。请先阅读 [导出限制说明](export-limitations.md) 了解哪些特性不支持导出。
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 📑 目录
|
|
11
|
+
|
|
12
|
+
- [概述](#概述)
|
|
13
|
+
- [快速开始](#快速开始)
|
|
14
|
+
- [MongoDB 导出](#mongodb-导出)
|
|
15
|
+
- [MySQL 导出](#mysql-导出)
|
|
16
|
+
- [PostgreSQL 导出](#postgresql-导出)
|
|
17
|
+
- [Markdown 导出](#markdown-导出)
|
|
18
|
+
- [导出对比](#导出对比)
|
|
19
|
+
- [最佳实践](#最佳实践)
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 概述
|
|
24
|
+
|
|
25
|
+
schema-dsl 支持将 JSON Schema 导出为多种数据库结构或文档格式,实现“一次定义,多处使用”。
|
|
26
|
+
|
|
27
|
+
### 支持的导出格式
|
|
28
|
+
|
|
29
|
+
| 类型 | 导出器 | 输出格式 |
|
|
30
|
+
|------|--------|----------|
|
|
31
|
+
| MongoDB | `MongoDBExporter` | `$jsonSchema` 验证文档 |
|
|
32
|
+
| MySQL | `MySQLExporter` | `CREATE TABLE` DDL |
|
|
33
|
+
| PostgreSQL | `PostgreSQLExporter` | `CREATE TABLE` DDL + COMMENT |
|
|
34
|
+
| Markdown | `MarkdownExporter` | 面向人类阅读的 Markdown 文档 |
|
|
35
|
+
|
|
36
|
+
其中 `MarkdownExporter` 更适合生成接口字段说明、表单文档或内部规范文档,完整用法见 [Markdown 导出器](./markdown-exporter.md)。
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 快速开始
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
const { dsl, exporters } = require('schema-dsl');
|
|
44
|
+
|
|
45
|
+
// 定义统一的 Schema
|
|
46
|
+
const userSchema = dsl({
|
|
47
|
+
id: 'uuid!',
|
|
48
|
+
username: 'string:3-32!'
|
|
49
|
+
.pattern(/^[a-zA-Z0-9_]+$/)
|
|
50
|
+
.description('用户登录名'),
|
|
51
|
+
email: 'email!'
|
|
52
|
+
.description('用户邮箱'),
|
|
53
|
+
age: 'number:18-120',
|
|
54
|
+
status: 'active|inactive|banned',
|
|
55
|
+
createdAt: 'datetime!'
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// 导出到不同目标
|
|
59
|
+
const mongoSchema = new exporters.MongoDBExporter().export(userSchema);
|
|
60
|
+
const mysqlDdl = new exporters.MySQLExporter().export('users', userSchema);
|
|
61
|
+
const pgDdl = new exporters.PostgreSQLExporter().export('users', userSchema);
|
|
62
|
+
const markdownDoc = exporters.MarkdownExporter.export(userSchema, {
|
|
63
|
+
title: '用户 Schema 文档'
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Markdown 导出
|
|
70
|
+
|
|
71
|
+
如果你的目标不是数据库,而是给研发、测试、产品或接口使用方生成一份可直接阅读的字段说明文档,可以使用 `MarkdownExporter`:
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
const { dsl, exporters } = require('schema-dsl');
|
|
75
|
+
|
|
76
|
+
const schema = dsl({
|
|
77
|
+
username: 'string:3-32!'.description('登录账号'),
|
|
78
|
+
email: 'email!'.description('联系邮箱'),
|
|
79
|
+
age: 'number:18-120'.description('年龄')
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const markdown = exporters.MarkdownExporter.export(schema, {
|
|
83
|
+
title: '用户注册字段说明',
|
|
84
|
+
locale: 'zh-CN'
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
console.log(markdown);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
更完整的选项、示例和多语言输出说明见 [Markdown 导出器](./markdown-exporter.md)。
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## MongoDB 导出
|
|
95
|
+
|
|
96
|
+
### 基本用法
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
const { dsl, exporters } = require('schema-dsl');
|
|
100
|
+
|
|
101
|
+
const schema = dsl({
|
|
102
|
+
username: 'string:3-32!',
|
|
103
|
+
email: 'email!',
|
|
104
|
+
age: 'number:18-120'
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const exporter = new exporters.MongoDBExporter();
|
|
108
|
+
const mongoSchema = exporter.export(schema);
|
|
109
|
+
|
|
110
|
+
console.log(JSON.stringify(mongoSchema, null, 2));
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**输出**:
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"$jsonSchema": {
|
|
118
|
+
"bsonType": "object",
|
|
119
|
+
"required": ["username", "email"],
|
|
120
|
+
"properties": {
|
|
121
|
+
"username": {
|
|
122
|
+
"bsonType": "string",
|
|
123
|
+
"minLength": 3,
|
|
124
|
+
"maxLength": 32
|
|
125
|
+
},
|
|
126
|
+
"email": {
|
|
127
|
+
"bsonType": "string"
|
|
128
|
+
},
|
|
129
|
+
"age": {
|
|
130
|
+
"bsonType": "double",
|
|
131
|
+
"minimum": 18,
|
|
132
|
+
"maximum": 120
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 生成创建命令
|
|
140
|
+
|
|
141
|
+
```javascript
|
|
142
|
+
const command = exporter.generateCommand('users', schema);
|
|
143
|
+
console.log(command);
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**输出**:
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
db.createCollection("users", {
|
|
150
|
+
"validator": {
|
|
151
|
+
"$jsonSchema": { ... }
|
|
152
|
+
},
|
|
153
|
+
"validationLevel": "moderate",
|
|
154
|
+
"validationAction": "error"
|
|
155
|
+
})
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 在 MongoDB 中使用
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
const { MongoClient } = require('mongodb');
|
|
162
|
+
|
|
163
|
+
async function setupCollection() {
|
|
164
|
+
const client = new MongoClient('mongodb://localhost:27017');
|
|
165
|
+
await client.connect();
|
|
166
|
+
|
|
167
|
+
const db = client.db('myapp');
|
|
168
|
+
const exporter = new exporters.MongoDBExporter({ strict: true });
|
|
169
|
+
const { options } = exporter.generateCreateCommand('users', schema);
|
|
170
|
+
|
|
171
|
+
await db.createCollection('users', options);
|
|
172
|
+
console.log('创建带验证的集合成功');
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## MySQL 导出
|
|
179
|
+
|
|
180
|
+
### 基本用法
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
const { dsl, exporters } = require('schema-dsl');
|
|
184
|
+
|
|
185
|
+
const schema = dsl({
|
|
186
|
+
id: 'string!',
|
|
187
|
+
username: 'string:3-32!',
|
|
188
|
+
email: 'email!',
|
|
189
|
+
age: 'number:0-150',
|
|
190
|
+
status: 'active|inactive'
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const exporter = new exporters.MySQLExporter();
|
|
194
|
+
const ddl = exporter.export('users', schema);
|
|
195
|
+
|
|
196
|
+
console.log(ddl);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**输出**:
|
|
200
|
+
|
|
201
|
+
```sql
|
|
202
|
+
CREATE TABLE `users` (
|
|
203
|
+
`id` VARCHAR(255) NOT NULL,
|
|
204
|
+
`username` VARCHAR(32) NOT NULL,
|
|
205
|
+
`email` VARCHAR(255) NOT NULL,
|
|
206
|
+
`age` DOUBLE NULL,
|
|
207
|
+
`status` VARCHAR(255) NULL,
|
|
208
|
+
PRIMARY KEY (`id`)
|
|
209
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### 配置选项
|
|
213
|
+
|
|
214
|
+
```javascript
|
|
215
|
+
const exporter = new exporters.MySQLExporter({
|
|
216
|
+
engine: 'InnoDB', // 存储引擎
|
|
217
|
+
charset: 'utf8mb4', // 字符集
|
|
218
|
+
collate: 'utf8mb4_unicode_ci' // 排序规则
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### 生成索引
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
// 唯一索引
|
|
226
|
+
console.log(exporter.generateIndex('users', 'email', { unique: true }));
|
|
227
|
+
// CREATE UNIQUE INDEX `idx_users_email` ON `users` (`email`);
|
|
228
|
+
|
|
229
|
+
// 普通索引
|
|
230
|
+
console.log(exporter.generateIndex('users', 'status'));
|
|
231
|
+
// CREATE INDEX `idx_users_status` ON `users` (`status`);
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## PostgreSQL 导出
|
|
237
|
+
|
|
238
|
+
### 基本用法
|
|
239
|
+
|
|
240
|
+
```javascript
|
|
241
|
+
const { dsl, exporters } = require('schema-dsl');
|
|
242
|
+
|
|
243
|
+
const schema = dsl({
|
|
244
|
+
id: 'uuid!',
|
|
245
|
+
username: 'string:3-32!'
|
|
246
|
+
.description('用户登录名'),
|
|
247
|
+
email: 'email!'
|
|
248
|
+
.description('用户邮箱'),
|
|
249
|
+
age: 'number:18-120',
|
|
250
|
+
status: 'active|inactive|banned',
|
|
251
|
+
metadata: {
|
|
252
|
+
lastLogin: 'datetime',
|
|
253
|
+
preferences: 'object'
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
const exporter = new exporters.PostgreSQLExporter();
|
|
258
|
+
const ddl = exporter.export('users', schema);
|
|
259
|
+
|
|
260
|
+
console.log(ddl);
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**输出**:
|
|
264
|
+
|
|
265
|
+
```sql
|
|
266
|
+
CREATE TABLE public.users (
|
|
267
|
+
id UUID NOT NULL,
|
|
268
|
+
username VARCHAR(32) NOT NULL CHECK (LENGTH(username) BETWEEN 3 AND 32),
|
|
269
|
+
email VARCHAR(255) NOT NULL,
|
|
270
|
+
age DOUBLE PRECISION CHECK (age BETWEEN 18 AND 120),
|
|
271
|
+
status VARCHAR(255) CHECK (status IN ('active', 'inactive', 'banned')),
|
|
272
|
+
metadata JSONB,
|
|
273
|
+
PRIMARY KEY (id)
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
COMMENT ON COLUMN public.users.username IS '用户登录名';
|
|
277
|
+
COMMENT ON COLUMN public.users.email IS '用户邮箱';
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### 配置选项
|
|
281
|
+
|
|
282
|
+
```javascript
|
|
283
|
+
const exporter = new exporters.PostgreSQLExporter({
|
|
284
|
+
schema: 'myapp' // PostgreSQL schema 名称
|
|
285
|
+
});
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### 生成索引
|
|
289
|
+
|
|
290
|
+
```javascript
|
|
291
|
+
// B-tree 索引(默认)
|
|
292
|
+
console.log(exporter.generateIndex('users', 'email', { unique: true }));
|
|
293
|
+
// CREATE UNIQUE INDEX idx_users_email ON public.users USING btree (email);
|
|
294
|
+
|
|
295
|
+
// GIN 索引(用于 JSONB)
|
|
296
|
+
console.log(exporter.generateIndex('users', 'metadata', { method: 'gin' }));
|
|
297
|
+
// CREATE INDEX idx_users_metadata ON public.users USING gin (metadata);
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## 导出对比
|
|
303
|
+
|
|
304
|
+
### 同一 Schema 的三种导出
|
|
305
|
+
|
|
306
|
+
```javascript
|
|
307
|
+
const schema = dsl({
|
|
308
|
+
id: 'uuid!',
|
|
309
|
+
name: 'string:3-100!',
|
|
310
|
+
score: 'number:0-100',
|
|
311
|
+
tags: 'array<string>',
|
|
312
|
+
active: 'boolean'
|
|
313
|
+
});
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
| 字段 | MongoDB | MySQL | PostgreSQL |
|
|
317
|
+
|------|---------|-------|------------|
|
|
318
|
+
| `id` | `bsonType: 'string'` | `VARCHAR(255) NOT NULL` | `UUID NOT NULL` |
|
|
319
|
+
| `name` | `bsonType: 'string', minLength: 3, maxLength: 100` | `VARCHAR(100) NOT NULL` | `VARCHAR(100) NOT NULL CHECK (...)` |
|
|
320
|
+
| `score` | `bsonType: 'double', minimum: 0, maximum: 100` | `DOUBLE NULL` | `DOUBLE PRECISION CHECK (...)` |
|
|
321
|
+
| `tags` | `bsonType: 'array', items: {...}` | `JSON NULL` | `JSONB` |
|
|
322
|
+
| `active` | `bsonType: 'bool'` | `BOOLEAN NULL` | `BOOLEAN` |
|
|
323
|
+
|
|
324
|
+
### 约束支持对比
|
|
325
|
+
|
|
326
|
+
| 约束类型 | MongoDB | MySQL | PostgreSQL |
|
|
327
|
+
|---------|---------|-------|------------|
|
|
328
|
+
| NOT NULL | ✅ `required` | ✅ `NOT NULL` | ✅ `NOT NULL` |
|
|
329
|
+
| 长度范围 | ✅ `minLength/maxLength` | ❌ | ✅ `CHECK` |
|
|
330
|
+
| 数值范围 | ✅ `minimum/maximum` | ❌ | ✅ `CHECK` |
|
|
331
|
+
| 枚举 | ✅ `enum` | ❌ | ✅ `CHECK` |
|
|
332
|
+
| 正则 | ✅ `pattern` | ❌ | ❌ |
|
|
333
|
+
| 默认值 | ❌ | ✅ `DEFAULT` | ✅ `DEFAULT` |
|
|
334
|
+
| 注释 | ❌ | ✅ `COMMENT` | ✅ `COMMENT ON` |
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## 最佳实践
|
|
339
|
+
|
|
340
|
+
### 1. 使用 description 添加注释
|
|
341
|
+
|
|
342
|
+
```javascript
|
|
343
|
+
const schema = dsl({
|
|
344
|
+
username: 'string:3-32!'
|
|
345
|
+
.description('用户登录名,只能包含字母数字下划线'),
|
|
346
|
+
email: 'email!'
|
|
347
|
+
.description('用户邮箱,用于登录和接收通知')
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// MySQL 和 PostgreSQL 会生成带注释的 DDL
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### 2. 统一定义,多处导出
|
|
354
|
+
|
|
355
|
+
```javascript
|
|
356
|
+
// schemas/user.js
|
|
357
|
+
const { dsl } = require('schema-dsl');
|
|
358
|
+
|
|
359
|
+
module.exports = dsl({
|
|
360
|
+
id: 'uuid!',
|
|
361
|
+
username: 'string:3-32!',
|
|
362
|
+
email: 'email!',
|
|
363
|
+
createdAt: 'datetime!'
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// 导出脚本
|
|
367
|
+
const { exporters } = require('schema-dsl');
|
|
368
|
+
const userSchema = require('./schemas/user');
|
|
369
|
+
|
|
370
|
+
// 生成所有数据库的 DDL
|
|
371
|
+
const outputs = {
|
|
372
|
+
mongo: new exporters.MongoDBExporter().generateCommand('users', userSchema),
|
|
373
|
+
mysql: new exporters.MySQLExporter().export('users', userSchema),
|
|
374
|
+
postgres: new exporters.PostgreSQLExporter().export('users', userSchema)
|
|
375
|
+
};
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### 3. 自动化迁移脚本
|
|
379
|
+
|
|
380
|
+
```javascript
|
|
381
|
+
const fs = require('fs');
|
|
382
|
+
const { dsl, exporters } = require('schema-dsl');
|
|
383
|
+
|
|
384
|
+
function generateMigration(schemaName, schema) {
|
|
385
|
+
const mysql = new exporters.MySQLExporter();
|
|
386
|
+
const pg = new exporters.PostgreSQLExporter();
|
|
387
|
+
|
|
388
|
+
const timestamp = Date.now();
|
|
389
|
+
|
|
390
|
+
// 生成 MySQL 迁移
|
|
391
|
+
fs.writeFileSync(
|
|
392
|
+
`migrations/${timestamp}_create_${schemaName}.mysql.sql`,
|
|
393
|
+
mysql.export(schemaName, schema)
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
// 生成 PostgreSQL 迁移
|
|
397
|
+
fs.writeFileSync(
|
|
398
|
+
`migrations/${timestamp}_create_${schemaName}.pg.sql`,
|
|
399
|
+
pg.export(schemaName, schema)
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
console.log(`生成迁移文件: ${schemaName}`);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
generateMigration('users', userSchema);
|
|
406
|
+
generateMigration('orders', orderSchema);
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### 4. 版本管理
|
|
410
|
+
|
|
411
|
+
```javascript
|
|
412
|
+
// 在 Schema 中添加版本信息
|
|
413
|
+
const userSchemaV1 = dsl({ username: 'string!' });
|
|
414
|
+
const userSchemaV2 = dsl({ username: 'string:3-32!', email: 'email!' });
|
|
415
|
+
|
|
416
|
+
// 导出时标注版本
|
|
417
|
+
function exportWithVersion(name, schema, version) {
|
|
418
|
+
const ddl = new exporters.MySQLExporter().export(name, schema);
|
|
419
|
+
return `-- Schema Version: ${version}\n-- Generated: ${new Date().toISOString()}\n\n${ddl}`;
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## 完整示例
|
|
426
|
+
|
|
427
|
+
### 电商系统 Schema 导出
|
|
428
|
+
|
|
429
|
+
```javascript
|
|
430
|
+
const { dsl, exporters } = require('schema-dsl');
|
|
431
|
+
const fs = require('fs');
|
|
432
|
+
|
|
433
|
+
// 用户 Schema
|
|
434
|
+
const userSchema = dsl({
|
|
435
|
+
id: 'uuid!',
|
|
436
|
+
username: 'string:3-32!'.description('用户名'),
|
|
437
|
+
email: 'email!'.description('邮箱'),
|
|
438
|
+
phone: 'string:11'.phone('cn').description('手机号'),
|
|
439
|
+
status: 'active|inactive|banned',
|
|
440
|
+
createdAt: 'datetime!'
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
// 商品 Schema
|
|
444
|
+
const productSchema = dsl({
|
|
445
|
+
id: 'uuid!',
|
|
446
|
+
name: 'string:3-200!'.description('商品名称'),
|
|
447
|
+
price: 'number:0-'.description('价格'),
|
|
448
|
+
stock: 'integer:0-'.description('库存'),
|
|
449
|
+
category: 'string:2-50!',
|
|
450
|
+
tags: 'array<string>',
|
|
451
|
+
active: 'boolean'
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
// 订单 Schema
|
|
455
|
+
const orderSchema = dsl({
|
|
456
|
+
id: 'uuid!',
|
|
457
|
+
userId: 'uuid!',
|
|
458
|
+
items: 'array!1-100',
|
|
459
|
+
totalAmount: 'number:0-!',
|
|
460
|
+
status: 'pending|paid|shipped|delivered|cancelled',
|
|
461
|
+
createdAt: 'datetime!',
|
|
462
|
+
updatedAt: 'datetime'
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
// 导出所有 Schema
|
|
466
|
+
const schemas = { users: userSchema, products: productSchema, orders: orderSchema };
|
|
467
|
+
const mysqlExporter = new exporters.MySQLExporter();
|
|
468
|
+
const pgExporter = new exporters.PostgreSQLExporter({ schema: 'ecommerce' });
|
|
469
|
+
|
|
470
|
+
let mysqlDdl = '';
|
|
471
|
+
let pgDdl = '';
|
|
472
|
+
|
|
473
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
474
|
+
mysqlDdl += mysqlExporter.export(name, schema) + '\n\n';
|
|
475
|
+
pgDdl += pgExporter.export(name, schema) + '\n\n';
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
fs.writeFileSync('schema.mysql.sql', mysqlDdl);
|
|
479
|
+
fs.writeFileSync('schema.pg.sql', pgDdl);
|
|
480
|
+
|
|
481
|
+
console.log('导出完成!');
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
## 相关文档
|
|
487
|
+
|
|
488
|
+
- [**导出限制说明**](export-limitations.md) ⚠️ **必读**
|
|
489
|
+
- [MongoDB 导出器](mongodb-exporter.md)
|
|
490
|
+
- [MySQL 导出器](mysql-exporter.md)
|
|
491
|
+
- [PostgreSQL 导出器](postgresql-exporter.md)
|
|
492
|
+
- [TypeConverter](type-converter.md)
|
|
493
|
+
- [DSL 语法](dsl-syntax.md)
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
## 对应示例文件
|
|
498
|
+
|
|
499
|
+
**示例入口**: [export-guide.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/export-guide.ts)
|
|
500
|
+
**说明**: 覆盖同一组 schema 同时导出到 MongoDB、MySQL 和 PostgreSQL 的最小工作流,便于对照多导出器结果。
|
|
501
|
+
|