@yanit/jsondb 0.1.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/README.md +903 -0
- package/dist/bin/cli-export.d.ts +7 -0
- package/dist/bin/cli-export.d.ts.map +1 -0
- package/dist/bin/cli-export.js +318 -0
- package/dist/bin/cli-export.js.map +1 -0
- package/dist/bin/cli-import.d.ts +7 -0
- package/dist/bin/cli-import.d.ts.map +1 -0
- package/dist/bin/cli-import.js +298 -0
- package/dist/bin/cli-import.js.map +1 -0
- package/dist/bin/server.d.ts +7 -0
- package/dist/bin/server.d.ts.map +1 -0
- package/dist/bin/server.js +92 -0
- package/dist/bin/server.js.map +1 -0
- package/dist/examples/sql-example.d.ts +7 -0
- package/dist/examples/sql-example.d.ts.map +1 -0
- package/dist/examples/sql-example.js +131 -0
- package/dist/examples/sql-example.js.map +1 -0
- package/dist/src/BulkOp.d.ts +74 -0
- package/dist/src/BulkOp.d.ts.map +1 -0
- package/dist/src/BulkOp.js +143 -0
- package/dist/src/BulkOp.js.map +1 -0
- package/dist/src/Collection.d.ts +232 -0
- package/dist/src/Collection.d.ts.map +1 -0
- package/dist/src/Collection.js +705 -0
- package/dist/src/Collection.js.map +1 -0
- package/dist/src/Cursor.d.ts +94 -0
- package/dist/src/Cursor.d.ts.map +1 -0
- package/dist/src/Cursor.js +259 -0
- package/dist/src/Cursor.js.map +1 -0
- package/dist/src/Database.d.ts +98 -0
- package/dist/src/Database.d.ts.map +1 -0
- package/dist/src/Database.js +198 -0
- package/dist/src/Database.js.map +1 -0
- package/dist/src/Operators.d.ts +73 -0
- package/dist/src/Operators.d.ts.map +1 -0
- package/dist/src/Operators.js +339 -0
- package/dist/src/Operators.js.map +1 -0
- package/dist/src/QueryCache.d.ts +87 -0
- package/dist/src/QueryCache.d.ts.map +1 -0
- package/dist/src/QueryCache.js +155 -0
- package/dist/src/QueryCache.js.map +1 -0
- package/dist/src/SQLExecutor.d.ts +60 -0
- package/dist/src/SQLExecutor.d.ts.map +1 -0
- package/dist/src/SQLExecutor.js +317 -0
- package/dist/src/SQLExecutor.js.map +1 -0
- package/dist/src/SQLParser.d.ts +181 -0
- package/dist/src/SQLParser.d.ts.map +1 -0
- package/dist/src/SQLParser.js +640 -0
- package/dist/src/SQLParser.js.map +1 -0
- package/dist/src/Schema.d.ts +92 -0
- package/dist/src/Schema.d.ts.map +1 -0
- package/dist/src/Schema.js +253 -0
- package/dist/src/Schema.js.map +1 -0
- package/dist/src/Transaction.d.ts +118 -0
- package/dist/src/Transaction.d.ts.map +1 -0
- package/dist/src/Transaction.js +233 -0
- package/dist/src/Transaction.js.map +1 -0
- package/dist/src/Utils.d.ts +68 -0
- package/dist/src/Utils.d.ts.map +1 -0
- package/dist/src/Utils.js +187 -0
- package/dist/src/Utils.js.map +1 -0
- package/dist/src/errors.d.ts +58 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +85 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/index.d.ts +39 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +44 -0
- package/dist/src/index.js.map +1 -0
- package/dist/test/basic.test.d.ts +5 -0
- package/dist/test/basic.test.d.ts.map +1 -0
- package/dist/test/basic.test.js +283 -0
- package/dist/test/basic.test.js.map +1 -0
- package/dist/test/index.test.d.ts +5 -0
- package/dist/test/index.test.d.ts.map +1 -0
- package/dist/test/index.test.js +126 -0
- package/dist/test/index.test.js.map +1 -0
- package/dist/test/jsonb.test.d.ts +5 -0
- package/dist/test/jsonb.test.d.ts.map +1 -0
- package/dist/test/jsonb.test.js +165 -0
- package/dist/test/jsonb.test.js.map +1 -0
- package/dist/test/optimization.test.d.ts +6 -0
- package/dist/test/optimization.test.d.ts.map +1 -0
- package/dist/test/optimization.test.js +196 -0
- package/dist/test/optimization.test.js.map +1 -0
- package/dist/test/schema.test.d.ts +5 -0
- package/dist/test/schema.test.d.ts.map +1 -0
- package/dist/test/schema.test.js +197 -0
- package/dist/test/schema.test.js.map +1 -0
- package/dist/test/sql.test.d.ts +7 -0
- package/dist/test/sql.test.d.ts.map +1 -0
- package/dist/test/sql.test.js +21 -0
- package/dist/test/sql.test.js.map +1 -0
- package/package.json +73 -0
- package/src/BulkOp.js +181 -0
- package/src/BulkOp.ts +191 -0
- package/src/Collection.js +843 -0
- package/src/Collection.ts +896 -0
- package/src/Cursor.js +315 -0
- package/src/Cursor.ts +319 -0
- package/src/Database.js +244 -0
- package/src/Database.ts +268 -0
- package/src/Operators.js +382 -0
- package/src/Operators.ts +375 -0
- package/src/QueryCache.js +190 -0
- package/src/QueryCache.ts +208 -0
- package/src/SQLExecutor.ts +391 -0
- package/src/SQLParser.ts +814 -0
- package/src/Schema.js +292 -0
- package/src/Schema.ts +317 -0
- package/src/Transaction.js +291 -0
- package/src/Transaction.ts +313 -0
- package/src/Utils.js +205 -0
- package/src/Utils.ts +205 -0
- package/src/errors.js +93 -0
- package/src/errors.ts +93 -0
- package/src/index.js +90 -0
- package/src/index.ts +106 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 简单事务模块
|
|
3
|
+
* 提供多操作的原子性保证
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { deepClone } from './Utils.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 事务状态
|
|
10
|
+
*/
|
|
11
|
+
export const TransactionStatus = {
|
|
12
|
+
ACTIVE: 'active',
|
|
13
|
+
COMMITTED: 'committed',
|
|
14
|
+
ROLLED_BACK: 'rolled_back',
|
|
15
|
+
FAILED: 'failed'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 操作类型
|
|
20
|
+
*/
|
|
21
|
+
export const OperationType = {
|
|
22
|
+
INSERT: 'insert',
|
|
23
|
+
UPDATE: 'update',
|
|
24
|
+
DELETE: 'delete'
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 事务类
|
|
29
|
+
*/
|
|
30
|
+
export class Transaction {
|
|
31
|
+
constructor(collections) {
|
|
32
|
+
this.collections = Array.isArray(collections) ? collections : [collections];
|
|
33
|
+
this.operations = [];
|
|
34
|
+
this.status = TransactionStatus.ACTIVE;
|
|
35
|
+
this.backup = new Map(); // 原始数据备份
|
|
36
|
+
this.results = [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 备份集合数据
|
|
41
|
+
* @private
|
|
42
|
+
*/
|
|
43
|
+
async _backup() {
|
|
44
|
+
for (const collection of this.collections) {
|
|
45
|
+
await collection._load();
|
|
46
|
+
|
|
47
|
+
// 深拷贝备份
|
|
48
|
+
this.backup.set(collection.name, {
|
|
49
|
+
documents: deepClone(collection._data._documents),
|
|
50
|
+
indexes: deepClone(collection._data._indexes),
|
|
51
|
+
meta: deepClone(collection._data._meta)
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 恢复集合数据
|
|
58
|
+
* @private
|
|
59
|
+
*/
|
|
60
|
+
async _restore() {
|
|
61
|
+
for (const [name, data] of this.backup.entries()) {
|
|
62
|
+
const collection = this.collections.find(c => c.name === name);
|
|
63
|
+
if (collection) {
|
|
64
|
+
collection._data._documents = data.documents;
|
|
65
|
+
collection._data._indexes = data.indexes;
|
|
66
|
+
collection._data._meta = data.meta;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 添加插入操作
|
|
73
|
+
* @param {Collection} collection - 集合
|
|
74
|
+
* @param {Object|Array} docs - 文档
|
|
75
|
+
* @returns {Transaction}
|
|
76
|
+
*/
|
|
77
|
+
insert(collection, docs) {
|
|
78
|
+
if (this.status !== TransactionStatus.ACTIVE) {
|
|
79
|
+
throw new Error('事务已不再活跃');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!this.collections.includes(collection)) {
|
|
83
|
+
throw new Error('集合不在事务范围内');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
this.operations.push({
|
|
87
|
+
type: OperationType.INSERT,
|
|
88
|
+
collection,
|
|
89
|
+
docs: Array.isArray(docs) ? docs : [docs]
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 添加更新操作
|
|
97
|
+
* @param {Collection} collection - 集合
|
|
98
|
+
* @param {Object} query - 查询条件
|
|
99
|
+
* @param {Object} update - 更新操作
|
|
100
|
+
* @param {Object} options - 选项
|
|
101
|
+
* @returns {Transaction}
|
|
102
|
+
*/
|
|
103
|
+
update(collection, query, update, options = {}) {
|
|
104
|
+
if (this.status !== TransactionStatus.ACTIVE) {
|
|
105
|
+
throw new Error('事务已不再活跃');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!this.collections.includes(collection)) {
|
|
109
|
+
throw new Error('集合不在事务范围内');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.operations.push({
|
|
113
|
+
type: OperationType.UPDATE,
|
|
114
|
+
collection,
|
|
115
|
+
query,
|
|
116
|
+
update,
|
|
117
|
+
options
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return this;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 添加删除操作
|
|
125
|
+
* @param {Collection} collection - 集合
|
|
126
|
+
* @param {Object} query - 查询条件
|
|
127
|
+
* @returns {Transaction}
|
|
128
|
+
*/
|
|
129
|
+
delete(collection, query) {
|
|
130
|
+
if (this.status !== TransactionStatus.ACTIVE) {
|
|
131
|
+
throw new Error('事务已不再活跃');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!this.collections.includes(collection)) {
|
|
135
|
+
throw new Error('集合不在事务范围内');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
this.operations.push({
|
|
139
|
+
type: OperationType.DELETE,
|
|
140
|
+
collection,
|
|
141
|
+
query
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return this;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* 提交事务
|
|
149
|
+
* @returns {Promise<Object>} 提交结果
|
|
150
|
+
*/
|
|
151
|
+
async commit() {
|
|
152
|
+
if (this.status !== TransactionStatus.ACTIVE) {
|
|
153
|
+
throw new Error('事务已不再活跃');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
// 1. 备份数据
|
|
158
|
+
await this._backup();
|
|
159
|
+
|
|
160
|
+
// 2. 执行所有操作
|
|
161
|
+
for (const op of this.operations) {
|
|
162
|
+
const result = await this._executeOperation(op);
|
|
163
|
+
this.results.push(result);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 3. 保存所有集合
|
|
167
|
+
for (const collection of this.collections) {
|
|
168
|
+
await collection._save();
|
|
169
|
+
// 失效缓存
|
|
170
|
+
collection._cache = null;
|
|
171
|
+
collection._cacheTime = 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
this.status = TransactionStatus.COMMITTED;
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
success: true,
|
|
178
|
+
operations: this.results
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
} catch (error) {
|
|
182
|
+
// 执行失败,回滚
|
|
183
|
+
await this.rollback();
|
|
184
|
+
this.status = TransactionStatus.FAILED;
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
success: false,
|
|
188
|
+
error: error.message
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* 执行单个操作
|
|
195
|
+
* @private
|
|
196
|
+
* @param {Object} op - 操作对象
|
|
197
|
+
* @returns {Object} 操作结果
|
|
198
|
+
*/
|
|
199
|
+
async _executeOperation(op) {
|
|
200
|
+
const { matchQuery, applyUpdate } = await import('./Operators.js');
|
|
201
|
+
const { generateId } = await import('./Utils.js');
|
|
202
|
+
|
|
203
|
+
switch (op.type) {
|
|
204
|
+
case OperationType.INSERT: {
|
|
205
|
+
const now = new Date().toISOString();
|
|
206
|
+
const docs = op.docs.map(doc => ({
|
|
207
|
+
...deepClone(doc),
|
|
208
|
+
_id: doc._id || generateId(),
|
|
209
|
+
createdAt: doc.createdAt || now
|
|
210
|
+
}));
|
|
211
|
+
op.collection._data._documents.push(...docs);
|
|
212
|
+
return { type: 'insert', count: docs.length, ids: docs.map(d => d._id) };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
case OperationType.UPDATE: {
|
|
216
|
+
let count = 0;
|
|
217
|
+
for (let i = 0; i < op.collection._data._documents.length; i++) {
|
|
218
|
+
const doc = op.collection._data._documents[i];
|
|
219
|
+
if (matchQuery(doc, op.query)) {
|
|
220
|
+
const updated = applyUpdate(doc, op.update);
|
|
221
|
+
updated.updatedAt = new Date().toISOString();
|
|
222
|
+
op.collection._data._documents[i] = updated;
|
|
223
|
+
count++;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return { type: 'update', count };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
case OperationType.DELETE: {
|
|
230
|
+
const initial = op.collection._data._documents.length;
|
|
231
|
+
op.collection._data._documents = op.collection._data._documents.filter(
|
|
232
|
+
doc => !matchQuery(doc, op.query)
|
|
233
|
+
);
|
|
234
|
+
return { type: 'delete', count: initial - op.collection._data._documents.length };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
default:
|
|
238
|
+
throw new Error(`未知操作类型:${op.type}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* 回滚事务
|
|
244
|
+
* @returns {Promise<void>}
|
|
245
|
+
*/
|
|
246
|
+
async rollback() {
|
|
247
|
+
if (this.status === TransactionStatus.COMMITTED) {
|
|
248
|
+
throw new Error('已提交的事务无法回滚');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
await this._restore();
|
|
252
|
+
this.status = TransactionStatus.ROLLED_BACK;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* 获取事务状态
|
|
257
|
+
* @returns {string} 状态
|
|
258
|
+
*/
|
|
259
|
+
getStatus() {
|
|
260
|
+
return this.status;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* 获取操作数量
|
|
265
|
+
* @returns {number}
|
|
266
|
+
*/
|
|
267
|
+
getOperationCount() {
|
|
268
|
+
return this.operations.length;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* 创建事务
|
|
274
|
+
* @param {Collection|Collection[]} collections - 集合实例
|
|
275
|
+
* @returns {Transaction}
|
|
276
|
+
*/
|
|
277
|
+
export function createTransaction(collections) {
|
|
278
|
+
return new Transaction(collections);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* 执行事务(快捷方式)
|
|
283
|
+
* @param {Collection[]} collections - 集合数组
|
|
284
|
+
* @param {Function} fn - 事务函数
|
|
285
|
+
* @returns {Promise<Object>}
|
|
286
|
+
*/
|
|
287
|
+
export async function withTransaction(collections, fn) {
|
|
288
|
+
const tx = createTransaction(collections);
|
|
289
|
+
await fn(tx);
|
|
290
|
+
return await tx.commit();
|
|
291
|
+
}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 简单事务模块
|
|
3
|
+
* 提供多操作的原子性保证
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { deepClone } from './Utils.js';
|
|
7
|
+
import type { Collection } from './Collection.js';
|
|
8
|
+
import type { IndexInfo } from './Collection.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 事务状态
|
|
12
|
+
*/
|
|
13
|
+
export const TransactionStatus = {
|
|
14
|
+
ACTIVE: 'active',
|
|
15
|
+
COMMITTED: 'committed',
|
|
16
|
+
ROLLED_BACK: 'rolled_back',
|
|
17
|
+
FAILED: 'failed'
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 操作类型
|
|
22
|
+
*/
|
|
23
|
+
export const OperationType = {
|
|
24
|
+
INSERT: 'insert',
|
|
25
|
+
UPDATE: 'update',
|
|
26
|
+
DELETE: 'delete'
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 事务状态类型
|
|
31
|
+
*/
|
|
32
|
+
export type TransactionStatusType = typeof TransactionStatus[keyof typeof TransactionStatus];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 操作类型类型
|
|
36
|
+
*/
|
|
37
|
+
export type OperationTypeType = typeof OperationType[keyof typeof OperationType];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 数据备份接口
|
|
41
|
+
*/
|
|
42
|
+
interface DataBackup {
|
|
43
|
+
documents: Array<Record<string, unknown>>;
|
|
44
|
+
indexes: Record<string, unknown>;
|
|
45
|
+
meta: Record<string, unknown>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 操作接口
|
|
50
|
+
*/
|
|
51
|
+
interface Operation {
|
|
52
|
+
type: OperationTypeType;
|
|
53
|
+
collection: Collection;
|
|
54
|
+
docs?: Array<Record<string, unknown>>;
|
|
55
|
+
query?: Record<string, unknown>;
|
|
56
|
+
update?: Record<string, unknown>;
|
|
57
|
+
options?: Record<string, unknown>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 事务类
|
|
62
|
+
*/
|
|
63
|
+
export class Transaction {
|
|
64
|
+
collections: Collection[];
|
|
65
|
+
operations: Operation[];
|
|
66
|
+
status: TransactionStatusType;
|
|
67
|
+
backup: Map<string, DataBackup>;
|
|
68
|
+
results: Array<Record<string, unknown>>;
|
|
69
|
+
|
|
70
|
+
constructor(collections: Collection | Collection[]) {
|
|
71
|
+
this.collections = Array.isArray(collections) ? collections : [collections];
|
|
72
|
+
this.operations = [];
|
|
73
|
+
this.status = TransactionStatus.ACTIVE;
|
|
74
|
+
this.backup = new Map();
|
|
75
|
+
this.results = [];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 备份集合数据
|
|
80
|
+
*/
|
|
81
|
+
private async _backup(): Promise<void> {
|
|
82
|
+
for (const collection of this.collections) {
|
|
83
|
+
await collection._load();
|
|
84
|
+
|
|
85
|
+
// 深拷贝备份
|
|
86
|
+
this.backup.set(collection.name, {
|
|
87
|
+
documents: deepClone(collection._data._documents),
|
|
88
|
+
indexes: deepClone(collection._data._indexes),
|
|
89
|
+
meta: deepClone(collection._data._meta)
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 恢复集合数据
|
|
96
|
+
*/
|
|
97
|
+
private async _restore(): Promise<void> {
|
|
98
|
+
for (const [name, data] of this.backup.entries()) {
|
|
99
|
+
const collection = this.collections.find(c => c.name === name);
|
|
100
|
+
if (collection && collection._data) {
|
|
101
|
+
collection._data._documents = data.documents as Array<Record<string, unknown>>;
|
|
102
|
+
collection._data._indexes = data.indexes as Record<string, Record<string, string[]>>;
|
|
103
|
+
collection._data._meta = data.meta as { name: string; count: number; indexes: IndexInfo[] };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* 添加插入操作
|
|
110
|
+
*/
|
|
111
|
+
insert(collection: Collection, docs: Record<string, unknown> | Array<Record<string, unknown>>): Transaction {
|
|
112
|
+
if (this.status !== TransactionStatus.ACTIVE) {
|
|
113
|
+
throw new Error('事务已不再活跃');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!this.collections.includes(collection)) {
|
|
117
|
+
throw new Error('集合不在事务范围内');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
this.operations.push({
|
|
121
|
+
type: OperationType.INSERT,
|
|
122
|
+
collection,
|
|
123
|
+
docs: Array.isArray(docs) ? docs : [docs]
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
return this;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 添加更新操作
|
|
131
|
+
*/
|
|
132
|
+
update(
|
|
133
|
+
collection: Collection,
|
|
134
|
+
query: Record<string, unknown>,
|
|
135
|
+
update: Record<string, unknown>,
|
|
136
|
+
options: Record<string, unknown> = {}
|
|
137
|
+
): Transaction {
|
|
138
|
+
if (this.status !== TransactionStatus.ACTIVE) {
|
|
139
|
+
throw new Error('事务已不再活跃');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (!this.collections.includes(collection)) {
|
|
143
|
+
throw new Error('集合不在事务范围内');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
this.operations.push({
|
|
147
|
+
type: OperationType.UPDATE,
|
|
148
|
+
collection,
|
|
149
|
+
query,
|
|
150
|
+
update,
|
|
151
|
+
options
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
return this;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* 添加删除操作
|
|
159
|
+
*/
|
|
160
|
+
delete(collection: Collection, query: Record<string, unknown>): Transaction {
|
|
161
|
+
if (this.status !== TransactionStatus.ACTIVE) {
|
|
162
|
+
throw new Error('事务已不再活跃');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (!this.collections.includes(collection)) {
|
|
166
|
+
throw new Error('集合不在事务范围内');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
this.operations.push({
|
|
170
|
+
type: OperationType.DELETE,
|
|
171
|
+
collection,
|
|
172
|
+
query
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
return this;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* 提交事务
|
|
180
|
+
*/
|
|
181
|
+
async commit(): Promise<{ success: boolean; operations?: Array<Record<string, unknown>>; error?: string }> {
|
|
182
|
+
if (this.status !== TransactionStatus.ACTIVE) {
|
|
183
|
+
throw new Error('事务已不再活跃');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
// 1. 备份数据
|
|
188
|
+
await this._backup();
|
|
189
|
+
|
|
190
|
+
// 2. 执行所有操作
|
|
191
|
+
for (const op of this.operations) {
|
|
192
|
+
const result = await this._executeOperation(op);
|
|
193
|
+
this.results.push(result);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// 3. 保存所有集合
|
|
197
|
+
for (const collection of this.collections) {
|
|
198
|
+
await collection._save();
|
|
199
|
+
// 失效缓存
|
|
200
|
+
collection._cache = null;
|
|
201
|
+
collection._cacheTime = 0;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
this.status = TransactionStatus.COMMITTED;
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
success: true,
|
|
208
|
+
operations: this.results
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
} catch (error) {
|
|
212
|
+
// 执行失败,回滚
|
|
213
|
+
await this.rollback();
|
|
214
|
+
this.status = TransactionStatus.FAILED;
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
success: false,
|
|
218
|
+
error: (error as Error).message
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* 执行单个操作
|
|
225
|
+
*/
|
|
226
|
+
private async _executeOperation(op: Operation): Promise<Record<string, unknown>> {
|
|
227
|
+
const { matchQuery, applyUpdate } = await import('./Operators.js');
|
|
228
|
+
const { generateId } = await import('./Utils.js');
|
|
229
|
+
|
|
230
|
+
switch (op.type) {
|
|
231
|
+
case OperationType.INSERT: {
|
|
232
|
+
const now = new Date().toISOString();
|
|
233
|
+
const docs = op.docs!.map(doc => ({
|
|
234
|
+
...deepClone(doc),
|
|
235
|
+
_id: doc._id || generateId(),
|
|
236
|
+
createdAt: doc.createdAt || now
|
|
237
|
+
}));
|
|
238
|
+
op.collection._data._documents.push(...docs);
|
|
239
|
+
return { type: 'insert', count: docs.length, ids: docs.map(d => d._id) };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
case OperationType.UPDATE: {
|
|
243
|
+
let count = 0;
|
|
244
|
+
for (let i = 0; i < op.collection._data._documents.length; i++) {
|
|
245
|
+
const doc = op.collection._data._documents[i];
|
|
246
|
+
if (matchQuery(doc, op.query!)) {
|
|
247
|
+
const updated = applyUpdate(doc, op.update!) as Record<string, unknown>;
|
|
248
|
+
updated.updatedAt = new Date().toISOString();
|
|
249
|
+
op.collection._data._documents[i] = updated;
|
|
250
|
+
count++;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return { type: 'update', count };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
case OperationType.DELETE: {
|
|
257
|
+
const initial = op.collection._data._documents.length;
|
|
258
|
+
op.collection._data._documents = op.collection._data._documents.filter(
|
|
259
|
+
doc => !matchQuery(doc, op.query!)
|
|
260
|
+
);
|
|
261
|
+
return { type: 'delete', count: initial - op.collection._data._documents.length };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
default:
|
|
265
|
+
throw new Error(`未知操作类型:${op.type}`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* 回滚事务
|
|
271
|
+
*/
|
|
272
|
+
async rollback(): Promise<void> {
|
|
273
|
+
if (this.status === TransactionStatus.COMMITTED) {
|
|
274
|
+
throw new Error('已提交的事务无法回滚');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
await this._restore();
|
|
278
|
+
this.status = TransactionStatus.ROLLED_BACK;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* 获取事务状态
|
|
283
|
+
*/
|
|
284
|
+
getStatus(): TransactionStatusType {
|
|
285
|
+
return this.status;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* 获取操作数量
|
|
290
|
+
*/
|
|
291
|
+
getOperationCount(): number {
|
|
292
|
+
return this.operations.length;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* 创建事务
|
|
298
|
+
*/
|
|
299
|
+
export function createTransaction(collections: Collection | Collection[]): Transaction {
|
|
300
|
+
return new Transaction(collections);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* 执行事务(快捷方式)
|
|
305
|
+
*/
|
|
306
|
+
export async function withTransaction(
|
|
307
|
+
collections: Collection[],
|
|
308
|
+
fn: (tx: Transaction) => Promise<void>
|
|
309
|
+
): Promise<{ success: boolean; operations?: Array<Record<string, unknown>>; error?: string }> {
|
|
310
|
+
const tx = createTransaction(collections);
|
|
311
|
+
await fn(tx);
|
|
312
|
+
return await tx.commit();
|
|
313
|
+
}
|