betterddb 0.8.0 → 0.8.2
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/dist/src/betterddb.d.ts +137 -0
- package/dist/src/betterddb.d.ts.map +1 -0
- package/dist/src/betterddb.js +165 -0
- package/dist/src/betterddb.js.map +1 -0
- package/dist/src/builders/batch-get-builder.d.ts +16 -0
- package/dist/src/builders/batch-get-builder.d.ts.map +1 -0
- package/dist/src/builders/batch-get-builder.js +54 -0
- package/dist/src/builders/batch-get-builder.js.map +1 -0
- package/dist/src/builders/create-builder.d.ts +12 -0
- package/dist/src/builders/create-builder.d.ts.map +1 -0
- package/dist/src/builders/create-builder.js +84 -0
- package/dist/src/builders/create-builder.js.map +1 -0
- package/dist/src/builders/delete-builder.d.ts +18 -0
- package/dist/src/builders/delete-builder.d.ts.map +1 -0
- package/dist/src/builders/delete-builder.js +75 -0
- package/dist/src/builders/delete-builder.js.map +1 -0
- package/dist/src/builders/get-builder.d.ts +18 -0
- package/dist/src/builders/get-builder.d.ts.map +1 -0
- package/dist/src/builders/get-builder.js +76 -0
- package/dist/src/builders/get-builder.js.map +1 -0
- package/dist/src/builders/index.d.ts +8 -0
- package/dist/src/builders/index.d.ts.map +1 -0
- package/{src/builders/index.ts → dist/src/builders/index.js} +1 -0
- package/dist/src/builders/index.js.map +1 -0
- package/dist/src/builders/query-builder.d.ts +29 -0
- package/dist/src/builders/query-builder.d.ts.map +1 -0
- package/dist/src/builders/query-builder.js +171 -0
- package/dist/src/builders/query-builder.js.map +1 -0
- package/dist/src/builders/scan-builder.d.ts +21 -0
- package/dist/src/builders/scan-builder.d.ts.map +1 -0
- package/dist/src/builders/scan-builder.js +76 -0
- package/dist/src/builders/scan-builder.js.map +1 -0
- package/dist/src/builders/update-builder.d.ts +37 -0
- package/dist/src/builders/update-builder.d.ts.map +1 -0
- package/dist/src/builders/update-builder.js +301 -0
- package/dist/src/builders/update-builder.js.map +1 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -0
- package/{src/index.ts → dist/src/index.js} +1 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/operator.d.ts +3 -0
- package/dist/src/operator.d.ts.map +1 -0
- package/dist/src/operator.js +28 -0
- package/dist/src/operator.js.map +1 -0
- package/dist/src/types/index.d.ts +2 -0
- package/dist/src/types/index.d.ts.map +1 -0
- package/{src/types/index.ts → dist/src/types/index.js} +1 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/types/paginated-result.d.ts +6 -0
- package/dist/src/types/paginated-result.d.ts.map +1 -0
- package/dist/src/types/paginated-result.js +2 -0
- package/dist/src/types/paginated-result.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +9 -8
- package/.github/workflows/npm-publish.yml +0 -33
- package/.github/workflows/test.yml +0 -42
- package/CONTRIBUTING.md +0 -225
- package/LICENCSE +0 -21
- package/babel.config.cjs +0 -6
- package/docker-compose.yml +0 -16
- package/eslint.config.mjs +0 -29
- package/jest.config.cjs +0 -17
- package/prettier.config.js +0 -6
- package/src/betterddb.ts +0 -267
- package/src/builders/batch-get-builder.ts +0 -56
- package/src/builders/create-builder.ts +0 -97
- package/src/builders/delete-builder.ts +0 -87
- package/src/builders/get-builder.ts +0 -78
- package/src/builders/query-builder.ts +0 -242
- package/src/builders/scan-builder.ts +0 -98
- package/src/builders/update-builder.ts +0 -363
- package/src/operator.ts +0 -43
- package/src/types/paginated-result.ts +0 -6
- package/test/batch-get.test.ts +0 -122
- package/test/create.test.ts +0 -121
- package/test/delete.test.ts +0 -93
- package/test/get.test.ts +0 -98
- package/test/query.test.ts +0 -206
- package/test/scan.test.ts +0 -130
- package/test/update.test.ts +0 -355
- package/test/utils/table-setup.ts +0 -62
- package/tsconfig.json +0 -23
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type AttributeValue,
|
|
3
|
-
type Put,
|
|
4
|
-
type TransactWriteItem,
|
|
5
|
-
} from "@aws-sdk/client-dynamodb";
|
|
6
|
-
import { type BetterDDB } from "../betterddb.js";
|
|
7
|
-
import { PutCommand, TransactWriteCommand } from "@aws-sdk/lib-dynamodb";
|
|
8
|
-
|
|
9
|
-
export class CreateBuilder<T> {
|
|
10
|
-
private extraTransactItems: TransactWriteItem[] = [];
|
|
11
|
-
|
|
12
|
-
constructor(
|
|
13
|
-
private parent: BetterDDB<T>,
|
|
14
|
-
private item: T,
|
|
15
|
-
) {}
|
|
16
|
-
|
|
17
|
-
public async execute(): Promise<T> {
|
|
18
|
-
const validated = this.parent.getSchema().parse(this.item);
|
|
19
|
-
if (this.extraTransactItems.length > 0) {
|
|
20
|
-
// Build our update transaction item.
|
|
21
|
-
const myTransactItem = this.toTransactPut();
|
|
22
|
-
// Combine with extra transaction items.
|
|
23
|
-
const allItems = [...this.extraTransactItems, myTransactItem];
|
|
24
|
-
await this.parent.getClient().send(
|
|
25
|
-
new TransactWriteCommand({
|
|
26
|
-
TransactItems: allItems,
|
|
27
|
-
}),
|
|
28
|
-
);
|
|
29
|
-
// After transaction, retrieve the updated item.
|
|
30
|
-
const result = await this.parent.get(this.item).execute();
|
|
31
|
-
if (result === null) {
|
|
32
|
-
throw new Error("Item not found after transaction create");
|
|
33
|
-
}
|
|
34
|
-
return result;
|
|
35
|
-
} else {
|
|
36
|
-
let finalItem: T = {
|
|
37
|
-
...this.item,
|
|
38
|
-
entityType: this.parent.getEntityType(),
|
|
39
|
-
};
|
|
40
|
-
if (this.parent.getTimestamps()) {
|
|
41
|
-
const now = new Date().toISOString();
|
|
42
|
-
finalItem = { ...finalItem, createdAt: now, updatedAt: now } as T;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Compute and merge primary key.
|
|
46
|
-
const computedKeys = this.parent.buildKey(validated as Partial<T>);
|
|
47
|
-
finalItem = { ...finalItem, ...computedKeys };
|
|
48
|
-
|
|
49
|
-
// Compute and merge index attributes.
|
|
50
|
-
const indexAttributes = this.parent.buildIndexes(validated as Partial<T>);
|
|
51
|
-
finalItem = { ...finalItem, ...indexAttributes };
|
|
52
|
-
|
|
53
|
-
await this.parent.getClient().send(
|
|
54
|
-
new PutCommand({
|
|
55
|
-
TableName: this.parent.getTableName(),
|
|
56
|
-
Item: finalItem as Record<string, AttributeValue>,
|
|
57
|
-
}),
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
return validated as T;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
public transactWrite(ops: TransactWriteItem[] | TransactWriteItem): this {
|
|
65
|
-
if (Array.isArray(ops)) {
|
|
66
|
-
this.extraTransactItems.push(...ops);
|
|
67
|
-
} else {
|
|
68
|
-
this.extraTransactItems.push(ops);
|
|
69
|
-
}
|
|
70
|
-
return this;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
public toTransactPut(): TransactWriteItem {
|
|
74
|
-
const validated = this.parent.getSchema().parse(this.item);
|
|
75
|
-
let finalItem: T = {
|
|
76
|
-
...this.item,
|
|
77
|
-
entityType: this.parent.getEntityType(),
|
|
78
|
-
};
|
|
79
|
-
if (this.parent.getTimestamps()) {
|
|
80
|
-
const now = new Date().toISOString();
|
|
81
|
-
finalItem = { ...finalItem, createdAt: now, updatedAt: now } as T;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Compute and merge primary key.
|
|
85
|
-
const computedKeys = this.parent.buildKey(validated as Partial<T>);
|
|
86
|
-
finalItem = { ...finalItem, ...computedKeys };
|
|
87
|
-
|
|
88
|
-
// Compute and merge index attributes.
|
|
89
|
-
const indexAttributes = this.parent.buildIndexes(validated as Partial<T>);
|
|
90
|
-
finalItem = { ...finalItem, ...indexAttributes };
|
|
91
|
-
const putItem: Put = {
|
|
92
|
-
TableName: this.parent.getTableName(),
|
|
93
|
-
Item: finalItem as Record<string, AttributeValue>,
|
|
94
|
-
};
|
|
95
|
-
return { Put: putItem };
|
|
96
|
-
}
|
|
97
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { type BetterDDB } from "../betterddb.js";
|
|
2
|
-
import {
|
|
3
|
-
type TransactWriteItem,
|
|
4
|
-
type DeleteItemInput,
|
|
5
|
-
} from "@aws-sdk/client-dynamodb";
|
|
6
|
-
import {
|
|
7
|
-
type NativeAttributeValue,
|
|
8
|
-
TransactWriteCommand,
|
|
9
|
-
DeleteCommand,
|
|
10
|
-
} from "@aws-sdk/lib-dynamodb";
|
|
11
|
-
export class DeleteBuilder<T> {
|
|
12
|
-
private condition?: {
|
|
13
|
-
expression: string;
|
|
14
|
-
attributeValues: Record<string, NativeAttributeValue>;
|
|
15
|
-
};
|
|
16
|
-
private extraTransactItems: TransactWriteItem[] = [];
|
|
17
|
-
constructor(
|
|
18
|
-
private parent: BetterDDB<T>,
|
|
19
|
-
private key: Partial<T>,
|
|
20
|
-
) {}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Specify a condition expression for the delete operation.
|
|
24
|
-
*/
|
|
25
|
-
public withCondition(
|
|
26
|
-
expression: string,
|
|
27
|
-
attributeValues: Record<string, NativeAttributeValue>,
|
|
28
|
-
): this {
|
|
29
|
-
if (this.condition) {
|
|
30
|
-
this.condition.expression += ` AND ${expression}`;
|
|
31
|
-
Object.assign(this.condition.attributeValues, attributeValues);
|
|
32
|
-
} else {
|
|
33
|
-
this.condition = { expression, attributeValues };
|
|
34
|
-
}
|
|
35
|
-
return this;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
public async execute(): Promise<void> {
|
|
39
|
-
if (this.extraTransactItems.length > 0) {
|
|
40
|
-
// Build our update transaction item.
|
|
41
|
-
const myTransactItem = this.toTransactDelete();
|
|
42
|
-
// Combine with extra transaction items.
|
|
43
|
-
const allItems = [...this.extraTransactItems, myTransactItem];
|
|
44
|
-
await this.parent.getClient().send(
|
|
45
|
-
new TransactWriteCommand({
|
|
46
|
-
TransactItems: allItems,
|
|
47
|
-
}),
|
|
48
|
-
);
|
|
49
|
-
// After transaction, retrieve the updated item.
|
|
50
|
-
const result = await this.parent.get(this.key).execute();
|
|
51
|
-
if (result === null) {
|
|
52
|
-
throw new Error("Item not found after transaction delete");
|
|
53
|
-
}
|
|
54
|
-
} else {
|
|
55
|
-
const params: DeleteItemInput = {
|
|
56
|
-
TableName: this.parent.getTableName(),
|
|
57
|
-
Key: this.parent.buildKey(this.key),
|
|
58
|
-
};
|
|
59
|
-
if (this.condition) {
|
|
60
|
-
params.ConditionExpression = this.condition.expression;
|
|
61
|
-
params.ExpressionAttributeValues = this.condition.attributeValues;
|
|
62
|
-
}
|
|
63
|
-
await this.parent.getClient().send(new DeleteCommand(params));
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
public transactWrite(ops: TransactWriteItem[] | TransactWriteItem): this {
|
|
68
|
-
if (Array.isArray(ops)) {
|
|
69
|
-
this.extraTransactItems.push(...ops);
|
|
70
|
-
} else {
|
|
71
|
-
this.extraTransactItems.push(ops);
|
|
72
|
-
}
|
|
73
|
-
return this;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
public toTransactDelete(): TransactWriteItem {
|
|
77
|
-
const deleteItem: DeleteItemInput = {
|
|
78
|
-
TableName: this.parent.getTableName(),
|
|
79
|
-
Key: this.parent.buildKey(this.key),
|
|
80
|
-
};
|
|
81
|
-
if (this.condition) {
|
|
82
|
-
deleteItem.ConditionExpression = this.condition.expression;
|
|
83
|
-
deleteItem.ExpressionAttributeValues = this.condition.attributeValues;
|
|
84
|
-
}
|
|
85
|
-
return { Delete: deleteItem };
|
|
86
|
-
}
|
|
87
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { type BetterDDB } from "../betterddb.js";
|
|
2
|
-
import { TransactGetCommand, GetCommand } from "@aws-sdk/lib-dynamodb";
|
|
3
|
-
import {
|
|
4
|
-
type GetItemInput,
|
|
5
|
-
type TransactGetItem,
|
|
6
|
-
} from "@aws-sdk/client-dynamodb";
|
|
7
|
-
export class GetBuilder<T> {
|
|
8
|
-
private projectionExpression?: string;
|
|
9
|
-
private expressionAttributeNames: Record<string, string> = {};
|
|
10
|
-
private extraTransactItems: TransactGetItem[] = [];
|
|
11
|
-
constructor(
|
|
12
|
-
private parent: BetterDDB<T>,
|
|
13
|
-
private key: Partial<T>,
|
|
14
|
-
) {}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Specify a projection by providing an array of attribute names.
|
|
18
|
-
*/
|
|
19
|
-
public withProjection(attributes: (keyof T)[]): this {
|
|
20
|
-
this.projectionExpression = attributes
|
|
21
|
-
.map((attr) => `#${String(attr)}`)
|
|
22
|
-
.join(", ");
|
|
23
|
-
for (const attr of attributes) {
|
|
24
|
-
this.expressionAttributeNames[`#${String(attr)}`] = String(attr);
|
|
25
|
-
}
|
|
26
|
-
return this;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
public async execute(): Promise<T | null> {
|
|
30
|
-
if (this.extraTransactItems.length > 0) {
|
|
31
|
-
// Build our update transaction item.
|
|
32
|
-
const myTransactItem = this.toTransactGet();
|
|
33
|
-
// Combine with extra transaction items.
|
|
34
|
-
const allItems = [...this.extraTransactItems, myTransactItem];
|
|
35
|
-
await this.parent.getClient().send(
|
|
36
|
-
new TransactGetCommand({
|
|
37
|
-
TransactItems: allItems,
|
|
38
|
-
}),
|
|
39
|
-
);
|
|
40
|
-
// After transaction, retrieve the updated item.
|
|
41
|
-
const result = await this.parent.get(this.key).execute();
|
|
42
|
-
return result;
|
|
43
|
-
} else {
|
|
44
|
-
const params: GetItemInput = {
|
|
45
|
-
TableName: this.parent.getTableName(),
|
|
46
|
-
Key: this.parent.buildKey(this.key),
|
|
47
|
-
};
|
|
48
|
-
if (this.projectionExpression) {
|
|
49
|
-
params.ProjectionExpression = this.projectionExpression;
|
|
50
|
-
params.ExpressionAttributeNames = this.expressionAttributeNames;
|
|
51
|
-
}
|
|
52
|
-
const result = await this.parent.getClient().send(new GetCommand(params));
|
|
53
|
-
if (!result.Item) return null;
|
|
54
|
-
return this.parent.getSchema().parse(result.Item) as T;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
public transactGet(ops: TransactGetItem[] | TransactGetItem): this {
|
|
59
|
-
if (Array.isArray(ops)) {
|
|
60
|
-
this.extraTransactItems.push(...ops);
|
|
61
|
-
} else {
|
|
62
|
-
this.extraTransactItems.push(ops);
|
|
63
|
-
}
|
|
64
|
-
return this;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
public toTransactGet(): TransactGetItem {
|
|
68
|
-
const getItem: GetItemInput = {
|
|
69
|
-
TableName: this.parent.getTableName(),
|
|
70
|
-
Key: this.parent.buildKey(this.key),
|
|
71
|
-
};
|
|
72
|
-
if (this.projectionExpression) {
|
|
73
|
-
getItem.ProjectionExpression = this.projectionExpression;
|
|
74
|
-
getItem.ExpressionAttributeNames = this.expressionAttributeNames;
|
|
75
|
-
}
|
|
76
|
-
return { Get: getItem };
|
|
77
|
-
}
|
|
78
|
-
}
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type NativeAttributeValue,
|
|
3
|
-
QueryCommand,
|
|
4
|
-
type QueryCommandInput,
|
|
5
|
-
} from "@aws-sdk/lib-dynamodb";
|
|
6
|
-
import { type BetterDDB, type GSIConfig } from "../betterddb.js";
|
|
7
|
-
import { getOperatorExpression, type Operator } from "../operator.js";
|
|
8
|
-
import { type PaginatedResult } from "../types/paginated-result.js";
|
|
9
|
-
|
|
10
|
-
export class QueryBuilder<T> {
|
|
11
|
-
private keyConditions: string[] = [];
|
|
12
|
-
private filterConditions: string[] = [];
|
|
13
|
-
private expressionAttributeNames: Record<string, string> = {};
|
|
14
|
-
private expressionAttributeValues: Record<string, NativeAttributeValue>;
|
|
15
|
-
private index?: GSIConfig<T>;
|
|
16
|
-
private limit?: number;
|
|
17
|
-
private lastKey?: Record<string, NativeAttributeValue>;
|
|
18
|
-
private ascending = true;
|
|
19
|
-
|
|
20
|
-
constructor(
|
|
21
|
-
private parent: BetterDDB<T>,
|
|
22
|
-
private key: Partial<T>,
|
|
23
|
-
) {
|
|
24
|
-
const keys = this.parent.getKeys();
|
|
25
|
-
const pkName = keys.primary.name;
|
|
26
|
-
const builtKey = this.parent.buildKey(this.key);
|
|
27
|
-
|
|
28
|
-
this.expressionAttributeNames = {
|
|
29
|
-
"#pk": pkName,
|
|
30
|
-
};
|
|
31
|
-
this.expressionAttributeValues = {
|
|
32
|
-
":pk_value": builtKey[pkName] as Record<string, NativeAttributeValue>,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
public usingIndex(indexName: string): this {
|
|
37
|
-
if (!this.parent.getKeys().gsis) {
|
|
38
|
-
throw new Error("No global secondary indexes defined for this table");
|
|
39
|
-
}
|
|
40
|
-
if (!(indexName in this.parent.getKeys().gsis!)) {
|
|
41
|
-
throw new Error("index does not exist");
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
this.index = this.parent.getKeys().gsis![indexName];
|
|
45
|
-
|
|
46
|
-
if (!this.index) {
|
|
47
|
-
throw new Error("Failed to get index configuration");
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const pkName = this.index.primary.name;
|
|
51
|
-
const builtKey = this.parent.buildIndexes(this.key);
|
|
52
|
-
this.expressionAttributeNames["#pk"] = pkName;
|
|
53
|
-
this.expressionAttributeValues[":pk_value"] = builtKey[pkName] as Record<
|
|
54
|
-
string,
|
|
55
|
-
NativeAttributeValue
|
|
56
|
-
>;
|
|
57
|
-
|
|
58
|
-
return this;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
public sortAscending(): this {
|
|
62
|
-
this.ascending = true;
|
|
63
|
-
return this;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public sortDescending(): this {
|
|
67
|
-
this.ascending = false;
|
|
68
|
-
return this;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
public where(
|
|
72
|
-
operator: Operator,
|
|
73
|
-
values: Partial<T> | [Partial<T>, Partial<T>],
|
|
74
|
-
): this {
|
|
75
|
-
const keys = this.parent.getKeys();
|
|
76
|
-
// Determine the sort key name from either the index or the primary keys.
|
|
77
|
-
const sortKeyName = this.index ? this.index.sort?.name : keys.sort?.name;
|
|
78
|
-
if (!sortKeyName) {
|
|
79
|
-
throw new Error("Sort key is not defined for this table/index.");
|
|
80
|
-
}
|
|
81
|
-
const nameKey = "#sk";
|
|
82
|
-
this.expressionAttributeNames[nameKey] = sortKeyName;
|
|
83
|
-
|
|
84
|
-
// Enforce that a complex sort key requires an object input.
|
|
85
|
-
if (typeof values !== "object" || values === null) {
|
|
86
|
-
throw new Error(
|
|
87
|
-
`For complex sort keys, please provide an object with all necessary properties.`,
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (operator === "between") {
|
|
92
|
-
if (!Array.isArray(values) || values.length !== 2) {
|
|
93
|
-
throw new Error(
|
|
94
|
-
`For 'between' operator, values must be a tuple of two objects`,
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
const valueKeyStart = ":sk_start";
|
|
98
|
-
const valueKeyEnd = ":sk_end";
|
|
99
|
-
// Use the key definition's build function to build the key from the full object.
|
|
100
|
-
this.expressionAttributeValues[valueKeyStart] = this.index
|
|
101
|
-
? (this.parent.buildIndexes(values[0])[sortKeyName] as Record<
|
|
102
|
-
string,
|
|
103
|
-
NativeAttributeValue
|
|
104
|
-
>)
|
|
105
|
-
: (this.parent.buildKey(values[0])[sortKeyName] as Record<
|
|
106
|
-
string,
|
|
107
|
-
NativeAttributeValue
|
|
108
|
-
>);
|
|
109
|
-
this.expressionAttributeValues[valueKeyEnd] = this.index
|
|
110
|
-
? (this.parent.buildIndexes(values[1])[sortKeyName] as Record<
|
|
111
|
-
string,
|
|
112
|
-
NativeAttributeValue
|
|
113
|
-
>)
|
|
114
|
-
: (this.parent.buildKey(values[1])[sortKeyName] as Record<
|
|
115
|
-
string,
|
|
116
|
-
NativeAttributeValue
|
|
117
|
-
>);
|
|
118
|
-
this.keyConditions.push(
|
|
119
|
-
`${nameKey} BETWEEN ${valueKeyStart} AND ${valueKeyEnd}`,
|
|
120
|
-
);
|
|
121
|
-
} else if (operator === "begins_with") {
|
|
122
|
-
const valueKey = ":sk_value";
|
|
123
|
-
this.expressionAttributeValues[valueKey] = this.index
|
|
124
|
-
? (this.parent.buildIndexes(values as Partial<T>)[
|
|
125
|
-
sortKeyName
|
|
126
|
-
] as Record<string, NativeAttributeValue>)
|
|
127
|
-
: (this.parent.buildKey(values as Partial<T>)[sortKeyName] as Record<
|
|
128
|
-
string,
|
|
129
|
-
NativeAttributeValue
|
|
130
|
-
>);
|
|
131
|
-
this.keyConditions.push(`begins_with(${nameKey}, ${valueKey})`);
|
|
132
|
-
} else {
|
|
133
|
-
// For eq, lt, lte, gt, gte:
|
|
134
|
-
const valueKey = ":sk_value";
|
|
135
|
-
this.expressionAttributeValues[valueKey] = this.index
|
|
136
|
-
? (this.parent.buildIndexes(values as Partial<T>)[
|
|
137
|
-
sortKeyName
|
|
138
|
-
] as Record<string, NativeAttributeValue>)
|
|
139
|
-
: (this.parent.buildKey(values as Partial<T>)[sortKeyName] as Record<
|
|
140
|
-
string,
|
|
141
|
-
NativeAttributeValue
|
|
142
|
-
>);
|
|
143
|
-
const condition = getOperatorExpression(operator, nameKey, valueKey);
|
|
144
|
-
this.keyConditions.push(condition);
|
|
145
|
-
}
|
|
146
|
-
return this;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
public filter(attribute: keyof T, operator: Operator, values: unknown): this {
|
|
150
|
-
const attrStr = String(attribute);
|
|
151
|
-
const randomString = Math.random().toString(36).substring(2, 15);
|
|
152
|
-
const placeholderName = `#attr_${attrStr}_${randomString}`;
|
|
153
|
-
this.expressionAttributeNames[placeholderName] = attrStr;
|
|
154
|
-
if (operator === "between") {
|
|
155
|
-
if (!Array.isArray(values) || values.length !== 2) {
|
|
156
|
-
throw new Error(
|
|
157
|
-
"For 'between' operator, values must be a tuple of two items",
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
const placeholderValueStart = `:val_start_${attrStr}_${randomString}`;
|
|
161
|
-
const placeholderValueEnd = `:val_end_${attrStr}_${randomString}`;
|
|
162
|
-
this.expressionAttributeValues[placeholderValueStart] =
|
|
163
|
-
values[0] as Record<string, NativeAttributeValue>;
|
|
164
|
-
this.expressionAttributeValues[placeholderValueEnd] = values[1] as Record<
|
|
165
|
-
string,
|
|
166
|
-
NativeAttributeValue
|
|
167
|
-
>;
|
|
168
|
-
this.filterConditions.push(
|
|
169
|
-
`${placeholderName} BETWEEN ${placeholderValueStart} AND ${placeholderValueEnd}`,
|
|
170
|
-
);
|
|
171
|
-
} else if (operator === "begins_with" || operator === "contains") {
|
|
172
|
-
const placeholderValue = `:val_${attrStr}_${randomString}`;
|
|
173
|
-
this.expressionAttributeValues[placeholderValue] = values as Record<
|
|
174
|
-
string,
|
|
175
|
-
NativeAttributeValue
|
|
176
|
-
>;
|
|
177
|
-
this.filterConditions.push(
|
|
178
|
-
`${operator}(${placeholderName}, ${placeholderValue})`,
|
|
179
|
-
);
|
|
180
|
-
} else {
|
|
181
|
-
const placeholderValue = `:val_${attrStr}_${randomString}`;
|
|
182
|
-
this.expressionAttributeValues[placeholderValue] = values as Record<
|
|
183
|
-
string,
|
|
184
|
-
NativeAttributeValue
|
|
185
|
-
>;
|
|
186
|
-
const condition = getOperatorExpression(
|
|
187
|
-
operator,
|
|
188
|
-
placeholderName,
|
|
189
|
-
placeholderValue,
|
|
190
|
-
);
|
|
191
|
-
this.filterConditions.push(condition);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return this;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
public limitResults(limit: number): this {
|
|
198
|
-
this.limit = limit;
|
|
199
|
-
return this;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
public startFrom(lastKey: Record<string, NativeAttributeValue>): this {
|
|
203
|
-
this.lastKey = lastKey;
|
|
204
|
-
return this;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Executes the query and returns a Promise that resolves with an array of items.
|
|
209
|
-
*/
|
|
210
|
-
public async execute(): Promise<PaginatedResult<T>> {
|
|
211
|
-
this.keyConditions.unshift(`#pk = :pk_value`);
|
|
212
|
-
const keyConditionExpression = this.keyConditions.join(" AND ");
|
|
213
|
-
|
|
214
|
-
const params: QueryCommandInput = {
|
|
215
|
-
TableName: this.parent.getTableName(),
|
|
216
|
-
KeyConditionExpression: keyConditionExpression,
|
|
217
|
-
ExpressionAttributeNames: this.expressionAttributeNames,
|
|
218
|
-
ExpressionAttributeValues: this.expressionAttributeValues,
|
|
219
|
-
ScanIndexForward: this.ascending,
|
|
220
|
-
Limit: this.limit,
|
|
221
|
-
ExclusiveStartKey: this.lastKey,
|
|
222
|
-
IndexName: this.index?.name ?? undefined,
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
if (this.parent.getEntityType()) {
|
|
226
|
-
this.filterConditions.push(`#entity = :entity_value`);
|
|
227
|
-
this.expressionAttributeNames["#entity"] = "entityType";
|
|
228
|
-
this.expressionAttributeValues[":entity_value"] =
|
|
229
|
-
this.parent.getEntityType();
|
|
230
|
-
}
|
|
231
|
-
params.FilterExpression =
|
|
232
|
-
this.filterConditions.length > 0
|
|
233
|
-
? this.filterConditions.join(" AND ")
|
|
234
|
-
: undefined;
|
|
235
|
-
|
|
236
|
-
const result = await this.parent.getClient().send(new QueryCommand(params));
|
|
237
|
-
return {
|
|
238
|
-
items: this.parent.getSchema().array().parse(result.Items) as T[],
|
|
239
|
-
lastKey: result.LastEvaluatedKey ?? undefined,
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
}
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ScanCommand,
|
|
3
|
-
type ScanCommandInput,
|
|
4
|
-
type NativeAttributeValue,
|
|
5
|
-
} from "@aws-sdk/lib-dynamodb";
|
|
6
|
-
import { type BetterDDB } from "../betterddb.js";
|
|
7
|
-
import { getOperatorExpression, type Operator } from "../operator.js";
|
|
8
|
-
import { type PaginatedResult } from "../types/paginated-result.js";
|
|
9
|
-
|
|
10
|
-
export class ScanBuilder<T> {
|
|
11
|
-
private filters: string[] = [];
|
|
12
|
-
private expressionAttributeNames: Record<string, string> = {};
|
|
13
|
-
private expressionAttributeValues: Record<string, NativeAttributeValue> = {};
|
|
14
|
-
private limit?: number;
|
|
15
|
-
private lastKey?: Record<string, NativeAttributeValue>;
|
|
16
|
-
|
|
17
|
-
constructor(private parent: BetterDDB<T>) {}
|
|
18
|
-
|
|
19
|
-
public where(attribute: keyof T, operator: Operator, values: unknown): this {
|
|
20
|
-
const attrStr = String(attribute);
|
|
21
|
-
const nameKey = `#attr_${attrStr}`;
|
|
22
|
-
this.expressionAttributeNames[nameKey] = attrStr;
|
|
23
|
-
if (operator === "between") {
|
|
24
|
-
if (!Array.isArray(values) || values.length !== 2) {
|
|
25
|
-
throw new Error(
|
|
26
|
-
`For 'between' operator, values must be a tuple of two items`,
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
const valueKeyStart = `:val_start_${attrStr}`;
|
|
30
|
-
const valueKeyEnd = `:val_end_${attrStr}`;
|
|
31
|
-
this.expressionAttributeValues[valueKeyStart] = values[0] as Record<
|
|
32
|
-
string,
|
|
33
|
-
NativeAttributeValue
|
|
34
|
-
>;
|
|
35
|
-
this.expressionAttributeValues[valueKeyEnd] = values[1] as Record<
|
|
36
|
-
string,
|
|
37
|
-
NativeAttributeValue
|
|
38
|
-
>;
|
|
39
|
-
this.filters.push(
|
|
40
|
-
`${nameKey} BETWEEN ${valueKeyStart} AND ${valueKeyEnd}`,
|
|
41
|
-
);
|
|
42
|
-
} else if (operator === "begins_with" || operator === "contains") {
|
|
43
|
-
const valueKey = `:val_${attrStr}`;
|
|
44
|
-
this.expressionAttributeValues[valueKey] = values as Record<
|
|
45
|
-
string,
|
|
46
|
-
NativeAttributeValue
|
|
47
|
-
>;
|
|
48
|
-
this.filters.push(`${operator}(${nameKey}, ${valueKey})`);
|
|
49
|
-
} else {
|
|
50
|
-
const valueKey = `:val_${attrStr}`;
|
|
51
|
-
this.expressionAttributeValues[valueKey] = values as Record<
|
|
52
|
-
string,
|
|
53
|
-
NativeAttributeValue
|
|
54
|
-
>;
|
|
55
|
-
const condition = getOperatorExpression(operator, nameKey, valueKey);
|
|
56
|
-
this.filters.push(condition);
|
|
57
|
-
}
|
|
58
|
-
return this;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
public limitResults(limit: number): this {
|
|
62
|
-
this.limit = limit;
|
|
63
|
-
return this;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public startFrom(lastKey: Record<string, NativeAttributeValue>): this {
|
|
67
|
-
this.lastKey = lastKey;
|
|
68
|
-
return this;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Executes the scan and returns a Promise that resolves with an array of items.
|
|
73
|
-
*/
|
|
74
|
-
public async execute(): Promise<PaginatedResult<T>> {
|
|
75
|
-
const params: ScanCommandInput = {
|
|
76
|
-
TableName: this.parent.getTableName(),
|
|
77
|
-
ExpressionAttributeNames: this.expressionAttributeNames,
|
|
78
|
-
ExpressionAttributeValues: this.expressionAttributeValues,
|
|
79
|
-
Limit: this.limit,
|
|
80
|
-
ExclusiveStartKey: this.lastKey,
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
if (this.parent.getEntityType()) {
|
|
84
|
-
this.filters.push(`#entity = :entity_value`);
|
|
85
|
-
this.expressionAttributeNames["#entity"] = "entityType";
|
|
86
|
-
this.expressionAttributeValues[":entity_value"] =
|
|
87
|
-
this.parent.getEntityType();
|
|
88
|
-
}
|
|
89
|
-
params.FilterExpression = this.filters.join(" AND ");
|
|
90
|
-
|
|
91
|
-
const result = await this.parent.getClient().send(new ScanCommand(params));
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
items: this.parent.getSchema().array().parse(result.Items) as T[],
|
|
95
|
-
lastKey: result.LastEvaluatedKey ?? undefined,
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
}
|