Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/execution option count #342

Merged
merged 6 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -507,4 +507,8 @@ All notable changes to this project will be documented in this file. Breaking ch
## [2.12.3] - 2023-12-26
### Fixed
- Collection queries returned an `undefined` cursor (currently typed as `string | null`) when using the `raw:true` execution option. Fixed to return `null` instead of `undefined`.
- Removed superfluous and unused files, `./library-data.json` and `test.csv`, accidentally published in version `2.12.2`.
- Removed superfluous and unused files, `./library-data.json` and `test.csv`, accidentally published in version `2.12.2`.

## [2.13.0] - 2023-12-28
### Added
- Adds new query execution option `count` which allows you to specify a specific item count to return from a query. This is useful for cases where you must return a specific/consistent number of items from a query, a deceptively difficult task with DynamoDB and Single Table Design.
2 changes: 2 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2529,6 +2529,7 @@ export interface QueryOptions {
params?: object;
table?: string;
limit?: number;
count?: number;
originalErr?: boolean;
ignoreOwnership?: boolean;
pages?: number | "all";
Expand Down Expand Up @@ -2655,6 +2656,7 @@ interface GoQueryTerminalOptions<Attributes> {
includeKeys?: boolean;
table?: string;
limit?: number;
count?: number;
params?: object;
originalErr?: boolean;
ignoreOwnership?: boolean;
Expand Down
13 changes: 8 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "electrodb",
"version": "2.12.3",
"version": "2.13.0",
"description": "A library to more easily create and interact with multiple entities and heretical relationships in dynamodb",
"main": "index.js",
"scripts": {
Expand All @@ -10,14 +10,15 @@
"test:ci": "npm install && npm test",
"test:run": "npm run test:types && npm run test:init && npm run test:unit",
"test:init": "node ./test/init.js",
"test:init:hard": "node ./test/init.js --recreate",
"test:unit": "mocha -r ts-node/register ./test/**.spec.*",
"test:types": "tsd",
"test:format": "prettier -c src/**/*.js examples/**/*",
"coverage": "npm run test:init && nyc npm run test:unit && nyc report --reporter=text-lcov | coveralls",
"coverage:local:coveralls": "npm run test:init && nyc npm run test:unit && nyc report --reporter=text-lcov | coveralls",
"coverage:local:html": "npm run test:init && nyc npm run test:unit && nyc report --reporter=html",
"coverage": "npm run test:init:hard && nyc npm run test:unit && nyc report --reporter=text-lcov | coveralls",
"coverage:local:coveralls": "npm run test:init:hard && nyc npm run test:unit && nyc report --reporter=text-lcov | coveralls",
"coverage:local:html": "npm run test:init:hard && nyc npm run test:unit && nyc report --reporter=html",
"ddb:start": "docker compose up -d dynamodb",
"ddb:load": "docker compose exec electro npm run test:init",
"ddb:load": "docker compose exec electro npm run test:init:hard",
"ddb:stop": "docker compose stop",
"examples:load:library": "npm run ddb:start && npm run local:init && ts-node ./examples/library/load.ts",
"examples:query:library": "npm run ddb:start && npm run local:init && ts-node ./examples/library/query.ts",
Expand All @@ -28,7 +29,9 @@
"examples:provisiontable": "npm run ddb:start && npm run local:init && ts-node ./examples/provisionTable",
"examples:locks": "npm run ddb:start && npm run local:init && ts-node ./examples/locks",
"local:init": "LOCAL_DYNAMO_ENDPOINT='http://localhost:8000' npm run test:init",
"local:init:hard": "LOCAL_DYNAMO_ENDPOINT='http://localhost:8000' npm run test:init:hard",
"local:start": "npm run ddb:start && npm run local:init",
"local:fresh": "npm run ddb:start && npm run local:init:hard",
"local:stop": "npm run ddb:stop",
"local:exec": "LOCAL_DYNAMO_ENDPOINT='http://localhost:8000' ts-node ./test/debug.ts",
"local:debug": "npm run local:start && npm run local:exec",
Expand Down
45 changes: 41 additions & 4 deletions src/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,21 @@ class Entity {
});
}

_safeMinimum(...values) {
let eligibleNumbers = [];
for (let value of values) {
if (typeof value === 'number') {
eligibleNumbers.push(value);
}
}

if (eligibleNumbers.length) {
return Math.min(...eligibleNumbers);
}

return undefined;
}

async executeBulkGet(parameters, config) {
if (!Array.isArray(parameters)) {
parameters = [parameters];
Expand Down Expand Up @@ -703,10 +718,11 @@ class Entity {
}

async executeQuery(method, parameters, config = {}) {
const indexName = parameters.IndexName;
let results = config._isCollectionQuery ? {} : [];
let ExclusiveStartKey = this._formatExclusiveStartKey({
indexName,
config,
indexName: parameters.IndexName,
});
if (ExclusiveStartKey === null) {
ExclusiveStartKey = undefined;
Expand All @@ -724,7 +740,9 @@ class Entity {
{ ExclusiveStartKey, ...parameters, Limit: limit },
config,
);

ExclusiveStartKey = response.LastEvaluatedKey;

response = this.formatResponse(response, parameters.IndexName, {
...config,
includeKeys: shouldHydrate || config.includeKeys,
Expand Down Expand Up @@ -755,10 +773,15 @@ class Entity {
results[entity] = [...results[entity], ...items];
}
} else if (Array.isArray(response.data)) {
if (max) {
let prevCount = count
if (!!max || !!config.count) {
count += response.data.length;
}
let items = response.data;
const moreItemsThanRequired = !!config.count && count > config.count;
if (moreItemsThanRequired) {
items = items.slice(0, config.count - prevCount);
}
if (shouldHydrate) {
const hydrated = await this.hydrate(
parameters.IndexName,
Expand All @@ -771,14 +794,20 @@ class Entity {
);
}
results = [...results, ...items];
if (moreItemsThanRequired || count === config.count) {
const lastItem = results[results.length - 1];
ExclusiveStartKey = this._fromCompositeToKeysByIndex({ indexName, provided: lastItem });
break;
}
} else {
return response;
}
iterations++;
} while (
ExclusiveStartKey &&
(pages === AllPages || iterations < pages) &&
(max === undefined || count < max)
(pages === AllPages || (config.count !== undefined || iterations < pages)) &&
(max === undefined || count < max) &&
(config.count === undefined || count < config.count)
);

const cursor = this._formatReturnPager(config, ExclusiveStartKey);
Expand Down Expand Up @@ -1654,6 +1683,7 @@ class Entity {
_isPagination: false,
_isCollectionQuery: false,
pages: 1,
count: undefined,
listeners: [],
preserveBatchOrder: false,
attributes: [],
Expand Down Expand Up @@ -1777,6 +1807,13 @@ class Entity {
}
}

if (option.count !== undefined) {
if (typeof option.count !== "number" || option.count < 1) {
throw new e.ElectroError(e.ErrorCodes.InvalidOptions, `Query option 'count' must be of type 'number' and greater than zero.`);
}
config.count = option.count;
}

if (option.limit !== undefined) {
config.limit = option.limit;
config.params.Limit = option.limit;
Expand Down
Loading
Loading