neopg 0.0.0 → 0.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/lib/ModelChain.js CHANGED
@@ -1,7 +1,8 @@
1
- 'use strict';
1
+ 'use strict'
2
2
 
3
- const makeId = require('./makeId.js');
4
- const makeTimestamp = require('./makeTimestamp.js');
3
+ const makeId = require('./makeId.js')
4
+ const makeTimestamp = require('./makeTimestamp.js')
5
+ const TransactionScope = require('./TransactionScope.js')
5
6
 
6
7
  /**
7
8
  * ModelChain - 链式查询构建器
@@ -50,68 +51,54 @@ class ModelChain {
50
51
  * 3. .where('age', '>', 18) -> age > 18 (兼容)
51
52
  */
52
53
  where(arg1, arg2, arg3) {
53
- if (!arg1) return this
54
-
55
- // 1. 识别 Postgres Fragment (Query)
56
- // 依据指令:直接检测构造函数名称是否为 'Query'
57
- if (arg1.constructor && arg1.constructor.name === 'Query') {
54
+ if (!arg1) return this
55
+ // 1. Fragment 检测
56
+ if (arg1.constructor && arg1.constructor.name === 'Query') {
58
57
  this._conditions.push(arg1)
59
58
  return this
60
- }
61
-
62
- // 2. 对象写法 .where({ id: 1, name: 'Neo' })
63
- if (typeof arg1 === 'object' && !Array.isArray(arg1)) {
64
- const keys = Object.keys(arg1)
65
- if (keys.length === 0) return this
66
-
67
- for (const key of keys) {
68
- const val = arg1[key];
69
- if (val === undefined) continue
70
-
71
- if (val === null) {
72
- this._conditions.push(this.sql`${this.sql(key)} IS NULL`)
73
- } else if (Array.isArray(val)) {
74
- this._conditions.push(this.sql`${this.sql(key)} IN ${this.sql(val)}`)
75
- } else {
76
- this._conditions.push(this.sql`${this.sql(key)} = ${val}`)
77
- }
78
59
  }
60
+
61
+ // 2. Object 写法
62
+ if (typeof arg1 === 'object' && !Array.isArray(arg1)) {
63
+ for (const k of Object.keys(arg1)) {
64
+ const v = arg1[k]
79
65
 
80
- return this
81
- }
66
+ if (v === undefined) continue
82
67
 
83
- // 3. 字符串/参数写法
84
- if (typeof arg1 === 'string') {
85
- // Case A: .where('id', 123) => id = 123 (默认等于)
86
- if (arg2 !== undefined && arg3 === undefined) {
87
- this._conditions.push(this.sql`${this.sql(arg1)} = ${arg2}`)
88
- return this
89
- }
90
-
91
- // Case B: .where('age', '>', 18)
92
- if (arg3 !== undefined) {
93
- // 注意:中间的操作符必须用 sql.unsafe,因为它不是变量
94
- this._conditions.push(this.sql`${this.sql(arg1)} ${this.sql.unsafe(arg2)} ${arg3}`)
68
+ if (v === null) this._conditions.push(this.sql`${this.sql(k)} IS NULL`)
69
+ else if (Array.isArray(v)) this._conditions.push(this.sql`${this.sql(k)} IN ${this.sql(v)}`)
70
+ else this._conditions.push(this.sql`${this.sql(k)} = ${v}`)
71
+ }
72
+
95
73
  return this
96
74
  }
97
75
 
98
- // Case C: .where('id = ?', 123) (简单兼容)
99
- if (arg1.includes('?') && arg2 !== undefined) {
100
- const parts = arg1.split('?')
101
-
102
- // 只支持单个参数简单替换,复杂的请用 sql``
103
- if (parts.length === 2) {
104
- this._conditions.push(this.sql`${this.sql.unsafe(parts[0])}${arg2}${this.sql.unsafe(parts[1])}`)
105
- return this
106
- }
76
+ // 3. String 写法
77
+ if (typeof arg1 === 'string') {
78
+ // .where('age', '>', 18)
79
+ if (arg3 !== undefined) {
80
+ this._conditions.push(this.sql`${this.sql(arg1)} ${this.sql.unsafe(arg2)} ${arg3}`)
81
+ return this
82
+ }
83
+ // .where('age', 18) -> age = 18
84
+ if (arg2 !== undefined) {
85
+ this._conditions.push(this.sql`${this.sql(arg1)} = ${arg2}`)
86
+ return this
87
+ }
88
+ // .where('id = ?', 123)
89
+ if (arg1.includes('?') && arg2 !== undefined) {
90
+ const p = arg1.split('?');
91
+ if(p.length===2) {
92
+ this._conditions.push(this.sql`${this.sql.unsafe(p[0])}${arg2}${this.sql.unsafe(p[1])}`)
93
+ return this
94
+ }
95
+ }
96
+ // .where('1=1') -> Raw SQL
97
+ // 注意:这里必须用 unsafe,否则 '1=1' 会被当成字符串值处理
98
+ this._conditions.push(this.sql.unsafe(arg1))
107
99
  }
108
-
109
- // Case D: 纯字符串 (视为 Raw SQL)
110
- // .where("status = 'active'")
111
- this._conditions.push(this.sql.unsafe(arg1))
112
- }
113
100
 
114
- return this
101
+ return this
115
102
  }
116
103
 
117
104
  whereIf(condition, arg1, arg2, arg3) {
@@ -131,43 +118,29 @@ class ModelChain {
131
118
  return this
132
119
  }
133
120
 
134
- /**
135
- * 排序
136
- * .order(sql`create_time DESC`)
137
- * .order('create_time', 'DESC')
138
- * .order({ create_time: 'DESC' })
139
- */
140
- order(arg1, arg2) {
141
- if (!arg1) return this
121
+ orderby(a, b) {
122
+ if(!a) return this
142
123
 
143
- // 1. Fragment
144
- if (arg1.constructor && arg1.constructor.name === 'Query') {
145
- this._order.push(arg1)
146
- return this
147
- }
148
-
149
- // 2. Object { id: 'DESC' }
150
- if (typeof arg1 === 'object') {
151
- for (const key in arg1) {
152
- const dir = arg1[key].toUpperCase()
153
- this._order.push(this.sql`${this.sql(key)} ${this.sql.unsafe(dir)}`)
124
+ if(a.constructor && a.constructor.name==='Query') {
125
+ this._order.push(a)
126
+ return this
154
127
  }
155
128
 
156
- return this
157
- }
129
+ if(typeof a==='object') {
130
+ for(const k in a) {
131
+ this._order.push(this.sql`${this.sql(k)} ${this.sql.unsafe(a[k].toUpperCase())}`)
132
+ }
133
+
134
+ return this
135
+ }
158
136
 
159
- // 3. String ('id', 'DESC')
160
- if (typeof arg1 === 'string') {
161
- const dir = arg2 ? arg2.toUpperCase() : 'ASC'
162
- // 检查 arg1 是否包含空格 (e.g. "id desc")
163
- if (arg1.includes(' ')) {
164
- this._order.push(this.sql.unsafe(arg1))
165
- } else {
166
- this._order.push(this.sql`${this.sql(arg1)} ${this.sql.unsafe(dir)}`)
137
+ if(typeof a==='string') {
138
+ const d = b ? b.toUpperCase() : 'ASC'
139
+ if(a.includes(' ')) this._order.push(this.sql.unsafe(a));
140
+ else this._order.push(this.sql`${this.sql(a)} ${this.sql.unsafe(d)}`);
167
141
  }
168
- }
169
142
 
170
- return this
143
+ return this
171
144
  }
172
145
 
173
146
  limit(count, offset = 0) {
@@ -190,44 +163,58 @@ class ModelChain {
190
163
  return this
191
164
  }
192
165
 
193
- // --- 核心:执行方法 ---
166
+ // --- 辅助:构建 Where 片段 (修复 Bug 的核心) ---
167
+ _buildWhere() {
168
+ const len = this._conditions.length
169
+ if (len === 0) return this.sql``
170
+
171
+ // 只有一个条件,直接返回,零开销
172
+ if (len === 1) {
173
+ return this.sql`WHERE ${this._conditions[0]}`
174
+ }
194
175
 
195
- async find() {
196
- const tableFragment = this.sql(this.tableName)
176
+ // 预分配数组:N个条件需要 N-1 个 'AND',总长 2N-1
177
+ // 使用 new Array 预分配内存,比 push 更快
178
+ const parts = new Array(len * 2 - 1)
179
+ const AND = this.sql.unsafe(' AND ')
180
+
181
+ for (let i = 0; i < len; i++) {
182
+ // 偶数位放条件
183
+ parts[i * 2] = this._conditions[i]
184
+ // 奇数位放 AND (除了最后一位)
185
+ if (i < len - 1) {
186
+ parts[i * 2 + 1] = AND
187
+ }
188
+ }
197
189
 
198
- const colsFragment = this._columns
199
- ? this.sql(this._columns)
200
- : this.sql`*`
190
+ // postgres.js 会自动展开这个扁平数组,性能极高
191
+ return this.sql`WHERE ${parts}`
192
+ }
201
193
 
202
- const whereFragment = this._conditions.length
203
- ? this.sql`WHERE ${this.sql(this._conditions, ' AND ')}`
204
- : this.sql``
194
+ // --- 辅助:构建 Order 片段 (修复 Bug) ---
195
+ _buildOrder() {
196
+ if (this._order.length === 0) return this.sql``
197
+ // 数组直接传入模板,postgres.js 默认用逗号连接,这正是 ORDER BY 需要的
198
+ // 不能用 this.sql(this._order),那样会试图转义为标识符
199
+ return this.sql`ORDER BY ${this._order}`
200
+ }
205
201
 
206
- const orderFragment = this._order.length
207
- ? this.sql`ORDER BY ${this.sql(this._order)}`
208
- : this.sql``
209
-
210
- const limitFragment = this._limit
211
- ? this.sql`LIMIT ${this._limit}`
212
- : this.sql``
213
-
214
- const offsetFragment = this._offset
215
- ? this.sql`OFFSET ${this._offset}`
216
- : this.sql``
202
+ // --- 核心:执行方法 ---
203
+
204
+ async find() {
205
+ const t = this.sql(this.tableName)
206
+ const c = this._columns ? this.sql(this._columns) : this.sql`*`
217
207
 
218
- const lockFragment = this._lock || this.sql``
219
-
220
- const fullTable = this.sql`${this.sql(this.schema)}.${tableFragment}`
221
-
222
- return await this.sql`
223
- SELECT ${colsFragment}
224
- FROM ${fullTable}
225
- ${whereFragment}
226
- ${orderFragment}
227
- ${limitFragment}
228
- ${offsetFragment}
229
- ${lockFragment}
230
- `
208
+ // 修复:使用新方法构建
209
+ const w = this._buildWhere()
210
+ const o = this._buildOrder()
211
+
212
+ const l = this._limit ? this.sql`LIMIT ${this._limit}` : this.sql``
213
+ const off = this._offset ? this.sql`OFFSET ${this._offset}` : this.sql``
214
+ const lck = this._lock || this.sql``
215
+ const ft = this.sql`${this.sql(this.schema)}.${t}`
216
+
217
+ return await this.sql`SELECT ${c} FROM ${ft} ${w} ${o} ${l} ${off} ${lck}`
231
218
  }
232
219
 
233
220
  // Thenable 接口
@@ -242,22 +229,15 @@ class ModelChain {
242
229
  }
243
230
 
244
231
  async count() {
245
- const tableFragment = this.sql(this.tableName);
246
- const whereFragment = this._conditions.length
247
- ? this.sql`WHERE ${this.sql(this._conditions, ' AND ')}`
248
- : this.sql``
232
+ const t = this.sql(this.tableName);
233
+ // 修复:使用新方法构建
234
+ const w = this._buildWhere()
235
+ const ft = this.sql`${this.sql(this.schema)}.${t}`
249
236
 
250
- const fullTable = this.sql`${this.sql(this.schema)}.${tableFragment}`
251
-
252
- const result = await this.sql`
253
- SELECT count(*) as total FROM ${fullTable} ${whereFragment}
254
- `
255
-
256
- return parseInt(result[0].total)
237
+ const r = await this.sql`SELECT count(*) as total FROM ${ft} ${w}`
238
+ return parseInt(r[0].total)
257
239
  }
258
240
 
259
- // --- 写入方法 ---
260
-
261
241
  async insert(data) {
262
242
  const isArray = Array.isArray(data)
263
243
  const inputs = isArray ? data : [data]
@@ -268,54 +248,39 @@ class ModelChain {
268
248
  }
269
249
 
270
250
  const fullTable = this.sql`${this.sql(this.schema)}.${this.sql(this.tableName)}`
271
-
272
- const result = await this.sql`
273
- INSERT INTO ${fullTable} ${this.sql(inputs)}
274
- RETURNING *
275
- `
251
+ const result = await this.sql`INSERT INTO ${fullTable} ${this.sql(inputs)} RETURNING *`
276
252
 
277
253
  if (!isArray && result.length > 0) return result[0]
278
-
279
254
  return result
280
255
  }
281
256
 
282
257
  async update(data) {
283
258
  if (!data || Object.keys(data).length === 0) throw new Error('[NeoPG] Update data cannot be empty')
284
-
285
- if (this.def) {
286
- this._prepareDataForUpdate(data)
287
- }
288
-
289
- if (this._conditions.length === 0) {
290
- throw new Error('[NeoPG] UPDATE requires a WHERE condition')
291
- }
292
-
259
+ if (this.def) { this._prepareDataForUpdate(data) }
260
+
261
+ if (this._conditions.length === 0) throw new Error('[NeoPG] UPDATE requires a WHERE condition')
262
+
293
263
  const fullTable = this.sql`${this.sql(this.schema)}.${this.sql(this.tableName)}`
294
- const whereFragment = this.sql`WHERE ${this.sql(this._conditions, ' AND ')}`
295
-
296
- // 使用 sql(data) 自动处理 set a=1, b=2
297
- return await this.sql`
298
- UPDATE ${fullTable}
299
- SET ${this.sql(data)}
300
- ${whereFragment}
301
- RETURNING *
302
- `
264
+ // 修复:使用新方法构建
265
+ const whereFragment = this._buildWhere()
266
+
267
+ return await this.sql`UPDATE ${fullTable} SET ${this.sql(data)} ${whereFragment} RETURNING *`
303
268
  }
304
269
 
305
270
  async delete() {
306
- if (this._conditions.length === 0) {
307
- throw new Error('[NeoPG] DELETE requires a WHERE condition')
308
- }
309
-
271
+ if (this._conditions.length === 0) throw new Error('[NeoPG] DELETE requires a WHERE condition')
310
272
  const fullTable = this.sql`${this.sql(this.schema)}.${this.sql(this.tableName)}`
273
+ // 修复:使用新方法构建
274
+ const whereFragment = this._buildWhere()
275
+
276
+ return await this.sql`DELETE FROM ${fullTable} ${whereFragment} RETURNING *`
277
+ }
311
278
 
312
- const whereFragment = this.sql`WHERE ${this.sql(this._conditions, ' AND ')}`
313
-
314
- return await this.sql`
315
- DELETE FROM ${fullTable}
316
- ${whereFragment}
317
- RETURNING *
318
- `
279
+ async transaction(callback) {
280
+ return await this.sql.begin(async (trxSql) => {
281
+ const scope = new TransactionScope(this.ctx, trxSql)
282
+ return await callback(scope)
283
+ })
319
284
  }
320
285
 
321
286
  // --- 内部辅助方法 ---
package/lib/ModelDef.js CHANGED
@@ -10,6 +10,9 @@ class ModelDef {
10
10
  this.modelName = rawSchema.modelName || rawSchema.tableName
11
11
  this.primaryKey = rawSchema.primaryKey || 'id'
12
12
  this.columns = rawSchema.column || {}
13
+ this.index = rawSchema.index || []
14
+ this.unique = rawSchema.unique || []
15
+ this.removeIndex = rawSchema.removeIndex || []
13
16
 
14
17
  this._parseColumns()
15
18
 
@@ -57,7 +60,7 @@ class ModelDef {
57
60
  throw new Error(`[NeoPG] Column name '${k}' in table '${this.tableName}' is FORBIDDEN.`)
58
61
  }
59
62
 
60
- if (!/^[a-z][a-z0-9_]*$/i.test(colName)) {
63
+ if (!(/^[a-z][a-z0-9_]*$/i).test(k)) {
61
64
  throw new Error(`[NeoPG] Column name '${k}' is invalid. Only alphanumeric and underscore allowed, must start with letter.`)
62
65
  }
63
66
  }
package/lib/NeoPG.js CHANGED
@@ -20,7 +20,7 @@ class NeoPG {
20
20
 
21
21
  table(tableName, schema = null) {
22
22
  const target = schema || this.defaultSchema
23
- return new this.ModelChain(this.driver, {tableName, isRaw: true}, target)
23
+ return new this.ModelChain(this, {tableName, isRaw: true}, target)
24
24
  }
25
25
 
26
26
  model(name, schema = null) {
@@ -28,7 +28,7 @@ class NeoPG {
28
28
  if (!item) throw new Error(`[NeoPG] Model '${name}' not found.`)
29
29
 
30
30
  const target = schema || this.defaultSchema
31
- return new item.Class(this.driver, item.def, target)
31
+ return new item.Class(this, item.def, target)
32
32
  }
33
33
 
34
34
  // --- 注册 ---
@@ -64,6 +64,12 @@ class NeoPG {
64
64
 
65
65
  // --- 同步 ---
66
66
  async sync(options = {}) {
67
+ if (!options || typeof options !== 'object') {
68
+ options = {}
69
+ }
70
+
71
+ if (!options.schema) options.schema = this.defaultSchema
72
+
67
73
  for (const item of this.registry.values()) {
68
74
  await SchemaSync.execute(this.driver, item.def, this, options)
69
75
  }
package/lib/SchemaSync.js CHANGED
@@ -32,14 +32,18 @@ const DefaultWithType = ['varchar', 'char', 'text', 'bytea', 'timestamp', 'times
32
32
 
33
33
  class SchemaSync {
34
34
 
35
+ static async createSchema(sql, schema) {
36
+ return sql.unsafe(`create schema if not exists ${schema};`)
37
+ }
38
+
35
39
  /**
36
40
  * 入口方法
37
41
  */
38
42
  static async execute(sql, def, ctx, options = {}) {
39
43
  const debug = options.debug || false;
40
44
  const force = options.force || false;
41
- const dropNotExistCol = options.dropNotExistCol || false;
42
- const schema = ctx.defaultSchema || 'public';
45
+ const dropNotExistCol = force || options.dropNotExistCol || false;
46
+ const schema = options.schema || ctx.defaultSchema || 'public';
43
47
  const tableName = def.tableName;
44
48
  const curTableName = `${schema}.${tableName}`;
45
49
 
@@ -130,7 +134,7 @@ class SchemaSync {
130
134
 
131
135
  if (col.default !== undefined) {
132
136
  if (col.default === null) line += ' default null';
133
- else line += ` default $${qtag}$${col.default}$${qtag}$`;
137
+ else line += ` default $_${qtag}_$${col.default}$_${qtag}_$`;
134
138
  }
135
139
  }
136
140
  colSqls.push(line);
@@ -190,7 +194,7 @@ class SchemaSync {
190
194
 
191
195
  if (col.default !== undefined) {
192
196
  if (col.default === null) addSql += ' default null';
193
- else addSql += ` default $${qtag}$${col.default}$${qtag}$`;
197
+ else addSql += ` default $_${qtag}_$${col.default}$_${qtag}_$`;
194
198
  }
195
199
 
196
200
  if (debug) console.log(addSql);
@@ -215,7 +219,7 @@ class SchemaSync {
215
219
  await sql.unsafe(`ALTER TABLE ${curTableName} DROP COLUMN ${this.fmtColName(k)}`);
216
220
  let reAddSql = `ALTER TABLE ${curTableName} ADD COLUMN ${this.fmtColName(k)} ${col.type}`;
217
221
  if (col.notNull !== false) reAddSql += ' not null';
218
- if (col.default !== undefined) reAddSql += ` default $${qtag}$${col.default}$${qtag}$`;
222
+ if (col.default !== undefined) reAddSql += ` default $_${qtag}_$${col.default}$_${qtag}_$`;
219
223
  await sql.unsafe(reAddSql);
220
224
  col.changed = true; // 标记变更,供外键处理使用
221
225
  continue;
@@ -240,7 +244,7 @@ class SchemaSync {
240
244
  // 简单比对逻辑 (注:PG存储的默认值格式可能不同,这里仅作简单触发)
241
245
  // 实际生产中可能需要更复杂的解析,这里保留原逻辑结构
242
246
  // 原逻辑用了 _realDefault 方法,这里我们简单处理,仅当需要时设置
243
- let default_val_sql = col.default === null ? 'null' : `$${qtag}$${col.default}$${qtag}$`;
247
+ let default_val_sql = col.default === null ? 'null' : `$_${qtag}_$${col.default}$_${qtag}_$`;
244
248
  // 这里为了简化,每次都重设默认值(开销很小),或者你需要实现 _realDefault
245
249
  await sql.unsafe(`ALTER TABLE ${curTableName} ALTER COLUMN ${this.fmtColName(k)} SET DEFAULT ${default_val_sql}`);
246
250
  }
@@ -271,12 +275,12 @@ class SchemaSync {
271
275
  static async syncIndex(sql, def, schema, curTableName, debug) {
272
276
  // 假设索引定义在 def.rawSchema.index (数组)
273
277
  // ModelDef 需要暴露这个属性,或 def.indices
274
- const indices = def.rawSchema && def.rawSchema.index ? def.rawSchema.index : [];
278
+ const indices = def.index || [];
275
279
  if (!Array.isArray(indices)) return;
276
280
 
277
281
  for (const indname of indices) {
278
282
  // 检查 removeIndex 配置
279
- const removeIndex = def.rawSchema.removeIndex || [];
283
+ const removeIndex = def.removeIndex || [];
280
284
  if (removeIndex.includes(indname)) continue;
281
285
 
282
286
  // 检查列是否存在
@@ -310,7 +314,7 @@ class SchemaSync {
310
314
  }
311
315
 
312
316
  static async syncUnique(sql, def, schema, curTableName, debug) {
313
- const uniques = def.rawSchema && def.rawSchema.unique ? def.rawSchema.unique : [];
317
+ const uniques = def.unique || [];
314
318
  if (!Array.isArray(uniques)) return;
315
319
 
316
320
  for (const indname of uniques) {
@@ -347,8 +351,8 @@ class SchemaSync {
347
351
  const currentIdxNames = allIdx.map(i => i.indexname);
348
352
 
349
353
  // 2. 计算应该保留的索引名
350
- const indices = def.rawSchema && def.rawSchema.index ? def.rawSchema.index : [];
351
- const uniques = def.rawSchema && def.rawSchema.unique ? def.rawSchema.unique : [];
354
+ const indices = def.index || [];
355
+ const uniques = def.unique || [];
352
356
 
353
357
  const keepSet = new Set();
354
358
  const makeName = (n) => `${tableName}_${n.split(',').map(x=>x.trim()).filter(x=>x).join('_')}_idx`;
@@ -9,14 +9,14 @@ class TransactionScope {
9
9
 
10
10
  table(tableName, schema = null) {
11
11
  const target = schema || this.parent.defaultSchema
12
- return new this.parent.ModelChain(this.driver, {tableName, isRaw: true}, target)
12
+ return new this.parent.ModelChain(this, {tableName, isRaw: true}, target)
13
13
  }
14
14
 
15
15
  model(name, schema = null) {
16
16
  const item = this.parent.registry.get(name)
17
17
  if (!item) throw new Error(`[NeoPG] Model '${name}' not found.`)
18
18
  const target = schema || this.parent.defaultSchema
19
- return new item.Class(this.driver, item.def, target)
19
+ return new item.Class(this, item.def, target)
20
20
  }
21
21
 
22
22
  async transaction(callback) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neopg",
3
- "version": "0.0.0",
3
+ "version": "0.0.1",
4
4
  "description": "orm for postgres",
5
5
  "keywords": [
6
6
  "postgres",
package/test/test-db.js CHANGED
@@ -1,44 +1,177 @@
1
1
  // index.js
2
2
  const NeoPG = require('../lib/NeoPG.js')
3
- const ModelChain = NeoPG.ModelChain
3
+ const {dataTypes, ModelChain} = NeoPG
4
4
 
5
5
  // 1. 初始化
6
6
  const db = new NeoPG({
7
7
  host: '127.0.0.1',
8
- database: 'eoms',
9
- schema: 'ai' // 全局默认 Schema
8
+ user: 'wy',
9
+ database: 'wdata',
10
+ schema: 'test'
10
11
  });
11
12
 
12
13
  // 2. 定义模型
13
- const UserSchema = {
14
+ const User = {
14
15
  modelName: 'User',
15
- tableName: 'sys_users',
16
+ tableName: 'users',
17
+ primaryKey: 'id',
18
+
16
19
  column: {
17
- id: { type: 'varchar', primaryKey: true },
18
- name: { type: 'varchar', notNull: true },
19
- age: { type: 'int', default: 18 }
20
- }
21
- };
20
+ id : {
21
+ type: dataTypes.UID
22
+ },
23
+
24
+ /**
25
+ * @type {column}
26
+ */
27
+ username: {
28
+ type: dataTypes.STRING(50)
29
+ },
30
+
31
+ mobile: {
32
+ type: dataTypes.STRING(16)
33
+ },
34
+
35
+ mobile_state: {
36
+ type: dataTypes.SMALLINT,
37
+ default: 0
38
+ },
39
+
40
+ //真实姓名
41
+ realname: {
42
+ type: dataTypes.STRING(50),
43
+ default: ''
44
+ },
45
+
46
+ sex: {
47
+ type: dataTypes.SMALLINT,
48
+ default: 0
49
+ },
50
+
51
+ //其他信息,以JSON格式存储
52
+ info: {
53
+ type: dataTypes.TEXT,
54
+ default: ''
55
+ },
56
+
57
+ /**
58
+ * @type {column}
59
+ */
60
+ passwd: {
61
+ type: dataTypes.STRING(240)
62
+ },
63
+
64
+ //当验证失败,需要进行重复密码验证
65
+ repasswd: {
66
+ type: dataTypes.STRING(240)
67
+ },
68
+
69
+ //通过触摸手势或轨迹生成的密码
70
+ touchpass: {
71
+ type: dataTypes.STRING(300)
72
+ },
73
+
74
+ is_external: {
75
+ type: dataTypes.SMALLINT,
76
+ default: 0
77
+ },
78
+
79
+ /**
80
+ * @type {column}
81
+ */
82
+ level: {
83
+ type: 'smallint',
84
+ default: 1,
85
+ validate: v => {
86
+ return v >= 0 && v < 99
87
+ }
88
+ },
89
+
90
+ /**
91
+ * @type {column}
92
+ */
93
+ email: {
94
+ type: dataTypes.STRING(60),
95
+ default: ''
96
+ },
97
+
98
+ email_state: {
99
+ type: dataTypes.SMALLINT,
100
+ default: 0
101
+ },
102
+
103
+ create_time: {
104
+ type: dataTypes.BIGINT,
105
+ default: 0,
106
+ timestamp: 'insert'
107
+ },
108
+
109
+ update_time: {
110
+ type: dataTypes.BIGINT,
111
+ default: 0,
112
+ timestamp: 'update'
113
+ },
114
+
115
+ failed_count: {
116
+ type: dataTypes.INT,
117
+ default: 0
118
+ },
119
+
120
+ failed_time: {
121
+ type: dataTypes.BIGINT,
122
+ default: 0
123
+ },
124
+
125
+ forbid: {
126
+ type: dataTypes.SMALLINT,
127
+ default: 0
128
+ },
129
+
130
+ is_root: {
131
+ type: dataTypes.SMALLINT,
132
+ default: 0
133
+ }
134
+ },
135
+
136
+ //索引
137
+ index: [
138
+ 'create_time',
139
+ 'level',
140
+ 'is_root'
141
+ ],
142
+
143
+ //唯一索引
144
+ unique: [
145
+ 'username',
146
+ 'email',
147
+ 'mobile'
148
+ ]
149
+ }
22
150
 
23
151
  // 3. 注册
24
- db.add(UserSchema);
152
+ db.add(User);
25
153
 
26
154
  ;(async () => {
155
+ await db.sync({force: true, debug: true})
27
156
  // 插入
28
- await db.model('User').insert({ name: 'Neo' });
29
157
 
30
- // 动态切换 Schema
31
- await db.model('User', 'tenant_a').select();
32
-
33
- // 链式切换
34
- await db.model('User').schema('tenant_b').where({ age: 20 }).select();
158
+ await db.model('User').insert({username: 'Neo', level: Math.floor((Math.random() * 101))})
35
159
 
36
160
  // 事务
37
161
  await db.transaction(async tx => {
38
- await tx.model('User').update({ age: 99 });
162
+ let data = {
163
+ level: Math.floor(Math.random() * 101),
164
+ info: `age=${Math.floor(Math.random() * 10 + 20)}`
165
+ }
166
+
167
+ console.log('update', data)
168
+ await tx.model('User').where(tx.sql`level > 10`).update(data)
169
+
39
170
  // 嵌套
40
- await tx.transaction(async subTx => {
171
+ /* await tx.transaction(async subTx => {
41
172
  await subTx.table('logs').insert({ msg: 'log' });
42
- });
173
+ }); */
43
174
  });
175
+
176
+ db.close()
44
177
  })();