@opra/mongodb 1.2.2 → 1.3.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.
Files changed (41) hide show
  1. package/cjs/{mongo-adapter.js → adapter/mongo-adapter.js} +4 -6
  2. package/cjs/adapter/mongo-patch-generator.js +124 -0
  3. package/cjs/index.js +7 -6
  4. package/cjs/{mongo-collection-service.js → services/mongo-collection-service.js} +1 -1
  5. package/cjs/{mongo-entity-service.js → services/mongo-entity-service.js} +58 -63
  6. package/cjs/{mongo-nested-service.js → services/mongo-nested-service.js} +10 -3
  7. package/cjs/{mongo-service.js → services/mongo-service.js} +4 -2
  8. package/cjs/{mongo-singleton-service.js → services/mongo-singleton-service.js} +1 -1
  9. package/esm/{mongo-adapter.js → adapter/mongo-adapter.js} +4 -6
  10. package/esm/adapter/mongo-patch-generator.js +120 -0
  11. package/esm/index.js +7 -6
  12. package/esm/{mongo-collection-service.js → services/mongo-collection-service.js} +1 -1
  13. package/esm/{mongo-entity-service.js → services/mongo-entity-service.js} +58 -63
  14. package/esm/{mongo-nested-service.js → services/mongo-nested-service.js} +10 -3
  15. package/esm/{mongo-service.js → services/mongo-service.js} +4 -2
  16. package/esm/{mongo-singleton-service.js → services/mongo-singleton-service.js} +1 -1
  17. package/package.json +5 -5
  18. package/types/{mongo-adapter.d.ts → adapter/mongo-adapter.d.ts} +4 -6
  19. package/types/adapter/mongo-patch-generator.d.ts +23 -0
  20. package/types/{adapter-utils → adapter}/prepare-filter.d.ts +1 -1
  21. package/types/index.d.cts +7 -6
  22. package/types/index.d.ts +7 -6
  23. package/types/{mongo-collection-service.d.ts → services/mongo-collection-service.d.ts} +1 -1
  24. package/types/{mongo-entity-service.d.ts → services/mongo-entity-service.d.ts} +9 -7
  25. package/types/{mongo-nested-service.d.ts → services/mongo-nested-service.d.ts} +2 -2
  26. package/types/{mongo-service.d.ts → services/mongo-service.d.ts} +1 -1
  27. package/types/{mongo-singleton-service.d.ts → services/mongo-singleton-service.d.ts} +1 -1
  28. package/cjs/adapter-utils/prepare-patch.js +0 -37
  29. package/esm/adapter-utils/prepare-patch.js +0 -34
  30. package/types/adapter-utils/prepare-patch.d.ts +0 -3
  31. /package/cjs/{adapter-utils → adapter}/prepare-filter.js +0 -0
  32. /package/cjs/{adapter-utils → adapter}/prepare-key-values.js +0 -0
  33. /package/cjs/{adapter-utils → adapter}/prepare-projection.js +0 -0
  34. /package/cjs/{adapter-utils → adapter}/prepare-sort.js +0 -0
  35. /package/esm/{adapter-utils → adapter}/prepare-filter.js +0 -0
  36. /package/esm/{adapter-utils → adapter}/prepare-key-values.js +0 -0
  37. /package/esm/{adapter-utils → adapter}/prepare-projection.js +0 -0
  38. /package/esm/{adapter-utils → adapter}/prepare-sort.js +0 -0
  39. /package/types/{adapter-utils → adapter}/prepare-key-values.d.ts +0 -0
  40. /package/types/{adapter-utils → adapter}/prepare-projection.d.ts +0 -0
  41. /package/types/{adapter-utils → adapter}/prepare-sort.d.ts +0 -0
@@ -2,16 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MongoAdapter = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const prepare_filter_js_1 = tslib_1.__importDefault(require("./adapter-utils/prepare-filter.js"));
6
- const prepare_key_values_js_1 = tslib_1.__importDefault(require("./adapter-utils/prepare-key-values.js"));
7
- const prepare_patch_js_1 = tslib_1.__importDefault(require("./adapter-utils/prepare-patch.js"));
8
- const prepare_projection_js_1 = tslib_1.__importDefault(require("./adapter-utils/prepare-projection.js"));
9
- const prepare_sort_js_1 = tslib_1.__importDefault(require("./adapter-utils/prepare-sort.js"));
5
+ const prepare_filter_js_1 = tslib_1.__importDefault(require("./prepare-filter.js"));
6
+ const prepare_key_values_js_1 = tslib_1.__importDefault(require("./prepare-key-values.js"));
7
+ const prepare_projection_js_1 = tslib_1.__importDefault(require("./prepare-projection.js"));
8
+ const prepare_sort_js_1 = tslib_1.__importDefault(require("./prepare-sort.js"));
10
9
  var MongoAdapter;
11
10
  (function (MongoAdapter) {
12
11
  MongoAdapter.prepareFilter = prepare_filter_js_1.default;
13
12
  MongoAdapter.prepareKeyValues = prepare_key_values_js_1.default;
14
- MongoAdapter.preparePatch = prepare_patch_js_1.default;
15
13
  MongoAdapter.prepareProjection = prepare_projection_js_1.default;
16
14
  MongoAdapter.prepareSort = prepare_sort_js_1.default;
17
15
  async function parseRequest(context) {
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MongoPatchGenerator = void 0;
4
+ const common_1 = require("@opra/common");
5
+ const FIELD_NAME_PATTERN = /^([-><*:])?(.+)$/;
6
+ class MongoPatchGenerator {
7
+ generatePatch(dataType, doc, options) {
8
+ const ctx = {};
9
+ this._processComplexType(ctx, dataType, options?.currentPath || '', doc);
10
+ const update = {};
11
+ if (ctx.$pull)
12
+ update.$pull = ctx.$pull;
13
+ if (ctx.$unset)
14
+ update.$unset = ctx.$unset;
15
+ if (ctx.$set)
16
+ update.$set = ctx.$set;
17
+ return {
18
+ update,
19
+ arrayFilters: ctx.arrayFilters,
20
+ };
21
+ }
22
+ _processComplexType(ctx, dataType, path, input) {
23
+ if (input.$remove) {
24
+ this._processRemove(ctx, dataType, path, input.$remove);
25
+ }
26
+ const keys = Object.keys(input);
27
+ const pathDot = path + (path ? '.' : '');
28
+ let field;
29
+ let key;
30
+ let value;
31
+ let keyField;
32
+ let keyValue;
33
+ let arrayIndex = 0;
34
+ let arrayFilterName = '';
35
+ for (key of keys) {
36
+ const m = FIELD_NAME_PATTERN.exec(key);
37
+ if (!m)
38
+ continue;
39
+ key = m[2];
40
+ value = input[key];
41
+ field = dataType.fields.get(key);
42
+ if (!field) {
43
+ if (dataType.additionalFields === true) {
44
+ if (value === null) {
45
+ ctx.$unset = ctx.$unset || {};
46
+ ctx.$unset[pathDot + key] = 1;
47
+ }
48
+ else {
49
+ ctx.$set = ctx.$set || {};
50
+ ctx.$set[pathDot + key] = value;
51
+ }
52
+ }
53
+ continue;
54
+ }
55
+ if (field.readonly)
56
+ continue;
57
+ if (value === null) {
58
+ ctx.$unset = ctx.$unset || {};
59
+ ctx.$unset[pathDot + field.name] = 1;
60
+ continue;
61
+ }
62
+ if (field.type instanceof common_1.ComplexType) {
63
+ if (!value)
64
+ continue;
65
+ if (field.isArray) {
66
+ if (!value.length)
67
+ continue;
68
+ keyField = field.keyField || field.type.keyField;
69
+ if (keyField) {
70
+ for (let v of value) {
71
+ /** Increase arrayIndex and determine a new name for array filter */
72
+ arrayFilterName = 'f' + String(++arrayIndex);
73
+ /** Extract key value from object */
74
+ keyValue = v[keyField];
75
+ if (keyValue == null)
76
+ continue;
77
+ v = { ...v };
78
+ /** Remove key field from object */
79
+ delete v[keyField];
80
+ /** Add array filter */
81
+ ctx.arrayFilters = ctx.arrayFilters || {};
82
+ ctx.arrayFilters[`${arrayFilterName}.${keyField}`] = keyValue;
83
+ /** Process each object in array */
84
+ this._processComplexType(ctx, field.type, pathDot + field.name + `.$[${arrayFilterName}]`, v);
85
+ }
86
+ continue;
87
+ }
88
+ }
89
+ if (!(typeof value === 'object'))
90
+ continue;
91
+ /** Process nested object */
92
+ this._processComplexType(ctx, field.type, pathDot + field.name, value);
93
+ continue;
94
+ }
95
+ ctx.$set = ctx.$set || {};
96
+ ctx.$set[pathDot + field.name] = value;
97
+ }
98
+ }
99
+ _processRemove(ctx, dataType, path, input) {
100
+ let field;
101
+ let key;
102
+ let value;
103
+ const pathDot = path + (path ? '.' : '');
104
+ const keys = Object.keys(input);
105
+ let keyField;
106
+ for (key of keys) {
107
+ value = input[key];
108
+ field = dataType.fields.get(key);
109
+ if (!(field && field.isArray))
110
+ continue;
111
+ ctx.$pull = ctx.$pull || {};
112
+ if (field.type instanceof common_1.ComplexType) {
113
+ keyField = field.keyField || field.type.keyField;
114
+ if (!keyField)
115
+ continue;
116
+ ctx.$pull[pathDot + key] = { $elemMatch: { [keyField]: Array.isArray(value) ? { $in: value } : value } };
117
+ }
118
+ else {
119
+ ctx.$pull[pathDot + key] = Array.isArray(value) ? { $in: value } : value;
120
+ }
121
+ }
122
+ }
123
+ }
124
+ exports.MongoPatchGenerator = MongoPatchGenerator;
package/cjs/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
- tslib_1.__exportStar(require("./mongo-adapter.js"), exports);
5
- tslib_1.__exportStar(require("./mongo-collection-service.js"), exports);
6
- tslib_1.__exportStar(require("./mongo-entity-service.js"), exports);
7
- tslib_1.__exportStar(require("./mongo-nested-service.js"), exports);
8
- tslib_1.__exportStar(require("./mongo-service.js"), exports);
9
- tslib_1.__exportStar(require("./mongo-singleton-service.js"), exports);
4
+ tslib_1.__exportStar(require("./adapter/mongo-adapter.js"), exports);
5
+ tslib_1.__exportStar(require("./adapter/mongo-patch-generator.js"), exports);
6
+ tslib_1.__exportStar(require("./services/mongo-collection-service.js"), exports);
7
+ tslib_1.__exportStar(require("./services/mongo-entity-service.js"), exports);
8
+ tslib_1.__exportStar(require("./services/mongo-nested-service.js"), exports);
9
+ tslib_1.__exportStar(require("./services/mongo-service.js"), exports);
10
+ tslib_1.__exportStar(require("./services/mongo-singleton-service.js"), exports);
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MongoCollectionService = void 0;
4
4
  const common_1 = require("@opra/common");
5
- const mongo_adapter_js_1 = require("./mongo-adapter.js");
5
+ const mongo_adapter_js_1 = require("../adapter/mongo-adapter.js");
6
6
  const mongo_entity_service_js_1 = require("./mongo-entity-service.js");
7
7
  /**
8
8
  * @class MongoCollectionService
@@ -4,7 +4,8 @@ exports.MongoEntityService = void 0;
4
4
  const objects_1 = require("@jsopen/objects");
5
5
  const common_1 = require("@opra/common");
6
6
  const valgen_1 = require("valgen");
7
- const mongo_adapter_js_1 = require("./mongo-adapter.js");
7
+ const mongo_adapter_js_1 = require("../adapter/mongo-adapter.js");
8
+ const mongo_patch_generator_js_1 = require("../adapter/mongo-patch-generator.js");
8
9
  const mongo_service_js_1 = require("./mongo-service.js");
9
10
  /**
10
11
  * @class MongoEntityService
@@ -288,16 +289,7 @@ class MongoEntityService extends mongo_service_js_1.MongoService {
288
289
  if (input && inputRaw) {
289
290
  throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
290
291
  }
291
- let update;
292
- if (input) {
293
- const inputCodec = this._getInputCodec('update');
294
- const doc = inputCodec(input);
295
- delete doc._id;
296
- update = mongo_adapter_js_1.MongoAdapter.preparePatch(doc);
297
- update.$set = update.$set || {};
298
- }
299
- else
300
- update = inputRaw;
292
+ const update = this._prepareUpdate(command);
301
293
  const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
302
294
  mongo_adapter_js_1.MongoAdapter.prepareKeyValues(command.documentId, ['_id']),
303
295
  options?.filter,
@@ -316,36 +308,6 @@ class MongoEntityService extends mongo_service_js_1.MongoService {
316
308
  if (out)
317
309
  return outputCodec(out);
318
310
  }
319
- /**
320
- * Replaces a document with the given id in the collection
321
- *
322
- * @param {MongoEntityService.ReplaceCommand<T>} command
323
- */
324
- async _replace(command) {
325
- const input = command.input;
326
- (0, valgen_1.isNotNullish)(input, { label: 'input' });
327
- (0, valgen_1.isNotNullish)(input._id, { label: 'input._id' });
328
- const inputCodec = this._getInputCodec('replace');
329
- const document = inputCodec(input);
330
- const { options } = command;
331
- const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
332
- mongo_adapter_js_1.MongoAdapter.prepareKeyValues(command.documentId, ['_id']),
333
- options?.filter,
334
- ]);
335
- const db = this.getDatabase();
336
- const collection = await this.getCollection(db);
337
- const out = await collection.findOneAndReplace(filter || {}, document, {
338
- upsert: undefined,
339
- ...options,
340
- returnDocument: 'after',
341
- includeResultMetadata: false,
342
- session: options?.session ?? this.getSession(),
343
- projection: mongo_adapter_js_1.MongoAdapter.prepareProjection(this.dataType, options?.projection),
344
- });
345
- const outputCodec = this._getOutputCodec('replace');
346
- if (out)
347
- return outputCodec(out);
348
- }
349
311
  /**
350
312
  * Updates a document in the collection with the specified ID.
351
313
  *
@@ -358,17 +320,7 @@ class MongoEntityService extends mongo_service_js_1.MongoService {
358
320
  if (input && inputRaw) {
359
321
  throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
360
322
  }
361
- let update;
362
- if (input) {
363
- const inputCodec = this._getInputCodec('update');
364
- const doc = inputCodec(input);
365
- delete doc._id;
366
- update = mongo_adapter_js_1.MongoAdapter.preparePatch(doc);
367
- if (!Object.keys(doc).length)
368
- return 0;
369
- }
370
- else
371
- update = inputRaw;
323
+ const update = this._prepareUpdate(command);
372
324
  const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
373
325
  mongo_adapter_js_1.MongoAdapter.prepareKeyValues(command.documentId, ['_id']),
374
326
  options?.filter,
@@ -393,17 +345,7 @@ class MongoEntityService extends mongo_service_js_1.MongoService {
393
345
  if (input && inputRaw) {
394
346
  throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
395
347
  }
396
- let update;
397
- if (input) {
398
- const inputCodec = this._getInputCodec('update');
399
- const doc = inputCodec(input);
400
- delete doc._id;
401
- update = mongo_adapter_js_1.MongoAdapter.preparePatch(doc);
402
- if (!Object.keys(doc).length)
403
- return 0;
404
- }
405
- else
406
- update = inputRaw;
348
+ const update = this._prepareUpdate(command);
407
349
  const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter(options?.filter);
408
350
  const db = this.getDatabase();
409
351
  const collection = await this.getCollection(db);
@@ -413,6 +355,59 @@ class MongoEntityService extends mongo_service_js_1.MongoService {
413
355
  upsert: false,
414
356
  })).matchedCount;
415
357
  }
358
+ /**
359
+ * Replaces a document with the given id in the collection
360
+ *
361
+ * @param {MongoEntityService.ReplaceCommand<T>} command
362
+ */
363
+ async _replace(command) {
364
+ const input = command.input;
365
+ (0, valgen_1.isNotNullish)(input, { label: 'input' });
366
+ (0, valgen_1.isNotNullish)(input._id, { label: 'input._id' });
367
+ const inputCodec = this._getInputCodec('replace');
368
+ const document = inputCodec(input);
369
+ const { options } = command;
370
+ const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
371
+ mongo_adapter_js_1.MongoAdapter.prepareKeyValues(command.documentId, ['_id']),
372
+ options?.filter,
373
+ ]);
374
+ const db = this.getDatabase();
375
+ const collection = await this.getCollection(db);
376
+ const out = await collection.findOneAndReplace(filter || {}, document, {
377
+ upsert: undefined,
378
+ ...options,
379
+ returnDocument: 'after',
380
+ includeResultMetadata: false,
381
+ session: options?.session ?? this.getSession(),
382
+ projection: mongo_adapter_js_1.MongoAdapter.prepareProjection(this.dataType, options?.projection),
383
+ });
384
+ const outputCodec = this._getOutputCodec('replace');
385
+ if (out)
386
+ return outputCodec(out);
387
+ }
388
+ _prepareUpdate(command) {
389
+ const { input, inputRaw } = command;
390
+ (0, valgen_1.isNotNullish)(input || inputRaw, { label: 'input' });
391
+ if (input && inputRaw) {
392
+ throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
393
+ }
394
+ if (inputRaw)
395
+ return inputRaw;
396
+ const inputCodec = this._getInputCodec('update');
397
+ const doc = inputCodec(input);
398
+ delete doc._id;
399
+ return this._generatePatch(command, doc);
400
+ }
401
+ _generatePatch(command, doc) {
402
+ const patchGenerator = new mongo_patch_generator_js_1.MongoPatchGenerator();
403
+ const { update, arrayFilters } = patchGenerator.generatePatch(this.dataType, doc);
404
+ command.options = command.options || {};
405
+ if (arrayFilters) {
406
+ command.options.arrayFilters = command.options.arrayFilters || [];
407
+ command.options.arrayFilters.push(arrayFilters);
408
+ }
409
+ return update;
410
+ }
416
411
  async _executeCommand(command, commandFn) {
417
412
  try {
418
413
  const result = await super._executeCommand(command, async () => {
@@ -4,7 +4,8 @@ exports.MongoNestedService = void 0;
4
4
  const objects_1 = require("@jsopen/objects");
5
5
  const common_1 = require("@opra/common");
6
6
  const valgen_1 = require("valgen");
7
- const mongo_adapter_js_1 = require("./mongo-adapter.js");
7
+ const mongo_adapter_js_1 = require("../adapter/mongo-adapter.js");
8
+ const mongo_patch_generator_js_1 = require("../adapter/mongo-patch-generator.js");
8
9
  const mongo_service_js_1 = require("./mongo-service.js");
9
10
  /**
10
11
  * A class that provides methods to perform operations on an array field in a MongoDB collection.
@@ -687,9 +688,15 @@ class MongoNestedService extends mongo_service_js_1.MongoService {
687
688
  const elemMatch = mongo_adapter_js_1.MongoAdapter.prepareFilter([options?.filter], { fieldPrefix: 'elem.' });
688
689
  options.arrayFilters = [elemMatch];
689
690
  }
690
- const update = mongo_adapter_js_1.MongoAdapter.preparePatch(doc, {
691
- fieldPrefix: this.fieldName + (options?.filter ? '.$[elem].' : '.$[].'),
691
+ const patchGenerator = new mongo_patch_generator_js_1.MongoPatchGenerator();
692
+ const { update, arrayFilters } = patchGenerator.generatePatch(this.dataType, doc, {
693
+ currentPath: this.fieldName + (options?.filter ? '.$[elem]' : '.$[]'),
692
694
  });
695
+ command.options = command.options || {};
696
+ if (arrayFilters) {
697
+ command.options.arrayFilters = command.options.arrayFilters || [];
698
+ command.options.arrayFilters.push(arrayFilters);
699
+ }
693
700
  // Count matching items, we will use this as result
694
701
  const count = await this._count({
695
702
  crud: 'read',
@@ -4,7 +4,7 @@ exports.MongoService = void 0;
4
4
  const common_1 = require("@opra/common");
5
5
  const core_1 = require("@opra/core");
6
6
  const mongodb_1 = require("mongodb");
7
- const mongo_adapter_js_1 = require("./mongo-adapter.js");
7
+ const mongo_adapter_js_1 = require("../adapter/mongo-adapter.js");
8
8
  const transactionKey = Symbol.for('transaction');
9
9
  /**
10
10
  * Class representing a MongoDB service for interacting with a collection.
@@ -232,8 +232,10 @@ class MongoService extends core_1.ServiceBase {
232
232
  if (validator)
233
233
  return validator;
234
234
  const options = { projection: '*' };
235
- if (operation === 'update')
235
+ if (operation === 'update') {
236
236
  options.partial = 'deep';
237
+ options.allowPatchOperators = true;
238
+ }
237
239
  const dataType = this.dataType;
238
240
  validator = dataType.generateCodec('decode', options);
239
241
  this._inputCodecs[operation] = validator;
@@ -4,7 +4,7 @@ exports.MongoSingletonService = void 0;
4
4
  const objects_1 = require("@jsopen/objects");
5
5
  const common_1 = require("@opra/common");
6
6
  const mongodb_1 = require("mongodb");
7
- const mongo_adapter_js_1 = require("./mongo-adapter.js");
7
+ const mongo_adapter_js_1 = require("../adapter/mongo-adapter.js");
8
8
  const mongo_entity_service_js_1 = require("./mongo-entity-service.js");
9
9
  /**
10
10
  * A class that provides access to a MongoDB collection, with support for singleton document operations.
@@ -1,13 +1,11 @@
1
- import _prepareFilter from './adapter-utils/prepare-filter.js';
2
- import _prepareKeyValues from './adapter-utils/prepare-key-values.js';
3
- import _preparePatch from './adapter-utils/prepare-patch.js';
4
- import _prepareProjection from './adapter-utils/prepare-projection.js';
5
- import _prepareSort from './adapter-utils/prepare-sort.js';
1
+ import _prepareFilter from './prepare-filter.js';
2
+ import _prepareKeyValues from './prepare-key-values.js';
3
+ import _prepareProjection from './prepare-projection.js';
4
+ import _prepareSort from './prepare-sort.js';
6
5
  export var MongoAdapter;
7
6
  (function (MongoAdapter) {
8
7
  MongoAdapter.prepareFilter = _prepareFilter;
9
8
  MongoAdapter.prepareKeyValues = _prepareKeyValues;
10
- MongoAdapter.preparePatch = _preparePatch;
11
9
  MongoAdapter.prepareProjection = _prepareProjection;
12
10
  MongoAdapter.prepareSort = _prepareSort;
13
11
  async function parseRequest(context) {
@@ -0,0 +1,120 @@
1
+ import { ComplexType } from '@opra/common';
2
+ const FIELD_NAME_PATTERN = /^([-><*:])?(.+)$/;
3
+ export class MongoPatchGenerator {
4
+ generatePatch(dataType, doc, options) {
5
+ const ctx = {};
6
+ this._processComplexType(ctx, dataType, options?.currentPath || '', doc);
7
+ const update = {};
8
+ if (ctx.$pull)
9
+ update.$pull = ctx.$pull;
10
+ if (ctx.$unset)
11
+ update.$unset = ctx.$unset;
12
+ if (ctx.$set)
13
+ update.$set = ctx.$set;
14
+ return {
15
+ update,
16
+ arrayFilters: ctx.arrayFilters,
17
+ };
18
+ }
19
+ _processComplexType(ctx, dataType, path, input) {
20
+ if (input.$remove) {
21
+ this._processRemove(ctx, dataType, path, input.$remove);
22
+ }
23
+ const keys = Object.keys(input);
24
+ const pathDot = path + (path ? '.' : '');
25
+ let field;
26
+ let key;
27
+ let value;
28
+ let keyField;
29
+ let keyValue;
30
+ let arrayIndex = 0;
31
+ let arrayFilterName = '';
32
+ for (key of keys) {
33
+ const m = FIELD_NAME_PATTERN.exec(key);
34
+ if (!m)
35
+ continue;
36
+ key = m[2];
37
+ value = input[key];
38
+ field = dataType.fields.get(key);
39
+ if (!field) {
40
+ if (dataType.additionalFields === true) {
41
+ if (value === null) {
42
+ ctx.$unset = ctx.$unset || {};
43
+ ctx.$unset[pathDot + key] = 1;
44
+ }
45
+ else {
46
+ ctx.$set = ctx.$set || {};
47
+ ctx.$set[pathDot + key] = value;
48
+ }
49
+ }
50
+ continue;
51
+ }
52
+ if (field.readonly)
53
+ continue;
54
+ if (value === null) {
55
+ ctx.$unset = ctx.$unset || {};
56
+ ctx.$unset[pathDot + field.name] = 1;
57
+ continue;
58
+ }
59
+ if (field.type instanceof ComplexType) {
60
+ if (!value)
61
+ continue;
62
+ if (field.isArray) {
63
+ if (!value.length)
64
+ continue;
65
+ keyField = field.keyField || field.type.keyField;
66
+ if (keyField) {
67
+ for (let v of value) {
68
+ /** Increase arrayIndex and determine a new name for array filter */
69
+ arrayFilterName = 'f' + String(++arrayIndex);
70
+ /** Extract key value from object */
71
+ keyValue = v[keyField];
72
+ if (keyValue == null)
73
+ continue;
74
+ v = { ...v };
75
+ /** Remove key field from object */
76
+ delete v[keyField];
77
+ /** Add array filter */
78
+ ctx.arrayFilters = ctx.arrayFilters || {};
79
+ ctx.arrayFilters[`${arrayFilterName}.${keyField}`] = keyValue;
80
+ /** Process each object in array */
81
+ this._processComplexType(ctx, field.type, pathDot + field.name + `.$[${arrayFilterName}]`, v);
82
+ }
83
+ continue;
84
+ }
85
+ }
86
+ if (!(typeof value === 'object'))
87
+ continue;
88
+ /** Process nested object */
89
+ this._processComplexType(ctx, field.type, pathDot + field.name, value);
90
+ continue;
91
+ }
92
+ ctx.$set = ctx.$set || {};
93
+ ctx.$set[pathDot + field.name] = value;
94
+ }
95
+ }
96
+ _processRemove(ctx, dataType, path, input) {
97
+ let field;
98
+ let key;
99
+ let value;
100
+ const pathDot = path + (path ? '.' : '');
101
+ const keys = Object.keys(input);
102
+ let keyField;
103
+ for (key of keys) {
104
+ value = input[key];
105
+ field = dataType.fields.get(key);
106
+ if (!(field && field.isArray))
107
+ continue;
108
+ ctx.$pull = ctx.$pull || {};
109
+ if (field.type instanceof ComplexType) {
110
+ keyField = field.keyField || field.type.keyField;
111
+ if (!keyField)
112
+ continue;
113
+ ctx.$pull[pathDot + key] = { $elemMatch: { [keyField]: Array.isArray(value) ? { $in: value } : value } };
114
+ }
115
+ else {
116
+ ctx.$pull[pathDot + key] = Array.isArray(value) ? { $in: value } : value;
117
+ }
118
+ }
119
+ }
120
+ }
package/esm/index.js CHANGED
@@ -1,6 +1,7 @@
1
- export * from './mongo-adapter.js';
2
- export * from './mongo-collection-service.js';
3
- export * from './mongo-entity-service.js';
4
- export * from './mongo-nested-service.js';
5
- export * from './mongo-service.js';
6
- export * from './mongo-singleton-service.js';
1
+ export * from './adapter/mongo-adapter.js';
2
+ export * from './adapter/mongo-patch-generator.js';
3
+ export * from './services/mongo-collection-service.js';
4
+ export * from './services/mongo-entity-service.js';
5
+ export * from './services/mongo-nested-service.js';
6
+ export * from './services/mongo-service.js';
7
+ export * from './services/mongo-singleton-service.js';
@@ -1,5 +1,5 @@
1
1
  import { ResourceNotAvailableError } from '@opra/common';
2
- import { MongoAdapter } from './mongo-adapter.js';
2
+ import { MongoAdapter } from '../adapter/mongo-adapter.js';
3
3
  import { MongoEntityService } from './mongo-entity-service.js';
4
4
  /**
5
5
  * @class MongoCollectionService
@@ -1,7 +1,8 @@
1
1
  import { omit } from '@jsopen/objects';
2
2
  import { InternalServerError } from '@opra/common';
3
3
  import { isNotNullish } from 'valgen';
4
- import { MongoAdapter } from './mongo-adapter.js';
4
+ import { MongoAdapter } from '../adapter/mongo-adapter.js';
5
+ import { MongoPatchGenerator } from '../adapter/mongo-patch-generator.js';
5
6
  import { MongoService } from './mongo-service.js';
6
7
  /**
7
8
  * @class MongoEntityService
@@ -285,16 +286,7 @@ export class MongoEntityService extends MongoService {
285
286
  if (input && inputRaw) {
286
287
  throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
287
288
  }
288
- let update;
289
- if (input) {
290
- const inputCodec = this._getInputCodec('update');
291
- const doc = inputCodec(input);
292
- delete doc._id;
293
- update = MongoAdapter.preparePatch(doc);
294
- update.$set = update.$set || {};
295
- }
296
- else
297
- update = inputRaw;
289
+ const update = this._prepareUpdate(command);
298
290
  const filter = MongoAdapter.prepareFilter([
299
291
  MongoAdapter.prepareKeyValues(command.documentId, ['_id']),
300
292
  options?.filter,
@@ -313,36 +305,6 @@ export class MongoEntityService extends MongoService {
313
305
  if (out)
314
306
  return outputCodec(out);
315
307
  }
316
- /**
317
- * Replaces a document with the given id in the collection
318
- *
319
- * @param {MongoEntityService.ReplaceCommand<T>} command
320
- */
321
- async _replace(command) {
322
- const input = command.input;
323
- isNotNullish(input, { label: 'input' });
324
- isNotNullish(input._id, { label: 'input._id' });
325
- const inputCodec = this._getInputCodec('replace');
326
- const document = inputCodec(input);
327
- const { options } = command;
328
- const filter = MongoAdapter.prepareFilter([
329
- MongoAdapter.prepareKeyValues(command.documentId, ['_id']),
330
- options?.filter,
331
- ]);
332
- const db = this.getDatabase();
333
- const collection = await this.getCollection(db);
334
- const out = await collection.findOneAndReplace(filter || {}, document, {
335
- upsert: undefined,
336
- ...options,
337
- returnDocument: 'after',
338
- includeResultMetadata: false,
339
- session: options?.session ?? this.getSession(),
340
- projection: MongoAdapter.prepareProjection(this.dataType, options?.projection),
341
- });
342
- const outputCodec = this._getOutputCodec('replace');
343
- if (out)
344
- return outputCodec(out);
345
- }
346
308
  /**
347
309
  * Updates a document in the collection with the specified ID.
348
310
  *
@@ -355,17 +317,7 @@ export class MongoEntityService extends MongoService {
355
317
  if (input && inputRaw) {
356
318
  throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
357
319
  }
358
- let update;
359
- if (input) {
360
- const inputCodec = this._getInputCodec('update');
361
- const doc = inputCodec(input);
362
- delete doc._id;
363
- update = MongoAdapter.preparePatch(doc);
364
- if (!Object.keys(doc).length)
365
- return 0;
366
- }
367
- else
368
- update = inputRaw;
320
+ const update = this._prepareUpdate(command);
369
321
  const filter = MongoAdapter.prepareFilter([
370
322
  MongoAdapter.prepareKeyValues(command.documentId, ['_id']),
371
323
  options?.filter,
@@ -390,17 +342,7 @@ export class MongoEntityService extends MongoService {
390
342
  if (input && inputRaw) {
391
343
  throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
392
344
  }
393
- let update;
394
- if (input) {
395
- const inputCodec = this._getInputCodec('update');
396
- const doc = inputCodec(input);
397
- delete doc._id;
398
- update = MongoAdapter.preparePatch(doc);
399
- if (!Object.keys(doc).length)
400
- return 0;
401
- }
402
- else
403
- update = inputRaw;
345
+ const update = this._prepareUpdate(command);
404
346
  const filter = MongoAdapter.prepareFilter(options?.filter);
405
347
  const db = this.getDatabase();
406
348
  const collection = await this.getCollection(db);
@@ -410,6 +352,59 @@ export class MongoEntityService extends MongoService {
410
352
  upsert: false,
411
353
  })).matchedCount;
412
354
  }
355
+ /**
356
+ * Replaces a document with the given id in the collection
357
+ *
358
+ * @param {MongoEntityService.ReplaceCommand<T>} command
359
+ */
360
+ async _replace(command) {
361
+ const input = command.input;
362
+ isNotNullish(input, { label: 'input' });
363
+ isNotNullish(input._id, { label: 'input._id' });
364
+ const inputCodec = this._getInputCodec('replace');
365
+ const document = inputCodec(input);
366
+ const { options } = command;
367
+ const filter = MongoAdapter.prepareFilter([
368
+ MongoAdapter.prepareKeyValues(command.documentId, ['_id']),
369
+ options?.filter,
370
+ ]);
371
+ const db = this.getDatabase();
372
+ const collection = await this.getCollection(db);
373
+ const out = await collection.findOneAndReplace(filter || {}, document, {
374
+ upsert: undefined,
375
+ ...options,
376
+ returnDocument: 'after',
377
+ includeResultMetadata: false,
378
+ session: options?.session ?? this.getSession(),
379
+ projection: MongoAdapter.prepareProjection(this.dataType, options?.projection),
380
+ });
381
+ const outputCodec = this._getOutputCodec('replace');
382
+ if (out)
383
+ return outputCodec(out);
384
+ }
385
+ _prepareUpdate(command) {
386
+ const { input, inputRaw } = command;
387
+ isNotNullish(input || inputRaw, { label: 'input' });
388
+ if (input && inputRaw) {
389
+ throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
390
+ }
391
+ if (inputRaw)
392
+ return inputRaw;
393
+ const inputCodec = this._getInputCodec('update');
394
+ const doc = inputCodec(input);
395
+ delete doc._id;
396
+ return this._generatePatch(command, doc);
397
+ }
398
+ _generatePatch(command, doc) {
399
+ const patchGenerator = new MongoPatchGenerator();
400
+ const { update, arrayFilters } = patchGenerator.generatePatch(this.dataType, doc);
401
+ command.options = command.options || {};
402
+ if (arrayFilters) {
403
+ command.options.arrayFilters = command.options.arrayFilters || [];
404
+ command.options.arrayFilters.push(arrayFilters);
405
+ }
406
+ return update;
407
+ }
413
408
  async _executeCommand(command, commandFn) {
414
409
  try {
415
410
  const result = await super._executeCommand(command, async () => {
@@ -1,7 +1,8 @@
1
1
  import { omit } from '@jsopen/objects';
2
2
  import { ComplexType, NotAcceptableError, ResourceNotAvailableError } from '@opra/common';
3
3
  import { isNotNullish } from 'valgen';
4
- import { MongoAdapter } from './mongo-adapter.js';
4
+ import { MongoAdapter } from '../adapter/mongo-adapter.js';
5
+ import { MongoPatchGenerator } from '../adapter/mongo-patch-generator.js';
5
6
  import { MongoService } from './mongo-service.js';
6
7
  /**
7
8
  * A class that provides methods to perform operations on an array field in a MongoDB collection.
@@ -684,9 +685,15 @@ export class MongoNestedService extends MongoService {
684
685
  const elemMatch = MongoAdapter.prepareFilter([options?.filter], { fieldPrefix: 'elem.' });
685
686
  options.arrayFilters = [elemMatch];
686
687
  }
687
- const update = MongoAdapter.preparePatch(doc, {
688
- fieldPrefix: this.fieldName + (options?.filter ? '.$[elem].' : '.$[].'),
688
+ const patchGenerator = new MongoPatchGenerator();
689
+ const { update, arrayFilters } = patchGenerator.generatePatch(this.dataType, doc, {
690
+ currentPath: this.fieldName + (options?.filter ? '.$[elem]' : '.$[]'),
689
691
  });
692
+ command.options = command.options || {};
693
+ if (arrayFilters) {
694
+ command.options.arrayFilters = command.options.arrayFilters || [];
695
+ command.options.arrayFilters.push(arrayFilters);
696
+ }
690
697
  // Count matching items, we will use this as result
691
698
  const count = await this._count({
692
699
  crud: 'read',
@@ -1,7 +1,7 @@
1
1
  import { DATATYPE_METADATA } from '@opra/common';
2
2
  import { ServiceBase } from '@opra/core';
3
3
  import { ObjectId } from 'mongodb';
4
- import { MongoAdapter } from './mongo-adapter.js';
4
+ import { MongoAdapter } from '../adapter/mongo-adapter.js';
5
5
  const transactionKey = Symbol.for('transaction');
6
6
  /**
7
7
  * Class representing a MongoDB service for interacting with a collection.
@@ -229,8 +229,10 @@ export class MongoService extends ServiceBase {
229
229
  if (validator)
230
230
  return validator;
231
231
  const options = { projection: '*' };
232
- if (operation === 'update')
232
+ if (operation === 'update') {
233
233
  options.partial = 'deep';
234
+ options.allowPatchOperators = true;
235
+ }
234
236
  const dataType = this.dataType;
235
237
  validator = dataType.generateCodec('decode', options);
236
238
  this._inputCodecs[operation] = validator;
@@ -1,7 +1,7 @@
1
1
  import { omit } from '@jsopen/objects';
2
2
  import { ResourceNotAvailableError } from '@opra/common';
3
3
  import { ObjectId } from 'mongodb';
4
- import { MongoAdapter } from './mongo-adapter.js';
4
+ import { MongoAdapter } from '../adapter/mongo-adapter.js';
5
5
  import { MongoEntityService } from './mongo-entity-service.js';
6
6
  /**
7
7
  * A class that provides access to a MongoDB collection, with support for singleton document operations.
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@opra/mongodb",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "Opra MongoDB adapter package",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
7
7
  "dependencies": {
8
- "@jsopen/objects": "^1.4.2",
8
+ "@jsopen/objects": "^1.5.0",
9
9
  "tslib": "^2.8.1",
10
10
  "valgen": "^5.12.0"
11
11
  },
12
12
  "peerDependencies": {
13
- "@opra/common": "^1.2.2",
14
- "@opra/core": "^1.2.2",
15
- "@opra/http": "^1.2.2",
13
+ "@opra/common": "^1.3.0",
14
+ "@opra/core": "^1.3.0",
15
+ "@opra/http": "^1.3.0",
16
16
  "mongodb": ">= 6.0.0"
17
17
  },
18
18
  "type": "module",
@@ -1,17 +1,15 @@
1
1
  import { OpraFilter } from '@opra/common';
2
2
  import type { ExecutionContext } from '@opra/core';
3
3
  import mongodb, { ObjectId } from 'mongodb';
4
- import _prepareFilter from './adapter-utils/prepare-filter.js';
5
- import _prepareKeyValues from './adapter-utils/prepare-key-values.js';
6
- import _preparePatch from './adapter-utils/prepare-patch.js';
7
- import _prepareProjection from './adapter-utils/prepare-projection.js';
8
- import _prepareSort from './adapter-utils/prepare-sort.js';
4
+ import _prepareFilter from './prepare-filter.js';
5
+ import _prepareKeyValues from './prepare-key-values.js';
6
+ import _prepareProjection from './prepare-projection.js';
7
+ import _prepareSort from './prepare-sort.js';
9
8
  export declare namespace MongoAdapter {
10
9
  type AnyId = string | number | ObjectId;
11
10
  type FilterInput<T = any> = OpraFilter.Expression | mongodb.Filter<T> | string | undefined;
12
11
  const prepareFilter: typeof _prepareFilter;
13
12
  const prepareKeyValues: typeof _prepareKeyValues;
14
- const preparePatch: typeof _preparePatch;
15
13
  const prepareProjection: typeof _prepareProjection;
16
14
  const prepareSort: typeof _prepareSort;
17
15
  interface TransformedRequest {
@@ -0,0 +1,23 @@
1
+ import { ComplexType } from '@opra/common';
2
+ import type { UpdateFilter } from 'mongodb';
3
+ import type { PatchDTO } from 'ts-gems';
4
+ interface Context {
5
+ $set?: Record<string, any>;
6
+ $unset?: Record<string, any>;
7
+ $pull?: Record<string, any>;
8
+ arrayFilters?: Record<string, any>;
9
+ }
10
+ export declare class MongoPatchGenerator {
11
+ generatePatch<T extends object>(dataType: ComplexType, doc: PatchDTO<T>, options?: MongoPatchGenerator.Options): {
12
+ update: UpdateFilter<T>;
13
+ arrayFilters?: Record<string, any>;
14
+ };
15
+ protected _processComplexType(ctx: Context, dataType: ComplexType, path: string, input: any): void;
16
+ protected _processRemove(ctx: Context, dataType: ComplexType, path: string, input: any): void;
17
+ }
18
+ export declare namespace MongoPatchGenerator {
19
+ interface Options {
20
+ currentPath?: string;
21
+ }
22
+ }
23
+ export {};
@@ -1,5 +1,5 @@
1
1
  import mongodb from 'mongodb';
2
- import type { MongoAdapter } from '../mongo-adapter.js';
2
+ import type { MongoAdapter } from './mongo-adapter';
3
3
  /**
4
4
  * Prepare the MongoDB filter based on the provided filters and options.
5
5
  *
package/types/index.d.cts CHANGED
@@ -1,6 +1,7 @@
1
- export * from './mongo-adapter.js';
2
- export * from './mongo-collection-service.js';
3
- export * from './mongo-entity-service.js';
4
- export * from './mongo-nested-service.js';
5
- export * from './mongo-service.js';
6
- export * from './mongo-singleton-service.js';
1
+ export * from './adapter/mongo-adapter.js';
2
+ export * from './adapter/mongo-patch-generator.js';
3
+ export * from './services/mongo-collection-service.js';
4
+ export * from './services/mongo-entity-service.js';
5
+ export * from './services/mongo-nested-service.js';
6
+ export * from './services/mongo-service.js';
7
+ export * from './services/mongo-singleton-service.js';
package/types/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- export * from './mongo-adapter.js';
2
- export * from './mongo-collection-service.js';
3
- export * from './mongo-entity-service.js';
4
- export * from './mongo-nested-service.js';
5
- export * from './mongo-service.js';
6
- export * from './mongo-singleton-service.js';
1
+ export * from './adapter/mongo-adapter.js';
2
+ export * from './adapter/mongo-patch-generator.js';
3
+ export * from './services/mongo-collection-service.js';
4
+ export * from './services/mongo-entity-service.js';
5
+ export * from './services/mongo-nested-service.js';
6
+ export * from './services/mongo-service.js';
7
+ export * from './services/mongo-singleton-service.js';
@@ -1,6 +1,6 @@
1
1
  import mongodb, { type UpdateFilter } from 'mongodb';
2
2
  import type { PartialDTO, PatchDTO, RequiredSome, Type } from 'ts-gems';
3
- import { MongoAdapter } from './mongo-adapter.js';
3
+ import { MongoAdapter } from '../adapter/mongo-adapter.js';
4
4
  import { MongoEntityService } from './mongo-entity-service.js';
5
5
  /**
6
6
  *
@@ -1,4 +1,4 @@
1
- import mongodb from 'mongodb';
1
+ import mongodb, { type UpdateFilter } from 'mongodb';
2
2
  import type { PartialDTO, PatchDTO, StrictOmit, Type } from 'ts-gems';
3
3
  import { MongoService } from './mongo-service.js';
4
4
  /**
@@ -168,12 +168,6 @@ export declare class MongoEntityService<T extends mongodb.Document> extends Mong
168
168
  * @param {MongoEntityService.UpdateOneCommand<T>} command
169
169
  */
170
170
  protected _update(command: MongoEntityService.UpdateOneCommand<T>): Promise<PartialDTO<T> | undefined>;
171
- /**
172
- * Replaces a document with the given id in the collection
173
- *
174
- * @param {MongoEntityService.ReplaceCommand<T>} command
175
- */
176
- protected _replace(command: MongoEntityService.ReplaceCommand<T>): Promise<PartialDTO<T> | undefined>;
177
171
  /**
178
172
  * Updates a document in the collection with the specified ID.
179
173
  *
@@ -186,6 +180,14 @@ export declare class MongoEntityService<T extends mongodb.Document> extends Mong
186
180
  * @param {MongoEntityService.UpdateManyCommand<T>} command
187
181
  */
188
182
  protected _updateMany(command: MongoEntityService.UpdateManyCommand<T>): Promise<number>;
183
+ /**
184
+ * Replaces a document with the given id in the collection
185
+ *
186
+ * @param {MongoEntityService.ReplaceCommand<T>} command
187
+ */
188
+ protected _replace(command: MongoEntityService.ReplaceCommand<T>): Promise<PartialDTO<T> | undefined>;
189
+ protected _prepareUpdate(command: MongoEntityService.UpdateOneCommand<T> | MongoEntityService.UpdateManyCommand<T>): UpdateFilter<T>;
190
+ protected _generatePatch(command: MongoEntityService.UpdateOneCommand<T> | MongoEntityService.UpdateManyCommand<T>, doc: any): mongodb.UpdateFilter<T>;
189
191
  protected _executeCommand(command: MongoEntityService.CommandInfo, commandFn: () => any): Promise<any>;
190
192
  protected _beforeCreate(command: MongoEntityService.CreateCommand<T>): Promise<void>;
191
193
  protected _beforeDelete(command: MongoEntityService.DeleteCommand<T>): Promise<void>;
@@ -1,8 +1,8 @@
1
1
  import { ComplexType } from '@opra/common';
2
2
  import mongodb from 'mongodb';
3
3
  import type { DTO, PartialDTO, PatchDTO, RequiredSome, StrictOmit, Type } from 'ts-gems';
4
- import { MongoAdapter } from './mongo-adapter.js';
5
- import type { MongoEntityService } from './mongo-entity-service';
4
+ import { MongoAdapter } from '../adapter/mongo-adapter.js';
5
+ import type { MongoEntityService } from './mongo-entity-service.js';
6
6
  import { MongoService } from './mongo-service.js';
7
7
  /**
8
8
  *
@@ -3,7 +3,7 @@ import { ExecutionContext, ServiceBase } from '@opra/core';
3
3
  import mongodb, { ClientSession, type Document, type TransactionOptions } from 'mongodb';
4
4
  import type { Nullish, StrictOmit, Type } from 'ts-gems';
5
5
  import type { IsObject } from 'valgen';
6
- import { MongoAdapter } from './mongo-adapter.js';
6
+ import { MongoAdapter } from '../adapter/mongo-adapter.js';
7
7
  /**
8
8
  * The namespace for the MongoService.
9
9
  *
@@ -1,6 +1,6 @@
1
1
  import mongodb, { type UpdateFilter } from 'mongodb';
2
2
  import type { PartialDTO, PatchDTO, RequiredSome, Type } from 'ts-gems';
3
- import { MongoAdapter } from './mongo-adapter.js';
3
+ import { MongoAdapter } from '../adapter/mongo-adapter.js';
4
4
  import { MongoEntityService } from './mongo-entity-service.js';
5
5
  /**
6
6
  *
@@ -1,37 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = preparePatch;
4
- const objects_1 = require("@jsopen/objects");
5
- function preparePatch(doc, options) {
6
- const trg = {};
7
- _preparePatch(doc, trg, '', options);
8
- trg.$set = trg.$set || {};
9
- return trg;
10
- }
11
- function _preparePatch(src, trg, path, options) {
12
- let f;
13
- let key;
14
- let field;
15
- trg = trg || {};
16
- const fieldPrefix = options?.fieldPrefix;
17
- for (const [k, v] of Object.entries(src)) {
18
- f = k.startsWith('*') ? k.substring(1) : k;
19
- key = path ? path + '.' + f : f;
20
- field = (fieldPrefix ? fieldPrefix : '') + key;
21
- if (v == null) {
22
- trg.$unset = trg.$unset || {};
23
- trg.$unset[field] = '';
24
- continue;
25
- }
26
- if (v && typeof v === 'object' && !(0, objects_1.isBuiltIn)(v)) {
27
- // If field name starts with "*", do "replace" operation except "merge"
28
- if (!k.startsWith('*')) {
29
- _preparePatch(v, trg, key);
30
- continue;
31
- }
32
- }
33
- trg.$set = trg.$set || {};
34
- trg.$set[field] = v;
35
- }
36
- return trg;
37
- }
@@ -1,34 +0,0 @@
1
- import { isBuiltIn } from '@jsopen/objects';
2
- export default function preparePatch(doc, options) {
3
- const trg = {};
4
- _preparePatch(doc, trg, '', options);
5
- trg.$set = trg.$set || {};
6
- return trg;
7
- }
8
- function _preparePatch(src, trg, path, options) {
9
- let f;
10
- let key;
11
- let field;
12
- trg = trg || {};
13
- const fieldPrefix = options?.fieldPrefix;
14
- for (const [k, v] of Object.entries(src)) {
15
- f = k.startsWith('*') ? k.substring(1) : k;
16
- key = path ? path + '.' + f : f;
17
- field = (fieldPrefix ? fieldPrefix : '') + key;
18
- if (v == null) {
19
- trg.$unset = trg.$unset || {};
20
- trg.$unset[field] = '';
21
- continue;
22
- }
23
- if (v && typeof v === 'object' && !isBuiltIn(v)) {
24
- // If field name starts with "*", do "replace" operation except "merge"
25
- if (!k.startsWith('*')) {
26
- _preparePatch(v, trg, key);
27
- continue;
28
- }
29
- }
30
- trg.$set = trg.$set || {};
31
- trg.$set[field] = v;
32
- }
33
- return trg;
34
- }
@@ -1,3 +0,0 @@
1
- export default function preparePatch(doc: any, options?: {
2
- fieldPrefix?: string;
3
- }): any;
File without changes
File without changes