neopg 2.0.2 → 2.0.4

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
@@ -16,12 +16,10 @@ const FLOAT_TYPES = new Set([
16
16
  ])
17
17
 
18
18
  class ModelChain {
19
- constructor(ctx, def, schema = 'public') {
19
+ constructor(ctx, def, schema='public') {
20
20
  this.ctx = ctx
21
21
  this.def = def
22
- this.sql = ctx.sql
23
-
24
- this.tableName = def.tableName
22
+ this.sql = ctx ? ctx.sql : null
25
23
  this.schema = schema
26
24
 
27
25
  // --- 查询状态 ---
@@ -35,7 +33,7 @@ class ModelChain {
35
33
  this._group = []
36
34
  this._lock = null
37
35
 
38
- this._isRaw = !!def.isRaw
36
+ this._isRaw = false
39
37
  this._executed = false
40
38
  }
41
39
 
@@ -50,7 +48,7 @@ class ModelChain {
50
48
  _ensureActive() {
51
49
  if (this._executed) {
52
50
  throw new Error(
53
- `[NeoPG] ModelChain for '${this.tableName}' has already been executed. ` +
51
+ `[NeoPG] ModelChain for '${this.def.tableName}' has already been executed. ` +
54
52
  `Do NOT reuse the chain variable. Use .clone() if you need to fork queries.`
55
53
  )
56
54
  }
@@ -266,7 +264,7 @@ class ModelChain {
266
264
  }
267
265
 
268
266
  _buildSelectQuery() {
269
- const t = this.sql(this.tableName)
267
+ const t = this.sql(this.def.tableName)
270
268
  const c = this._columns ? this.sql(this._columns) : this.sql`*`
271
269
 
272
270
  const w = this._buildWhere()
@@ -310,7 +308,7 @@ class ModelChain {
310
308
  const dataQuery = this._buildSelectQuery()
311
309
 
312
310
  // 2. 总数查询
313
- const t = this.sql(this.tableName)
311
+ const t = this.sql(this.def.tableName)
314
312
  const w = this._buildWhere()
315
313
  const j = this._buildJoins()
316
314
  const g = this._buildGroup()
@@ -338,7 +336,7 @@ class ModelChain {
338
336
  async count() {
339
337
  this._ensureActive()
340
338
  try {
341
- const t = this.sql(this.tableName)
339
+ const t = this.sql(this.def.tableName)
342
340
  const w = this._buildWhere()
343
341
  const j = this._buildJoins()
344
342
  const g = this._buildGroup()
@@ -366,11 +364,11 @@ class ModelChain {
366
364
  const inputs = isArray ? data : [data]
367
365
  if (inputs.length === 0) throw new Error('[NeoPG] Insert data cannot be empty')
368
366
 
369
- if (this.def) {
367
+ if (!this._isRaw) {
370
368
  this._prepareDataForInsert(inputs)
371
369
  }
372
370
 
373
- const fullTable = this.sql`${this.sql(this.schema)}.${this.sql(this.tableName)}`
371
+ const fullTable = this.sql`${this.sql(this.schema)}.${this.sql(this.def.tableName)}`
374
372
  const retFragment = this._buildReturning()
375
373
 
376
374
  const result = await this.sql`INSERT INTO ${fullTable} ${this.sql(inputs)} ${retFragment}`
@@ -389,10 +387,14 @@ class ModelChain {
389
387
  this._ensureActive()
390
388
  try {
391
389
  if (!data || Object.keys(data).length === 0) throw new Error('[NeoPG] Update data cannot be empty')
392
- if (this.def) { this._prepareDataForUpdate(data) }
390
+
391
+ if (!this._isRaw) {
392
+ this._prepareDataForUpdate(data)
393
+ }
394
+
393
395
  if (this._conditions.length === 0) throw new Error('[NeoPG] UPDATE requires a WHERE condition')
394
396
 
395
- const fullTable = this.sql`${this.sql(this.schema)}.${this.sql(this.tableName)}`
397
+ const fullTable = this.sql`${this.sql(this.schema)}.${this.sql(this.def.tableName)}`
396
398
  const whereFragment = this._buildWhere()
397
399
  const retFragment = this._buildReturning()
398
400
 
@@ -412,7 +414,7 @@ class ModelChain {
412
414
  this._ensureActive()
413
415
  try {
414
416
  if (this._conditions.length === 0) throw new Error('[NeoPG] DELETE requires a WHERE condition')
415
- const fullTable = this.sql`${this.sql(this.schema)}.${this.sql(this.tableName)}`
417
+ const fullTable = this.sql`${this.sql(this.schema)}.${this.sql(this.def.tableName)}`
416
418
  const whereFragment = this._buildWhere()
417
419
  const retFragment = this._buildReturning()
418
420
 
@@ -432,7 +434,7 @@ class ModelChain {
432
434
  try {
433
435
  if (!field) throw new Error(`[NeoPG] ${func} requires a field name.`)
434
436
 
435
- const t = this.sql(this.tableName)
437
+ const t = this.sql(this.def.tableName)
436
438
  const w = this._buildWhere()
437
439
  const j = this._buildJoins()
438
440
  const ft = this.sql`${this.sql(this.schema)}.${t}`
@@ -524,9 +526,11 @@ class ModelChain {
524
526
  if (autoId && row[pk] === undefined) {
525
527
  row[pk] = this.def.makeId(pkLen)
526
528
  }
529
+
527
530
  if (make_timestamp) {
528
531
  for (const t of ts.insert) makeTimestamp(row, t)
529
532
  }
533
+
530
534
  for (const key in row) {
531
535
  this.def.validateField(key, row[key])
532
536
  }
@@ -538,6 +542,7 @@ class ModelChain {
538
542
  if (ts.update && ts.update.length > 0) {
539
543
  for (const t of ts.update) makeTimestamp(row, t)
540
544
  }
545
+
541
546
  for (const key in row) {
542
547
  this.def.validateField(key, row[key])
543
548
  }
package/lib/NeoPG.js CHANGED
@@ -11,6 +11,17 @@ const path = require('node:path')
11
11
  const fs = require('node:fs')
12
12
  const { pathToFileURL } = require('node:url')
13
13
 
14
+ /**
15
+ * 将下划线命名转换为大驼峰命名 (e.g. shop_order -> ShopOrder)
16
+ */
17
+ function toPascalCase(str) {
18
+ if (!str) return ''
19
+
20
+ return str.split('_')
21
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1))
22
+ .join('')
23
+ }
24
+
14
25
  class NeoPG {
15
26
  constructor(config) {
16
27
  this.driver = postgres(config)
@@ -24,7 +35,9 @@ class NeoPG {
24
35
 
25
36
  table(tableName, schema = null) {
26
37
  const target = schema || this.defaultSchema
27
- return new this.ModelChain(this, {tableName, isRaw: true}, target)
38
+ let m = new this.ModelChain(this, {tableName, isRaw: true}, target)
39
+ m._isRaw = true
40
+ return m
28
41
  }
29
42
 
30
43
  model(name, schema = null) {
@@ -32,7 +45,16 @@ class NeoPG {
32
45
  if (!item) throw new Error(`[NeoPG] Model '${name}' not found.`)
33
46
 
34
47
  const target = schema || this.defaultSchema
35
- return new item.Class(this, item.def, target)
48
+ let m = new item.Class(this, item.def, target)
49
+
50
+ if (!m.def) {
51
+ m.ctx = this
52
+ m.sql = this.sql
53
+ m.def = item.def
54
+ m.schema = target
55
+ }
56
+
57
+ return m
36
58
  }
37
59
 
38
60
  // --- 注册 ---
@@ -50,6 +72,41 @@ class NeoPG {
50
72
 
51
73
  if (!rawSchema) throw new Error(`[NeoPG] Missing static schema for ${ModelClass.name}`)
52
74
 
75
+ // 如果没有显式指定 modelName,则尝试自动推断
76
+ if (!rawSchema.modelName) {
77
+ const className = ModelClass.name
78
+
79
+ const genericNames = ['AnonymousModel', 'ModelChain', 'Function', '']
80
+
81
+ // 1. 优先尝试使用类名 (如果类名不是通用的)
82
+ if (className && !genericNames.includes(className)) {
83
+ rawSchema.modelName = className
84
+ } else if (rawSchema.tableName) {
85
+ rawSchema.modelName = toPascalCase(rawSchema.tableName)
86
+ setTimeout(() => {
87
+ console.error(`\x1b[33;5m[NeoPG]Warning: modelName is not specified, `
88
+ + `use ${rawSchema.modelName} as the modelName\x1b[0m`)
89
+ }, 100)
90
+ } else {
91
+ // 此时说明modelName无法确定,但是tableName也没有指定
92
+ throw new Error(`\x1b[31m[NeoPG] Missing modelName and tableName\x1b[0m`)
93
+ }
94
+ }
95
+
96
+ //经过以上处理,modelName已经确定了,此时若没有指定tableName则把modelName转换为小写作为tableName
97
+ if (!rawSchema.tableName) {
98
+ rawSchema.tableName = rawSchema.modelName.toLowerCase()
99
+
100
+ setTimeout(() => {
101
+ console.error(`\x1b[33;5m[NeoPG]Warning: tableName is not specified, `
102
+ + `use ${rawSchema.modelName.toLowerCase()} as the tableName\x1b[0m`)
103
+ }, 100)
104
+ }
105
+
106
+ if ((/[a-z]/).test(rawSchema.modelName[0])) {
107
+ throw new Error(`\x1b[31;5m[NeoPG] ${rawSchema.modelName}: modelName must start with an uppercase letter.\x1b[0m`)
108
+ }
109
+
53
110
  const def = new ModelDef(rawSchema)
54
111
 
55
112
  //已经存在又不是更新,则报错
@@ -93,6 +150,16 @@ class NeoPG {
93
150
 
94
151
  if (!options.schema) options.schema = this.defaultSchema
95
152
 
153
+ if (options.model) {
154
+ let model = this.registry.get(options.model)
155
+
156
+ if (!model) {
157
+ throw new Error(`[NeoPG] sync: ${options.model} not found.`)
158
+ }
159
+
160
+ return await SchemaSync.execute(this.driver, model.def, this, options)
161
+ }
162
+
96
163
  for (const item of this.registry.values()) {
97
164
  await SchemaSync.execute(this.driver, item.def, this, options)
98
165
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neopg",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "description": "orm for postgres",
5
5
  "keywords": [
6
6
  "neopg",
package/test/test-db.js CHANGED
@@ -138,12 +138,79 @@ const User = {
138
138
  }
139
139
 
140
140
  // 3. 注册
141
- db.add(User);
141
+ db.add(User)
142
142
 
143
143
  ;(async () => {
144
144
  await db.sync({force: true, debug: true})
145
145
  // 插入
146
146
 
147
+ //测试modelName和tableName
148
+
149
+ class ShopOrder extends ModelChain {
150
+ static schema = {
151
+ column: {
152
+ id: {
153
+ type: dataTypes.ID
154
+ },
155
+
156
+ name: {
157
+ type: dataTypes.STRING(30)
158
+ },
159
+
160
+ order_no: {
161
+ type: dataTypes.STRING(40)
162
+ }
163
+ }
164
+ }
165
+
166
+ constructor() {
167
+ super()
168
+ }
169
+ }
170
+
171
+ db.define(ShopOrder)
172
+
173
+ //未指定modelName
174
+ let Cart = {
175
+ tableName: 'cart',
176
+ column: {
177
+
178
+ }
179
+ }
180
+
181
+ db.define(Cart)
182
+
183
+ let Category = {
184
+ column: {}
185
+ }
186
+
187
+ try {
188
+ db.define(Category)
189
+ } catch (err) {
190
+ setTimeout(() => {
191
+ console.error(err.message)
192
+ }, 100)
193
+ }
194
+
195
+ db.sync({force: true, debug: true, model: 'ShopOrder'})
196
+
197
+ await db.model('ShopOrder').where('1=1').delete()
198
+ await db.model('ShopOrder').insert([
199
+ {
200
+ name: 'topbit',
201
+ order_no: Math.random().toString(16)
202
+ },
203
+
204
+ {
205
+ name: 'neopg',
206
+ order_no: Math.random().toString(16)
207
+ }
208
+ ])
209
+
210
+ console.log('get shoporder...\n',
211
+ await db.model('ShopOrder').where('1=1').find()
212
+ )
213
+
147
214
  await db.model('User').where('1=1').delete()
148
215
 
149
216
  try {