neopg 0.0.1 → 1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) [2025] [Copyright Holder]
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any
4
+ purpose with or without fee is hereby granted, provided that the above
5
+ copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
+
15
+ ---------------------------------------------
16
+
17
+ 版权所有 (c) [2025] [版权所有者]
18
+
19
+ 特此授权,允许出于任何目的使用、复制、修改和/或分发本软件,无论是否收费,前提是必须在所有副本中保留上述版权声明和本许可声明。
20
+
21
+ 本软件按“原样”提供,作者不作任何明示或暗示的保证,包括但不限于适销性和特定用途适用性的暗示保证。在任何情况下,作者均不对因使用或运行本软件而引起的或与之相关的任何特殊、直接、间接或后果性损害,或因使用、数据或利润损失而导致的任何损害承担责任,无论是在合同诉讼、疏忽或其他侵权行为诉讼中。
package/README.cn.md ADDED
@@ -0,0 +1,415 @@
1
+ ![](./images/neopg.png)
2
+
3
+ # NeoPG
4
+
5
+ ### Node.js 的下一代 PostgreSQL ORM
6
+
7
+ **NeoPG** 是一个基于 [postgres.js](https://github.com/porsager/postgres)(Node.js 生态中最快的 PostgreSQL 客户端)构建的高性能、零依赖 ORM。
8
+
9
+ 它完美地融合了**链式查询构造器(Query Builder)**带来的极佳开发体验(DX)与**原生 SQL 模板字符串(Template Literals)**的极致性能。
10
+
11
+ ### [📃 English Document 🔗](./README.md)
12
+
13
+ ## 🚀 核心特性
14
+
15
+ * **基于 [postgres.js](https://github.com/porsager/postgres)**:继承了 Node.js 最快 PG 客户端的惊人速度和稳定性。
16
+ * **零依赖(Zero Dependencies)**:核心驱动已内置并在内部进行了优化,没有臃肿的依赖树。
17
+ * **混合 API 设计**:既享受流畅的**链式调用**(如 `.where().select()`),又能随时利用**标签模板字符串**处理复杂逻辑。
18
+ * **性能优先**:内部拒绝低效的字符串拼接。所有查询均被编译为高效的片段(Fragment)并原生执行。
19
+ * **自动表结构同步**:在代码中定义模型,NeoPG 会自动同步数据库表结构、索引和外键。
20
+ * **智能类型处理**:自动处理聚合函数的类型转换(例如 `sum`, `avg` 直接返回数字而非字符串),并原生支持 JSON 处理。
21
+
22
+ ---
23
+
24
+ ## 📦 安装
25
+
26
+ ```bash
27
+ npm install neopg
28
+ ```
29
+
30
+ ---
31
+
32
+ ## 🔌 初始化
33
+
34
+ ### 连接数据库
35
+
36
+ ```javascript
37
+ const NeoPG = require('neopg');
38
+
39
+ const config = {
40
+ host: 'localhost',
41
+ port: 5432,
42
+ database: 'my_db',
43
+ user: 'postgres',
44
+ password: 'password',
45
+ max: 10, // 连接池大小
46
+ idle_timeout: 30, // 空闲连接超时时间(秒)
47
+ debug: false, // 是否打印查询日志
48
+ schema: 'public' // 默认 Schema
49
+ };
50
+
51
+ const db = new NeoPG(config);
52
+ ```
53
+
54
+ ### 关闭连接
55
+
56
+ ```javascript
57
+ await db.close();
58
+ ```
59
+
60
+ ---
61
+
62
+ ## 📝 定义模型
63
+
64
+ 创建一个模型文件(例如 `models/User.js`)。您的类应当继承自 `NeoPG.ModelChain`。
65
+
66
+ ```javascript
67
+ const { ModelChain, dataTypes } = require('neopg')
68
+
69
+ class User extends ModelChain {
70
+ static schema = {
71
+ tableName: 'users',
72
+ modelName: 'User', // 可选,默认为 tableName
73
+ primaryKey: 'id',
74
+
75
+ // 基于此定义自动同步表结构
76
+ column: {
77
+ id: {
78
+ type: dataTypes.ID, // 自动生成类似雪花算法的高性能 ID
79
+ },
80
+ username: {
81
+ type: dataTypes.STRING(100),
82
+ required: true
83
+ },
84
+ email: {
85
+ type: dataTypes.STRING(255),
86
+ required: true
87
+ },
88
+ age: {
89
+ type: dataTypes.INT,
90
+ default: 18
91
+ },
92
+ meta: {
93
+ type: dataTypes.JSONB
94
+ },
95
+ created_at: {
96
+ type: dataTypes.BIGINT,
97
+ timestamp: 'insert' // 插入时自动填充时间戳
98
+ },
99
+ updated_at: {
100
+ type: dataTypes.BIGINT,
101
+ timestamp: 'update' // 插入和更新时自动填充
102
+ }
103
+ },
104
+
105
+ // 索引定义
106
+ index: ['email', 'age'],
107
+ // 唯一索引定义
108
+ unique: ['username']
109
+ }
110
+ }
111
+
112
+ module.exports = User
113
+ ```
114
+
115
+ ## 🛠 CLI 模型生成器
116
+
117
+ NeoPG 内置了一个 CLI 工具,可以快速生成带有样板代码的模型文件。
118
+
119
+ ### 用法
120
+
121
+ 通过 `npx` 直接运行(无需全局安装):
122
+
123
+ ```bash
124
+ npx neopg-model [选项] [模型名称...]
125
+ ```
126
+
127
+ ### 选项
128
+
129
+ * `--dir=<path>`: 指定输出目录(默认:`./model`)。
130
+
131
+ ### 示例
132
+
133
+ **1. 基础生成**
134
+ ```bash
135
+ npx neopg-model user
136
+ # 创建文件: ./model/user.js
137
+ # 类名: User
138
+ # 表名: user
139
+ ```
140
+
141
+ **2. 命名规范(连字符处理)**
142
+ 输入带连字符的名称,NeoPG 会自动将类名转换为 **大驼峰(CamelCase)**,将表名转换为 **下划线(snake_case)**。
143
+
144
+ ```bash
145
+ npx neopg-model user-log
146
+ # 创建文件: ./model/user-log.js
147
+ # 类名: UserLog
148
+ # 表名: user_log
149
+ ```
150
+
151
+ **3. 批量生成与自定义目录**
152
+ ```bash
153
+ npx neopg-model --dir=./src/models product order-item
154
+ # 创建:
155
+ # ./src/models/product.js
156
+ # ./src/models/order-item.js
157
+ ```
158
+
159
+ **4. ES Modules (.mjs)**
160
+ 如果在名称后加上 `.mjs` 后缀,将生成 ESM 语法(`export default`)的文件。
161
+ ```bash
162
+ npx neopg-model config.mjs
163
+ ```
164
+
165
+ ---
166
+
167
+ ## ⚙️ 注册与同步
168
+
169
+ 初始化 NeoPG 并注册您的模型。您可以使用类(Class)或配置对象(Object)来定义模型。
170
+
171
+ ### 注册模型
172
+
173
+ NeoPG 提供了三种注册方法以应对不同场景:
174
+
175
+ * **`define(model)`**:标准注册方法。如果同名模型已存在,会抛出错误(`modelName conflict`),防止意外覆盖。
176
+ * **`add(model)`**:同 `define`,行为一致。
177
+ * **`set(model)`**:**强制覆盖/重置**。如果模型已存在,则更新其定义。适用于热重载或动态 Schema 场景。
178
+
179
+ ```javascript
180
+ const User = require('./models/User')
181
+
182
+ // 1. 标准注册 (安全模式)
183
+ // 如果 'User' 已经被注册过,此处会报错
184
+ db.define(User)
185
+
186
+ // 2. 强制覆盖 (重置模式)
187
+ // 即使 'User' 已存在,也会使用新的定义覆盖它
188
+ db.set(User)
189
+
190
+ // 3. 使用纯对象注册 (快速原型)
191
+ db.define({
192
+ tableName: 'logs',
193
+ column: {
194
+ message: 'string',
195
+ level: 'int'
196
+ }
197
+ })
198
+
199
+ ```
200
+
201
+ ### 同步数据库
202
+
203
+ 根据已注册的模型同步数据库表结构。
204
+
205
+ ```javascript
206
+ // 同步表结构 (DDL)
207
+ // options: { force: true } 开启强制模式,会删除 Schema 中未定义的字段,请谨慎使用
208
+ await db.sync({ force: false })
209
+
210
+ console.log('数据库结构已同步!')
211
+ ```
212
+
213
+ ---
214
+
215
+ ## 🔍 查询数据
216
+
217
+ NeoPG 提供了自然流畅的链式 API。
218
+
219
+ ### 基础查询
220
+
221
+ ```javascript
222
+ // 获取所有用户
223
+ const users = await db.model('User').find();
224
+
225
+ // 选择特定列
226
+ const users = await db.model('User')
227
+ .select('id, username')
228
+ .limit(10)
229
+ .find();
230
+
231
+ // 获取单条记录
232
+ const user = await db.model('User').where({ id: '123' }).get();
233
+
234
+ // 分页查询
235
+ const page2 = await db.model('User').page(2, 20).find(); // 第 2 页,每页 20 条
236
+ ```
237
+
238
+ ### 链式 Where 条件
239
+
240
+ ```javascript
241
+ await db.model('User')
242
+ // 对象风格 (自动处理 AND)
243
+ .where({
244
+ age: 18,
245
+ status: 'active'
246
+ })
247
+ // 操作符风格
248
+ .where('create_time', '>', 1600000000)
249
+ // SQL 片段风格 (强大且灵活!)
250
+ .where('id IS NOT NULL')
251
+ .find();
252
+ ```
253
+
254
+ ### 结合模板字符串的复杂查询
255
+
256
+ 这是 NeoPG 的亮点所在。您可以从上下文中解构出 `sql` 标签,安全地混合原生 SQL 片段。
257
+
258
+ ```javascript
259
+ // db.sql 是原生的 postgres 实例
260
+ const { sql } = db;
261
+
262
+ await db.model('User')
263
+ .where({ status: 'active' })
264
+ // 通过模板字符串安全地注入参数
265
+ .where(sql`age > ${20} AND email LIKE ${'%@gmail.com'}`)
266
+ .find();
267
+ ```
268
+
269
+ ---
270
+
271
+ ## 📊 聚合函数
272
+
273
+ NeoPG 会自动处理类型转换(例如将 PostgreSQL 返回的 `count` 字符串转换为 JavaScript 数字)。
274
+
275
+ ```javascript
276
+ // 计数
277
+ const total = await db.model('User').where({ age: 18 }).count();
278
+
279
+ // 最大值 / 最小值
280
+ const maxAge = await db.model('User').max('age');
281
+
282
+ // 求和 / 平均值 (返回 Number 类型,而非 String)
283
+ const totalScore = await db.model('User').sum('score');
284
+ const avgScore = await db.model('User').avg('score');
285
+
286
+ // 分组统计
287
+ const stats = await db.model('User')
288
+ .select('city, count(*) as num')
289
+ .group('city')
290
+ .find();
291
+ ```
292
+
293
+ ---
294
+
295
+ ## ✏️ 写入操作
296
+
297
+ ### 插入 (Insert)
298
+
299
+ ```javascript
300
+ // 插入单条
301
+ const newUser = await db.model('User').insert({
302
+ username: 'neo',
303
+ email: 'neo@matrix.com'
304
+ });
305
+ // 如果在 Schema 中配置了,ID 和时间戳会自动生成
306
+
307
+ // 批量插入 (Batch)
308
+ await db.model('User').insert([
309
+ { username: 'a' },
310
+ { username: 'b' }
311
+ ]);
312
+ ```
313
+
314
+ ### 更新 (Update)
315
+
316
+ ```javascript
317
+ const updated = await db.model('User')
318
+ .where({ id: '123' })
319
+ .update({
320
+ age: 99,
321
+ meta: { role: 'admin' }
322
+ });
323
+ ```
324
+
325
+ ### 删除 (Delete)
326
+
327
+ ```javascript
328
+ await db.model('User')
329
+ .where('age', '<', 10)
330
+ .delete();
331
+ ```
332
+
333
+ ### 返回数据 (Returning)
334
+
335
+ 出于性能考虑,写入操作默认可能不返回所有数据。您可以使用 `returning` 强制返回:
336
+
337
+ ```javascript
338
+ const deletedUsers = await db.model('User')
339
+ .where('status', 'banned')
340
+ .returning('id, username') // 或者 returning('*')
341
+ .delete();
342
+ ```
343
+
344
+ ---
345
+
346
+ ## ⚡ 原生 SQL (模板字符串)
347
+
348
+ NeoPG 暴露了 `postgres.js` 的全部能力。对于极其复杂的查询,您可以跳过 ModelChain 直接使用原生方式。
349
+
350
+ > 📚 **参考文档**: `sql` 标签的完整用法请参阅 [postgres.js GitHub 主页](https://github.com/porsager/postgres)。
351
+
352
+ ```javascript
353
+ // 访问原生驱动
354
+ const sql = db.sql;
355
+
356
+ // 安全执行原生 SQL
357
+ const users = await sql`
358
+ SELECT * FROM users
359
+ WHERE age > ${20}
360
+ `;
361
+
362
+ // 使用 helper 处理动态表名/列名
363
+ const table = 'users';
364
+ const column = 'age';
365
+ const result = await sql`
366
+ SELECT ${sql(column)}
367
+ FROM ${sql(table)}
368
+ `;
369
+ ```
370
+
371
+ ---
372
+
373
+ ## 🤝 事务处理
374
+
375
+ NeoPG 提供了一套统一的事务 API,并自动支持嵌套事务(Savepoints)。
376
+
377
+ ### 使用 NeoPG 上下文 (推荐)
378
+
379
+ ```javascript
380
+ // 开启一个事务作用域
381
+ const result = await db.transaction(async (tx) => {
382
+ // 'tx' 是一个 TransactionScope,拥有和 'db' 几乎一致的 API
383
+
384
+ // 1. 写入操作 (自动绑定到当前事务)
385
+ const user = await tx.model('User').insert({ username: 'alice' });
386
+
387
+ // 2. 读取操作
388
+ const count = await tx.model('User').count();
389
+
390
+ // 3. 抛出错误会自动回滚 (ROLLBACK)
391
+ if (count > 100) {
392
+ throw new Error('Limit reached');
393
+ }
394
+
395
+ return user;
396
+ });
397
+ // 如果无错误,此处已自动提交 (COMMIT)
398
+ ```
399
+
400
+ ### 使用原生 Postgres 事务
401
+
402
+ ```javascript
403
+ await db.sql.begin(async (sql) => {
404
+ // sql 是当前的事务连接对象
405
+ await sql`INSERT INTO users (name) VALUES ('bob')`;
406
+ });
407
+ ```
408
+
409
+ ---
410
+
411
+ ## License
412
+
413
+ ISC
414
+
415
+ ![](./images/neopg-programming.jpeg)