electrodb 2.12.2 → 2.13.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/index.d.ts CHANGED
@@ -2529,6 +2529,7 @@ export interface QueryOptions {
2529
2529
  params?: object;
2530
2530
  table?: string;
2531
2531
  limit?: number;
2532
+ count?: number;
2532
2533
  originalErr?: boolean;
2533
2534
  ignoreOwnership?: boolean;
2534
2535
  pages?: number | "all";
@@ -2655,6 +2656,7 @@ interface GoQueryTerminalOptions<Attributes> {
2655
2656
  includeKeys?: boolean;
2656
2657
  table?: string;
2657
2658
  limit?: number;
2659
+ count?: number;
2658
2660
  params?: object;
2659
2661
  originalErr?: boolean;
2660
2662
  ignoreOwnership?: boolean;
package/output.json ADDED
@@ -0,0 +1,12 @@
1
+
2
+ > electrodb@2.12.3 test:unit /Users/tylerw.walch/Desktop/media/code/projects/electrodb
3
+ > mocha -r ts-node/register ./test/**.spec.* "--grep" "Paginate without overlapping values with count"
4
+
5
+
6
+
7
+ Query Pagination
8
+ ✓ Paginate without overlapping values with count (44ms)
9
+
10
+
11
+ 1 passing (6s)
12
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electrodb",
3
- "version": "2.12.2",
3
+ "version": "2.13.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": {
@@ -10,14 +10,15 @@
10
10
  "test:ci": "npm install && npm test",
11
11
  "test:run": "npm run test:types && npm run test:init && npm run test:unit",
12
12
  "test:init": "node ./test/init.js",
13
+ "test:init:hard": "node ./test/init.js --recreate",
13
14
  "test:unit": "mocha -r ts-node/register ./test/**.spec.*",
14
15
  "test:types": "tsd",
15
16
  "test:format": "prettier -c src/**/*.js examples/**/*",
16
- "coverage": "npm run test:init && nyc npm run test:unit && nyc report --reporter=text-lcov | coveralls",
17
- "coverage:local:coveralls": "npm run test:init && nyc npm run test:unit && nyc report --reporter=text-lcov | coveralls",
18
- "coverage:local:html": "npm run test:init && nyc npm run test:unit && nyc report --reporter=html",
17
+ "coverage": "npm run test:init:hard && nyc npm run test:unit && nyc report --reporter=text-lcov | coveralls",
18
+ "coverage:local:coveralls": "npm run test:init:hard && nyc npm run test:unit && nyc report --reporter=text-lcov | coveralls",
19
+ "coverage:local:html": "npm run test:init:hard && nyc npm run test:unit && nyc report --reporter=html",
19
20
  "ddb:start": "docker compose up -d dynamodb",
20
- "ddb:load": "docker compose exec electro npm run test:init",
21
+ "ddb:load": "docker compose exec electro npm run test:init:hard",
21
22
  "ddb:stop": "docker compose stop",
22
23
  "examples:load:library": "npm run ddb:start && npm run local:init && ts-node ./examples/library/load.ts",
23
24
  "examples:query:library": "npm run ddb:start && npm run local:init && ts-node ./examples/library/query.ts",
@@ -28,7 +29,9 @@
28
29
  "examples:provisiontable": "npm run ddb:start && npm run local:init && ts-node ./examples/provisionTable",
29
30
  "examples:locks": "npm run ddb:start && npm run local:init && ts-node ./examples/locks",
30
31
  "local:init": "LOCAL_DYNAMO_ENDPOINT='http://localhost:8000' npm run test:init",
32
+ "local:init:hard": "LOCAL_DYNAMO_ENDPOINT='http://localhost:8000' npm run test:init:hard",
31
33
  "local:start": "npm run ddb:start && npm run local:init",
34
+ "local:fresh": "npm run ddb:start && npm run local:init:hard",
32
35
  "local:stop": "npm run ddb:stop",
33
36
  "local:exec": "LOCAL_DYNAMO_ENDPOINT='http://localhost:8000' ts-node ./test/debug.ts",
34
37
  "local:debug": "npm run local:start && npm run local:exec",
package/src/entity.js CHANGED
@@ -343,6 +343,7 @@ class Entity {
343
343
  collection(collection = "", clauses = {}, facets = {}, options = {}) {
344
344
  const chainOptions = {
345
345
  ...options,
346
+ _isPagination: true,
346
347
  _isCollectionQuery: true,
347
348
  };
348
349
 
@@ -624,6 +625,21 @@ class Entity {
624
625
  });
625
626
  }
626
627
 
628
+ _safeMinimum(...values) {
629
+ let eligibleNumbers = [];
630
+ for (let value of values) {
631
+ if (typeof value === 'number') {
632
+ eligibleNumbers.push(value);
633
+ }
634
+ }
635
+
636
+ if (eligibleNumbers.length) {
637
+ return Math.min(...eligibleNumbers);
638
+ }
639
+
640
+ return undefined;
641
+ }
642
+
627
643
  async executeBulkGet(parameters, config) {
628
644
  if (!Array.isArray(parameters)) {
629
645
  parameters = [parameters];
@@ -702,10 +718,11 @@ class Entity {
702
718
  }
703
719
 
704
720
  async executeQuery(method, parameters, config = {}) {
721
+ const indexName = parameters.IndexName;
705
722
  let results = config._isCollectionQuery ? {} : [];
706
723
  let ExclusiveStartKey = this._formatExclusiveStartKey({
724
+ indexName,
707
725
  config,
708
- indexName: parameters.IndexName,
709
726
  });
710
727
  if (ExclusiveStartKey === null) {
711
728
  ExclusiveStartKey = undefined;
@@ -723,7 +740,9 @@ class Entity {
723
740
  { ExclusiveStartKey, ...parameters, Limit: limit },
724
741
  config,
725
742
  );
743
+
726
744
  ExclusiveStartKey = response.LastEvaluatedKey;
745
+
727
746
  response = this.formatResponse(response, parameters.IndexName, {
728
747
  ...config,
729
748
  includeKeys: shouldHydrate || config.includeKeys,
@@ -754,10 +773,15 @@ class Entity {
754
773
  results[entity] = [...results[entity], ...items];
755
774
  }
756
775
  } else if (Array.isArray(response.data)) {
757
- if (max) {
776
+ let prevCount = count
777
+ if (!!max || !!config.count) {
758
778
  count += response.data.length;
759
779
  }
760
780
  let items = response.data;
781
+ const moreItemsThanRequired = !!config.count && count > config.count;
782
+ if (moreItemsThanRequired) {
783
+ items = items.slice(0, config.count - prevCount);
784
+ }
761
785
  if (shouldHydrate) {
762
786
  const hydrated = await this.hydrate(
763
787
  parameters.IndexName,
@@ -770,14 +794,20 @@ class Entity {
770
794
  );
771
795
  }
772
796
  results = [...results, ...items];
797
+ if (moreItemsThanRequired || count === config.count) {
798
+ const lastItem = results[results.length - 1];
799
+ ExclusiveStartKey = this._fromCompositeToKeysByIndex({ indexName, provided: lastItem });
800
+ break;
801
+ }
773
802
  } else {
774
803
  return response;
775
804
  }
776
805
  iterations++;
777
806
  } while (
778
807
  ExclusiveStartKey &&
779
- (pages === AllPages || iterations < pages) &&
780
- (max === undefined || count < max)
808
+ (pages === AllPages || (config.count !== undefined || iterations < pages)) &&
809
+ (max === undefined || count < max) &&
810
+ (config.count === undefined || count < config.count)
781
811
  );
782
812
 
783
813
  const cursor = this._formatReturnPager(config, ExclusiveStartKey);
@@ -1653,6 +1683,7 @@ class Entity {
1653
1683
  _isPagination: false,
1654
1684
  _isCollectionQuery: false,
1655
1685
  pages: 1,
1686
+ count: undefined,
1656
1687
  listeners: [],
1657
1688
  preserveBatchOrder: false,
1658
1689
  attributes: [],
@@ -1776,6 +1807,13 @@ class Entity {
1776
1807
  }
1777
1808
  }
1778
1809
 
1810
+ if (option.count !== undefined) {
1811
+ if (typeof option.count !== "number" || option.count < 1) {
1812
+ throw new e.ElectroError(e.ErrorCodes.InvalidOptions, `Query option 'count' must be of type 'number' and greater than zero.`);
1813
+ }
1814
+ config.count = option.count;
1815
+ }
1816
+
1779
1817
  if (option.limit !== undefined) {
1780
1818
  config.limit = option.limit;
1781
1819
  config.params.Limit = option.limit;