electrodb 2.1.2 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,16 +1,18 @@
1
1
  {
2
2
  "name": "electrodb",
3
- "version": "2.1.2",
3
+ "version": "2.2.0",
4
4
  "description": "A library to more easily create and interact with multiple entities and heretical relationships in dynamodb",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "npm run test:types && npm run test:unit",
8
- "test:unit": "LOCAL_DYNAMO_ENDPOINT=http://localhost:8000 node ./test/init.js && LOCAL_DYNAMO_ENDPOINT=http://localhost:8000 mocha -r ts-node/register ./test/**.spec.*",
7
+ "test": "npm run test:types && npm run test:init && npm run test:unit",
8
+ "test:init": "LOCAL_DYNAMO_ENDPOINT=http://localhost:8000 node ./test/init.js",
9
+ "test:unit": "LOCAL_DYNAMO_ENDPOINT=http://localhost:8000 mocha -r ts-node/register ./test/**.spec.*",
9
10
  "test:types": "tsd",
10
- "coverage": "nyc npm test && nyc report --reporter=text-lcov | coveralls",
11
- "coverage-coveralls-local": "nyc npm test && nyc report --reporter=text-lcov | coveralls",
12
- "coverage-html-local": "nyc npm test && nyc report --reporter=html",
13
- "build:browser": "browserify playground/browser.js -o playground/bundle.js"
11
+ "coverage": "npm run test:init && nyc npm run test:unit && nyc report --reporter=text-lcov | coveralls",
12
+ "coverage:local:coveralls": "npm run test:init && nyc npm run test:unit && nyc report --reporter=text-lcov | coveralls",
13
+ "coverage:local:html": "npm run test:init && nyc npm run test:unit && nyc report --reporter=html",
14
+ "build:browser": "browserify playground/browser.js -o playground/bundle.js",
15
+ "build": "sh buildbrowser.sh"
14
16
  },
15
17
  "repository": {
16
18
  "type": "git",
package/src/clauses.js CHANGED
@@ -1,4 +1,4 @@
1
- const { QueryTypes, MethodTypes, ItemOperations, ExpressionTypes, TableIndex, TerminalOperation, KeyTypes } = require("./types");
1
+ const { QueryTypes, MethodTypes, ItemOperations, ExpressionTypes, TableIndex, TerminalOperation, KeyTypes, IndexTypes } = require("./types");
2
2
  const {AttributeOperationProxy, UpdateOperations, FilterOperationNames} = require("./operations");
3
3
  const {UpdateExpression} = require("./update");
4
4
  const {FilterExpression} = require("./where");
@@ -28,7 +28,36 @@ function batchAction(action, type, entity, state, payload) {
28
28
  let clauses = {
29
29
  index: {
30
30
  name: "index",
31
- children: ["get", "delete", "update", "query", "put", "scan", "collection", "create", "remove", "patch", "batchPut", "batchDelete", "batchGet"],
31
+ children: ["get", "delete", "update", "query", "put", "scan", "collection", "clusteredCollection", "create", "remove", "patch", "batchPut", "batchDelete", "batchGet"],
32
+ },
33
+ clusteredCollection: {
34
+ name: "clusteredCollection",
35
+ action(entity, state, collection = "", facets /* istanbul ignore next */ = {}) {
36
+ if (state.getError() !== null) {
37
+ return state;
38
+ }
39
+ try {
40
+ const {pk, sk} = state.getCompositeAttributes();
41
+ return state
42
+ .setType(QueryTypes.clustered_collection)
43
+ .setMethod(MethodTypes.query)
44
+ .setCollection(collection)
45
+ .setPK(entity._expectFacets(facets, pk))
46
+ .ifSK(() => {
47
+ const {composites, unused} = state.identifyCompositeAttributes(facets, sk, pk);
48
+ state.setSK(composites);
49
+ // we must apply eq on filter on all provided because if the user then does a sort key operation, it'd actually then unexpect results
50
+ if (sk.length > 1) {
51
+ state.filterProperties(FilterOperationNames.eq, {...unused, ...composites});
52
+ }
53
+ });
54
+
55
+ } catch(err) {
56
+ state.setError(err);
57
+ return state;
58
+ }
59
+ },
60
+ children: ["between", "gte", "gt", "lte", "lt", "begins", "params", "go"],
32
61
  },
33
62
  collection: {
34
63
  name: "collection",
@@ -38,13 +67,12 @@ let clauses = {
38
67
  return state;
39
68
  }
40
69
  try {
41
- const {pk} = state.getCompositeAttributes();
70
+ const {pk, sk} = state.getCompositeAttributes();
42
71
  return state
43
72
  .setType(QueryTypes.collection)
44
73
  .setMethod(MethodTypes.query)
45
74
  .setCollection(collection)
46
75
  .setPK(entity._expectFacets(facets, pk));
47
-
48
76
  } catch(err) {
49
77
  state.setError(err);
50
78
  return state;
@@ -75,14 +103,15 @@ let clauses = {
75
103
  return state;
76
104
  }
77
105
  try {
78
- const attributes = state.getCompositeAttributes();
106
+ const {pk, sk} = state.getCompositeAttributes();
107
+ const {composites} = state.identifyCompositeAttributes(facets, sk, pk);
79
108
  return state
80
109
  .setMethod(MethodTypes.get)
81
110
  .setType(QueryTypes.eq)
82
- .setPK(entity._expectFacets(facets, attributes.pk))
111
+ .setPK(entity._expectFacets(facets, pk))
83
112
  .ifSK(() => {
84
- entity._expectFacets(facets, attributes.sk);
85
- state.setSK(entity._buildQueryFacets(facets, attributes.sk));
113
+ entity._expectFacets(facets, sk);
114
+ state.setSK(composites);
86
115
  });
87
116
  } catch(err) {
88
117
  state.setError(err);
@@ -109,14 +138,14 @@ let clauses = {
109
138
  return state;
110
139
  }
111
140
  try {
112
- const attributes = state.getCompositeAttributes();
141
+ const {pk, sk} = state.getCompositeAttributes();
113
142
  return state
114
143
  .setMethod(MethodTypes.delete)
115
144
  .setType(QueryTypes.eq)
116
- .setPK(entity._expectFacets(facets, attributes.pk))
145
+ .setPK(entity._expectFacets(facets, pk))
117
146
  .ifSK(() => {
118
- entity._expectFacets(facets, attributes.sk);
119
- state.setSK(entity._buildQueryFacets(facets, attributes.sk));
147
+ entity._expectFacets(facets, sk);
148
+ state.setSK(state.buildQueryComposites(facets, sk));
120
149
  });
121
150
  } catch(err) {
122
151
  state.setError(err);
@@ -146,7 +175,7 @@ let clauses = {
146
175
  .setPK(entity._expectFacets(facets, attributes.pk))
147
176
  .ifSK(() => {
148
177
  entity._expectFacets(facets, attributes.sk);
149
- state.setSK(entity._buildQueryFacets(facets, attributes.sk));
178
+ state.setSK(state.buildQueryComposites(facets, attributes.sk));
150
179
  });
151
180
  } catch(err) {
152
181
  state.setError(err);
@@ -172,7 +201,7 @@ let clauses = {
172
201
  .setPK(entity._expectFacets(record, attributes.pk))
173
202
  .ifSK(() => {
174
203
  entity._expectFacets(record, attributes.sk);
175
- state.setSK(entity._buildQueryFacets(record, attributes.sk));
204
+ state.setSK(state.buildQueryComposites(record, attributes.sk));
176
205
  });
177
206
  } catch(err) {
178
207
  state.setError(err);
@@ -208,7 +237,7 @@ let clauses = {
208
237
  .setPK(entity._expectFacets(record, attributes.pk))
209
238
  .ifSK(() => {
210
239
  entity._expectFacets(record, attributes.sk);
211
- state.setSK(entity._buildQueryFacets(record, attributes.sk));
240
+ state.setSK(state.buildQueryComposites(record, attributes.sk));
212
241
  });
213
242
  } catch(err) {
214
243
  state.setError(err);
@@ -237,7 +266,7 @@ let clauses = {
237
266
  .setPK(entity._expectFacets(facets, attributes.pk))
238
267
  .ifSK(() => {
239
268
  entity._expectFacets(facets, attributes.sk);
240
- state.setSK(entity._buildQueryFacets(facets, attributes.sk));
269
+ state.setSK(state.buildQueryComposites(facets, attributes.sk));
241
270
  });
242
271
  } catch(err) {
243
272
  state.setError(err);
@@ -260,7 +289,7 @@ let clauses = {
260
289
  .setPK(entity._expectFacets(facets, attributes.pk))
261
290
  .ifSK(() => {
262
291
  entity._expectFacets(facets, attributes.sk);
263
- state.setSK(entity._buildQueryFacets(facets, attributes.sk));
292
+ state.setSK(state.buildQueryComposites(facets, attributes.sk));
264
293
  });
265
294
  } catch(err) {
266
295
  state.setError(err);
@@ -403,13 +432,22 @@ let clauses = {
403
432
  }
404
433
  try {
405
434
  state.addOption('_isPagination', true);
406
- const attributes = state.getCompositeAttributes();
435
+ const {pk, sk} = state.getCompositeAttributes();
407
436
  return state
408
437
  .setMethod(MethodTypes.query)
409
438
  .setType(QueryTypes.is)
410
- .setPK(entity._expectFacets(facets, attributes.pk))
439
+ .setPK(entity._expectFacets(facets, pk))
411
440
  .ifSK(() => {
412
- state.setSK(entity._buildQueryFacets(facets, attributes.sk));
441
+ const {composites, unused} = state.identifyCompositeAttributes(facets, sk, pk);
442
+ state.setSK(state.buildQueryComposites(facets, sk));
443
+ // we must apply eq on filter on all provided because if the user then does a sort key operation, it'd actually then unexpect results
444
+ if (sk.length > 1) {
445
+ state.filterProperties(FilterOperationNames.eq, {...unused, ...composites});
446
+ }
447
+ if (state.query.options.indexType === IndexTypes.clustered && Object.keys(composites).length < sk.length) {
448
+ state.unsafeApplyFilter(FilterOperationNames.eq, entity.identifiers.entity, entity.getName())
449
+ .unsafeApplyFilter(FilterOperationNames.eq, entity.identifiers.version, entity.getVersion());
450
+ }
413
451
  });
414
452
  } catch(err) {
415
453
  state.setError(err);
@@ -425,12 +463,15 @@ let clauses = {
425
463
  return state;
426
464
  }
427
465
  try {
428
- const attributes = state.getCompositeAttributes();
466
+ const {pk, sk} = state.getCompositeAttributes();
467
+ const endingSk = state.identifyCompositeAttributes(endingFacets, sk, pk);
468
+ const startingSk = state.identifyCompositeAttributes(startingFacets, sk, pk);
429
469
  return state
430
470
  .setType(QueryTypes.and)
431
- .setSK(entity._buildQueryFacets(endingFacets, attributes.sk))
471
+ .setSK(endingSk.composites)
432
472
  .setType(QueryTypes.between)
433
- .setSK(entity._buildQueryFacets(startingFacets, attributes.sk))
473
+ .setSK(startingSk.composites)
474
+ .filterProperties(FilterOperationNames.lte, endingSk.composites);
434
475
  } catch(err) {
435
476
  state.setError(err);
436
477
  return state;
@@ -449,7 +490,7 @@ let clauses = {
449
490
  .setType(QueryTypes.begins)
450
491
  .ifSK(state => {
451
492
  const attributes = state.getCompositeAttributes();
452
- state.setSK(entity._buildQueryFacets(facets, attributes.sk))
493
+ state.setSK(state.buildQueryComposites(facets, attributes.sk));
453
494
  });
454
495
  } catch(err) {
455
496
  state.setError(err);
@@ -465,11 +506,16 @@ let clauses = {
465
506
  return state;
466
507
  }
467
508
  try {
509
+
468
510
  return state
469
511
  .setType(QueryTypes.gt)
470
512
  .ifSK(state => {
471
- const attributes = state.getCompositeAttributes();
472
- state.setSK(entity._buildQueryFacets(facets, attributes.sk))
513
+ const {pk, sk} = state.getCompositeAttributes();
514
+ const {composites} = state.identifyCompositeAttributes(facets, sk, pk);
515
+ state.setSK(composites);
516
+ state.filterProperties(FilterOperationNames.gt, {
517
+ ...composites,
518
+ });
473
519
  });
474
520
  } catch(err) {
475
521
  state.setError(err);
@@ -489,7 +535,7 @@ let clauses = {
489
535
  .setType(QueryTypes.gte)
490
536
  .ifSK(state => {
491
537
  const attributes = state.getCompositeAttributes();
492
- state.setSK(entity._buildQueryFacets(facets, attributes.sk))
538
+ state.setSK(state.buildQueryComposites(facets, attributes.sk));
493
539
  });
494
540
  } catch(err) {
495
541
  state.setError(err);
@@ -507,8 +553,9 @@ let clauses = {
507
553
  try {
508
554
  return state.setType(QueryTypes.lt)
509
555
  .ifSK(state => {
510
- const attributes = state.getCompositeAttributes();
511
- state.setSK(entity._buildQueryFacets(facets, attributes.sk))
556
+ const {pk, sk} = state.getCompositeAttributes();
557
+ const {composites} = state.identifyCompositeAttributes(facets, sk, pk);
558
+ state.setSK(composites);
512
559
  });
513
560
  } catch(err) {
514
561
  state.setError(err);
@@ -526,8 +573,12 @@ let clauses = {
526
573
  try {
527
574
  return state.setType(QueryTypes.lte)
528
575
  .ifSK(state => {
529
- const attributes = state.getCompositeAttributes();
530
- state.setSK(entity._buildQueryFacets(facets, attributes.sk))
576
+ const {pk, sk} = state.getCompositeAttributes();
577
+ const {composites} = state.identifyCompositeAttributes(facets, sk, pk);
578
+ state.setSK(composites);
579
+ state.filterProperties(FilterOperationNames.lte, {
580
+ ...composites,
581
+ });
531
582
  });
532
583
  } catch(err) {
533
584
  state.setError(err);
@@ -543,7 +594,7 @@ let clauses = {
543
594
  throw state.error;
544
595
  }
545
596
  try {
546
- if (!v.isStringHasLength(options.table) && !v.isStringHasLength(entity._getTableName())) {
597
+ if (!v.isStringHasLength(options.table) && !v.isStringHasLength(entity.getTableName())) {
547
598
  throw new e.ElectroError(e.ErrorCodes.MissingTable, `Table name not defined. Table names must be either defined on the model, instance configuration, or as a query option.`);
548
599
  }
549
600
  const method = state.getMethod();
@@ -693,6 +744,71 @@ class ChainState {
693
744
  return this.query.facets;
694
745
  }
695
746
 
747
+ buildQueryComposites(provided, definition) {
748
+ return definition
749
+ .map(name => [name, provided[name]])
750
+ .reduce(
751
+ (result, [name, value]) => {
752
+ if (value !== undefined) {
753
+ result[name] = value;
754
+ }
755
+ return result;
756
+ },
757
+ {},
758
+ );
759
+ }
760
+
761
+ identifyCompositeAttributes(provided, defined, skip) {
762
+ // todo: make sure attributes are valid
763
+ const composites = {};
764
+ const unused = {};
765
+ const definedSet = new Set(defined || []);
766
+ const skipSet = new Set(skip || []);
767
+ for (const key of Object.keys(provided)) {
768
+ const value = provided[key];
769
+ if (definedSet.has(key)) {
770
+ composites[key] = value;
771
+ } else if (skipSet.has(key)) {
772
+ continue;
773
+ } else {
774
+ unused[key] = value;
775
+ }
776
+ }
777
+
778
+ return {
779
+ composites,
780
+ unused,
781
+ }
782
+ }
783
+
784
+ applyFilter(operation, name, ...values) {
785
+ if (FilterOperationNames[operation] !== undefined & name !== undefined && values.length > 0) {
786
+ const attribute = this.attributes[name];
787
+ if (attribute !== undefined) {
788
+ this.unsafeApplyFilter(operation, attribute.field, ...values);
789
+ }
790
+ }
791
+ return this;
792
+ }
793
+
794
+ unsafeApplyFilter(operation, name, ...values) {
795
+ if (FilterOperationNames[operation] !== undefined & name !== undefined && values.length > 0) {
796
+ const filter = this.query.filter[ExpressionTypes.FilterExpression];
797
+ filter.unsafeSet(operation, name, ...values);
798
+ }
799
+ return this;
800
+ }
801
+
802
+ filterProperties(operation, obj = {}) {
803
+ for (const property in obj) {
804
+ const value = obj[property];
805
+ if (value !== undefined) {
806
+ this.applyFilter(operation, property, value);
807
+ }
808
+ }
809
+ return this;
810
+ }
811
+
696
812
  setSK(attributes, type = this.query.type) {
697
813
  if (this.hasSortKey) {
698
814
  this.query.keys.sk.push({
package/src/client.js CHANGED
@@ -1,6 +1,6 @@
1
+ const lib = require('@aws-sdk/lib-dynamodb')
1
2
  const { isFunction } = require('./validations');
2
3
  const { ElectroError, ErrorCodes } = require('./errors');
3
- const lib = require('@aws-sdk/lib-dynamodb');
4
4
 
5
5
  const DocumentClientVersions = {
6
6
  v2: 'v2',
@@ -144,4 +144,4 @@ module.exports = {
144
144
  DocumentClientVersions,
145
145
  supportedClientVersions,
146
146
  DocumentClientV3Wrapper,
147
- };
147
+ };