@push.rocks/smartmongo 2.0.14 → 2.2.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/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/congodb/congodb.plugins.d.ts +10 -0
- package/dist_ts/congodb/congodb.plugins.js +14 -0
- package/dist_ts/congodb/engine/AggregationEngine.d.ts +66 -0
- package/dist_ts/congodb/engine/AggregationEngine.js +189 -0
- package/dist_ts/congodb/engine/IndexEngine.d.ts +77 -0
- package/dist_ts/congodb/engine/IndexEngine.js +376 -0
- package/dist_ts/congodb/engine/QueryEngine.d.ts +54 -0
- package/dist_ts/congodb/engine/QueryEngine.js +271 -0
- package/dist_ts/congodb/engine/TransactionEngine.d.ts +85 -0
- package/dist_ts/congodb/engine/TransactionEngine.js +287 -0
- package/dist_ts/congodb/engine/UpdateEngine.d.ts +47 -0
- package/dist_ts/congodb/engine/UpdateEngine.js +461 -0
- package/dist_ts/congodb/errors/CongoErrors.d.ts +100 -0
- package/dist_ts/congodb/errors/CongoErrors.js +155 -0
- package/dist_ts/congodb/index.d.ts +19 -0
- package/dist_ts/congodb/index.js +26 -0
- package/dist_ts/congodb/server/CommandRouter.d.ts +51 -0
- package/dist_ts/congodb/server/CommandRouter.js +132 -0
- package/dist_ts/congodb/server/CongoServer.d.ts +95 -0
- package/dist_ts/congodb/server/CongoServer.js +227 -0
- package/dist_ts/congodb/server/WireProtocol.d.ts +117 -0
- package/dist_ts/congodb/server/WireProtocol.js +298 -0
- package/dist_ts/congodb/server/handlers/AdminHandler.d.ts +100 -0
- package/dist_ts/congodb/server/handlers/AdminHandler.js +568 -0
- package/dist_ts/congodb/server/handlers/AggregateHandler.d.ts +31 -0
- package/dist_ts/congodb/server/handlers/AggregateHandler.js +277 -0
- package/dist_ts/congodb/server/handlers/DeleteHandler.d.ts +8 -0
- package/dist_ts/congodb/server/handlers/DeleteHandler.js +83 -0
- package/dist_ts/congodb/server/handlers/FindHandler.d.ts +31 -0
- package/dist_ts/congodb/server/handlers/FindHandler.js +261 -0
- package/dist_ts/congodb/server/handlers/HelloHandler.d.ts +11 -0
- package/dist_ts/congodb/server/handlers/HelloHandler.js +62 -0
- package/dist_ts/congodb/server/handlers/IndexHandler.d.ts +20 -0
- package/dist_ts/congodb/server/handlers/IndexHandler.js +183 -0
- package/dist_ts/congodb/server/handlers/InsertHandler.d.ts +8 -0
- package/dist_ts/congodb/server/handlers/InsertHandler.js +76 -0
- package/dist_ts/congodb/server/handlers/UpdateHandler.d.ts +24 -0
- package/dist_ts/congodb/server/handlers/UpdateHandler.js +270 -0
- package/dist_ts/congodb/server/handlers/index.d.ts +8 -0
- package/dist_ts/congodb/server/handlers/index.js +10 -0
- package/dist_ts/congodb/server/index.d.ts +6 -0
- package/dist_ts/congodb/server/index.js +7 -0
- package/dist_ts/congodb/storage/FileStorageAdapter.d.ts +61 -0
- package/dist_ts/congodb/storage/FileStorageAdapter.js +396 -0
- package/dist_ts/congodb/storage/IStorageAdapter.d.ts +140 -0
- package/dist_ts/congodb/storage/IStorageAdapter.js +2 -0
- package/dist_ts/congodb/storage/MemoryStorageAdapter.d.ts +66 -0
- package/dist_ts/congodb/storage/MemoryStorageAdapter.js +367 -0
- package/dist_ts/congodb/storage/OpLog.d.ts +93 -0
- package/dist_ts/congodb/storage/OpLog.js +221 -0
- package/dist_ts/congodb/types/interfaces.d.ts +363 -0
- package/dist_ts/congodb/types/interfaces.js +2 -0
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +8 -6
- package/npmextra.json +17 -7
- package/package.json +20 -12
- package/readme.hints.md +79 -0
- package/readme.md +398 -44
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/congodb/congodb.plugins.ts +17 -0
- package/ts/congodb/engine/AggregationEngine.ts +283 -0
- package/ts/congodb/engine/IndexEngine.ts +479 -0
- package/ts/congodb/engine/QueryEngine.ts +301 -0
- package/ts/congodb/engine/TransactionEngine.ts +351 -0
- package/ts/congodb/engine/UpdateEngine.ts +506 -0
- package/ts/congodb/errors/CongoErrors.ts +181 -0
- package/ts/congodb/index.ts +37 -0
- package/ts/congodb/server/CommandRouter.ts +180 -0
- package/ts/congodb/server/CongoServer.ts +298 -0
- package/ts/congodb/server/WireProtocol.ts +416 -0
- package/ts/congodb/server/handlers/AdminHandler.ts +614 -0
- package/ts/congodb/server/handlers/AggregateHandler.ts +342 -0
- package/ts/congodb/server/handlers/DeleteHandler.ts +100 -0
- package/ts/congodb/server/handlers/FindHandler.ts +301 -0
- package/ts/congodb/server/handlers/HelloHandler.ts +78 -0
- package/ts/congodb/server/handlers/IndexHandler.ts +207 -0
- package/ts/congodb/server/handlers/InsertHandler.ts +91 -0
- package/ts/congodb/server/handlers/UpdateHandler.ts +315 -0
- package/ts/congodb/server/handlers/index.ts +10 -0
- package/ts/congodb/server/index.ts +10 -0
- package/ts/congodb/storage/FileStorageAdapter.ts +479 -0
- package/ts/congodb/storage/IStorageAdapter.ts +202 -0
- package/ts/congodb/storage/MemoryStorageAdapter.ts +443 -0
- package/ts/congodb/storage/OpLog.ts +282 -0
- package/ts/congodb/types/interfaces.ts +433 -0
- package/ts/index.ts +3 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import * as plugins from '../../congodb.plugins.js';
|
|
2
|
+
import type { ICommandHandler, IHandlerContext } from '../CommandRouter.js';
|
|
3
|
+
import { QueryEngine } from '../../engine/QueryEngine.js';
|
|
4
|
+
import { UpdateEngine } from '../../engine/UpdateEngine.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* UpdateHandler - Handles update, findAndModify commands
|
|
8
|
+
*/
|
|
9
|
+
export class UpdateHandler implements ICommandHandler {
|
|
10
|
+
async handle(context: IHandlerContext): Promise<plugins.bson.Document> {
|
|
11
|
+
const { command } = context;
|
|
12
|
+
|
|
13
|
+
// Check findAndModify first since it also has an 'update' field
|
|
14
|
+
if (command.findAndModify) {
|
|
15
|
+
return this.handleFindAndModify(context);
|
|
16
|
+
} else if (command.update && typeof command.update === 'string') {
|
|
17
|
+
// 'update' command has collection name as the value
|
|
18
|
+
return this.handleUpdate(context);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
ok: 0,
|
|
23
|
+
errmsg: 'Unknown update-related command',
|
|
24
|
+
code: 59,
|
|
25
|
+
codeName: 'CommandNotFound',
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Handle update command
|
|
31
|
+
*/
|
|
32
|
+
private async handleUpdate(context: IHandlerContext): Promise<plugins.bson.Document> {
|
|
33
|
+
const { storage, database, command, documentSequences } = context;
|
|
34
|
+
|
|
35
|
+
const collection = command.update;
|
|
36
|
+
if (typeof collection !== 'string') {
|
|
37
|
+
return {
|
|
38
|
+
ok: 0,
|
|
39
|
+
errmsg: 'update command requires a collection name',
|
|
40
|
+
code: 2,
|
|
41
|
+
codeName: 'BadValue',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Get updates from command or document sequences
|
|
46
|
+
let updates: plugins.bson.Document[] = command.updates || [];
|
|
47
|
+
|
|
48
|
+
// Check for OP_MSG document sequences
|
|
49
|
+
if (documentSequences && documentSequences.has('updates')) {
|
|
50
|
+
updates = documentSequences.get('updates')!;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!Array.isArray(updates) || updates.length === 0) {
|
|
54
|
+
return {
|
|
55
|
+
ok: 0,
|
|
56
|
+
errmsg: 'update command requires updates array',
|
|
57
|
+
code: 2,
|
|
58
|
+
codeName: 'BadValue',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const ordered = command.ordered !== false;
|
|
63
|
+
const writeErrors: plugins.bson.Document[] = [];
|
|
64
|
+
let totalMatched = 0;
|
|
65
|
+
let totalModified = 0;
|
|
66
|
+
let totalUpserted = 0;
|
|
67
|
+
const upserted: plugins.bson.Document[] = [];
|
|
68
|
+
|
|
69
|
+
// Ensure collection exists
|
|
70
|
+
await storage.createCollection(database, collection);
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < updates.length; i++) {
|
|
73
|
+
const updateSpec = updates[i];
|
|
74
|
+
const filter = updateSpec.q || updateSpec.filter || {};
|
|
75
|
+
const update = updateSpec.u || updateSpec.update || {};
|
|
76
|
+
const multi = updateSpec.multi || false;
|
|
77
|
+
const upsert = updateSpec.upsert || false;
|
|
78
|
+
const arrayFilters = updateSpec.arrayFilters;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
// Get all documents
|
|
82
|
+
let documents = await storage.findAll(database, collection);
|
|
83
|
+
|
|
84
|
+
// Apply filter
|
|
85
|
+
let matchingDocs = QueryEngine.filter(documents, filter);
|
|
86
|
+
|
|
87
|
+
if (matchingDocs.length === 0 && upsert) {
|
|
88
|
+
// Upsert: create new document
|
|
89
|
+
const newDoc: plugins.bson.Document = { _id: new plugins.bson.ObjectId() };
|
|
90
|
+
|
|
91
|
+
// Apply filter fields to the new document
|
|
92
|
+
this.applyFilterToDoc(newDoc, filter);
|
|
93
|
+
|
|
94
|
+
// Apply update
|
|
95
|
+
const updatedDoc = UpdateEngine.applyUpdate(newDoc as any, update, arrayFilters);
|
|
96
|
+
|
|
97
|
+
// Handle $setOnInsert
|
|
98
|
+
if (update.$setOnInsert) {
|
|
99
|
+
Object.assign(updatedDoc, update.$setOnInsert);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
await storage.insertOne(database, collection, updatedDoc);
|
|
103
|
+
totalUpserted++;
|
|
104
|
+
upserted.push({ index: i, _id: updatedDoc._id });
|
|
105
|
+
} else {
|
|
106
|
+
// Update existing documents
|
|
107
|
+
const docsToUpdate = multi ? matchingDocs : matchingDocs.slice(0, 1);
|
|
108
|
+
totalMatched += docsToUpdate.length;
|
|
109
|
+
|
|
110
|
+
for (const doc of docsToUpdate) {
|
|
111
|
+
const updatedDoc = UpdateEngine.applyUpdate(doc, update, arrayFilters);
|
|
112
|
+
|
|
113
|
+
// Check if document actually changed
|
|
114
|
+
const changed = JSON.stringify(doc) !== JSON.stringify(updatedDoc);
|
|
115
|
+
if (changed) {
|
|
116
|
+
await storage.updateById(database, collection, doc._id, updatedDoc);
|
|
117
|
+
totalModified++;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
} catch (error: any) {
|
|
122
|
+
writeErrors.push({
|
|
123
|
+
index: i,
|
|
124
|
+
code: error.code || 1,
|
|
125
|
+
errmsg: error.message || 'Update failed',
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
if (ordered) {
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const response: plugins.bson.Document = {
|
|
135
|
+
ok: 1,
|
|
136
|
+
n: totalMatched + totalUpserted,
|
|
137
|
+
nModified: totalModified,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
if (upserted.length > 0) {
|
|
141
|
+
response.upserted = upserted;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (writeErrors.length > 0) {
|
|
145
|
+
response.writeErrors = writeErrors;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return response;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Handle findAndModify command
|
|
153
|
+
*/
|
|
154
|
+
private async handleFindAndModify(context: IHandlerContext): Promise<plugins.bson.Document> {
|
|
155
|
+
const { storage, database, command } = context;
|
|
156
|
+
|
|
157
|
+
const collection = command.findAndModify;
|
|
158
|
+
const query = command.query || {};
|
|
159
|
+
const update = command.update;
|
|
160
|
+
const remove = command.remove || false;
|
|
161
|
+
const returnNew = command.new || false;
|
|
162
|
+
const upsert = command.upsert || false;
|
|
163
|
+
const sort = command.sort;
|
|
164
|
+
const fields = command.fields;
|
|
165
|
+
const arrayFilters = command.arrayFilters;
|
|
166
|
+
|
|
167
|
+
// Validate - either update or remove, not both
|
|
168
|
+
if (update && remove) {
|
|
169
|
+
return {
|
|
170
|
+
ok: 0,
|
|
171
|
+
errmsg: 'cannot specify both update and remove',
|
|
172
|
+
code: 2,
|
|
173
|
+
codeName: 'BadValue',
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!update && !remove) {
|
|
178
|
+
return {
|
|
179
|
+
ok: 0,
|
|
180
|
+
errmsg: 'either update or remove is required',
|
|
181
|
+
code: 2,
|
|
182
|
+
codeName: 'BadValue',
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Ensure collection exists
|
|
187
|
+
await storage.createCollection(database, collection);
|
|
188
|
+
|
|
189
|
+
// Get matching documents
|
|
190
|
+
let documents = await storage.findAll(database, collection);
|
|
191
|
+
let matchingDocs = QueryEngine.filter(documents, query);
|
|
192
|
+
|
|
193
|
+
// Apply sort if specified
|
|
194
|
+
if (sort) {
|
|
195
|
+
matchingDocs = QueryEngine.sort(matchingDocs, sort);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const doc = matchingDocs[0];
|
|
199
|
+
|
|
200
|
+
if (remove) {
|
|
201
|
+
// Delete operation
|
|
202
|
+
if (!doc) {
|
|
203
|
+
return { ok: 1, value: null };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
await storage.deleteById(database, collection, doc._id);
|
|
207
|
+
|
|
208
|
+
let result = doc;
|
|
209
|
+
if (fields) {
|
|
210
|
+
result = QueryEngine.project([doc], fields)[0] as any;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
ok: 1,
|
|
215
|
+
value: result,
|
|
216
|
+
lastErrorObject: {
|
|
217
|
+
n: 1,
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
} else {
|
|
221
|
+
// Update operation
|
|
222
|
+
if (!doc && !upsert) {
|
|
223
|
+
return { ok: 1, value: null };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
let resultDoc: plugins.bson.Document;
|
|
227
|
+
let originalDoc: plugins.bson.Document | null = null;
|
|
228
|
+
let isUpsert = false;
|
|
229
|
+
|
|
230
|
+
if (doc) {
|
|
231
|
+
// Update existing
|
|
232
|
+
originalDoc = { ...doc };
|
|
233
|
+
resultDoc = UpdateEngine.applyUpdate(doc, update, arrayFilters);
|
|
234
|
+
await storage.updateById(database, collection, doc._id, resultDoc as any);
|
|
235
|
+
} else {
|
|
236
|
+
// Upsert
|
|
237
|
+
isUpsert = true;
|
|
238
|
+
const newDoc: plugins.bson.Document = { _id: new plugins.bson.ObjectId() };
|
|
239
|
+
this.applyFilterToDoc(newDoc, query);
|
|
240
|
+
resultDoc = UpdateEngine.applyUpdate(newDoc as any, update, arrayFilters);
|
|
241
|
+
|
|
242
|
+
if (update.$setOnInsert) {
|
|
243
|
+
Object.assign(resultDoc, update.$setOnInsert);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
await storage.insertOne(database, collection, resultDoc);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Apply projection
|
|
250
|
+
let returnValue = returnNew ? resultDoc : (originalDoc || null);
|
|
251
|
+
if (returnValue && fields) {
|
|
252
|
+
returnValue = QueryEngine.project([returnValue as any], fields)[0];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const response: plugins.bson.Document = {
|
|
256
|
+
ok: 1,
|
|
257
|
+
value: returnValue,
|
|
258
|
+
lastErrorObject: {
|
|
259
|
+
n: 1,
|
|
260
|
+
updatedExisting: !isUpsert && doc !== undefined,
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
if (isUpsert) {
|
|
265
|
+
response.lastErrorObject.upserted = resultDoc._id;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return response;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Apply filter equality conditions to a new document (for upsert)
|
|
274
|
+
*/
|
|
275
|
+
private applyFilterToDoc(doc: plugins.bson.Document, filter: plugins.bson.Document): void {
|
|
276
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
277
|
+
// Skip operators
|
|
278
|
+
if (key.startsWith('$')) continue;
|
|
279
|
+
|
|
280
|
+
// Handle nested paths
|
|
281
|
+
if (typeof value === 'object' && value !== null) {
|
|
282
|
+
// Check if it's an operator
|
|
283
|
+
const valueKeys = Object.keys(value);
|
|
284
|
+
if (valueKeys.some(k => k.startsWith('$'))) {
|
|
285
|
+
// Extract equality value from $eq if present
|
|
286
|
+
if ('$eq' in value) {
|
|
287
|
+
this.setNestedValue(doc, key, value.$eq);
|
|
288
|
+
}
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Direct value assignment
|
|
294
|
+
this.setNestedValue(doc, key, value);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Set a nested value using dot notation
|
|
300
|
+
*/
|
|
301
|
+
private setNestedValue(obj: plugins.bson.Document, path: string, value: any): void {
|
|
302
|
+
const parts = path.split('.');
|
|
303
|
+
let current = obj;
|
|
304
|
+
|
|
305
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
306
|
+
const part = parts[i];
|
|
307
|
+
if (!(part in current)) {
|
|
308
|
+
current[part] = {};
|
|
309
|
+
}
|
|
310
|
+
current = current[part];
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
current[parts[parts.length - 1]] = value;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Export all command handlers
|
|
2
|
+
|
|
3
|
+
export { HelloHandler } from './HelloHandler.js';
|
|
4
|
+
export { InsertHandler } from './InsertHandler.js';
|
|
5
|
+
export { FindHandler } from './FindHandler.js';
|
|
6
|
+
export { UpdateHandler } from './UpdateHandler.js';
|
|
7
|
+
export { DeleteHandler } from './DeleteHandler.js';
|
|
8
|
+
export { AggregateHandler } from './AggregateHandler.js';
|
|
9
|
+
export { IndexHandler } from './IndexHandler.js';
|
|
10
|
+
export { AdminHandler } from './AdminHandler.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Server module exports
|
|
2
|
+
|
|
3
|
+
export { CongoServer } from './CongoServer.js';
|
|
4
|
+
export type { ICongoServerOptions } from './CongoServer.js';
|
|
5
|
+
export { WireProtocol } from './WireProtocol.js';
|
|
6
|
+
export { CommandRouter } from './CommandRouter.js';
|
|
7
|
+
export type { ICommandHandler, IHandlerContext, ICursorState } from './CommandRouter.js';
|
|
8
|
+
|
|
9
|
+
// Export handlers
|
|
10
|
+
export * from './handlers/index.js';
|