@event-driven-io/pongo 0.17.0-beta.41 → 0.17.0-beta.43
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/cli.cjs +571 -16
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +560 -3
- package/dist/cli.js.map +1 -1
- package/dist/cloudflare-CzlFuMmZ.js +38 -0
- package/dist/cloudflare-CzlFuMmZ.js.map +1 -0
- package/dist/cloudflare-WGIFZXop.cjs +42 -0
- package/dist/cloudflare-WGIFZXop.cjs.map +1 -0
- package/dist/cloudflare.cjs +1463 -12
- package/dist/cloudflare.cjs.map +1 -1
- package/dist/cloudflare.d.cts +435 -2
- package/dist/cloudflare.d.ts +435 -2
- package/dist/cloudflare.js +1455 -3
- package/dist/cloudflare.js.map +1 -1
- package/dist/{core-CH0SOCr3.js → core-BW9XZXW8.js} +33 -46
- package/dist/core-BW9XZXW8.js.map +1 -0
- package/dist/{core-CkmE5dkK.cjs → core-BicrzIKX.cjs} +38 -52
- package/dist/core-BicrzIKX.cjs.map +1 -0
- package/dist/index.cjs +1477 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +717 -1
- package/dist/index.d.ts +717 -1
- package/dist/index.js +1429 -4
- package/dist/index.js.map +1 -1
- package/dist/pg-BdtFllz7.cjs +399 -0
- package/dist/pg-BdtFllz7.cjs.map +1 -0
- package/dist/pg-BtUtOO8Y.js +395 -0
- package/dist/pg-BtUtOO8Y.js.map +1 -0
- package/dist/pg.cjs +1155 -38
- package/dist/pg.cjs.map +1 -1
- package/dist/pg.d.cts +430 -2
- package/dist/pg.d.ts +430 -2
- package/dist/pg.js +1145 -27
- package/dist/pg.js.map +1 -1
- package/dist/shim.cjs +554 -6
- package/dist/shim.cjs.map +1 -1
- package/dist/shim.d.cts +445 -1
- package/dist/shim.d.ts +445 -1
- package/dist/shim.js +551 -2
- package/dist/shim.js.map +1 -1
- package/dist/sqlite3-CFwH2ot3.cjs +44 -0
- package/dist/sqlite3-CFwH2ot3.cjs.map +1 -0
- package/dist/sqlite3-Dv0y-lg6.js +40 -0
- package/dist/sqlite3-Dv0y-lg6.js.map +1 -0
- package/dist/sqlite3.cjs +1463 -12
- package/dist/sqlite3.cjs.map +1 -1
- package/dist/sqlite3.d.cts +435 -2
- package/dist/sqlite3.d.ts +435 -2
- package/dist/sqlite3.js +1455 -3
- package/dist/sqlite3.js.map +1 -1
- package/package.json +2 -2
- package/dist/core-BHdOCUrr.js +0 -1429
- package/dist/core-BHdOCUrr.js.map +0 -1
- package/dist/core-C9SB3XMx.cjs +0 -1717
- package/dist/core-C9SB3XMx.cjs.map +0 -1
- package/dist/core-CH0SOCr3.js.map +0 -1
- package/dist/core-CkmE5dkK.cjs.map +0 -1
- package/dist/index-C3pnS1S_.d.cts +0 -720
- package/dist/index-CZOmOsQt.d.ts +0 -10
- package/dist/index-FXnldVnn.d.cts +0 -10
- package/dist/index-r7V4paf_.d.ts +0 -720
package/dist/pg.cjs
CHANGED
|
@@ -1,31 +1,1147 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
const require_core = require('./core-C9SB3XMx.cjs');
|
|
3
|
-
const require_cli = require('./cli.cjs');
|
|
4
2
|
let _event_driven_io_dumbo = require("@event-driven-io/dumbo");
|
|
3
|
+
let _event_driven_io_dumbo_postgresql = require("@event-driven-io/dumbo/postgresql");
|
|
4
|
+
let lru_cache = require("lru-cache");
|
|
5
|
+
let uuid = require("uuid");
|
|
5
6
|
let _event_driven_io_dumbo_pg = require("@event-driven-io/dumbo/pg");
|
|
6
7
|
|
|
8
|
+
//#region src/core/cache/pongoCacheWrapper.ts
|
|
9
|
+
const pongoCacheWrapper = (options) => {
|
|
10
|
+
const { provider, hooks } = options;
|
|
11
|
+
const onError = (error, operation) => {
|
|
12
|
+
hooks?.onError?.(error, operation);
|
|
13
|
+
};
|
|
14
|
+
let isClosed = false;
|
|
15
|
+
return {
|
|
16
|
+
cacheType: provider.cacheType,
|
|
17
|
+
async get(key) {
|
|
18
|
+
try {
|
|
19
|
+
const result = await provider.get(key);
|
|
20
|
+
if (result !== void 0) hooks?.onHit?.(key);
|
|
21
|
+
else hooks?.onMiss?.(key);
|
|
22
|
+
return result;
|
|
23
|
+
} catch (error) {
|
|
24
|
+
onError(error, "get");
|
|
25
|
+
hooks?.onMiss?.(key);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
async getMany(keys) {
|
|
30
|
+
try {
|
|
31
|
+
return await provider.getMany(keys);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
onError(error, "getMany");
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
async set(key, value) {
|
|
38
|
+
try {
|
|
39
|
+
await provider.set(key, value);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
onError(error, "set");
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
async setMany(entries) {
|
|
45
|
+
try {
|
|
46
|
+
await provider.setMany(entries);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
onError(error, "setMany");
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
async update(key, updater) {
|
|
52
|
+
try {
|
|
53
|
+
await provider.update(key, updater);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
onError(error, "update");
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
async updateMany(keys, updater) {
|
|
59
|
+
try {
|
|
60
|
+
await provider.updateMany(keys, updater);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
onError(error, "updateMany");
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
async delete(key) {
|
|
66
|
+
try {
|
|
67
|
+
await provider.delete(key);
|
|
68
|
+
hooks?.onEvict?.(key);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
onError(error, "delete");
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
async deleteMany(keys) {
|
|
74
|
+
try {
|
|
75
|
+
await provider.deleteMany(keys);
|
|
76
|
+
for (const key of keys) hooks?.onEvict?.(key);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
onError(error, "deleteMany");
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
clear() {
|
|
82
|
+
return provider.clear();
|
|
83
|
+
},
|
|
84
|
+
close() {
|
|
85
|
+
if (isClosed) return;
|
|
86
|
+
isClosed = true;
|
|
87
|
+
return provider.close();
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region src/core/cache/providers/identityMapCache.ts
|
|
94
|
+
const identityMapCache = () => {
|
|
95
|
+
const store = /* @__PURE__ */ new Map();
|
|
96
|
+
return {
|
|
97
|
+
cacheType: "pongo:cache:identity-map",
|
|
98
|
+
get: (key) => Promise.resolve(store.has(key) ? store.get(key) : void 0),
|
|
99
|
+
getMany: (keys) => keys.map((k) => store.has(k) ? store.get(k) : void 0),
|
|
100
|
+
set: (key, value) => {
|
|
101
|
+
store.set(key, value);
|
|
102
|
+
},
|
|
103
|
+
setMany: (entries) => {
|
|
104
|
+
for (const { key, value } of entries) store.set(key, value);
|
|
105
|
+
},
|
|
106
|
+
update: (key, _updater) => {
|
|
107
|
+
store.delete(key);
|
|
108
|
+
},
|
|
109
|
+
updateMany: (keys, _updater) => {
|
|
110
|
+
for (const key of keys) store.delete(key);
|
|
111
|
+
},
|
|
112
|
+
delete: (key) => {
|
|
113
|
+
store.delete(key);
|
|
114
|
+
},
|
|
115
|
+
deleteMany: (keys) => {
|
|
116
|
+
for (const key of keys) store.delete(key);
|
|
117
|
+
},
|
|
118
|
+
clear: () => {
|
|
119
|
+
store.clear();
|
|
120
|
+
},
|
|
121
|
+
close: () => {
|
|
122
|
+
store.clear();
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/core/cache/providers/lruCache.ts
|
|
129
|
+
const defaultLRUCacheOptions = { max: 1e3 };
|
|
130
|
+
const lruCache = (options) => {
|
|
131
|
+
const cache = new lru_cache.LRUCache({
|
|
132
|
+
...defaultLRUCacheOptions,
|
|
133
|
+
...options
|
|
134
|
+
});
|
|
135
|
+
return {
|
|
136
|
+
cacheType: "pongo:cache:lru",
|
|
137
|
+
get: (key) => {
|
|
138
|
+
const entry = cache.get(key);
|
|
139
|
+
if (entry === void 0) return void 0;
|
|
140
|
+
return entry.doc;
|
|
141
|
+
},
|
|
142
|
+
getMany: (keys) => keys.map((k) => {
|
|
143
|
+
const entry = cache.get(k);
|
|
144
|
+
if (entry === void 0) return void 0;
|
|
145
|
+
return entry.doc;
|
|
146
|
+
}),
|
|
147
|
+
set: (key, value) => {
|
|
148
|
+
cache.set(key, { doc: value });
|
|
149
|
+
},
|
|
150
|
+
setMany: (entries) => {
|
|
151
|
+
for (const { key, value } of entries) cache.set(key, { doc: value });
|
|
152
|
+
},
|
|
153
|
+
update: (key, _updater) => {
|
|
154
|
+
cache.delete(key);
|
|
155
|
+
},
|
|
156
|
+
updateMany(keys, _updater) {
|
|
157
|
+
for (const key of keys) cache.delete(key);
|
|
158
|
+
},
|
|
159
|
+
delete: (key) => {
|
|
160
|
+
cache.delete(key);
|
|
161
|
+
},
|
|
162
|
+
deleteMany: (keys) => {
|
|
163
|
+
for (const key of keys) cache.delete(key);
|
|
164
|
+
},
|
|
165
|
+
clear: () => {
|
|
166
|
+
cache.clear();
|
|
167
|
+
},
|
|
168
|
+
close: () => {
|
|
169
|
+
cache.clear();
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region src/core/cache/providers/noopCache.ts
|
|
176
|
+
const noopCacheProvider = {
|
|
177
|
+
cacheType: "pongo:cache:no-op",
|
|
178
|
+
get: () => void 0,
|
|
179
|
+
set: () => {},
|
|
180
|
+
update: () => {},
|
|
181
|
+
delete: () => {},
|
|
182
|
+
getMany: (keys) => keys.map(() => void 0),
|
|
183
|
+
setMany: () => {},
|
|
184
|
+
updateMany: () => {},
|
|
185
|
+
deleteMany: () => {},
|
|
186
|
+
clear: () => {},
|
|
187
|
+
close: () => {}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
//#endregion
|
|
191
|
+
//#region src/core/cache/pongoCache.ts
|
|
192
|
+
const DEFAULT_CONFIG = { type: "in-memory" };
|
|
193
|
+
const pongoCache = (options) => {
|
|
194
|
+
if (options === void 0 || options === "disabled") return noopCacheProvider;
|
|
195
|
+
if ("cacheType" in options) return options;
|
|
196
|
+
const config = options ?? DEFAULT_CONFIG;
|
|
197
|
+
if (config.type === "identity-map") return identityMapCache();
|
|
198
|
+
return pongoCacheWrapper({ provider: lruCache(config) });
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/core/collection/filters/filters.ts
|
|
203
|
+
const asPlainObjectWithSingleKey = (filter, key) => filter && typeof filter === "object" && !Array.isArray(filter) && Object.keys(filter).length === 1 && key in filter ? { [key]: filter[key] } : void 0;
|
|
204
|
+
const idFromFilter = (filter) => {
|
|
205
|
+
const idFilter = asPlainObjectWithSingleKey(filter, "_id");
|
|
206
|
+
return typeof idFilter?.["_id"] === "string" ? idFilter["_id"] : void 0;
|
|
207
|
+
};
|
|
208
|
+
const getIdsFromIdOnlyFilter = (filter) => {
|
|
209
|
+
const idFilter = asPlainObjectWithSingleKey(filter, "_id");
|
|
210
|
+
if (!idFilter) return void 0;
|
|
211
|
+
const idValue = idFilter["_id"];
|
|
212
|
+
if (typeof idValue === "string") return [idValue];
|
|
213
|
+
const $in = idValue && typeof idValue === "object" && "$in" in idValue ? idValue["$in"] : void 0;
|
|
214
|
+
return Array.isArray($in) && $in.every((i) => typeof i === "string") ? $in : void 0;
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
//#endregion
|
|
218
|
+
//#region src/core/collection/handle.ts
|
|
219
|
+
function DocumentCommandHandler(deps) {
|
|
220
|
+
const fn = async (input, handle, options) => {
|
|
221
|
+
const results = (await handleDocuments(deps.storage, normalizeInput(input), handle, options)).map(({ change, result: outcome }) => toHandleResult(deps, change, outcome));
|
|
222
|
+
return Array.isArray(input) ? results : results[0];
|
|
223
|
+
};
|
|
224
|
+
return fn;
|
|
225
|
+
}
|
|
226
|
+
async function handleDocuments(storage, inputs, handle, options) {
|
|
227
|
+
if (inputs.length === 0) return [];
|
|
228
|
+
const { parallel, ...operationOptions } = options ?? {};
|
|
229
|
+
await storage.ensureCollectionCreated(operationOptions);
|
|
230
|
+
const docs = await storage.fetchByIds(inputs.map((i) => i._id), operationOptions);
|
|
231
|
+
return await executeStorageChanges(storage, await mapAsync(inputs, (item, i) => handleDocument({
|
|
232
|
+
...item,
|
|
233
|
+
existing: docs[i] ?? null
|
|
234
|
+
}, handle), { parallel }), operationOptions);
|
|
235
|
+
}
|
|
236
|
+
async function handleDocument(item, handle) {
|
|
237
|
+
const { _id: id, existing, expectedVersion } = item;
|
|
238
|
+
if (hasVersionMismatch(existing, expectedVersion)) return {
|
|
239
|
+
type: "noop",
|
|
240
|
+
existing,
|
|
241
|
+
versionMismatch: true
|
|
242
|
+
};
|
|
243
|
+
const handlers = Array.isArray(handle) ? handle : [handle];
|
|
244
|
+
let result = existing ? { ...existing } : null;
|
|
245
|
+
for (const handler of handlers) result = await handler(result, id);
|
|
246
|
+
return toDocumentChange(id, existing, result);
|
|
247
|
+
}
|
|
248
|
+
async function executeStorageChanges(storage, changes, operationOptions) {
|
|
249
|
+
const toInsert = changes.flatMap((c) => c.type === "insert" ? [c.doc] : []);
|
|
250
|
+
const toReplace = changes.flatMap((c) => {
|
|
251
|
+
if (c.type !== "replace") return [];
|
|
252
|
+
const { _version: _, ...cleanResult } = c.result;
|
|
253
|
+
return [c._version !== void 0 ? {
|
|
254
|
+
...cleanResult,
|
|
255
|
+
_version: c._version
|
|
256
|
+
} : cleanResult];
|
|
257
|
+
});
|
|
258
|
+
const toDelete = changes.flatMap((c) => c.type === "delete" ? [c._version !== void 0 ? {
|
|
259
|
+
_id: c.docId,
|
|
260
|
+
_version: c._version
|
|
261
|
+
} : { _id: c.docId }] : []);
|
|
262
|
+
const insertedIds = toInsert.length > 0 ? new Set((await storage.insertMany(toInsert, operationOptions)).insertedIds) : /* @__PURE__ */ new Set();
|
|
263
|
+
const replaceResult = toReplace.length > 0 ? await storage.replaceMany(toReplace, operationOptions) : null;
|
|
264
|
+
const deletedIds = toDelete.length > 0 ? (await storage.deleteManyByIds(toDelete, operationOptions)).deletedIds : /* @__PURE__ */ new Set();
|
|
265
|
+
const toDocumentHandlerResult = (change) => {
|
|
266
|
+
if (change.type === "noop") return { succeeded: !change.versionMismatch };
|
|
267
|
+
if (change.type === "insert") return {
|
|
268
|
+
succeeded: insertedIds.has(change.doc._id),
|
|
269
|
+
newVersion: 1n
|
|
270
|
+
};
|
|
271
|
+
if (change.type === "delete") return { succeeded: deletedIds.has(change.docId) };
|
|
272
|
+
const id = change.result._id;
|
|
273
|
+
return {
|
|
274
|
+
succeeded: replaceResult?.modifiedIds.includes(id) ?? false,
|
|
275
|
+
newVersion: replaceResult?.nextExpectedVersions.get(id) ?? 0n
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
return changes.map((change) => ({
|
|
279
|
+
change,
|
|
280
|
+
result: toDocumentHandlerResult(change)
|
|
281
|
+
}));
|
|
282
|
+
}
|
|
283
|
+
function normalizeInput(input) {
|
|
284
|
+
if (typeof input === "string") return [{ _id: input }];
|
|
285
|
+
if (!Array.isArray(input)) return [input];
|
|
286
|
+
return input.map((item) => typeof item === "string" ? { _id: item } : item);
|
|
287
|
+
}
|
|
288
|
+
function hasVersionMismatch(existing, version) {
|
|
289
|
+
const expected = expectedVersionValue(version);
|
|
290
|
+
return existing == null && version === "DOCUMENT_EXISTS" || existing == null && expected != null || existing != null && version === "DOCUMENT_DOES_NOT_EXIST" || existing != null && expected !== null && existing._version !== expected;
|
|
291
|
+
}
|
|
292
|
+
function toDocumentChange(docId, existing, result) {
|
|
293
|
+
if (deepEquals(existing, result)) return {
|
|
294
|
+
type: "noop",
|
|
295
|
+
existing
|
|
296
|
+
};
|
|
297
|
+
if (!existing && result) return {
|
|
298
|
+
type: "insert",
|
|
299
|
+
doc: {
|
|
300
|
+
...result,
|
|
301
|
+
_id: docId
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
if (existing && !result) return {
|
|
305
|
+
type: "delete",
|
|
306
|
+
docId,
|
|
307
|
+
_version: existing._version
|
|
308
|
+
};
|
|
309
|
+
return {
|
|
310
|
+
type: "replace",
|
|
311
|
+
existing,
|
|
312
|
+
result: {
|
|
313
|
+
...result,
|
|
314
|
+
_id: docId
|
|
315
|
+
},
|
|
316
|
+
_version: existing._version
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
function toHandleResult(deps, change, { succeeded, newVersion }) {
|
|
320
|
+
const opMeta = {
|
|
321
|
+
operationName: "handle",
|
|
322
|
+
collectionName: deps.collectionName,
|
|
323
|
+
serializer: deps.serializer,
|
|
324
|
+
errors: deps.errors
|
|
325
|
+
};
|
|
326
|
+
const toResult = (op, document) => ({
|
|
327
|
+
...operationResult(op, opMeta),
|
|
328
|
+
document
|
|
329
|
+
});
|
|
330
|
+
if (change.type === "noop") return toResult({ successful: succeeded }, change.existing);
|
|
331
|
+
if (change.type === "insert") return toResult({
|
|
332
|
+
successful: succeeded,
|
|
333
|
+
insertedId: succeeded ? change.doc._id : null,
|
|
334
|
+
nextExpectedVersion: 1n
|
|
335
|
+
}, succeeded ? {
|
|
336
|
+
...change.doc,
|
|
337
|
+
_version: 1n
|
|
338
|
+
} : null);
|
|
339
|
+
if (change.type === "delete") return toResult({
|
|
340
|
+
successful: succeeded,
|
|
341
|
+
deletedCount: succeeded ? 1 : 0,
|
|
342
|
+
matchedCount: 1
|
|
343
|
+
}, null);
|
|
344
|
+
return toResult({
|
|
345
|
+
successful: succeeded,
|
|
346
|
+
modifiedCount: succeeded ? 1 : 0,
|
|
347
|
+
matchedCount: 1,
|
|
348
|
+
nextExpectedVersion: newVersion ?? 0n
|
|
349
|
+
}, succeeded ? {
|
|
350
|
+
...change.result,
|
|
351
|
+
_version: newVersion
|
|
352
|
+
} : change.existing);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
//#endregion
|
|
356
|
+
//#region src/core/collection/pongoCollection.ts
|
|
357
|
+
const enlistIntoTransactionIfActive = async (db, options) => {
|
|
358
|
+
const transaction = options?.session?.transaction;
|
|
359
|
+
if (!transaction || !transaction.isActive) return null;
|
|
360
|
+
return await transaction.enlistDatabase(db);
|
|
361
|
+
};
|
|
362
|
+
const transactionExecutorOrDefault = async (db, options, defaultSqlExecutor) => {
|
|
363
|
+
return (await enlistIntoTransactionIfActive(db, options))?.execute ?? defaultSqlExecutor;
|
|
364
|
+
};
|
|
365
|
+
const pongoCollection = ({ db, collectionName, pool, schemaComponent, schema, errors, serializer, cache: cacheOptions }) => {
|
|
366
|
+
const SqlFor = schemaComponent.sqlBuilder;
|
|
367
|
+
const sqlExecutor = pool.execute;
|
|
368
|
+
const cache = pongoCache(cacheOptions);
|
|
369
|
+
const columnMapping = { mapping: {
|
|
370
|
+
...(0, _event_driven_io_dumbo.mapColumnToJSON)("data", serializer),
|
|
371
|
+
...(0, _event_driven_io_dumbo.mapColumnToBigint)("_version")
|
|
372
|
+
} };
|
|
373
|
+
const command = async (sql, options) => (await transactionExecutorOrDefault(db, options, sqlExecutor)).command(sql, columnMapping);
|
|
374
|
+
const query = async (sql, options) => (await transactionExecutorOrDefault(db, options, sqlExecutor)).query(sql, columnMapping);
|
|
375
|
+
let shouldMigrate = schema?.autoMigration !== "None";
|
|
376
|
+
const createCollection = (options) => {
|
|
377
|
+
shouldMigrate = false;
|
|
378
|
+
if (options?.session) return command(SqlFor.createCollection(), options);
|
|
379
|
+
else return command(SqlFor.createCollection());
|
|
380
|
+
};
|
|
381
|
+
const ensureCollectionCreated = (options) => {
|
|
382
|
+
if (!shouldMigrate) return Promise.resolve();
|
|
383
|
+
return createCollection(options);
|
|
384
|
+
};
|
|
385
|
+
const upcast = schema?.versioning?.upcast ?? ((doc) => doc);
|
|
386
|
+
const downcast = schema?.versioning?.downcast ?? ((doc) => doc);
|
|
387
|
+
const toStored = (document) => ({
|
|
388
|
+
...downcast(document),
|
|
389
|
+
_id: document._id,
|
|
390
|
+
_version: document._version
|
|
391
|
+
});
|
|
392
|
+
const fromStored = (stored) => ({
|
|
393
|
+
...upcast(stored),
|
|
394
|
+
_id: stored._id,
|
|
395
|
+
_version: stored._version
|
|
396
|
+
});
|
|
397
|
+
const findOneStoredFromDb = async (filter, options) => {
|
|
398
|
+
const row = (await query(SqlFor.findOne(filter), options)).rows[0];
|
|
399
|
+
return row ? {
|
|
400
|
+
...row.data,
|
|
401
|
+
_id: row._id,
|
|
402
|
+
_version: row._version
|
|
403
|
+
} : null;
|
|
404
|
+
};
|
|
405
|
+
const cacheKey = (id) => `${db.databaseName}:${collectionName}:${id}`;
|
|
406
|
+
const txCacheFor = (options) => options?.session?.transaction?.cache ?? null;
|
|
407
|
+
const resolveFromCache = async (key, options) => {
|
|
408
|
+
const txCache = txCacheFor(options);
|
|
409
|
+
if (txCache) {
|
|
410
|
+
const cached = await txCache.get(key);
|
|
411
|
+
if (cached !== void 0) return cached;
|
|
412
|
+
}
|
|
413
|
+
return cache.get(key);
|
|
414
|
+
};
|
|
415
|
+
const findManyFromCache = async (keys, options) => {
|
|
416
|
+
const txCache = txCacheFor(options);
|
|
417
|
+
if (!txCache) return cache.getMany(keys);
|
|
418
|
+
const txResults = await txCache.getMany(keys);
|
|
419
|
+
const mainResults = await cache.getMany(keys);
|
|
420
|
+
return keys.map((_, i) => txResults[i] !== void 0 ? txResults[i] : mainResults[i]);
|
|
421
|
+
};
|
|
422
|
+
const fetchByIds = async (ids, options) => {
|
|
423
|
+
const cachedResults = await findManyFromCache(ids.map(cacheKey), options);
|
|
424
|
+
const missIds = ids.filter((_, i) => cachedResults[i] === void 0);
|
|
425
|
+
let dbDocsById = /* @__PURE__ */ new Map();
|
|
426
|
+
if (missIds.length > 0) {
|
|
427
|
+
const dbDocs = (await query(SqlFor.find({ _id: { $in: missIds } }, options))).rows.map((row) => ({
|
|
428
|
+
...row.data,
|
|
429
|
+
_id: row._id,
|
|
430
|
+
_version: row._version
|
|
431
|
+
}));
|
|
432
|
+
dbDocsById = new Map(dbDocs.map((d) => [d._id, d]));
|
|
433
|
+
const leftovers = missIds.map((id) => [id, dbDocsById.get(id) ?? null]);
|
|
434
|
+
await cacheSetMany(leftovers.map((d) => d[1]).filter((d) => d !== null), options);
|
|
435
|
+
await cacheDeleteMany(leftovers.filter(([, doc]) => doc === null).map(([id]) => id), options);
|
|
436
|
+
}
|
|
437
|
+
return ids.map((id, i) => {
|
|
438
|
+
const cached = cachedResults[i];
|
|
439
|
+
if (cached !== void 0) return cached;
|
|
440
|
+
return dbDocsById.get(id) ?? null;
|
|
441
|
+
});
|
|
442
|
+
};
|
|
443
|
+
const findManyByIds = async (ids, options) => {
|
|
444
|
+
return (await fetchByIds(ids, options)).filter((doc) => doc !== null).map(fromStored);
|
|
445
|
+
};
|
|
446
|
+
const cacheSet = (value, options) => {
|
|
447
|
+
const key = cacheKey(value._id);
|
|
448
|
+
const txCache = txCacheFor(options);
|
|
449
|
+
if (txCache) return txCache.set(key, value, { mainCache: cache });
|
|
450
|
+
return cache.set(key, value);
|
|
451
|
+
};
|
|
452
|
+
const cacheSetMany = (documents, options) => {
|
|
453
|
+
const txCache = txCacheFor(options);
|
|
454
|
+
const entries = documents.map((d) => ({
|
|
455
|
+
key: cacheKey(d._id),
|
|
456
|
+
value: d
|
|
457
|
+
}));
|
|
458
|
+
if (txCache) return txCache.setMany(entries, { mainCache: cache });
|
|
459
|
+
return cache.setMany(entries);
|
|
460
|
+
};
|
|
461
|
+
const cacheDelete = (id, options) => {
|
|
462
|
+
const txCache = txCacheFor(options);
|
|
463
|
+
const key = cacheKey(id);
|
|
464
|
+
if (txCache) return txCache.delete(key, { mainCache: cache });
|
|
465
|
+
return cache.delete(key);
|
|
466
|
+
};
|
|
467
|
+
const cacheDeleteMany = (ids, options) => {
|
|
468
|
+
const txCache = txCacheFor(options);
|
|
469
|
+
const keys = ids.map(cacheKey);
|
|
470
|
+
if (txCache) return txCache.deleteMany(keys, { mainCache: cache });
|
|
471
|
+
return cache.deleteMany(keys);
|
|
472
|
+
};
|
|
473
|
+
const deleteManyByIds = async (ids, options) => {
|
|
474
|
+
await ensureCollectionCreated(options);
|
|
475
|
+
const result = await command(SqlFor.deleteManyByIds(ids), options);
|
|
476
|
+
const deletedIds = new Set(result.rows.filter((row) => (row.deleted ?? 1) > 0).map((row) => row._id));
|
|
477
|
+
if (!options?.skipCache) await cacheDeleteMany([...deletedIds], options);
|
|
478
|
+
return operationResult({
|
|
479
|
+
successful: deletedIds.size > 0,
|
|
480
|
+
deletedCount: deletedIds.size,
|
|
481
|
+
matchedCount: ids.length,
|
|
482
|
+
deletedIds
|
|
483
|
+
}, {
|
|
484
|
+
operationName: "deleteManyByIds",
|
|
485
|
+
collectionName,
|
|
486
|
+
serializer,
|
|
487
|
+
errors
|
|
488
|
+
});
|
|
489
|
+
};
|
|
490
|
+
const executeUpsert = async (storedRows, options) => {
|
|
491
|
+
const result = await command(SqlFor.insertOrReplace(storedRows), options);
|
|
492
|
+
const writtenIds = result.rows.map((r) => r._id);
|
|
493
|
+
const versions = new Map(result.rows.map((r) => [r._id, BigInt(r.version ?? 1n)]));
|
|
494
|
+
if (!options?.skipCache) {
|
|
495
|
+
const writtenSet = new Set(writtenIds);
|
|
496
|
+
const cacheEntries = storedRows.filter((r) => writtenSet.has(r._id)).map((r) => ({
|
|
497
|
+
...r,
|
|
498
|
+
_version: versions.get(r._id) ?? 1n
|
|
499
|
+
}));
|
|
500
|
+
if (cacheEntries.length > 0) await cacheSetMany(cacheEntries, options);
|
|
501
|
+
}
|
|
502
|
+
return {
|
|
503
|
+
writtenIds,
|
|
504
|
+
versions
|
|
505
|
+
};
|
|
506
|
+
};
|
|
507
|
+
const collection = {
|
|
508
|
+
dbName: db.databaseName,
|
|
509
|
+
collectionName,
|
|
510
|
+
createCollection: async (options) => {
|
|
511
|
+
await createCollection(options);
|
|
512
|
+
},
|
|
513
|
+
insertOne: async (document, options) => {
|
|
514
|
+
await ensureCollectionCreated(options);
|
|
515
|
+
const _id = document._id ?? (0, uuid.v7)();
|
|
516
|
+
const _version = document._version ?? 1n;
|
|
517
|
+
const stored = toStored({
|
|
518
|
+
...document,
|
|
519
|
+
_id,
|
|
520
|
+
_version
|
|
521
|
+
});
|
|
522
|
+
if (options?.upsert) {
|
|
523
|
+
const { writtenIds, versions } = await executeUpsert([stored], options);
|
|
524
|
+
const successful = writtenIds.length > 0;
|
|
525
|
+
const nextExpectedVersion = versions.get(_id) ?? _version;
|
|
526
|
+
return operationResult({
|
|
527
|
+
successful,
|
|
528
|
+
insertedId: successful ? _id : null,
|
|
529
|
+
nextExpectedVersion
|
|
530
|
+
}, {
|
|
531
|
+
operationName: "insertOne",
|
|
532
|
+
collectionName,
|
|
533
|
+
serializer,
|
|
534
|
+
errors
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
const successful = ((await command(SqlFor.insertOne(stored), options)).rowCount ?? 0) > 0;
|
|
538
|
+
if (successful && !options?.skipCache) await cacheSet(stored, options);
|
|
539
|
+
return operationResult({
|
|
540
|
+
successful,
|
|
541
|
+
insertedId: successful ? _id : null,
|
|
542
|
+
nextExpectedVersion: _version
|
|
543
|
+
}, {
|
|
544
|
+
operationName: "insertOne",
|
|
545
|
+
collectionName,
|
|
546
|
+
serializer,
|
|
547
|
+
errors
|
|
548
|
+
});
|
|
549
|
+
},
|
|
550
|
+
insertMany: async (documents, options) => {
|
|
551
|
+
await ensureCollectionCreated(options);
|
|
552
|
+
const rows = documents.map((doc) => doc._id && doc._version ? doc : {
|
|
553
|
+
...doc,
|
|
554
|
+
_id: doc._id ?? (0, uuid.v7)(),
|
|
555
|
+
_version: doc._version ?? 1n
|
|
556
|
+
}).map((d) => toStored(d));
|
|
557
|
+
if (options?.upsert) {
|
|
558
|
+
const { writtenIds } = await executeUpsert(rows, options);
|
|
559
|
+
return operationResult({
|
|
560
|
+
successful: writtenIds.length === rows.length,
|
|
561
|
+
insertedCount: writtenIds.length,
|
|
562
|
+
insertedIds: writtenIds
|
|
563
|
+
}, {
|
|
564
|
+
operationName: "insertMany",
|
|
565
|
+
collectionName,
|
|
566
|
+
serializer,
|
|
567
|
+
errors
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
const result = await command(SqlFor.insertMany(rows), options);
|
|
571
|
+
if (!options?.skipCache) {
|
|
572
|
+
const insertedIdSet = new Set(result.rows.map((d) => d._id));
|
|
573
|
+
await cacheSetMany(rows.filter((r) => insertedIdSet.has(r._id)), options);
|
|
574
|
+
}
|
|
575
|
+
return operationResult({
|
|
576
|
+
successful: result.rowCount === rows.length,
|
|
577
|
+
insertedCount: result.rowCount ?? 0,
|
|
578
|
+
insertedIds: result.rows.map((d) => d._id)
|
|
579
|
+
}, {
|
|
580
|
+
operationName: "insertMany",
|
|
581
|
+
collectionName,
|
|
582
|
+
serializer,
|
|
583
|
+
errors
|
|
584
|
+
});
|
|
585
|
+
},
|
|
586
|
+
updateOne: async (filter, update, options) => {
|
|
587
|
+
await ensureCollectionCreated(options);
|
|
588
|
+
const result = await command(SqlFor.updateOne(filter, update, options), options);
|
|
589
|
+
const opResult = operationResult({
|
|
590
|
+
successful: result.rows.length > 0 && result.rows[0].modified === result.rows[0].matched,
|
|
591
|
+
modifiedCount: Number(result.rows[0]?.modified ?? 0),
|
|
592
|
+
matchedCount: Number(result.rows[0]?.matched ?? 0),
|
|
593
|
+
upsertedId: null,
|
|
594
|
+
upsertedCount: 0,
|
|
595
|
+
nextExpectedVersion: BigInt(result.rows[0]?.version ?? 0n)
|
|
596
|
+
}, {
|
|
597
|
+
operationName: "updateOne",
|
|
598
|
+
collectionName,
|
|
599
|
+
serializer,
|
|
600
|
+
errors
|
|
601
|
+
});
|
|
602
|
+
if (opResult.successful && !options?.skipCache) {
|
|
603
|
+
const id = idFromFilter(filter);
|
|
604
|
+
if (id) await cacheDelete(id, options);
|
|
605
|
+
}
|
|
606
|
+
return opResult;
|
|
607
|
+
},
|
|
608
|
+
replaceOne: async (filter, document, options) => {
|
|
609
|
+
await ensureCollectionCreated(options);
|
|
610
|
+
const noConcurrencyCheck = options?.expectedVersion === void 0 || options.expectedVersion === "NO_CONCURRENCY_CHECK";
|
|
611
|
+
if (options?.upsert && noConcurrencyCheck) {
|
|
612
|
+
const _id = idFromFilter(filter) ?? (0, uuid.v7)();
|
|
613
|
+
const { writtenIds, versions } = await executeUpsert([toStored({
|
|
614
|
+
...document,
|
|
615
|
+
_id,
|
|
616
|
+
_version: 1n
|
|
617
|
+
})], options);
|
|
618
|
+
const written = writtenIds.length > 0;
|
|
619
|
+
const nextExpectedVersion = versions.get(_id) ?? 1n;
|
|
620
|
+
const inserted = written && nextExpectedVersion === 1n;
|
|
621
|
+
return operationResult({
|
|
622
|
+
successful: written,
|
|
623
|
+
modifiedCount: written && !inserted ? 1 : 0,
|
|
624
|
+
matchedCount: written && !inserted ? 1 : 0,
|
|
625
|
+
upsertedId: inserted ? _id : null,
|
|
626
|
+
upsertedCount: inserted ? 1 : 0,
|
|
627
|
+
nextExpectedVersion
|
|
628
|
+
}, {
|
|
629
|
+
operationName: "replaceOne",
|
|
630
|
+
collectionName,
|
|
631
|
+
serializer,
|
|
632
|
+
errors
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
const downcasted = downcast(document);
|
|
636
|
+
const result = await command(SqlFor.replaceOne(filter, downcasted, options), options);
|
|
637
|
+
const opResult = operationResult({
|
|
638
|
+
successful: result.rows.length > 0 && result.rows[0].modified > 0,
|
|
639
|
+
modifiedCount: Number(result.rows[0]?.modified ?? 0),
|
|
640
|
+
matchedCount: Number(result.rows[0]?.matched ?? 0),
|
|
641
|
+
upsertedId: null,
|
|
642
|
+
upsertedCount: 0,
|
|
643
|
+
nextExpectedVersion: BigInt(result.rows[0]?.version ?? 0n)
|
|
644
|
+
}, {
|
|
645
|
+
operationName: "replaceOne",
|
|
646
|
+
collectionName,
|
|
647
|
+
serializer,
|
|
648
|
+
errors
|
|
649
|
+
});
|
|
650
|
+
if (opResult.successful && !options?.skipCache) {
|
|
651
|
+
const _id = idFromFilter(filter);
|
|
652
|
+
if (_id) await cacheSet(toStored({
|
|
653
|
+
...document,
|
|
654
|
+
_id,
|
|
655
|
+
_version: opResult.nextExpectedVersion
|
|
656
|
+
}), options);
|
|
657
|
+
}
|
|
658
|
+
return opResult;
|
|
659
|
+
},
|
|
660
|
+
updateMany: async (filter, update, options) => {
|
|
661
|
+
await ensureCollectionCreated(options);
|
|
662
|
+
const result = await command(SqlFor.updateMany(filter, update), options);
|
|
663
|
+
return operationResult({
|
|
664
|
+
successful: true,
|
|
665
|
+
modifiedCount: result.rowCount ?? 0,
|
|
666
|
+
matchedCount: result.rowCount ?? 0
|
|
667
|
+
}, {
|
|
668
|
+
operationName: "updateMany",
|
|
669
|
+
collectionName,
|
|
670
|
+
serializer,
|
|
671
|
+
errors
|
|
672
|
+
});
|
|
673
|
+
},
|
|
674
|
+
deleteOne: async (filter, options) => {
|
|
675
|
+
await ensureCollectionCreated(options);
|
|
676
|
+
const result = await command(SqlFor.deleteOne(filter ?? {}, options), options);
|
|
677
|
+
const opResult = operationResult({
|
|
678
|
+
successful: result.rows.length > 0 && result.rows[0].deleted > 0,
|
|
679
|
+
deletedCount: Number(result.rows[0]?.deleted ?? 0),
|
|
680
|
+
matchedCount: Number(result.rows[0]?.matched ?? 0)
|
|
681
|
+
}, {
|
|
682
|
+
operationName: "deleteOne",
|
|
683
|
+
collectionName,
|
|
684
|
+
serializer,
|
|
685
|
+
errors
|
|
686
|
+
});
|
|
687
|
+
if (opResult.successful && !options?.skipCache && filter) {
|
|
688
|
+
const id = idFromFilter(filter);
|
|
689
|
+
if (id) await cacheDelete(id, options);
|
|
690
|
+
}
|
|
691
|
+
return opResult;
|
|
692
|
+
},
|
|
693
|
+
deleteMany: async (filter, options) => {
|
|
694
|
+
const ids = filter ? getIdsFromIdOnlyFilter(filter) : null;
|
|
695
|
+
if (ids) return deleteManyByIds(ids.map((id) => ({ _id: id })), options);
|
|
696
|
+
await ensureCollectionCreated(options);
|
|
697
|
+
const result = await command(SqlFor.deleteMany(filter ?? {}), options);
|
|
698
|
+
return operationResult({
|
|
699
|
+
successful: (result.rowCount ?? 0) > 0,
|
|
700
|
+
deletedCount: result.rowCount ?? 0,
|
|
701
|
+
matchedCount: result.rowCount ?? 0
|
|
702
|
+
}, {
|
|
703
|
+
operationName: "deleteMany",
|
|
704
|
+
collectionName,
|
|
705
|
+
serializer,
|
|
706
|
+
errors
|
|
707
|
+
});
|
|
708
|
+
},
|
|
709
|
+
findOne: async (filter, options) => {
|
|
710
|
+
await ensureCollectionCreated(options);
|
|
711
|
+
const id = filter && !options?.skipCache ? idFromFilter(filter) : null;
|
|
712
|
+
if (id) {
|
|
713
|
+
const cached = await resolveFromCache(cacheKey(id), options);
|
|
714
|
+
if (cached !== void 0) return cached !== null ? fromStored(cached) : null;
|
|
715
|
+
const stored = await findOneStoredFromDb(filter, options);
|
|
716
|
+
if (stored) await cacheSet(stored, options);
|
|
717
|
+
else await cacheDelete(id, options);
|
|
718
|
+
return stored ? fromStored(stored) : null;
|
|
719
|
+
}
|
|
720
|
+
const stored = await findOneStoredFromDb(filter ?? {}, options);
|
|
721
|
+
return stored ? fromStored(stored) : null;
|
|
722
|
+
},
|
|
723
|
+
findOneAndDelete: async (filter, options) => {
|
|
724
|
+
await ensureCollectionCreated(options);
|
|
725
|
+
const existingDoc = await collection.findOne(filter, options);
|
|
726
|
+
if (existingDoc === null) return null;
|
|
727
|
+
await collection.deleteOne(filter, options);
|
|
728
|
+
return existingDoc;
|
|
729
|
+
},
|
|
730
|
+
findOneAndReplace: async (filter, replacement, options) => {
|
|
731
|
+
await ensureCollectionCreated(options);
|
|
732
|
+
const existingDoc = await collection.findOne(filter, options);
|
|
733
|
+
if (existingDoc === null) return null;
|
|
734
|
+
await collection.replaceOne(filter, replacement, options);
|
|
735
|
+
return existingDoc;
|
|
736
|
+
},
|
|
737
|
+
findOneAndUpdate: async (filter, update, options) => {
|
|
738
|
+
await ensureCollectionCreated(options);
|
|
739
|
+
const existingDoc = await collection.findOne(filter, options);
|
|
740
|
+
if (existingDoc === null) return null;
|
|
741
|
+
await collection.updateOne(filter, update, options);
|
|
742
|
+
return existingDoc;
|
|
743
|
+
},
|
|
744
|
+
replaceMany: async (documents, options) => {
|
|
745
|
+
await ensureCollectionCreated(options);
|
|
746
|
+
const rows = documents.map((d) => toStored(d));
|
|
747
|
+
if (options?.upsert) {
|
|
748
|
+
const versioned = documents.map((d) => "_version" in d && d._version !== void 0);
|
|
749
|
+
const hasVersions = versioned.some((v) => v);
|
|
750
|
+
const allVersioned = versioned.every((v) => v);
|
|
751
|
+
if (hasVersions && !allVersioned) throw new Error("replaceMany with upsert cannot mix documents with and without _version in a single batch");
|
|
752
|
+
if (!hasVersions) {
|
|
753
|
+
const { writtenIds, versions } = await executeUpsert(rows, options);
|
|
754
|
+
const conflictIds = documents.map((d) => d._id).filter((id) => !writtenIds.includes(id));
|
|
755
|
+
return operationResult({
|
|
756
|
+
successful: conflictIds.length === 0,
|
|
757
|
+
modifiedCount: writtenIds.length,
|
|
758
|
+
matchedCount: documents.length,
|
|
759
|
+
modifiedIds: [...writtenIds],
|
|
760
|
+
conflictIds: [...conflictIds],
|
|
761
|
+
nextExpectedVersions: versions
|
|
762
|
+
}, {
|
|
763
|
+
operationName: "replaceMany",
|
|
764
|
+
collectionName,
|
|
765
|
+
serializer,
|
|
766
|
+
errors
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
const result = await command(SqlFor.replaceMany(rows), options);
|
|
771
|
+
const modifiedIds = result.rows.map((row) => row._id);
|
|
772
|
+
const conflictIds = documents.map((d) => d._id).filter((id) => !modifiedIds.includes(id));
|
|
773
|
+
const versions = new Map(result.rows.map((row) => [row._id, BigInt(row.version ?? 1n)]));
|
|
774
|
+
if (!options?.skipCache) {
|
|
775
|
+
const cacheEntries = rows.filter((r) => modifiedIds.includes(r._id)).map((r) => ({
|
|
776
|
+
...r,
|
|
777
|
+
_version: versions.get(r._id) ?? r._version ?? 1n
|
|
778
|
+
}));
|
|
779
|
+
if (cacheEntries.length > 0) await cacheSetMany(cacheEntries, options);
|
|
780
|
+
if (conflictIds.length > 0) await cacheDeleteMany([...conflictIds], options);
|
|
781
|
+
}
|
|
782
|
+
return operationResult({
|
|
783
|
+
successful: modifiedIds.length > 0 && conflictIds.length === 0,
|
|
784
|
+
modifiedCount: modifiedIds.length,
|
|
785
|
+
matchedCount: documents.length,
|
|
786
|
+
modifiedIds: [...modifiedIds],
|
|
787
|
+
conflictIds: [...conflictIds],
|
|
788
|
+
nextExpectedVersions: versions
|
|
789
|
+
}, {
|
|
790
|
+
operationName: "replaceMany",
|
|
791
|
+
collectionName,
|
|
792
|
+
serializer,
|
|
793
|
+
errors
|
|
794
|
+
});
|
|
795
|
+
},
|
|
796
|
+
handle: DocumentCommandHandler({
|
|
797
|
+
collectionName,
|
|
798
|
+
serializer,
|
|
799
|
+
errors,
|
|
800
|
+
storage: {
|
|
801
|
+
ensureCollectionCreated,
|
|
802
|
+
fetchByIds: (ids, options) => fetchByIds(ids, options).then((rows) => rows.map((stored) => stored ? fromStored(stored) : null)),
|
|
803
|
+
insertMany: (docs, options) => collection.insertMany(docs, options),
|
|
804
|
+
replaceMany: (docs, options) => collection.replaceMany(docs, options),
|
|
805
|
+
deleteManyByIds
|
|
806
|
+
}
|
|
807
|
+
}),
|
|
808
|
+
find: async (filter, options) => {
|
|
809
|
+
await ensureCollectionCreated(options);
|
|
810
|
+
if (!options?.skipCache && filter) {
|
|
811
|
+
const ids = getIdsFromIdOnlyFilter(filter);
|
|
812
|
+
if (ids && ids.length > 0) return findManyByIds(ids, options);
|
|
813
|
+
}
|
|
814
|
+
return (await query(SqlFor.find(filter ?? {}, options))).rows.map((row) => fromStored({
|
|
815
|
+
...row.data,
|
|
816
|
+
_id: row._id,
|
|
817
|
+
_version: row._version
|
|
818
|
+
}));
|
|
819
|
+
},
|
|
820
|
+
countDocuments: async (filter, options) => {
|
|
821
|
+
await ensureCollectionCreated(options);
|
|
822
|
+
const { count } = await (0, _event_driven_io_dumbo.single)(query(SqlFor.countDocuments(filter ?? {})));
|
|
823
|
+
return count;
|
|
824
|
+
},
|
|
825
|
+
drop: async (options) => {
|
|
826
|
+
await ensureCollectionCreated(options);
|
|
827
|
+
return ((await command(SqlFor.drop()))?.rowCount ?? 0) > 0;
|
|
828
|
+
},
|
|
829
|
+
rename: async (newName, options) => {
|
|
830
|
+
await ensureCollectionCreated(options);
|
|
831
|
+
await command(SqlFor.rename(newName));
|
|
832
|
+
collectionName = newName;
|
|
833
|
+
return collection;
|
|
834
|
+
},
|
|
835
|
+
close: () => cache.close(),
|
|
836
|
+
sql: {
|
|
837
|
+
async query(sql, options) {
|
|
838
|
+
await ensureCollectionCreated(options);
|
|
839
|
+
return (await query(sql, options)).rows;
|
|
840
|
+
},
|
|
841
|
+
async command(sql, options) {
|
|
842
|
+
await ensureCollectionCreated(options);
|
|
843
|
+
return command(sql, options);
|
|
844
|
+
}
|
|
845
|
+
},
|
|
846
|
+
schema: {
|
|
847
|
+
component: schemaComponent,
|
|
848
|
+
migrate: (options) => (0, _event_driven_io_dumbo.runSQLMigrations)(pool, schemaComponent.migrations, options)
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
return collection;
|
|
852
|
+
};
|
|
853
|
+
|
|
854
|
+
//#endregion
|
|
855
|
+
//#region src/core/collection/pongoCollectionSchemaComponent.ts
|
|
856
|
+
const PongoCollectionSchemaComponent = ({ definition, migrationsOrSchemaComponents, sqlBuilder }) => ({
|
|
857
|
+
...(0, _event_driven_io_dumbo.schemaComponent)(`sc:pongo:collection:${definition.name}`, migrationsOrSchemaComponents),
|
|
858
|
+
sqlBuilder,
|
|
859
|
+
definition,
|
|
860
|
+
collectionName: definition.name
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
//#endregion
|
|
864
|
+
//#region src/core/collection/query.ts
|
|
865
|
+
const QueryOperators = {
|
|
866
|
+
$eq: "$eq",
|
|
867
|
+
$gt: "$gt",
|
|
868
|
+
$gte: "$gte",
|
|
869
|
+
$lt: "$lt",
|
|
870
|
+
$lte: "$lte",
|
|
871
|
+
$ne: "$ne",
|
|
872
|
+
$in: "$in",
|
|
873
|
+
$nin: "$nin",
|
|
874
|
+
$elemMatch: "$elemMatch",
|
|
875
|
+
$all: "$all",
|
|
876
|
+
$size: "$size"
|
|
877
|
+
};
|
|
878
|
+
const OperatorMap = {
|
|
879
|
+
$gt: ">",
|
|
880
|
+
$gte: ">=",
|
|
881
|
+
$lt: "<",
|
|
882
|
+
$lte: "<=",
|
|
883
|
+
$ne: "!="
|
|
884
|
+
};
|
|
885
|
+
const isOperator = (key) => key.startsWith("$");
|
|
886
|
+
const hasOperators = (value) => Object.keys(value).some(isOperator);
|
|
887
|
+
|
|
888
|
+
//#endregion
|
|
889
|
+
//#region src/core/typing/entries.ts
|
|
890
|
+
const objectEntries = (obj) => Object.entries(obj).map(([key, value]) => [key, value]);
|
|
891
|
+
|
|
892
|
+
//#endregion
|
|
893
|
+
//#region src/core/errors/index.ts
|
|
894
|
+
const isNumber = (val) => typeof val === "number" && val === val;
|
|
895
|
+
const isString = (val) => typeof val === "string";
|
|
896
|
+
var PongoError = class PongoError extends Error {
|
|
897
|
+
errorCode;
|
|
898
|
+
constructor(options) {
|
|
899
|
+
const errorCode = options && typeof options === "object" && "errorCode" in options ? options.errorCode : isNumber(options) ? options : 500;
|
|
900
|
+
const message = options && typeof options === "object" && "message" in options ? options.message : isString(options) ? options : `Error with status code '${errorCode}' ocurred during Pongo processing`;
|
|
901
|
+
super(message);
|
|
902
|
+
this.errorCode = errorCode;
|
|
903
|
+
Object.setPrototypeOf(this, PongoError.prototype);
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
var ConcurrencyError = class ConcurrencyError extends PongoError {
|
|
907
|
+
constructor(message) {
|
|
908
|
+
super({
|
|
909
|
+
errorCode: 412,
|
|
910
|
+
message: message ?? `Expected document state does not match current one!`
|
|
911
|
+
});
|
|
912
|
+
Object.setPrototypeOf(this, ConcurrencyError.prototype);
|
|
913
|
+
}
|
|
914
|
+
};
|
|
915
|
+
|
|
916
|
+
//#endregion
|
|
917
|
+
//#region src/core/typing/operations.ts
|
|
918
|
+
const DOCUMENT_DOES_NOT_EXIST = "DOCUMENT_DOES_NOT_EXIST";
|
|
919
|
+
const NO_CONCURRENCY_CHECK = "NO_CONCURRENCY_CHECK";
|
|
920
|
+
const isGeneralExpectedDocumentVersion = (version) => version === "DOCUMENT_DOES_NOT_EXIST" || version === "DOCUMENT_EXISTS" || version === "NO_CONCURRENCY_CHECK";
|
|
921
|
+
const expectedVersionValue = (version) => version === void 0 || isGeneralExpectedDocumentVersion(version) ? null : version;
|
|
922
|
+
const expectedVersionPredicate = (version) => {
|
|
923
|
+
if (version === void 0) return { operator: "none" };
|
|
924
|
+
if (isGeneralExpectedDocumentVersion(version)) return version === "DOCUMENT_DOES_NOT_EXIST" ? {
|
|
925
|
+
operator: "=",
|
|
926
|
+
value: 0n
|
|
927
|
+
} : { operator: "none" };
|
|
928
|
+
return {
|
|
929
|
+
operator: "=",
|
|
930
|
+
value: version
|
|
931
|
+
};
|
|
932
|
+
};
|
|
933
|
+
const operationResult = (result, options) => {
|
|
934
|
+
const operationResult = {
|
|
935
|
+
...result,
|
|
936
|
+
acknowledged: true,
|
|
937
|
+
successful: result.successful,
|
|
938
|
+
assertSuccessful: (errorMessage) => {
|
|
939
|
+
const { successful } = result;
|
|
940
|
+
const { operationName, collectionName } = options;
|
|
941
|
+
if (!successful) throw new ConcurrencyError(errorMessage ?? `${operationName} on ${collectionName} failed. Expected document state does not match current one! Result: ${options.serializer.serialize(result)}!`);
|
|
942
|
+
}
|
|
943
|
+
};
|
|
944
|
+
if (options.errors?.throwOnOperationFailures) operationResult.assertSuccessful();
|
|
945
|
+
return operationResult;
|
|
946
|
+
};
|
|
947
|
+
|
|
948
|
+
//#endregion
|
|
949
|
+
//#region src/core/schema/index.ts
|
|
950
|
+
const pongoCollectionSchema = (name) => ({ name });
|
|
951
|
+
pongoCollectionSchema.from = (collectionNames) => collectionNames.reduce((acc, collectionName) => (acc[collectionName] = pongoSchema.collection(collectionName), acc), {});
|
|
952
|
+
function pongoDbSchema(nameOrCollections, collections) {
|
|
953
|
+
if (collections === void 0) {
|
|
954
|
+
if (typeof nameOrCollections === "string") throw new Error("You need to provide colleciton definition");
|
|
955
|
+
return { collections: nameOrCollections };
|
|
956
|
+
}
|
|
957
|
+
return nameOrCollections && typeof nameOrCollections === "string" ? {
|
|
958
|
+
name: nameOrCollections,
|
|
959
|
+
collections
|
|
960
|
+
} : { collections };
|
|
961
|
+
}
|
|
962
|
+
pongoDbSchema.from = (databaseName, collectionNames) => databaseName ? pongoDbSchema(databaseName, pongoCollectionSchema.from(collectionNames)) : pongoDbSchema(pongoCollectionSchema.from(collectionNames));
|
|
963
|
+
const pongoClientSchema = (dbs) => ({ dbs });
|
|
964
|
+
const pongoSchema = {
|
|
965
|
+
client: pongoClientSchema,
|
|
966
|
+
db: pongoDbSchema,
|
|
967
|
+
collection: pongoCollectionSchema
|
|
968
|
+
};
|
|
969
|
+
const proxyPongoDbWithSchema = (pongoDb, dbSchema, collections) => {
|
|
970
|
+
const collectionNames = Object.keys(dbSchema.collections);
|
|
971
|
+
for (const collectionName of collectionNames) collections.set(collectionName, pongoDb.collection(collectionName));
|
|
972
|
+
return new Proxy(pongoDb, { get(target, prop) {
|
|
973
|
+
return collections.get(prop) ?? target[prop];
|
|
974
|
+
} });
|
|
975
|
+
};
|
|
976
|
+
|
|
977
|
+
//#endregion
|
|
978
|
+
//#region src/core/database/pongoDatabaseSchemaComponent.ts
|
|
979
|
+
const PongoDatabaseSchemaComponent = ({ definition, collectionFactory }) => {
|
|
980
|
+
const collections = Object.values(definition.collections).map(collectionFactory) ?? [];
|
|
981
|
+
return {
|
|
982
|
+
...(0, _event_driven_io_dumbo.schemaComponent)(`sc:dumbo:database:${definition.name}`, { components: collections }),
|
|
983
|
+
definition,
|
|
984
|
+
collections,
|
|
985
|
+
collection: (schema) => {
|
|
986
|
+
const existing = collections.find((c) => c.collectionName === schema.name);
|
|
987
|
+
if (existing) return existing;
|
|
988
|
+
const newCollection = collectionFactory(pongoSchema.collection(schema.name));
|
|
989
|
+
collections.push(newCollection);
|
|
990
|
+
definition.collections[schema.name] = schema;
|
|
991
|
+
return newCollection;
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
};
|
|
995
|
+
|
|
996
|
+
//#endregion
|
|
997
|
+
//#region src/core/database/pongoDb.ts
|
|
998
|
+
const PongoDatabase = (options) => {
|
|
999
|
+
const { databaseName, schemaComponent, pool, cache: cacheOptions, serializer } = options;
|
|
1000
|
+
const cache = cacheOptions === "disabled" || cacheOptions === void 0 ? "disabled" : pongoCache(cacheOptions);
|
|
1001
|
+
const collections = /* @__PURE__ */ new Map();
|
|
1002
|
+
const command = async (sql, options) => (await transactionExecutorOrDefault(db, options, pool.execute)).command(sql, options);
|
|
1003
|
+
const query = async (sql, options) => (await transactionExecutorOrDefault(db, options, pool.execute)).query(sql, options);
|
|
1004
|
+
const db = {
|
|
1005
|
+
driverType: pool.driverType,
|
|
1006
|
+
databaseName,
|
|
1007
|
+
connect: () => Promise.resolve(),
|
|
1008
|
+
close: async () => {
|
|
1009
|
+
await Promise.allSettled([
|
|
1010
|
+
pool.close(),
|
|
1011
|
+
cache !== "disabled" ? cache.close() : Promise.resolve(),
|
|
1012
|
+
...collections.values().map((collection) => collection.close())
|
|
1013
|
+
]);
|
|
1014
|
+
},
|
|
1015
|
+
collections: () => [...collections.values()],
|
|
1016
|
+
collection: (collectionName, collectionOptions) => collections.get(collectionName) ?? pongoCollection({
|
|
1017
|
+
collectionName,
|
|
1018
|
+
db,
|
|
1019
|
+
pool,
|
|
1020
|
+
schemaComponent: schemaComponent.collection(pongoSchema.collection(collectionName)),
|
|
1021
|
+
schema: {
|
|
1022
|
+
...options.schema,
|
|
1023
|
+
...collectionOptions?.schema
|
|
1024
|
+
},
|
|
1025
|
+
serializer,
|
|
1026
|
+
errors: {
|
|
1027
|
+
...options.errors,
|
|
1028
|
+
...collectionOptions?.errors
|
|
1029
|
+
},
|
|
1030
|
+
cache: collectionOptions?.cache !== void 0 ? collectionOptions.cache : cache
|
|
1031
|
+
}),
|
|
1032
|
+
transaction: () => pool.transaction(),
|
|
1033
|
+
withTransaction: (handle) => pool.withTransaction(handle),
|
|
1034
|
+
schema: {
|
|
1035
|
+
component: schemaComponent,
|
|
1036
|
+
migrate: (options) => (0, _event_driven_io_dumbo.runSQLMigrations)(pool, schemaComponent.migrations, options)
|
|
1037
|
+
},
|
|
1038
|
+
sql: {
|
|
1039
|
+
async query(sql, options) {
|
|
1040
|
+
return (await query(sql, options)).rows;
|
|
1041
|
+
},
|
|
1042
|
+
async command(sql, options) {
|
|
1043
|
+
return command(sql, options);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
};
|
|
1047
|
+
const dbSchema = options?.schema?.definition;
|
|
1048
|
+
if (dbSchema) return proxyPongoDbWithSchema(db, dbSchema, collections);
|
|
1049
|
+
return db;
|
|
1050
|
+
};
|
|
1051
|
+
|
|
1052
|
+
//#endregion
|
|
1053
|
+
//#region src/core/drivers/databaseDriver.ts
|
|
1054
|
+
const PongoDriverRegistry = () => {
|
|
1055
|
+
const drivers = /* @__PURE__ */ new Map();
|
|
1056
|
+
const register = (driverType, driver) => {
|
|
1057
|
+
const entry = drivers.get(driverType);
|
|
1058
|
+
if (entry && (typeof entry !== "function" || typeof driver === "function")) return;
|
|
1059
|
+
drivers.set(driverType, driver);
|
|
1060
|
+
};
|
|
1061
|
+
const tryResolve = async (driverType) => {
|
|
1062
|
+
const entry = drivers.get(driverType);
|
|
1063
|
+
if (!entry) return null;
|
|
1064
|
+
if (typeof entry !== "function") return entry;
|
|
1065
|
+
const driver = await entry();
|
|
1066
|
+
register(driverType, driver);
|
|
1067
|
+
return driver;
|
|
1068
|
+
};
|
|
1069
|
+
const tryGet = (driverType) => {
|
|
1070
|
+
const entry = drivers.get(driverType);
|
|
1071
|
+
return entry && typeof entry !== "function" ? entry : null;
|
|
1072
|
+
};
|
|
1073
|
+
const has = (driverType) => drivers.has(driverType);
|
|
1074
|
+
return {
|
|
1075
|
+
register,
|
|
1076
|
+
tryResolve,
|
|
1077
|
+
tryGet,
|
|
1078
|
+
has,
|
|
1079
|
+
get databaseDriverTypes() {
|
|
1080
|
+
return Array.from(drivers.keys());
|
|
1081
|
+
}
|
|
1082
|
+
};
|
|
1083
|
+
};
|
|
1084
|
+
const pongoDriverRegistry = globalThis.pongoDriverRegistry = globalThis.pongoDriverRegistry ?? PongoDriverRegistry();
|
|
1085
|
+
|
|
1086
|
+
//#endregion
|
|
1087
|
+
//#region src/core/utils/deepEquals.ts
|
|
1088
|
+
const deepEquals = (left, right) => {
|
|
1089
|
+
if (isEquatable(left)) return left.equals(right);
|
|
1090
|
+
if (Array.isArray(left)) return Array.isArray(right) && left.length === right.length && left.every((val, index) => deepEquals(val, right[index]));
|
|
1091
|
+
if (typeof left !== "object" || typeof right !== "object" || left === null || right === null) return left === right;
|
|
1092
|
+
if (Array.isArray(right)) return false;
|
|
1093
|
+
const keys1 = Object.keys(left);
|
|
1094
|
+
const keys2 = Object.keys(right);
|
|
1095
|
+
if (keys1.length !== keys2.length || !keys1.every((key) => keys2.includes(key))) return false;
|
|
1096
|
+
for (const key in left) {
|
|
1097
|
+
if (left[key] instanceof Function && right[key] instanceof Function) continue;
|
|
1098
|
+
if (!deepEquals(left[key], right[key])) return false;
|
|
1099
|
+
}
|
|
1100
|
+
return true;
|
|
1101
|
+
};
|
|
1102
|
+
const isEquatable = (left) => {
|
|
1103
|
+
return left && typeof left === "object" && "equals" in left && typeof left["equals"] === "function";
|
|
1104
|
+
};
|
|
1105
|
+
|
|
1106
|
+
//#endregion
|
|
1107
|
+
//#region src/core/utils/mapAsync.ts
|
|
1108
|
+
async function mapSequential(items, fn) {
|
|
1109
|
+
const results = [];
|
|
1110
|
+
for (let i = 0; i < items.length; i++) results.push(await fn(items[i], i));
|
|
1111
|
+
return results;
|
|
1112
|
+
}
|
|
1113
|
+
const mapParallel = (items, fn) => Promise.all(items.map(fn));
|
|
1114
|
+
const mapAsync = (items, fn, options = { parallel: false }) => options?.parallel ? mapParallel(items, fn) : mapSequential(items, fn);
|
|
1115
|
+
|
|
1116
|
+
//#endregion
|
|
7
1117
|
//#region src/storage/postgresql/core/sqlBuilder/filter/queryOperators.ts
|
|
8
1118
|
const handleOperator = (path, operator, value, serializer) => {
|
|
9
1119
|
if (path === "_id" || path === "_version") return handleMetadataOperator(path, operator, value);
|
|
10
1120
|
switch (operator) {
|
|
11
1121
|
case "$eq": {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
return _event_driven_io_dumbo.SQL`(data @> ${nestedPath}::jsonb OR jsonb_path_exists(data, '$.${_event_driven_io_dumbo.SQL.plain(path)}[*] ? (@ == ${_event_driven_io_dumbo.SQL.plain(serializedValue)})'))`;
|
|
1122
|
+
const field = _event_driven_io_dumbo_postgresql.PostgreSQLJSON.field(_event_driven_io_dumbo.SQL`data`, path);
|
|
1123
|
+
return _event_driven_io_dumbo.SQL`(${field} = ${_event_driven_io_dumbo.JSONParam.value(value, serializer)}::jsonb OR ${field} @> ${_event_driven_io_dumbo.JSONParam.arrayContaining(value, serializer)}::jsonb)`;
|
|
15
1124
|
}
|
|
16
1125
|
case "$gt":
|
|
17
1126
|
case "$gte":
|
|
18
1127
|
case "$lt":
|
|
19
1128
|
case "$lte":
|
|
20
|
-
case "$ne": return _event_driven_io_dumbo.SQL
|
|
21
|
-
case "$in": return _event_driven_io_dumbo.SQL`data
|
|
22
|
-
case "$nin": return _event_driven_io_dumbo.SQL`data
|
|
1129
|
+
case "$ne": return _event_driven_io_dumbo.SQL`${_event_driven_io_dumbo_postgresql.PostgreSQLJSON.textField(_event_driven_io_dumbo.SQL`data`, path)} ${_event_driven_io_dumbo.SQL.plain(OperatorMap[operator])} ${value}`;
|
|
1130
|
+
case "$in": return _event_driven_io_dumbo.SQL`${_event_driven_io_dumbo_postgresql.PostgreSQLJSON.textField(_event_driven_io_dumbo.SQL`data`, path)} = ANY (${value})`;
|
|
1131
|
+
case "$nin": return _event_driven_io_dumbo.SQL`${_event_driven_io_dumbo_postgresql.PostgreSQLJSON.textField(_event_driven_io_dumbo.SQL`data`, path)} != ALL (${value})`;
|
|
23
1132
|
case "$elemMatch": {
|
|
24
|
-
const
|
|
25
|
-
return _event_driven_io_dumbo.SQL`
|
|
1133
|
+
const field = _event_driven_io_dumbo_postgresql.PostgreSQLJSON.field(_event_driven_io_dumbo.SQL`data`, path);
|
|
1134
|
+
return _event_driven_io_dumbo.SQL`EXISTS (
|
|
1135
|
+
SELECT 1
|
|
1136
|
+
FROM jsonb_array_elements(${_event_driven_io_dumbo.SQL`CASE WHEN jsonb_typeof(${field}) = 'array' THEN ${field} ELSE '[]'::jsonb END`}) AS elem(value)
|
|
1137
|
+
WHERE elem.value @> ${_event_driven_io_dumbo.JSONParam.value(value, serializer)}::jsonb
|
|
1138
|
+
)`;
|
|
26
1139
|
}
|
|
27
|
-
case "$all":
|
|
28
|
-
|
|
1140
|
+
case "$all": {
|
|
1141
|
+
const serializedValue = _event_driven_io_dumbo.JSONParam.value(value, serializer);
|
|
1142
|
+
return _event_driven_io_dumbo.SQL`${_event_driven_io_dumbo_postgresql.PostgreSQLJSON.field(_event_driven_io_dumbo.SQL`data`, path)} @> ${serializedValue}::jsonb`;
|
|
1143
|
+
}
|
|
1144
|
+
case "$size": return _event_driven_io_dumbo.SQL`jsonb_array_length(${_event_driven_io_dumbo_postgresql.PostgreSQLJSON.field(_event_driven_io_dumbo.SQL`data`, path)}) = ${value}`;
|
|
29
1145
|
default: throw new Error(`Unsupported operator: ${operator}`);
|
|
30
1146
|
}
|
|
31
1147
|
};
|
|
@@ -36,13 +1152,12 @@ const handleMetadataOperator = (fieldName, operator, value) => {
|
|
|
36
1152
|
case "$gte":
|
|
37
1153
|
case "$lt":
|
|
38
1154
|
case "$lte":
|
|
39
|
-
case "$ne": return _event_driven_io_dumbo.SQL`${_event_driven_io_dumbo.SQL.plain(fieldName)} ${_event_driven_io_dumbo.SQL.plain(
|
|
1155
|
+
case "$ne": return _event_driven_io_dumbo.SQL`${_event_driven_io_dumbo.SQL.plain(fieldName)} ${_event_driven_io_dumbo.SQL.plain(OperatorMap[operator])} ${value}`;
|
|
40
1156
|
case "$in": return _event_driven_io_dumbo.SQL`${_event_driven_io_dumbo.SQL.plain(fieldName)} = ANY (${value})`;
|
|
41
1157
|
case "$nin": return _event_driven_io_dumbo.SQL`${_event_driven_io_dumbo.SQL.plain(fieldName)} != ALL (${value})`;
|
|
42
1158
|
default: throw new Error(`Unsupported operator: ${operator}`);
|
|
43
1159
|
}
|
|
44
1160
|
};
|
|
45
|
-
const buildNestedObject = (path, value) => path.split(".").reverse().reduce((acc, key) => ({ [key]: acc }), value);
|
|
46
1161
|
|
|
47
1162
|
//#endregion
|
|
48
1163
|
//#region src/storage/postgresql/core/sqlBuilder/filter/index.ts
|
|
@@ -66,7 +1181,7 @@ const constructFilterQuery = (filter, serializer) => {
|
|
|
66
1181
|
if (!_event_driven_io_dumbo.SQL.check.isEmpty(norFilterQuery)) parts.push(norFilterQuery);
|
|
67
1182
|
return _event_driven_io_dumbo.SQL.merge(parts, ` ${AND} `);
|
|
68
1183
|
};
|
|
69
|
-
const constructFieldFilterQuery = (filter, serializer) => _event_driven_io_dumbo.SQL.merge(
|
|
1184
|
+
const constructFieldFilterQuery = (filter, serializer) => _event_driven_io_dumbo.SQL.merge(objectEntries(filter).flatMap(([key, value]) => isLogicalRootOperator(key) ? [] : [isRecord(value) ? constructComplexFilterQuery(key, value, serializer) : handleOperator(key, QueryOperators.$eq, value, serializer)]), ` ${AND} `);
|
|
70
1185
|
const constructLogicalFilterQuery = (filters, joinOperator, serializer) => {
|
|
71
1186
|
if (!filters?.length) return _event_driven_io_dumbo.SQL.EMPTY;
|
|
72
1187
|
const subFilterQueries = filters.reduce((queries, filter) => {
|
|
@@ -84,8 +1199,8 @@ const constructNorFilterQuery = (filters, serializer) => {
|
|
|
84
1199
|
return _event_driven_io_dumbo.SQL.check.isEmpty(logicalFilterQuery) ? _event_driven_io_dumbo.SQL.EMPTY : _event_driven_io_dumbo.SQL`NOT ${logicalFilterQuery}`;
|
|
85
1200
|
};
|
|
86
1201
|
const constructComplexFilterQuery = (key, value, serializer) => {
|
|
87
|
-
const isEquality = !
|
|
88
|
-
return _event_driven_io_dumbo.SQL.merge(
|
|
1202
|
+
const isEquality = !hasOperators(value);
|
|
1203
|
+
return _event_driven_io_dumbo.SQL.merge(objectEntries(value).map(([nestedKey, val]) => isEquality ? handleOperator(`${key}.${nestedKey}`, QueryOperators.$eq, val, serializer) : handleOperator(key, nestedKey, val, serializer)), ` ${AND} `);
|
|
89
1204
|
};
|
|
90
1205
|
const wrapFilterQuery = (filterQuery) => _event_driven_io_dumbo.SQL`(${filterQuery})`;
|
|
91
1206
|
const ensureSupportedRootOperators = (filter) => {
|
|
@@ -96,7 +1211,7 @@ const isRecord = (value) => value !== null && typeof value === "object" && !Arra
|
|
|
96
1211
|
|
|
97
1212
|
//#endregion
|
|
98
1213
|
//#region src/storage/postgresql/core/sqlBuilder/update/index.ts
|
|
99
|
-
const buildUpdateQuery = (update, serializer) =>
|
|
1214
|
+
const buildUpdateQuery = (update, serializer) => objectEntries(update).reduce((currentUpdateQuery, [op, value]) => {
|
|
100
1215
|
switch (op) {
|
|
101
1216
|
case "$set": return buildSetQuery(value, currentUpdateQuery, serializer);
|
|
102
1217
|
case "$unset": return buildUnsetQuery(value, currentUpdateQuery);
|
|
@@ -105,16 +1220,20 @@ const buildUpdateQuery = (update, serializer) => require_core.objectEntries(upda
|
|
|
105
1220
|
default: return currentUpdateQuery;
|
|
106
1221
|
}
|
|
107
1222
|
}, _event_driven_io_dumbo.SQL`data`);
|
|
108
|
-
const buildSetQuery = (set, currentUpdateQuery, serializer) => _event_driven_io_dumbo.SQL`${currentUpdateQuery} || ${
|
|
109
|
-
const buildUnsetQuery = (unset, currentUpdateQuery) =>
|
|
1223
|
+
const buildSetQuery = (set, currentUpdateQuery, serializer) => _event_driven_io_dumbo.SQL`${currentUpdateQuery} || ${_event_driven_io_dumbo.JSONParam.value(set, serializer)}::jsonb`;
|
|
1224
|
+
const buildUnsetQuery = (unset, currentUpdateQuery) => {
|
|
1225
|
+
let query = currentUpdateQuery;
|
|
1226
|
+
for (const key of Object.keys(unset)) query = _event_driven_io_dumbo.SQL`${query} - ${key}`;
|
|
1227
|
+
return query;
|
|
1228
|
+
};
|
|
110
1229
|
const buildIncQuery = (inc, currentUpdateQuery) => {
|
|
111
|
-
for (const [key, value] of Object.entries(inc)) currentUpdateQuery = typeof value === "bigint" ? _event_driven_io_dumbo.SQL`jsonb_set(${currentUpdateQuery},
|
|
1230
|
+
for (const [key, value] of Object.entries(inc)) currentUpdateQuery = typeof value === "bigint" ? _event_driven_io_dumbo.SQL`jsonb_set(${currentUpdateQuery}, ${_event_driven_io_dumbo_postgresql.PostgreSQLJSON.path(key)}, to_jsonb((COALESCE((${_event_driven_io_dumbo_postgresql.PostgreSQLJSON.textField(_event_driven_io_dumbo.SQL`data`, key)})::BIGINT, 0) + ${value})::TEXT), true)` : _event_driven_io_dumbo.SQL`jsonb_set(${currentUpdateQuery}, ${_event_driven_io_dumbo_postgresql.PostgreSQLJSON.path(key)}, to_jsonb(COALESCE((${_event_driven_io_dumbo_postgresql.PostgreSQLJSON.textField(_event_driven_io_dumbo.SQL`data`, key)})::NUMERIC, 0) + ${value}), true)`;
|
|
112
1231
|
return currentUpdateQuery;
|
|
113
1232
|
};
|
|
114
1233
|
const buildPushQuery = (push, currentUpdateQuery, serializer) => {
|
|
115
1234
|
for (const [key, value] of Object.entries(push)) {
|
|
116
|
-
const serializedValue =
|
|
117
|
-
currentUpdateQuery = _event_driven_io_dumbo.SQL`jsonb_set(${currentUpdateQuery},
|
|
1235
|
+
const serializedValue = _event_driven_io_dumbo.JSONParam.arrayContaining(value, serializer);
|
|
1236
|
+
currentUpdateQuery = _event_driven_io_dumbo.SQL`jsonb_set(${currentUpdateQuery}, ${_event_driven_io_dumbo_postgresql.PostgreSQLJSON.path(key)}, (coalesce(${_event_driven_io_dumbo_postgresql.PostgreSQLJSON.field(_event_driven_io_dumbo.SQL`data`, key)}, '[]'::jsonb) || ${serializedValue}::jsonb), true)`;
|
|
118
1237
|
}
|
|
119
1238
|
return currentUpdateQuery;
|
|
120
1239
|
};
|
|
@@ -123,7 +1242,7 @@ const buildPushQuery = (push, currentUpdateQuery, serializer) => {
|
|
|
123
1242
|
//#region src/storage/postgresql/core/sqlBuilder/index.ts
|
|
124
1243
|
const versionCheckClause = (collectionName, expectedVersion) => {
|
|
125
1244
|
const col = _event_driven_io_dumbo.SQL.identifier(collectionName);
|
|
126
|
-
const predicate =
|
|
1245
|
+
const predicate = expectedVersionPredicate(expectedVersion);
|
|
127
1246
|
return predicate.operator === "none" ? _event_driven_io_dumbo.SQL.EMPTY : _event_driven_io_dumbo.SQL`AND ${col}._version = ${predicate.value}`;
|
|
128
1247
|
};
|
|
129
1248
|
const createCollection = (collectionName) => _event_driven_io_dumbo.SQL`
|
|
@@ -141,7 +1260,7 @@ const pongoCollectionPostgreSQLMigrations = (collectionName) => [(0, _event_driv
|
|
|
141
1260
|
const postgresSQLBuilder = (collectionName, serializer) => ({
|
|
142
1261
|
createCollection: () => createCollection(collectionName),
|
|
143
1262
|
insertOne: (document) => {
|
|
144
|
-
const serialized =
|
|
1263
|
+
const serialized = _event_driven_io_dumbo.JSONParam.document(document, serializer);
|
|
145
1264
|
const id = document._id;
|
|
146
1265
|
const version = document._version ?? 1n;
|
|
147
1266
|
return _event_driven_io_dumbo.SQL`
|
|
@@ -149,7 +1268,7 @@ const postgresSQLBuilder = (collectionName, serializer) => ({
|
|
|
149
1268
|
VALUES (${id}, ${serialized}, ${version}) ON CONFLICT(_id) DO NOTHING;`;
|
|
150
1269
|
},
|
|
151
1270
|
insertMany: (documents) => {
|
|
152
|
-
const values = _event_driven_io_dumbo.SQL.merge(documents.map((doc) => _event_driven_io_dumbo.SQL`(${doc._id}, ${
|
|
1271
|
+
const values = _event_driven_io_dumbo.SQL.merge(documents.map((doc) => _event_driven_io_dumbo.SQL`(${doc._id}, ${_event_driven_io_dumbo.JSONParam.document(doc, serializer)}, ${doc._version ?? 1n})`), ",");
|
|
153
1272
|
return _event_driven_io_dumbo.SQL`
|
|
154
1273
|
INSERT INTO ${_event_driven_io_dumbo.SQL.identifier(collectionName)} (_id, data, _version) VALUES ${values}
|
|
155
1274
|
ON CONFLICT(_id) DO NOTHING
|
|
@@ -159,7 +1278,7 @@ const postgresSQLBuilder = (collectionName, serializer) => ({
|
|
|
159
1278
|
const col = _event_driven_io_dumbo.SQL.identifier(collectionName);
|
|
160
1279
|
return _event_driven_io_dumbo.SQL`
|
|
161
1280
|
INSERT INTO ${col} (_id, data, _version)
|
|
162
|
-
VALUES ${_event_driven_io_dumbo.SQL.merge(documents.map((d) => _event_driven_io_dumbo.SQL`(${d._id}::text, ${
|
|
1281
|
+
VALUES ${_event_driven_io_dumbo.SQL.merge(documents.map((d) => _event_driven_io_dumbo.SQL`(${d._id}::text, ${_event_driven_io_dumbo.JSONParam.document(d, serializer)}::jsonb || jsonb_build_object('_id', ${d._id}::text) || jsonb_build_object('_version', '1'::text), 1::bigint)`), ",")}
|
|
163
1282
|
ON CONFLICT(_id) DO UPDATE SET
|
|
164
1283
|
data = EXCLUDED.data
|
|
165
1284
|
|| jsonb_build_object('_id', ${col}._id)
|
|
@@ -207,7 +1326,7 @@ const postgresSQLBuilder = (collectionName, serializer) => ({
|
|
|
207
1326
|
updated AS (
|
|
208
1327
|
UPDATE ${_event_driven_io_dumbo.SQL.identifier(collectionName)}
|
|
209
1328
|
SET
|
|
210
|
-
data = ${
|
|
1329
|
+
data = ${_event_driven_io_dumbo.JSONParam.document(document, serializer)} || jsonb_build_object('_id', ${_event_driven_io_dumbo.SQL.identifier(collectionName)}._id) || jsonb_build_object('_version', (_version + 1)::text),
|
|
211
1330
|
_version = _version + 1
|
|
212
1331
|
FROM existing
|
|
213
1332
|
WHERE ${_event_driven_io_dumbo.SQL.identifier(collectionName)}._id = existing._id ${expectedVersionUpdate}
|
|
@@ -264,7 +1383,7 @@ const postgresSQLBuilder = (collectionName, serializer) => ({
|
|
|
264
1383
|
WITH replacements(_id, data, expected_version) AS (
|
|
265
1384
|
VALUES ${_event_driven_io_dumbo.SQL.merge(documents.map((d) => {
|
|
266
1385
|
const expectedVersion = d._version;
|
|
267
|
-
return expectedVersion !== void 0 ? _event_driven_io_dumbo.SQL`(${d._id}::text, ${
|
|
1386
|
+
return expectedVersion !== void 0 ? _event_driven_io_dumbo.SQL`(${d._id}::text, ${_event_driven_io_dumbo.JSONParam.document(d, serializer)}::jsonb, ${expectedVersion}::bigint)` : _event_driven_io_dumbo.SQL`(${d._id}::text, ${_event_driven_io_dumbo.JSONParam.document(d, serializer)}::jsonb, NULL::bigint)`;
|
|
268
1387
|
}), ",")}
|
|
269
1388
|
)
|
|
270
1389
|
UPDATE ${_event_driven_io_dumbo.SQL.identifier(collectionName)} t
|
|
@@ -278,7 +1397,7 @@ const postgresSQLBuilder = (collectionName, serializer) => ({
|
|
|
278
1397
|
RETURNING t._id, t._version AS version;`;
|
|
279
1398
|
return _event_driven_io_dumbo.SQL`
|
|
280
1399
|
WITH replacements(_id, data) AS (
|
|
281
|
-
VALUES ${_event_driven_io_dumbo.SQL.merge(documents.map((d) => _event_driven_io_dumbo.SQL`(${d._id}::text, ${
|
|
1400
|
+
VALUES ${_event_driven_io_dumbo.SQL.merge(documents.map((d) => _event_driven_io_dumbo.SQL`(${d._id}::text, ${_event_driven_io_dumbo.JSONParam.document(d, serializer)}::jsonb)`), ",")}
|
|
282
1401
|
)
|
|
283
1402
|
UPDATE ${_event_driven_io_dumbo.SQL.identifier(collectionName)} t
|
|
284
1403
|
SET
|
|
@@ -331,9 +1450,7 @@ const postgresSQLBuilder = (collectionName, serializer) => ({
|
|
|
331
1450
|
query.push(where(filterQuery));
|
|
332
1451
|
if (options?.sort && Object.keys(options.sort).length > 0) {
|
|
333
1452
|
const clauses = Object.entries(options.sort).map(([field, dir]) => {
|
|
334
|
-
const
|
|
335
|
-
const isNested = !isMetadata && field.includes(".");
|
|
336
|
-
const accessor = isMetadata ? _event_driven_io_dumbo.SQL`${_event_driven_io_dumbo.SQL.plain(field)}` : isNested ? _event_driven_io_dumbo.SQL`data #> '${_event_driven_io_dumbo.SQL.plain(`{${field.split(".").join(",")}}`)}'` : _event_driven_io_dumbo.SQL`data -> '${_event_driven_io_dumbo.SQL.plain(field)}'`;
|
|
1453
|
+
const accessor = field === "_id" || field === "_version" ? _event_driven_io_dumbo.SQL`${_event_driven_io_dumbo.SQL.plain(field)}` : _event_driven_io_dumbo_postgresql.PostgreSQLJSON.field(_event_driven_io_dumbo.SQL`data`, field);
|
|
337
1454
|
return dir === 1 ? _event_driven_io_dumbo.SQL`${accessor} ASC NULLS FIRST` : _event_driven_io_dumbo.SQL`${accessor} DESC NULLS LAST`;
|
|
338
1455
|
});
|
|
339
1456
|
query.push(_event_driven_io_dumbo.SQL`ORDER BY ${_event_driven_io_dumbo.SQL.merge(clauses, ",")}`);
|
|
@@ -357,7 +1474,7 @@ const pgPongoDriver = {
|
|
|
357
1474
|
driverType: _event_driven_io_dumbo_pg.PgDriverType,
|
|
358
1475
|
databaseFactory: (options) => {
|
|
359
1476
|
const databaseName = options.databaseName ?? _event_driven_io_dumbo_pg.postgreSQLMetadata.parseDatabaseName(options.connectionString) ?? _event_driven_io_dumbo_pg.postgreSQLMetadata.defaultDatabaseName;
|
|
360
|
-
return
|
|
1477
|
+
return PongoDatabase({
|
|
361
1478
|
...options,
|
|
362
1479
|
pool: (0, _event_driven_io_dumbo.dumbo)({
|
|
363
1480
|
connectionString: options.connectionString,
|
|
@@ -365,22 +1482,22 @@ const pgPongoDriver = {
|
|
|
365
1482
|
...options.connectionOptions,
|
|
366
1483
|
serialization: { serializer: options.serializer }
|
|
367
1484
|
}),
|
|
368
|
-
schemaComponent:
|
|
1485
|
+
schemaComponent: PongoDatabaseSchemaComponent({
|
|
369
1486
|
driverType: _event_driven_io_dumbo_pg.PgDriverType,
|
|
370
|
-
collectionFactory: (schema) =>
|
|
1487
|
+
collectionFactory: (schema) => PongoCollectionSchemaComponent({
|
|
371
1488
|
driverType: _event_driven_io_dumbo_pg.PgDriverType,
|
|
372
1489
|
definition: schema,
|
|
373
1490
|
migrationsOrSchemaComponents: { migrations: pongoCollectionPostgreSQLMigrations(schema.name) },
|
|
374
1491
|
sqlBuilder: postgresSQLBuilder(schema.name, options.serialization?.serializer ?? _event_driven_io_dumbo.JSONSerializer)
|
|
375
1492
|
}),
|
|
376
|
-
definition: options.schema?.definition ??
|
|
1493
|
+
definition: options.schema?.definition ?? pongoSchema.db(databaseName, {})
|
|
377
1494
|
}),
|
|
378
1495
|
databaseName
|
|
379
1496
|
});
|
|
380
1497
|
}
|
|
381
1498
|
};
|
|
382
1499
|
const usePgPongoDriver = () => {
|
|
383
|
-
|
|
1500
|
+
pongoDriverRegistry.register(_event_driven_io_dumbo_pg.PgDriverType, pgPongoDriver);
|
|
384
1501
|
};
|
|
385
1502
|
usePgPongoDriver();
|
|
386
1503
|
|