@cocreate/mongodb 1.24.0 → 1.25.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/CHANGELOG.md +20 -0
- package/package.json +5 -5
- package/src/array.js +201 -0
- package/src/database.js +110 -0
- package/src/index.js +20 -918
- package/src/object.js +509 -0
- package/src/utilities.js +296 -0
package/src/object.js
ADDED
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
/********************************************************************************
|
|
2
|
+
* Copyright (C) 2023 CoCreate and Contributors.
|
|
3
|
+
*
|
|
4
|
+
* This program is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU Affero General Public License as published
|
|
6
|
+
* by the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU Affero General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
********************************************************************************/
|
|
17
|
+
|
|
18
|
+
import { ObjectId } from "mongodb";
|
|
19
|
+
import {
|
|
20
|
+
dotNotationToObject,
|
|
21
|
+
searchData,
|
|
22
|
+
isValidDate
|
|
23
|
+
} from "@cocreate/utils";
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
dbClient,
|
|
27
|
+
createFilter,
|
|
28
|
+
createData,
|
|
29
|
+
errorHandler
|
|
30
|
+
} from "./utilities.js";
|
|
31
|
+
|
|
32
|
+
function object(method, data) {
|
|
33
|
+
return new Promise(
|
|
34
|
+
async (resolve, reject) => {
|
|
35
|
+
try {
|
|
36
|
+
const client = await dbClient(data);
|
|
37
|
+
|
|
38
|
+
if (!client || client.status === false) return resolve(data);
|
|
39
|
+
|
|
40
|
+
let type = "object";
|
|
41
|
+
let documents = [];
|
|
42
|
+
|
|
43
|
+
if (!data["timeStamp"])
|
|
44
|
+
data["timeStamp"] = new Date().toISOString();
|
|
45
|
+
|
|
46
|
+
let databases = data.database;
|
|
47
|
+
if (!Array.isArray(databases)) databases = [databases];
|
|
48
|
+
|
|
49
|
+
for (let database of databases) {
|
|
50
|
+
let arrays = data.array;
|
|
51
|
+
if (!Array.isArray(arrays)) arrays = [arrays];
|
|
52
|
+
|
|
53
|
+
for (let array of arrays) {
|
|
54
|
+
const db = client.db(database);
|
|
55
|
+
const arrayObj = db.collection(array);
|
|
56
|
+
const reference = {
|
|
57
|
+
$storage: data.storageName,
|
|
58
|
+
$database: database,
|
|
59
|
+
$array: array
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
if (!data[type]) data[type] = [];
|
|
63
|
+
else if (typeof data[type] === "string")
|
|
64
|
+
data[type] = [{ _id: data[type] }];
|
|
65
|
+
else if (!Array.isArray(data[type]))
|
|
66
|
+
data[type] = [data[type]];
|
|
67
|
+
|
|
68
|
+
let isFilter;
|
|
69
|
+
if (data.$filter) isFilter = true;
|
|
70
|
+
if ((isFilter && !data[type].length) || data.isFilter)
|
|
71
|
+
data[type].splice(0, 0, {
|
|
72
|
+
isFilter: "isEmptyObjectFilter"
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
let filter = await createFilter(data, arrayObj);
|
|
76
|
+
|
|
77
|
+
let projections = {},
|
|
78
|
+
projection = {},
|
|
79
|
+
globalUpdate = {},
|
|
80
|
+
options = {};
|
|
81
|
+
|
|
82
|
+
if (data.$filter && data.$filter.key)
|
|
83
|
+
projection = data.$filter.key;
|
|
84
|
+
|
|
85
|
+
if (method === "update")
|
|
86
|
+
createUpdate(globalUpdate, options, data, true);
|
|
87
|
+
|
|
88
|
+
for (let i = 0; i < data[type].length; i++) {
|
|
89
|
+
let $storage = data[type][i].$storage || [];
|
|
90
|
+
let $database = data[type][i].$database || [];
|
|
91
|
+
let $array = data[type][i].$array || [];
|
|
92
|
+
let update = { ...globalUpdate };
|
|
93
|
+
|
|
94
|
+
if (!Array.isArray($storage))
|
|
95
|
+
$storage = [data[type][i].$storage];
|
|
96
|
+
if (!Array.isArray($database))
|
|
97
|
+
$database = [data[type][i].$database];
|
|
98
|
+
if (!Array.isArray($array))
|
|
99
|
+
$array = [data[type][i].$array];
|
|
100
|
+
|
|
101
|
+
if (!$storage.includes(data.storageName))
|
|
102
|
+
$storage.push(data.storageName);
|
|
103
|
+
if (!$database.includes(database))
|
|
104
|
+
$database.push(database);
|
|
105
|
+
if (!$array.includes(array)) $array.push(array);
|
|
106
|
+
|
|
107
|
+
delete data[type][i].$storage;
|
|
108
|
+
delete data[type][i].$database;
|
|
109
|
+
delete data[type][i].$array;
|
|
110
|
+
|
|
111
|
+
if (method !== "create" && data[type][i].$filter) {
|
|
112
|
+
isFilter = true;
|
|
113
|
+
reference["$filter"] = data[type][i].$filter;
|
|
114
|
+
filter = await createFilter(
|
|
115
|
+
{ $filter: data[type][i].$filter },
|
|
116
|
+
arrayObj
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let { query, sort, index, limit } = filter;
|
|
121
|
+
|
|
122
|
+
if (method === "create") {
|
|
123
|
+
if (typeof replaceArray === 'function') {
|
|
124
|
+
data[type][i] = replaceArray(data[type][i]);
|
|
125
|
+
}
|
|
126
|
+
data[type][i] = dotNotationToObject(
|
|
127
|
+
data[type][i]
|
|
128
|
+
);
|
|
129
|
+
data[type][i]["organization_id"] =
|
|
130
|
+
data["organization_id"];
|
|
131
|
+
data[type][i]["created"] = {
|
|
132
|
+
on: new Date(data.timeStamp),
|
|
133
|
+
by: data.user_id || data.clientId
|
|
134
|
+
};
|
|
135
|
+
} else if (method === "update") {
|
|
136
|
+
if (!data[type][i].modified)
|
|
137
|
+
data[type][i].modified = {
|
|
138
|
+
on: new Date(data.timeStamp),
|
|
139
|
+
by: data.user_id || data.clientId
|
|
140
|
+
};
|
|
141
|
+
else
|
|
142
|
+
data[type][i].modified.on = new Date(
|
|
143
|
+
data[type][i].modified.on
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
data[type][i].organization_id =
|
|
147
|
+
data.organization_id;
|
|
148
|
+
createUpdate(update, options, data[type][i]);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (data[type][i]._id || method === "create" || isFilter) {
|
|
152
|
+
if (method !== "create") {
|
|
153
|
+
try {
|
|
154
|
+
if (data[type][i]._id) {
|
|
155
|
+
query._id = new ObjectId(data[type][i]._id);
|
|
156
|
+
}
|
|
157
|
+
} catch (error) {
|
|
158
|
+
if (
|
|
159
|
+
method === "update" &&
|
|
160
|
+
options.upsert
|
|
161
|
+
) {
|
|
162
|
+
data[type][i]._id = new ObjectId();
|
|
163
|
+
query._id = data[type][i]._id;
|
|
164
|
+
} else {
|
|
165
|
+
errorHandler(
|
|
166
|
+
data,
|
|
167
|
+
error,
|
|
168
|
+
database,
|
|
169
|
+
array
|
|
170
|
+
);
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
process.emit("usage", {
|
|
178
|
+
type: "egress",
|
|
179
|
+
data: { query, update, projection, options },
|
|
180
|
+
organization_id: data.organization_id
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
let result;
|
|
184
|
+
if (method === "create") {
|
|
185
|
+
if (data[type][i]._id) {
|
|
186
|
+
try {
|
|
187
|
+
data[type][i]._id = new ObjectId(
|
|
188
|
+
data[type][i]._id
|
|
189
|
+
);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
delete data[type][i]._id;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
result = await arrayObj.insertOne(
|
|
195
|
+
data[type][i]
|
|
196
|
+
);
|
|
197
|
+
data[type][i]._id =
|
|
198
|
+
result.insertedId.toString();
|
|
199
|
+
documents.push({ ...data[type][i], ...reference });
|
|
200
|
+
} else {
|
|
201
|
+
let cursor = arrayObj.find(query, { projection });
|
|
202
|
+
if (sort && Object.keys(sort).length > 0) cursor = cursor.sort(sort);
|
|
203
|
+
if (limit) cursor = cursor.limit(limit);
|
|
204
|
+
|
|
205
|
+
let hasDocuments = false;
|
|
206
|
+
|
|
207
|
+
for await (const document of cursor) {
|
|
208
|
+
hasDocuments = true;
|
|
209
|
+
|
|
210
|
+
process.emit("usage", {
|
|
211
|
+
type: "ingress",
|
|
212
|
+
data: document,
|
|
213
|
+
organization_id: data.organization_id
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
if (data.$filter && data.$filter.search) {
|
|
217
|
+
let isMatch = searchData(
|
|
218
|
+
document,
|
|
219
|
+
data.$filter.search
|
|
220
|
+
);
|
|
221
|
+
if (!isMatch) continue;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (method === "read") {
|
|
225
|
+
document._id = document._id.toString();
|
|
226
|
+
let object = data[type].find(
|
|
227
|
+
(obj) =>
|
|
228
|
+
obj._id &&
|
|
229
|
+
obj._id.toString() === document._id.toString()
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
if (object && object.$storage && object.modified && object.modified.on) {
|
|
233
|
+
if (document.modified && new Date(object.modified.on) > new Date(document.modified.on)) {
|
|
234
|
+
object = { ...document, ...object };
|
|
235
|
+
let docUpdate = {};
|
|
236
|
+
let docOptions = {};
|
|
237
|
+
createUpdate(docUpdate, docOptions, object);
|
|
238
|
+
let updateResult = await arrayObj.updateOne(
|
|
239
|
+
{ _id: new ObjectId(document._id) },
|
|
240
|
+
docUpdate,
|
|
241
|
+
docOptions
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
process.emit("usage", {
|
|
245
|
+
type: "ingress",
|
|
246
|
+
data: updateResult,
|
|
247
|
+
organization_id: data.organization_id
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
documents.push({ ...document, ...reference });
|
|
252
|
+
} else {
|
|
253
|
+
process.emit("usage", {
|
|
254
|
+
type: "egress",
|
|
255
|
+
data: { _id: document._id, update, options },
|
|
256
|
+
organization_id: data.organization_id
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (method === "update") {
|
|
260
|
+
if (update["$pull"] && update["$unset"]) {
|
|
261
|
+
result = await arrayObj.updateOne(
|
|
262
|
+
{ _id: document._id },
|
|
263
|
+
{ $unset: update["$unset"] },
|
|
264
|
+
options
|
|
265
|
+
);
|
|
266
|
+
delete update["$unset"];
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (options.returnNewDocument) {
|
|
270
|
+
let updatedObj = await arrayObj.findOneAndUpdate(
|
|
271
|
+
{ _id: document._id },
|
|
272
|
+
update,
|
|
273
|
+
{ ...options, returnDocument: 'after' }
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
if (updatedObj && updatedObj.value) {
|
|
277
|
+
for (let key of Object.keys(updatedObj.value)) {
|
|
278
|
+
if (key === "_id" || !options.newArray) continue;
|
|
279
|
+
let newArrayKey = options.newArray[key];
|
|
280
|
+
if (newArrayKey) {
|
|
281
|
+
let idx = updatedObj.value[key].length - 1;
|
|
282
|
+
if (idx >= 0) {
|
|
283
|
+
data[type][i][newArrayKey.replace("[]", `[${idx}]`)] = data[type][i][newArrayKey];
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
289
|
+
result = await arrayObj.updateOne(
|
|
290
|
+
{ _id: document._id },
|
|
291
|
+
update,
|
|
292
|
+
options
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
} else if (method === "delete") {
|
|
296
|
+
result = await arrayObj.deleteOne({
|
|
297
|
+
_id: document._id
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
process.emit("usage", {
|
|
302
|
+
type: "ingress",
|
|
303
|
+
data: result,
|
|
304
|
+
organization_id: data.organization_id
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
documents.push({
|
|
308
|
+
...data[type][i],
|
|
309
|
+
...reference,
|
|
310
|
+
_id: document._id.toString()
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (!hasDocuments && method === "update" && (data.upsert || options.upsert)) {
|
|
316
|
+
let upsertId = data[type][i]._id ? new ObjectId(data[type][i]._id) : new ObjectId();
|
|
317
|
+
let result = await arrayObj.updateOne(
|
|
318
|
+
{ _id: upsertId },
|
|
319
|
+
update,
|
|
320
|
+
{ ...options, upsert: true }
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
process.emit("usage", {
|
|
324
|
+
type: "ingress",
|
|
325
|
+
data: result,
|
|
326
|
+
organization_id: data.organization_id
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
documents.push({
|
|
330
|
+
...data[type][i],
|
|
331
|
+
...reference,
|
|
332
|
+
_id: upsertId.toString()
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
} catch (error) {
|
|
337
|
+
errorHandler(data, error, database, array);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
data = createData(
|
|
345
|
+
data,
|
|
346
|
+
documents,
|
|
347
|
+
type
|
|
348
|
+
);
|
|
349
|
+
resolve(data);
|
|
350
|
+
} catch (error) {
|
|
351
|
+
errorHandler(data, error);
|
|
352
|
+
console.log(method, "error", error);
|
|
353
|
+
resolve(data);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function createUpdate(update, options, data, isGlobal) {
|
|
360
|
+
if (data.upsert) options.upsert = data.upsert;
|
|
361
|
+
if (data.$upsert) options.upsert = data.$upsert;
|
|
362
|
+
if (data.$update) delete data.$update;
|
|
363
|
+
|
|
364
|
+
Object.keys(data).forEach((key) => {
|
|
365
|
+
if ((isGlobal && !key.startsWith("$")) || key === "_id") return;
|
|
366
|
+
|
|
367
|
+
if (isValidDate(data[key])) data[key] = new Date(data[key]);
|
|
368
|
+
|
|
369
|
+
let operator;
|
|
370
|
+
if (key.endsWith("]")) {
|
|
371
|
+
const regex = /^(.*(?:\[\d+\].*?)?)\[(.*?)\](?:\[\])?$/;
|
|
372
|
+
var match = key.match(regex);
|
|
373
|
+
var index = parseInt(match[2], 10);
|
|
374
|
+
if (index === NaN) operator = match[2];
|
|
375
|
+
var arrayKey = match[1].replace(/\[(\d+)\]/g, ".$1");
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (key.startsWith("$")) operator = key.split(".")[0];
|
|
379
|
+
else if (
|
|
380
|
+
!operator &&
|
|
381
|
+
typeof data[key] === "string" &&
|
|
382
|
+
data[key].startsWith("$")
|
|
383
|
+
)
|
|
384
|
+
operator = data[key];
|
|
385
|
+
|
|
386
|
+
if (!update["$set"]) update["$set"] = {};
|
|
387
|
+
|
|
388
|
+
let originalKey = key;
|
|
389
|
+
key = key.replace(/\[(\d+)\]/g, ".$1");
|
|
390
|
+
|
|
391
|
+
if (originalKey.endsWith("[]")) {
|
|
392
|
+
if (!options.projection) {
|
|
393
|
+
options.projection = {};
|
|
394
|
+
options.arrayKey = {};
|
|
395
|
+
options.returnNewDocument = true;
|
|
396
|
+
} else {
|
|
397
|
+
options.projection[key.replace(operator + ".", "")] = 1;
|
|
398
|
+
options.arrayKey[key.replace(operator + ".", "")] = originalKey;
|
|
399
|
+
}
|
|
400
|
+
if (!key.startsWith("$")) operator = "$push";
|
|
401
|
+
if (key.endsWith("[]")) key = key.replace("[]", "");
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
let operators = [
|
|
405
|
+
"$rename",
|
|
406
|
+
"$inc",
|
|
407
|
+
"$push",
|
|
408
|
+
"$each",
|
|
409
|
+
"$splice",
|
|
410
|
+
"$unset",
|
|
411
|
+
"$delete",
|
|
412
|
+
"$slice",
|
|
413
|
+
"$pop",
|
|
414
|
+
"$shift",
|
|
415
|
+
"$addToSet",
|
|
416
|
+
"$pull",
|
|
417
|
+
"$currentDate"
|
|
418
|
+
];
|
|
419
|
+
if (!operators.includes(operator) && typeof index !== "number") {
|
|
420
|
+
if (!isGlobal) update["$set"][key] = data[originalKey];
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
let updates = {};
|
|
425
|
+
if (operator === "$rename") {
|
|
426
|
+
if (key === "$rename")
|
|
427
|
+
for (let oldkey of Object.keys(data[originalKey])) {
|
|
428
|
+
key = "$rename." + oldkey;
|
|
429
|
+
updates[key] = data[originalKey][oldkey].replace(
|
|
430
|
+
/\[(\d+)\]/g,
|
|
431
|
+
".$1"
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
else updates[key] = data[originalKey].replace(/\[(\d+)\]/g, ".$1");
|
|
435
|
+
} else if (
|
|
436
|
+
operator === "$delete" ||
|
|
437
|
+
operator === "$unset" ||
|
|
438
|
+
operator === "$slice"
|
|
439
|
+
) {
|
|
440
|
+
operator = "$unset";
|
|
441
|
+
updates[key] = 1;
|
|
442
|
+
const pullkey = key.split(".");
|
|
443
|
+
pullkey.shift();
|
|
444
|
+
pullkey.pop();
|
|
445
|
+
if (!update["$pull"]) update["$pull"] = {};
|
|
446
|
+
update["$pull"][pullkey.join(".")] = null;
|
|
447
|
+
} else if (operator === "$pop") {
|
|
448
|
+
key = arrayKey;
|
|
449
|
+
updates[key] = index || 1;
|
|
450
|
+
} else if (operator === "$addToSet" || operator === "$pull") {
|
|
451
|
+
updates[key] = data[originalKey];
|
|
452
|
+
} else if (
|
|
453
|
+
operator === "$push" ||
|
|
454
|
+
operator === "$each" ||
|
|
455
|
+
typeof index === "number"
|
|
456
|
+
) {
|
|
457
|
+
updates[key] = data[originalKey];
|
|
458
|
+
if (typeof index === "number" && index >= 0) {
|
|
459
|
+
if (!operator) operator = "$set";
|
|
460
|
+
else {
|
|
461
|
+
if (operator === "$push")
|
|
462
|
+
updates[key] = [data[originalKey]];
|
|
463
|
+
|
|
464
|
+
let insert = { $each: updates[key] };
|
|
465
|
+
insert.$postion = index;
|
|
466
|
+
if (index >= 0) updates[arrayKey] = insert;
|
|
467
|
+
else updates[key] = insert;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
} else if (operator === "$inc") {
|
|
471
|
+
updates[key] = data[originalKey];
|
|
472
|
+
} else if (operator === "$currentDate") {
|
|
473
|
+
updates[key] = data[originalKey];
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (!update[operator]) update[operator] = {};
|
|
477
|
+
|
|
478
|
+
if (key === operator)
|
|
479
|
+
update[operator] = {
|
|
480
|
+
...update[operator],
|
|
481
|
+
...replaceArray(updates[key])
|
|
482
|
+
};
|
|
483
|
+
else update[operator][key.replace(operator + ".", "")] = updates[key];
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function createProjection(projection, data) {
|
|
488
|
+
Object.keys(data).forEach((key) => {
|
|
489
|
+
if (
|
|
490
|
+
!["_id", "organization_id", "isFIlter"].includes(key) &&
|
|
491
|
+
!key.startsWith("$")
|
|
492
|
+
)
|
|
493
|
+
projection[key.replace(/\[(\d+)\]/g, ".$1")] = 1;
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
return projection;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function replaceArray(data = {}) {
|
|
500
|
+
let object = {};
|
|
501
|
+
|
|
502
|
+
Object.keys(data).forEach((key) => {
|
|
503
|
+
object[key.replace(/\[(\d+)\]/g, ".$1")] = data[key];
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
return object;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
export default object;
|