neopg 0.0.1 → 1.0.0
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 +21 -0
- package/README.cn.md +336 -0
- package/README.md +336 -0
- package/images/neopg-programming.jpeg +0 -0
- package/images/neopg.png +0 -0
- package/lib/ModelChain.js +323 -15
- package/lib/NeoPG.js +49 -1
- package/lib/SchemaSync.js +62 -25
- package/lib/TransactionScope.js +4 -0
- package/package.json +9 -2
- package/test/test-db.js +57 -17
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,336 @@
|
|
|
1
|
+

|
|
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
|
+
---
|
|
116
|
+
|
|
117
|
+
## ⚙️ 注册与同步
|
|
118
|
+
|
|
119
|
+
初始化 NeoPG 并注册您的模型。您还可以将表结构定义同步到数据库中。
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
const User = require('./models/User');
|
|
123
|
+
|
|
124
|
+
// 1. 注册模型
|
|
125
|
+
db.define(User);
|
|
126
|
+
|
|
127
|
+
// 2. 同步表结构 (DDL)
|
|
128
|
+
// options: { force: true } 开启强制模式,会删除 Schema 中未定义的字段,请谨慎使用
|
|
129
|
+
await db.sync({ force: false });
|
|
130
|
+
|
|
131
|
+
console.log('数据库结构已同步!');
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 🔍 查询数据
|
|
137
|
+
|
|
138
|
+
NeoPG 提供了自然流畅的链式 API。
|
|
139
|
+
|
|
140
|
+
### 基础查询
|
|
141
|
+
|
|
142
|
+
```javascript
|
|
143
|
+
// 获取所有用户
|
|
144
|
+
const users = await db.model('User').find();
|
|
145
|
+
|
|
146
|
+
// 选择特定列
|
|
147
|
+
const users = await db.model('User')
|
|
148
|
+
.select('id, username')
|
|
149
|
+
.limit(10)
|
|
150
|
+
.find();
|
|
151
|
+
|
|
152
|
+
// 获取单条记录
|
|
153
|
+
const user = await db.model('User').where({ id: '123' }).get();
|
|
154
|
+
|
|
155
|
+
// 分页查询
|
|
156
|
+
const page2 = await db.model('User').page(2, 20).find(); // 第 2 页,每页 20 条
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### 链式 Where 条件
|
|
160
|
+
|
|
161
|
+
```javascript
|
|
162
|
+
await db.model('User')
|
|
163
|
+
// 对象风格 (自动处理 AND)
|
|
164
|
+
.where({
|
|
165
|
+
age: 18,
|
|
166
|
+
status: 'active'
|
|
167
|
+
})
|
|
168
|
+
// 操作符风格
|
|
169
|
+
.where('create_time', '>', 1600000000)
|
|
170
|
+
// SQL 片段风格 (强大且灵活!)
|
|
171
|
+
.where('id IS NOT NULL')
|
|
172
|
+
.find();
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### 结合模板字符串的复杂查询
|
|
176
|
+
|
|
177
|
+
这是 NeoPG 的亮点所在。您可以从上下文中解构出 `sql` 标签,安全地混合原生 SQL 片段。
|
|
178
|
+
|
|
179
|
+
```javascript
|
|
180
|
+
// db.sql 是原生的 postgres 实例
|
|
181
|
+
const { sql } = db;
|
|
182
|
+
|
|
183
|
+
await db.model('User')
|
|
184
|
+
.where({ status: 'active' })
|
|
185
|
+
// 通过模板字符串安全地注入参数
|
|
186
|
+
.where(sql`age > ${20} AND email LIKE ${'%@gmail.com'}`)
|
|
187
|
+
.find();
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## 📊 聚合函数
|
|
193
|
+
|
|
194
|
+
NeoPG 会自动处理类型转换(例如将 PostgreSQL 返回的 `count` 字符串转换为 JavaScript 数字)。
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
// 计数
|
|
198
|
+
const total = await db.model('User').where({ age: 18 }).count();
|
|
199
|
+
|
|
200
|
+
// 最大值 / 最小值
|
|
201
|
+
const maxAge = await db.model('User').max('age');
|
|
202
|
+
|
|
203
|
+
// 求和 / 平均值 (返回 Number 类型,而非 String)
|
|
204
|
+
const totalScore = await db.model('User').sum('score');
|
|
205
|
+
const avgScore = await db.model('User').avg('score');
|
|
206
|
+
|
|
207
|
+
// 分组统计
|
|
208
|
+
const stats = await db.model('User')
|
|
209
|
+
.select('city, count(*) as num')
|
|
210
|
+
.group('city')
|
|
211
|
+
.find();
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## ✏️ 写入操作
|
|
217
|
+
|
|
218
|
+
### 插入 (Insert)
|
|
219
|
+
|
|
220
|
+
```javascript
|
|
221
|
+
// 插入单条
|
|
222
|
+
const newUser = await db.model('User').insert({
|
|
223
|
+
username: 'neo',
|
|
224
|
+
email: 'neo@matrix.com'
|
|
225
|
+
});
|
|
226
|
+
// 如果在 Schema 中配置了,ID 和时间戳会自动生成
|
|
227
|
+
|
|
228
|
+
// 批量插入 (Batch)
|
|
229
|
+
await db.model('User').insert([
|
|
230
|
+
{ username: 'a' },
|
|
231
|
+
{ username: 'b' }
|
|
232
|
+
]);
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### 更新 (Update)
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
const updated = await db.model('User')
|
|
239
|
+
.where({ id: '123' })
|
|
240
|
+
.update({
|
|
241
|
+
age: 99,
|
|
242
|
+
meta: { role: 'admin' }
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### 删除 (Delete)
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
await db.model('User')
|
|
250
|
+
.where('age', '<', 10)
|
|
251
|
+
.delete();
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### 返回数据 (Returning)
|
|
255
|
+
|
|
256
|
+
出于性能考虑,写入操作默认可能不返回所有数据。您可以使用 `returning` 强制返回:
|
|
257
|
+
|
|
258
|
+
```javascript
|
|
259
|
+
const deletedUsers = await db.model('User')
|
|
260
|
+
.where('status', 'banned')
|
|
261
|
+
.returning('id, username') // 或者 returning('*')
|
|
262
|
+
.delete();
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## ⚡ 原生 SQL (模板字符串)
|
|
268
|
+
|
|
269
|
+
NeoPG 暴露了 `postgres.js` 的全部能力。对于极其复杂的查询,您可以跳过 ModelChain 直接使用原生方式。
|
|
270
|
+
|
|
271
|
+
> 📚 **参考文档**: `sql` 标签的完整用法请参阅 [postgres.js GitHub 主页](https://github.com/porsager/postgres)。
|
|
272
|
+
|
|
273
|
+
```javascript
|
|
274
|
+
// 访问原生驱动
|
|
275
|
+
const sql = db.sql;
|
|
276
|
+
|
|
277
|
+
// 安全执行原生 SQL
|
|
278
|
+
const users = await sql`
|
|
279
|
+
SELECT * FROM users
|
|
280
|
+
WHERE age > ${20}
|
|
281
|
+
`;
|
|
282
|
+
|
|
283
|
+
// 使用 helper 处理动态表名/列名
|
|
284
|
+
const table = 'users';
|
|
285
|
+
const column = 'age';
|
|
286
|
+
const result = await sql`
|
|
287
|
+
SELECT ${sql(column)}
|
|
288
|
+
FROM ${sql(table)}
|
|
289
|
+
`;
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## 🤝 事务处理
|
|
295
|
+
|
|
296
|
+
NeoPG 提供了一套统一的事务 API,并自动支持嵌套事务(Savepoints)。
|
|
297
|
+
|
|
298
|
+
### 使用 NeoPG 上下文 (推荐)
|
|
299
|
+
|
|
300
|
+
```javascript
|
|
301
|
+
// 开启一个事务作用域
|
|
302
|
+
const result = await db.transaction(async (tx) => {
|
|
303
|
+
// 'tx' 是一个 TransactionScope,拥有和 'db' 几乎一致的 API
|
|
304
|
+
|
|
305
|
+
// 1. 写入操作 (自动绑定到当前事务)
|
|
306
|
+
const user = await tx.model('User').insert({ username: 'alice' });
|
|
307
|
+
|
|
308
|
+
// 2. 读取操作
|
|
309
|
+
const count = await tx.model('User').count();
|
|
310
|
+
|
|
311
|
+
// 3. 抛出错误会自动回滚 (ROLLBACK)
|
|
312
|
+
if (count > 100) {
|
|
313
|
+
throw new Error('Limit reached');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return user;
|
|
317
|
+
});
|
|
318
|
+
// 如果无错误,此处已自动提交 (COMMIT)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### 使用原生 Postgres 事务
|
|
322
|
+
|
|
323
|
+
```javascript
|
|
324
|
+
await db.sql.begin(async (sql) => {
|
|
325
|
+
// sql 是当前的事务连接对象
|
|
326
|
+
await sql`INSERT INTO users (name) VALUES ('bob')`;
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## License
|
|
333
|
+
|
|
334
|
+
ISC
|
|
335
|
+
|
|
336
|
+

|
package/README.md
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
# NeoPG
|
|
4
|
+
|
|
5
|
+
### The Next Generation PostgreSQL ORM for Node.js
|
|
6
|
+
|
|
7
|
+
**NeoPG** is a high-performance, zero-dependency ORM built directly on top of [postgres.js](https://github.com/porsager/postgres) — the fastest PostgreSQL client for Node.js.
|
|
8
|
+
|
|
9
|
+
It bridges the gap between the developer experience (DX) of a chainable Query Builder and the raw performance of native SQL Template Literals.
|
|
10
|
+
|
|
11
|
+
### [🪭 中文文档 ☯️](./README.cn.md)
|
|
12
|
+
|
|
13
|
+
## 🚀 Key Features
|
|
14
|
+
|
|
15
|
+
* **Powered by [postgres.js](https://github.com/porsager/postgres)**: Inherits the incredible speed and stability of the fastest PG client.
|
|
16
|
+
* **Zero Dependencies**: The core driver is vendored and optimized internally. No heavy dependency tree.
|
|
17
|
+
* **Hybrid API**: Enjoy the ease of **Chainable/Fluent APIs** (like `.where().select()`) combined with the power of **Tagged Template Literals**.
|
|
18
|
+
* **Performance First**: Zero string concatenation logic. All queries are compiled into efficient fragments and executed natively.
|
|
19
|
+
* **Auto Schema Sync**: define your models in code, and NeoPG syncs the table structure, indices, and foreign keys automatically.
|
|
20
|
+
* **Type Smart**: Automatic type casting for aggregations (`sum`, `avg` returns numbers, not strings) and JSON handling.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 📦 Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install neopg
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 🔌 Initialization
|
|
33
|
+
|
|
34
|
+
### Connect to Database
|
|
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, // Connection pool size
|
|
46
|
+
idle_timeout: 30, // Idle connection timeout in seconds
|
|
47
|
+
debug: false, // Enable query logging
|
|
48
|
+
schema: 'public' // Default schema
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const db = new NeoPG(config);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Close Connection
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
await db.close();
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 📝 Defining a Model
|
|
63
|
+
|
|
64
|
+
Create a model file (e.g., `models/User.js`). Your class should extend `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', // Optional, defaults to tableName
|
|
73
|
+
primaryKey: 'id',
|
|
74
|
+
|
|
75
|
+
// Auto-sync table structure based on this definition
|
|
76
|
+
column: {
|
|
77
|
+
id: {
|
|
78
|
+
type: dataTypes.ID, // Auto-generates Snowflake-like 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' // Auto-fill on insert
|
|
98
|
+
},
|
|
99
|
+
updated_at: {
|
|
100
|
+
type: dataTypes.BIGINT,
|
|
101
|
+
timestamp: 'update' // Auto-fill on insert & update
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// Indexes
|
|
106
|
+
index: ['email', 'age'],
|
|
107
|
+
unique: ['username']
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = User;
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## ⚙️ Registration & Sync
|
|
117
|
+
|
|
118
|
+
Initialize NeoPG and register your models. You can also sync the table structure to the database.
|
|
119
|
+
|
|
120
|
+
```javascript
|
|
121
|
+
const User = require('./models/User');
|
|
122
|
+
|
|
123
|
+
// 1. Register Model
|
|
124
|
+
db.define(User);
|
|
125
|
+
|
|
126
|
+
// 2. Sync Table Structure (DDL)
|
|
127
|
+
// options: { force: true } will drop columns not defined in schema
|
|
128
|
+
await db.sync({ force: false });
|
|
129
|
+
|
|
130
|
+
console.log('Database synced!');
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 🔍 Querying
|
|
136
|
+
|
|
137
|
+
NeoPG provides a fluent, chainable API that feels natural to use.
|
|
138
|
+
|
|
139
|
+
### Basic Find
|
|
140
|
+
|
|
141
|
+
```javascript
|
|
142
|
+
// Get all users
|
|
143
|
+
const users = await db.model('User').find();
|
|
144
|
+
|
|
145
|
+
// Select specific columns
|
|
146
|
+
const users = await db.model('User')
|
|
147
|
+
.select('id, username')
|
|
148
|
+
.limit(10)
|
|
149
|
+
.find();
|
|
150
|
+
|
|
151
|
+
// Get a single record
|
|
152
|
+
const user = await db.model('User').where({ id: '123' }).get();
|
|
153
|
+
|
|
154
|
+
// Pagination
|
|
155
|
+
const page2 = await db.model('User').page(2, 20).find(); // Page 2, Size 20
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Chained Where
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
await db.model('User')
|
|
162
|
+
// Object style (AND logic)
|
|
163
|
+
.where({
|
|
164
|
+
age: 18,
|
|
165
|
+
status: 'active'
|
|
166
|
+
})
|
|
167
|
+
// Operator style
|
|
168
|
+
.where('create_time', '>', 1600000000)
|
|
169
|
+
// SQL Fragment style (Powerful!)
|
|
170
|
+
.where('id IS NOT NULL')
|
|
171
|
+
.find();
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Complex Where with Template Literals
|
|
175
|
+
|
|
176
|
+
This is where NeoPG shines. You can mix raw SQL fragments safely using the `sql` tag from the context.
|
|
177
|
+
|
|
178
|
+
```javascript
|
|
179
|
+
// db.sql is the native postgres instance
|
|
180
|
+
const { sql } = db;
|
|
181
|
+
|
|
182
|
+
await db.model('User')
|
|
183
|
+
.where({ status: 'active' })
|
|
184
|
+
// Safe parameter injection via Template Literals
|
|
185
|
+
.where(sql`age > ${20} AND email LIKE ${'%@gmail.com'}`)
|
|
186
|
+
.find();
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 📊 Aggregation
|
|
192
|
+
|
|
193
|
+
NeoPG handles type casting automatically (e.g., converting PostgreSQL `count` string results to Javascript Numbers).
|
|
194
|
+
|
|
195
|
+
```javascript
|
|
196
|
+
// Count
|
|
197
|
+
const total = await db.model('User').where({ age: 18 }).count();
|
|
198
|
+
|
|
199
|
+
// Max / Min
|
|
200
|
+
const maxAge = await db.model('User').max('age');
|
|
201
|
+
|
|
202
|
+
// Sum / Avg (Returns Number, not String)
|
|
203
|
+
const totalScore = await db.model('User').sum('score');
|
|
204
|
+
const avgScore = await db.model('User').avg('score');
|
|
205
|
+
|
|
206
|
+
// Group By
|
|
207
|
+
const stats = await db.model('User')
|
|
208
|
+
.select('city, count(*) as num')
|
|
209
|
+
.group('city')
|
|
210
|
+
.find();
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## ✏️ Write Operations
|
|
216
|
+
|
|
217
|
+
### Insert
|
|
218
|
+
|
|
219
|
+
```javascript
|
|
220
|
+
// Insert one
|
|
221
|
+
const newUser = await db.model('User').insert({
|
|
222
|
+
username: 'neo',
|
|
223
|
+
email: 'neo@matrix.com'
|
|
224
|
+
});
|
|
225
|
+
// ID and Timestamps are automatically generated if configured in Schema
|
|
226
|
+
|
|
227
|
+
// Insert multiple (Batch)
|
|
228
|
+
await db.model('User').insert([
|
|
229
|
+
{ username: 'a' },
|
|
230
|
+
{ username: 'b' }
|
|
231
|
+
]);
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Update
|
|
235
|
+
|
|
236
|
+
```javascript
|
|
237
|
+
const updated = await db.model('User')
|
|
238
|
+
.where({ id: '123' })
|
|
239
|
+
.update({
|
|
240
|
+
age: 99,
|
|
241
|
+
meta: { role: 'admin' }
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Delete
|
|
246
|
+
|
|
247
|
+
```javascript
|
|
248
|
+
await db.model('User')
|
|
249
|
+
.where('age', '<', 10)
|
|
250
|
+
.delete();
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Returning Data
|
|
254
|
+
|
|
255
|
+
By default, write operations might not return data depending on the driver optimization. You can enforce it:
|
|
256
|
+
|
|
257
|
+
```javascript
|
|
258
|
+
const deletedUsers = await db.model('User')
|
|
259
|
+
.where('status', 'banned')
|
|
260
|
+
.returning('id, username') // or returning('*')
|
|
261
|
+
.delete();
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## ⚡ Raw SQL (Template Literals)
|
|
267
|
+
|
|
268
|
+
NeoPG exposes the full power of `postgres.js`. You don't need the ModelChain for everything.
|
|
269
|
+
|
|
270
|
+
> 📚 **Reference**: Full documentation for the SQL tag can be found at the [postgres.js GitHub page](https://github.com/porsager/postgres).
|
|
271
|
+
|
|
272
|
+
```javascript
|
|
273
|
+
// Access the native driver
|
|
274
|
+
const sql = db.sql;
|
|
275
|
+
|
|
276
|
+
// Execute raw SQL safely
|
|
277
|
+
const users = await sql`
|
|
278
|
+
SELECT * FROM users
|
|
279
|
+
WHERE age > ${20}
|
|
280
|
+
`;
|
|
281
|
+
|
|
282
|
+
// Dynamic tables/columns using helper
|
|
283
|
+
const table = 'users';
|
|
284
|
+
const column = 'age';
|
|
285
|
+
const result = await sql`
|
|
286
|
+
SELECT ${sql(column)}
|
|
287
|
+
FROM ${sql(table)}
|
|
288
|
+
`;
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## 🤝 Transactions
|
|
294
|
+
|
|
295
|
+
NeoPG provides a unified transaction API. It supports nested transactions (Savepoints) automatically.
|
|
296
|
+
|
|
297
|
+
### Using NeoPG Context (Recommended)
|
|
298
|
+
|
|
299
|
+
```javascript
|
|
300
|
+
// Start a transaction scope
|
|
301
|
+
const result = await db.transaction(async (tx) => {
|
|
302
|
+
// 'tx' is a TransactionScope that mimics 'db'
|
|
303
|
+
|
|
304
|
+
// 1. Write operation
|
|
305
|
+
const user = await tx.model('User').insert({ username: 'alice' });
|
|
306
|
+
|
|
307
|
+
// 2. Read operation within transaction
|
|
308
|
+
const count = await tx.model('User').count();
|
|
309
|
+
|
|
310
|
+
// 3. Throwing an error will automatically ROLLBACK
|
|
311
|
+
if (count > 100) {
|
|
312
|
+
throw new Error('Limit reached');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return user;
|
|
316
|
+
});
|
|
317
|
+
// Automatically COMMITTED here if no error
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Using Raw Postgres Transaction
|
|
321
|
+
|
|
322
|
+
```javascript
|
|
323
|
+
await db.sql.begin(async (sql) => {
|
|
324
|
+
// sql is the transaction connection
|
|
325
|
+
await sql`INSERT INTO users (name) VALUES ('bob')`;
|
|
326
|
+
});
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## License
|
|
332
|
+
|
|
333
|
+
ISC
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+

|
|
Binary file
|
package/images/neopg.png
ADDED
|
Binary file
|