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: seek and until #478

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -579,4 +579,10 @@ All notable changes to this project will be documented in this file. Breaking ch

## [3.4.1]
### Fixed
- [Issue #475](https://github.com/tywalch/electrodb/issues/475); Fixes issue where some users reported errors exporting entities and/or types when using the `CustomAttributeType` function. They would receive an error similar to `Exported variable '...' has or is using name 'OpaquePrimitiveSymbol' from external module "..." but cannot be named.`.
- [Issue #475](https://github.com/tywalch/electrodb/issues/475); Fixes issue where some users reported errors exporting entities and/or types when using the `CustomAttributeType` function. They would receive an error similar to `Exported variable '...' has or is using name 'OpaquePrimitiveSymbol' from external module "..." but cannot be named.`.

## [3.5.0]
### Added
- New `seek` execution option instructs ElectroDB to continue querying DynamoDB until at least one item is returned. This is useful when the circumstances of a table, model, and/or query request results in an empty return.
- New `until` execution option instructs ElectroDB to continue querying DynamoDB until at least the number of items specified is returned. Unlike `count`, this option will not guarantee the exact number of items returned, but will ensure at least the number specified is returned. This option has fewer post-processing steps than `count` and is more performant in cases where the exact number of items is not critical.
- Adds `params` parameter to `ElectroResultsEvent` event. This is useful for debugging, specifically correlating the request and response from DynamoDB.
44 changes: 28 additions & 16 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,27 @@ type TransactGetCommandInput = {
TransactItems: TransactGetItem[];
};

export type DocumentClientV2 = {
get: DocumentClientMethod;
put: DocumentClientMethod;
delete: DocumentClientMethod;
update: DocumentClientMethod;
batchWrite: DocumentClientMethod;
batchGet: DocumentClientMethod;
scan: DocumentClientMethod;
transactGet: DocumentClientMethod;
transactWrite: DocumentClientMethod;
query: DocumentClientMethod;
createSet: (...params: any[]) => any;
};

export type DocumentClientV3 = {
send: (command: any) => Promise<any>;
};

export type DocumentClient =
| {
get: DocumentClientMethod;
put: DocumentClientMethod;
delete: DocumentClientMethod;
update: DocumentClientMethod;
batchWrite: DocumentClientMethod;
batchGet: DocumentClientMethod;
scan: DocumentClientMethod;
transactGet: DocumentClientMethod;
transactWrite: DocumentClientMethod;
}
| {
send: (command: any) => Promise<any>;
};
| DocumentClientV2
| DocumentClientV3;

export type AllCollectionNames<
E extends { [name: string]: Entity<any, any, any, any> },
Expand Down Expand Up @@ -1028,11 +1034,12 @@ export interface ElectroQueryEvent<P extends any = any> {
params: P;
}

export interface ElectroResultsEvent<R extends any = any> {
export interface ElectroResultsEvent<R extends any = any, P extends any = any> {
type: "results";
method: ElectroDBMethodTypes;
config: any;
results: R;
params: P;
success: boolean;
}

Expand Down Expand Up @@ -2530,14 +2537,15 @@ export interface QueryOptions {
table?: string;
limit?: number;
count?: number;
seek?: boolean;
until?: number;
originalErr?: boolean;
ignoreOwnership?: boolean;
pages?: number | "all";
listeners?: Array<ElectroEventListener>;
logger?: ElectroEventListener;
data?: "raw" | "includeKeys" | "attributes";
order?: "asc" | "desc";

consistent?: boolean;
}

Expand Down Expand Up @@ -2638,6 +2646,8 @@ type ServiceQueryGoTerminalOptions = {
data?: "raw" | "includeKeys" | "attributes";
table?: string;
limit?: number;
until?: number;
seek?: boolean;
params?: object;
originalErr?: boolean;
ignoreOwnership?: boolean;
Expand All @@ -2655,6 +2665,8 @@ type GoQueryTerminalOptions<Attributes> = {
table?: string;
limit?: number;
count?: number;
until?: number;
seek?: boolean;
params?: object;
originalErr?: boolean;
ignoreOwnership?: boolean;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "electrodb",
"version": "3.4.1",
"version": "3.5.0",
"description": "A library to more easily create and interact with multiple entities and heretical relationships in dynamodb",
"main": "index.js",
"scripts": {
Expand Down
66 changes: 49 additions & 17 deletions src/entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,8 @@ class Entity {
}

upsert(attributes = {}) {
let index = TableIndex;
return this._makeChain(
index,
TableIndex,
this._clausesWithFilters,
clauses.index,
).upsert(attributes);
Expand All @@ -395,19 +394,17 @@ class Entity {
}

update(facets = {}) {
let index = TableIndex;
return this._makeChain(
index,
TableIndex,
this._clausesWithFilters,
clauses.index,
).update(facets);
}

patch(facets = {}) {
let index = TableIndex;
let options = {};
return this._makeChain(
index,
TableIndex,
this._clausesWithFilters,
clauses.index,
options,
Expand All @@ -426,21 +423,19 @@ class Entity {
}

async transactWrite(parameters, config) {
let response = await this._exec(
return this._exec(
MethodTypes.transactWrite,
parameters,
config,
);
return response;
}

async transactGet(parameters, config) {
let response = await this._exec(
return this._exec(
MethodTypes.transactGet,
parameters,
config,
);
return response;
}

async go(method, parameters = {}, config = {}) {
Expand Down Expand Up @@ -505,6 +500,7 @@ class Entity {
config,
success,
results,
params,
},
config.listeners,
);
Expand Down Expand Up @@ -689,6 +685,7 @@ class Entity {
let iterations = 0;
let count = 0;
let hydratedUnprocessed = [];
let morePaginationRequired = false;
const shouldHydrate = config.hydrate && method === MethodTypes.query;
do {
let response = await this._exec(
Expand Down Expand Up @@ -727,6 +724,9 @@ class Entity {
}
results[entity] = results[entity] || [];
results[entity] = [...results[entity], ...items];
if (config.count) {
count += items.length;
}
}
} else if (Array.isArray(response.data)) {
let prevCount = count;
Expand Down Expand Up @@ -761,14 +761,29 @@ class Entity {
} else {
return response;
}

iterations++;
} while (
ExclusiveStartKey &&
(pages === AllPages ||
config.count !== undefined ||
iterations < pages) &&
(config.count === undefined || count < config.count)
);

const countOptionRequiresMorePagination = (
config.count !== undefined && !config._isCollectionQuery && count < config.count
);

const pagesOptionRequiresMorePagination =
pages === AllPages || iterations < pages;

const untilOptionRequiresMorePagination =
config.until !== undefined && count < config.until;

const seekOptionRequiresMorePagination =
config.seek && count === 0;

morePaginationRequired =
untilOptionRequiresMorePagination ||
countOptionRequiresMorePagination ||
pagesOptionRequiresMorePagination ||
seekOptionRequiresMorePagination;

} while (ExclusiveStartKey && morePaginationRequired);

const cursor = this._formatReturnPager(config, ExclusiveStartKey);

Expand Down Expand Up @@ -1641,6 +1656,8 @@ class Entity {
_isPagination: false,
_isCollectionQuery: false,
pages: 1,
seek: false,
until: 0,
count: undefined,
listeners: [],
preserveBatchOrder: false,
Expand Down Expand Up @@ -1671,6 +1688,21 @@ class Entity {
}
}

if (option.until !== undefined) {
if (isNaN(option.until)) {
throw new e.ElectroError(
e.ErrorCodes.InvalidOptions,
`Invalid value for query option "until" provided. Unable to parse integer value.`,
);
}

config.until = parseInt(option.until);
}

if (option.seek) {
config.seek = option.seek;
}

if (typeof option.compare === "string") {
const type = ComparisonTypes[option.compare.toLowerCase()];
if (type) {
Expand Down
Loading
Loading