dyno-table 2.2.1 → 2.3.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/README.md +200 -1860
- package/dist/builders.cjs +55 -0
- package/dist/builders.d.cts +4 -0
- package/dist/builders.d.ts +4 -0
- package/dist/builders.js +2 -0
- package/dist/chunk-2EWNZOUK.js +618 -0
- package/dist/chunk-2WIBY7PZ.js +46 -0
- package/dist/chunk-7UJJ7JXM.cjs +63 -0
- package/dist/chunk-DTFJJASK.js +3200 -0
- package/dist/chunk-EODPMYPE.js +558 -0
- package/dist/chunk-KA3VPIPS.cjs +560 -0
- package/dist/chunk-NTA6GDPP.cjs +622 -0
- package/dist/chunk-PB7BBCZO.cjs +32 -0
- package/dist/chunk-QVRMYGC4.js +29 -0
- package/dist/chunk-XYL43FDX.cjs +3217 -0
- package/dist/conditions.cjs +67 -62
- package/dist/conditions.js +1 -48
- package/dist/entity.cjs +14 -625
- package/dist/entity.d.cts +2 -10
- package/dist/entity.d.ts +2 -10
- package/dist/entity.js +2 -626
- package/dist/index-2cbm07Bi.d.ts +2797 -0
- package/dist/index-DlN8G9hd.d.cts +2797 -0
- package/dist/index.cjs +111 -4460
- package/dist/index.d.cts +2 -10
- package/dist/index.d.ts +2 -10
- package/dist/index.js +5 -4442
- package/dist/standard-schema.cjs +0 -2
- package/dist/standard-schema.js +0 -2
- package/dist/table.cjs +7 -3796
- package/dist/table.d.cts +163 -12
- package/dist/table.d.ts +163 -12
- package/dist/table.js +3 -3799
- package/dist/types.cjs +0 -2
- package/dist/types.js +0 -2
- package/dist/utils.cjs +10 -30
- package/dist/utils.js +1 -31
- package/package.json +6 -66
- package/dist/batch-builder-BiQDIZ7p.d.cts +0 -398
- package/dist/batch-builder-CNsLS6sR.d.ts +0 -398
- package/dist/builder-types-BTVhQSHI.d.cts +0 -169
- package/dist/builder-types-CzuLR4Th.d.ts +0 -169
- package/dist/builders/condition-check-builder.cjs +0 -422
- package/dist/builders/condition-check-builder.cjs.map +0 -1
- package/dist/builders/condition-check-builder.d.cts +0 -153
- package/dist/builders/condition-check-builder.d.ts +0 -153
- package/dist/builders/condition-check-builder.js +0 -420
- package/dist/builders/condition-check-builder.js.map +0 -1
- package/dist/builders/delete-builder.cjs +0 -484
- package/dist/builders/delete-builder.cjs.map +0 -1
- package/dist/builders/delete-builder.d.cts +0 -211
- package/dist/builders/delete-builder.d.ts +0 -211
- package/dist/builders/delete-builder.js +0 -482
- package/dist/builders/delete-builder.js.map +0 -1
- package/dist/builders/paginator.cjs +0 -193
- package/dist/builders/paginator.cjs.map +0 -1
- package/dist/builders/paginator.d.cts +0 -155
- package/dist/builders/paginator.d.ts +0 -155
- package/dist/builders/paginator.js +0 -191
- package/dist/builders/paginator.js.map +0 -1
- package/dist/builders/put-builder.cjs +0 -554
- package/dist/builders/put-builder.cjs.map +0 -1
- package/dist/builders/put-builder.d.cts +0 -319
- package/dist/builders/put-builder.d.ts +0 -319
- package/dist/builders/put-builder.js +0 -552
- package/dist/builders/put-builder.js.map +0 -1
- package/dist/builders/query-builder.cjs +0 -757
- package/dist/builders/query-builder.cjs.map +0 -1
- package/dist/builders/query-builder.d.cts +0 -6
- package/dist/builders/query-builder.d.ts +0 -6
- package/dist/builders/query-builder.js +0 -755
- package/dist/builders/query-builder.js.map +0 -1
- package/dist/builders/transaction-builder.cjs +0 -906
- package/dist/builders/transaction-builder.cjs.map +0 -1
- package/dist/builders/transaction-builder.d.cts +0 -464
- package/dist/builders/transaction-builder.d.ts +0 -464
- package/dist/builders/transaction-builder.js +0 -904
- package/dist/builders/transaction-builder.js.map +0 -1
- package/dist/builders/update-builder.cjs +0 -668
- package/dist/builders/update-builder.cjs.map +0 -1
- package/dist/builders/update-builder.d.cts +0 -374
- package/dist/builders/update-builder.d.ts +0 -374
- package/dist/builders/update-builder.js +0 -666
- package/dist/builders/update-builder.js.map +0 -1
- package/dist/conditions.cjs.map +0 -1
- package/dist/conditions.js.map +0 -1
- package/dist/entity.cjs.map +0 -1
- package/dist/entity.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/query-builder-D3URwK9k.d.cts +0 -477
- package/dist/query-builder-cfEkU0_w.d.ts +0 -477
- package/dist/standard-schema.cjs.map +0 -1
- package/dist/standard-schema.js.map +0 -1
- package/dist/table-ClST8nkR.d.cts +0 -276
- package/dist/table-vE3cGoDy.d.ts +0 -276
- package/dist/table.cjs.map +0 -1
- package/dist/table.js.map +0 -1
- package/dist/types.cjs.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils.cjs.map +0 -1
- package/dist/utils.js.map +0 -1
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
import { GetBuilder, PutBuilder, QueryBuilder, ScanBuilder, DeleteBuilder, UpdateBuilder, TransactionBuilder, BatchBuilder, ConditionCheckBuilder, buildExpression, generateAttributeName, debugCommand } from './chunk-DTFJJASK.js';
|
|
2
|
+
import { eq, and, beginsWith, between, gte, gt, lte, lt } from './chunk-2WIBY7PZ.js';
|
|
3
|
+
|
|
4
|
+
// src/utils/chunk-array.ts
|
|
5
|
+
function* chunkArray(array, size) {
|
|
6
|
+
if (size <= 0) {
|
|
7
|
+
throw new Error("Chunk size must be greater than 0");
|
|
8
|
+
}
|
|
9
|
+
for (let i = 0; i < array.length; i += size) {
|
|
10
|
+
yield array.slice(i, i + size);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// src/table.ts
|
|
15
|
+
var DDB_BATCH_WRITE_LIMIT = 25;
|
|
16
|
+
var DDB_BATCH_GET_LIMIT = 100;
|
|
17
|
+
var Table = class {
|
|
18
|
+
dynamoClient;
|
|
19
|
+
tableName;
|
|
20
|
+
/**
|
|
21
|
+
* The column name of the partitionKey for the Table
|
|
22
|
+
*/
|
|
23
|
+
partitionKey;
|
|
24
|
+
/**
|
|
25
|
+
* The column name of the sortKey for the Table
|
|
26
|
+
*/
|
|
27
|
+
sortKey;
|
|
28
|
+
/**
|
|
29
|
+
* The Global Secondary Indexes that are configured on this table
|
|
30
|
+
*/
|
|
31
|
+
gsis;
|
|
32
|
+
constructor(config) {
|
|
33
|
+
this.dynamoClient = config.client;
|
|
34
|
+
this.tableName = config.tableName;
|
|
35
|
+
this.partitionKey = config.indexes.partitionKey;
|
|
36
|
+
this.sortKey = config.indexes.sortKey;
|
|
37
|
+
this.gsis = config.indexes.gsis || {};
|
|
38
|
+
}
|
|
39
|
+
createKeyForPrimaryIndex(keyCondition) {
|
|
40
|
+
const primaryCondition = { [this.partitionKey]: keyCondition.pk };
|
|
41
|
+
if (this.sortKey) {
|
|
42
|
+
if (!keyCondition.sk) {
|
|
43
|
+
throw new Error("Sort key has not been provided but the Table has a sort key");
|
|
44
|
+
}
|
|
45
|
+
primaryCondition[this.sortKey] = keyCondition.sk;
|
|
46
|
+
}
|
|
47
|
+
return primaryCondition;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Creates a new item in the table, it will fail if the item already exists.
|
|
51
|
+
*
|
|
52
|
+
* By default, this method returns the input values passed to the create operation
|
|
53
|
+
* upon successful creation.
|
|
54
|
+
*
|
|
55
|
+
* You can customise the return behaviour by chaining the `.returnValues()` method:
|
|
56
|
+
*
|
|
57
|
+
* @param item The item to create
|
|
58
|
+
* @returns A PutBuilder instance for chaining additional conditions and executing the create operation
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* // Create with default behavior (returns input values)
|
|
63
|
+
* const result = await table.create({
|
|
64
|
+
* id: 'user-123',
|
|
65
|
+
* name: 'John Doe',
|
|
66
|
+
* email: 'john@example.com'
|
|
67
|
+
* }).execute();
|
|
68
|
+
* console.log(result); // Returns the input object
|
|
69
|
+
*
|
|
70
|
+
* // Create with no return value for better performance
|
|
71
|
+
* await table.create(userData).returnValues('NONE').execute();
|
|
72
|
+
*
|
|
73
|
+
* // Create and get fresh data from dynamodb using a strongly consistent read
|
|
74
|
+
* const freshData = await table.create(userData).returnValues('CONSISTENT').execute();
|
|
75
|
+
*
|
|
76
|
+
* // Create and get previous values (if the item was overwritten)
|
|
77
|
+
* const oldData = await table.create(userData).returnValues('ALL_OLD').execute();
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
create(item) {
|
|
81
|
+
return this.put(item).condition((op) => op.attributeNotExists(this.partitionKey)).returnValues("INPUT");
|
|
82
|
+
}
|
|
83
|
+
get(keyCondition) {
|
|
84
|
+
const executor = async (params) => {
|
|
85
|
+
try {
|
|
86
|
+
const result = await this.dynamoClient.get({
|
|
87
|
+
TableName: params.tableName,
|
|
88
|
+
Key: this.createKeyForPrimaryIndex(keyCondition),
|
|
89
|
+
ProjectionExpression: params.projectionExpression,
|
|
90
|
+
ExpressionAttributeNames: params.expressionAttributeNames,
|
|
91
|
+
ConsistentRead: params.consistentRead
|
|
92
|
+
});
|
|
93
|
+
return {
|
|
94
|
+
item: result.Item ? result.Item : void 0
|
|
95
|
+
};
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error("Error getting item:", error);
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
return new GetBuilder(executor, keyCondition, this.tableName);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Updates an item in the table
|
|
105
|
+
*
|
|
106
|
+
* @param item The item to update
|
|
107
|
+
* @returns A PutBuilder instance for chaining conditions and executing the put operation
|
|
108
|
+
*/
|
|
109
|
+
put(item) {
|
|
110
|
+
const executor = async (params) => {
|
|
111
|
+
try {
|
|
112
|
+
const result = await this.dynamoClient.put({
|
|
113
|
+
TableName: params.tableName,
|
|
114
|
+
Item: params.item,
|
|
115
|
+
ConditionExpression: params.conditionExpression,
|
|
116
|
+
ExpressionAttributeNames: params.expressionAttributeNames,
|
|
117
|
+
ExpressionAttributeValues: params.expressionAttributeValues,
|
|
118
|
+
// CONSISTENT and INPUT are not valid ReturnValues for DDB, so we set NONE as we are not interested in its
|
|
119
|
+
// response and will be handling these cases separately
|
|
120
|
+
ReturnValues: params.returnValues === "CONSISTENT" || params.returnValues === "INPUT" ? "NONE" : params.returnValues
|
|
121
|
+
});
|
|
122
|
+
if (params.returnValues === "INPUT") {
|
|
123
|
+
return params.item;
|
|
124
|
+
}
|
|
125
|
+
if (params.returnValues === "CONSISTENT") {
|
|
126
|
+
const getResult = await this.dynamoClient.get({
|
|
127
|
+
TableName: params.tableName,
|
|
128
|
+
Key: this.createKeyForPrimaryIndex({
|
|
129
|
+
pk: params.item[this.partitionKey],
|
|
130
|
+
...this.sortKey && { sk: params.item[this.sortKey] }
|
|
131
|
+
}),
|
|
132
|
+
ConsistentRead: true
|
|
133
|
+
});
|
|
134
|
+
return getResult.Item;
|
|
135
|
+
}
|
|
136
|
+
return result.Attributes;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error("Error creating item:", error);
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
return new PutBuilder(executor, item, this.tableName);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Creates a query builder for complex queries
|
|
146
|
+
* If useIndex is called on the returned QueryBuilder, it will use the GSI configuration
|
|
147
|
+
*/
|
|
148
|
+
query(keyCondition) {
|
|
149
|
+
const pkAttributeName = this.partitionKey;
|
|
150
|
+
const skAttributeName = this.sortKey;
|
|
151
|
+
let keyConditionExpression = eq(pkAttributeName, keyCondition.pk);
|
|
152
|
+
if (keyCondition.sk) {
|
|
153
|
+
if (!skAttributeName) {
|
|
154
|
+
throw new Error("Sort key is not defined for Index");
|
|
155
|
+
}
|
|
156
|
+
const keyConditionOperator = {
|
|
157
|
+
eq: (value) => eq(skAttributeName, value),
|
|
158
|
+
lt: (value) => lt(skAttributeName, value),
|
|
159
|
+
lte: (value) => lte(skAttributeName, value),
|
|
160
|
+
gt: (value) => gt(skAttributeName, value),
|
|
161
|
+
gte: (value) => gte(skAttributeName, value),
|
|
162
|
+
between: (lower, upper) => between(skAttributeName, lower, upper),
|
|
163
|
+
beginsWith: (value) => beginsWith(skAttributeName, value),
|
|
164
|
+
and: (...conditions) => and(...conditions)
|
|
165
|
+
};
|
|
166
|
+
const skCondition = keyCondition.sk(keyConditionOperator);
|
|
167
|
+
keyConditionExpression = and(eq(pkAttributeName, keyCondition.pk), skCondition);
|
|
168
|
+
}
|
|
169
|
+
const executor = async (originalKeyCondition, options) => {
|
|
170
|
+
let finalKeyCondition = originalKeyCondition;
|
|
171
|
+
if (options.indexName) {
|
|
172
|
+
const gsiName = String(options.indexName);
|
|
173
|
+
const gsi = this.gsis[gsiName];
|
|
174
|
+
if (!gsi) {
|
|
175
|
+
throw new Error(`GSI with name "${gsiName}" does not exist on table "${this.tableName}"`);
|
|
176
|
+
}
|
|
177
|
+
const gsiPkAttributeName = gsi.partitionKey;
|
|
178
|
+
const gsiSkAttributeName = gsi.sortKey;
|
|
179
|
+
let pkValue;
|
|
180
|
+
let skValue;
|
|
181
|
+
let extractedSkCondition;
|
|
182
|
+
if (originalKeyCondition.type === "eq") {
|
|
183
|
+
pkValue = originalKeyCondition.value;
|
|
184
|
+
} else if (originalKeyCondition.type === "and" && originalKeyCondition.conditions) {
|
|
185
|
+
const pkCondition = originalKeyCondition.conditions.find(
|
|
186
|
+
(c) => c.type === "eq" && c.attr === pkAttributeName
|
|
187
|
+
);
|
|
188
|
+
if (pkCondition && pkCondition.type === "eq") {
|
|
189
|
+
pkValue = pkCondition.value;
|
|
190
|
+
}
|
|
191
|
+
const skConditions = originalKeyCondition.conditions.filter((c) => c.attr === skAttributeName);
|
|
192
|
+
if (skConditions.length > 0) {
|
|
193
|
+
if (skConditions.length === 1) {
|
|
194
|
+
extractedSkCondition = skConditions[0];
|
|
195
|
+
if (extractedSkCondition && extractedSkCondition.type === "eq") {
|
|
196
|
+
skValue = extractedSkCondition.value;
|
|
197
|
+
}
|
|
198
|
+
} else if (skConditions.length > 1) {
|
|
199
|
+
extractedSkCondition = and(...skConditions);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (!pkValue) {
|
|
204
|
+
throw new Error("Could not extract partition key value from key condition");
|
|
205
|
+
}
|
|
206
|
+
let gsiKeyCondition = eq(gsiPkAttributeName, pkValue);
|
|
207
|
+
if (skValue && gsiSkAttributeName) {
|
|
208
|
+
gsiKeyCondition = and(gsiKeyCondition, eq(gsiSkAttributeName, skValue));
|
|
209
|
+
} else if (extractedSkCondition && gsiSkAttributeName) {
|
|
210
|
+
if (extractedSkCondition.attr === skAttributeName) {
|
|
211
|
+
const updatedSkCondition = {
|
|
212
|
+
...extractedSkCondition,
|
|
213
|
+
attr: gsiSkAttributeName
|
|
214
|
+
};
|
|
215
|
+
gsiKeyCondition = and(gsiKeyCondition, updatedSkCondition);
|
|
216
|
+
} else {
|
|
217
|
+
gsiKeyCondition = and(gsiKeyCondition, extractedSkCondition);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
finalKeyCondition = gsiKeyCondition;
|
|
221
|
+
}
|
|
222
|
+
const expressionParams = {
|
|
223
|
+
expressionAttributeNames: {},
|
|
224
|
+
expressionAttributeValues: {},
|
|
225
|
+
valueCounter: { count: 0 }
|
|
226
|
+
};
|
|
227
|
+
const keyConditionExpression2 = buildExpression(finalKeyCondition, expressionParams);
|
|
228
|
+
let filterExpression;
|
|
229
|
+
if (options.filter) {
|
|
230
|
+
filterExpression = buildExpression(options.filter, expressionParams);
|
|
231
|
+
}
|
|
232
|
+
const projectionExpression = options.projection?.map((p) => generateAttributeName(expressionParams, p)).join(", ");
|
|
233
|
+
const { expressionAttributeNames, expressionAttributeValues } = expressionParams;
|
|
234
|
+
const { indexName, limit, consistentRead, scanIndexForward, lastEvaluatedKey } = options;
|
|
235
|
+
const params = {
|
|
236
|
+
TableName: this.tableName,
|
|
237
|
+
KeyConditionExpression: keyConditionExpression2,
|
|
238
|
+
FilterExpression: filterExpression,
|
|
239
|
+
ExpressionAttributeNames: expressionAttributeNames,
|
|
240
|
+
ExpressionAttributeValues: expressionAttributeValues,
|
|
241
|
+
IndexName: indexName,
|
|
242
|
+
Limit: limit,
|
|
243
|
+
ConsistentRead: consistentRead,
|
|
244
|
+
ScanIndexForward: scanIndexForward,
|
|
245
|
+
ProjectionExpression: projectionExpression,
|
|
246
|
+
ExclusiveStartKey: lastEvaluatedKey
|
|
247
|
+
};
|
|
248
|
+
try {
|
|
249
|
+
const result = await this.dynamoClient.query(params);
|
|
250
|
+
return {
|
|
251
|
+
items: result.Items,
|
|
252
|
+
lastEvaluatedKey: result.LastEvaluatedKey
|
|
253
|
+
};
|
|
254
|
+
} catch (error) {
|
|
255
|
+
console.log(debugCommand(params));
|
|
256
|
+
console.error("Error querying items:", error);
|
|
257
|
+
throw error;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
return new QueryBuilder(executor, keyConditionExpression);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Creates a scan builder for scanning the entire table
|
|
264
|
+
* Use this when you need to:
|
|
265
|
+
* - Process all items in a table
|
|
266
|
+
* - Apply filters to a large dataset
|
|
267
|
+
* - Use a GSI for scanning
|
|
268
|
+
*
|
|
269
|
+
* @returns A ScanBuilder instance for chaining operations
|
|
270
|
+
*/
|
|
271
|
+
scan() {
|
|
272
|
+
const executor = async (options) => {
|
|
273
|
+
const expressionParams = {
|
|
274
|
+
expressionAttributeNames: {},
|
|
275
|
+
expressionAttributeValues: {},
|
|
276
|
+
valueCounter: { count: 0 }
|
|
277
|
+
};
|
|
278
|
+
let filterExpression;
|
|
279
|
+
if (options.filter) {
|
|
280
|
+
filterExpression = buildExpression(options.filter, expressionParams);
|
|
281
|
+
}
|
|
282
|
+
const projectionExpression = options.projection?.map((p) => generateAttributeName(expressionParams, p)).join(", ");
|
|
283
|
+
const { expressionAttributeNames, expressionAttributeValues } = expressionParams;
|
|
284
|
+
const { indexName, limit, consistentRead, lastEvaluatedKey } = options;
|
|
285
|
+
const params = {
|
|
286
|
+
TableName: this.tableName,
|
|
287
|
+
FilterExpression: filterExpression,
|
|
288
|
+
ExpressionAttributeNames: Object.keys(expressionAttributeNames).length > 0 ? expressionAttributeNames : void 0,
|
|
289
|
+
ExpressionAttributeValues: Object.keys(expressionAttributeValues).length > 0 ? expressionAttributeValues : void 0,
|
|
290
|
+
IndexName: indexName,
|
|
291
|
+
Limit: limit,
|
|
292
|
+
ConsistentRead: consistentRead,
|
|
293
|
+
ProjectionExpression: projectionExpression,
|
|
294
|
+
ExclusiveStartKey: lastEvaluatedKey
|
|
295
|
+
};
|
|
296
|
+
try {
|
|
297
|
+
const result = await this.dynamoClient.scan(params);
|
|
298
|
+
return {
|
|
299
|
+
items: result.Items,
|
|
300
|
+
lastEvaluatedKey: result.LastEvaluatedKey
|
|
301
|
+
};
|
|
302
|
+
} catch (error) {
|
|
303
|
+
console.log(debugCommand(params));
|
|
304
|
+
console.error("Error scanning items:", error);
|
|
305
|
+
throw error;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
return new ScanBuilder(executor);
|
|
309
|
+
}
|
|
310
|
+
delete(keyCondition) {
|
|
311
|
+
const executor = async (params) => {
|
|
312
|
+
try {
|
|
313
|
+
const result = await this.dynamoClient.delete({
|
|
314
|
+
TableName: params.tableName,
|
|
315
|
+
Key: this.createKeyForPrimaryIndex(keyCondition),
|
|
316
|
+
ConditionExpression: params.conditionExpression,
|
|
317
|
+
ExpressionAttributeNames: params.expressionAttributeNames,
|
|
318
|
+
ExpressionAttributeValues: params.expressionAttributeValues,
|
|
319
|
+
ReturnValues: params.returnValues
|
|
320
|
+
});
|
|
321
|
+
return {
|
|
322
|
+
item: result.Attributes
|
|
323
|
+
};
|
|
324
|
+
} catch (error) {
|
|
325
|
+
console.error("Error deleting item:", error);
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
return new DeleteBuilder(executor, this.tableName, keyCondition);
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Updates an item in the table
|
|
333
|
+
*
|
|
334
|
+
* @param keyCondition The primary key of the item to update
|
|
335
|
+
* @returns An UpdateBuilder instance for chaining update operations and conditions
|
|
336
|
+
*/
|
|
337
|
+
update(keyCondition) {
|
|
338
|
+
const executor = async (params) => {
|
|
339
|
+
try {
|
|
340
|
+
const result = await this.dynamoClient.update({
|
|
341
|
+
TableName: params.tableName,
|
|
342
|
+
Key: this.createKeyForPrimaryIndex(keyCondition),
|
|
343
|
+
UpdateExpression: params.updateExpression,
|
|
344
|
+
ConditionExpression: params.conditionExpression,
|
|
345
|
+
ExpressionAttributeNames: params.expressionAttributeNames,
|
|
346
|
+
ExpressionAttributeValues: params.expressionAttributeValues,
|
|
347
|
+
ReturnValues: params.returnValues
|
|
348
|
+
});
|
|
349
|
+
return {
|
|
350
|
+
item: result.Attributes
|
|
351
|
+
};
|
|
352
|
+
} catch (error) {
|
|
353
|
+
console.error("Error updating item:", error);
|
|
354
|
+
throw error;
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
return new UpdateBuilder(executor, this.tableName, keyCondition);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Creates a transaction builder for performing multiple operations atomically
|
|
361
|
+
*/
|
|
362
|
+
transactionBuilder() {
|
|
363
|
+
const executor = async (params) => {
|
|
364
|
+
await this.dynamoClient.transactWrite(params);
|
|
365
|
+
};
|
|
366
|
+
return new TransactionBuilder(executor, {
|
|
367
|
+
partitionKey: this.partitionKey,
|
|
368
|
+
sortKey: this.sortKey
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Creates a batch builder for performing multiple operations efficiently with optional type inference
|
|
373
|
+
*
|
|
374
|
+
* @example Basic Usage
|
|
375
|
+
* ```typescript
|
|
376
|
+
* const batch = table.batchBuilder();
|
|
377
|
+
*
|
|
378
|
+
* // Add operations
|
|
379
|
+
* userRepo.create(newUser).withBatch(batch);
|
|
380
|
+
* orderRepo.get({ id: 'order-1' }).withBatch(batch);
|
|
381
|
+
*
|
|
382
|
+
* // Execute operations
|
|
383
|
+
* const result = await batch.execute();
|
|
384
|
+
* ```
|
|
385
|
+
*
|
|
386
|
+
* @example Typed Usage
|
|
387
|
+
* ```typescript
|
|
388
|
+
* // Define entity types for the batch
|
|
389
|
+
* const batch = table.batchBuilder<{
|
|
390
|
+
* User: UserEntity;
|
|
391
|
+
* Order: OrderEntity;
|
|
392
|
+
* Product: ProductEntity;
|
|
393
|
+
* }>();
|
|
394
|
+
*
|
|
395
|
+
* // Add operations with type information
|
|
396
|
+
* userRepo.create(newUser).withBatch(batch, 'User');
|
|
397
|
+
* orderRepo.get({ id: 'order-1' }).withBatch(batch, 'Order');
|
|
398
|
+
* productRepo.delete({ id: 'old-product' }).withBatch(batch, 'Product');
|
|
399
|
+
*
|
|
400
|
+
* // Execute and get typed results
|
|
401
|
+
* const result = await batch.execute();
|
|
402
|
+
* const users: UserEntity[] = result.reads.itemsByType.User;
|
|
403
|
+
* const orders: OrderEntity[] = result.reads.itemsByType.Order;
|
|
404
|
+
* ```
|
|
405
|
+
*/
|
|
406
|
+
batchBuilder() {
|
|
407
|
+
const batchWriteExecutor = async (operations) => {
|
|
408
|
+
return this.batchWrite(operations);
|
|
409
|
+
};
|
|
410
|
+
const batchGetExecutor = async (keys) => {
|
|
411
|
+
return this.batchGet(keys);
|
|
412
|
+
};
|
|
413
|
+
return new BatchBuilder(batchWriteExecutor, batchGetExecutor, {
|
|
414
|
+
partitionKey: this.partitionKey,
|
|
415
|
+
sortKey: this.sortKey
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Executes a transaction using a callback function
|
|
420
|
+
*
|
|
421
|
+
* @param callback A function that receives a transaction context and performs operations on it
|
|
422
|
+
* @param options Optional transaction options
|
|
423
|
+
* @returns A promise that resolves when the transaction is complete
|
|
424
|
+
*/
|
|
425
|
+
async transaction(callback, options) {
|
|
426
|
+
const transactionExecutor = async (params) => {
|
|
427
|
+
await this.dynamoClient.transactWrite(params);
|
|
428
|
+
};
|
|
429
|
+
const transaction = new TransactionBuilder(transactionExecutor, {
|
|
430
|
+
partitionKey: this.partitionKey,
|
|
431
|
+
sortKey: this.sortKey
|
|
432
|
+
});
|
|
433
|
+
if (options) {
|
|
434
|
+
transaction.withOptions(options);
|
|
435
|
+
}
|
|
436
|
+
const result = await callback(transaction);
|
|
437
|
+
await transaction.execute();
|
|
438
|
+
return result;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Creates a condition check operation for use in transactions
|
|
442
|
+
*
|
|
443
|
+
* This is useful for when you require a transaction to succeed only when a specific condition is met on a
|
|
444
|
+
* a record within the database that you are not directly updating.
|
|
445
|
+
*
|
|
446
|
+
* For example, you are updating a record and you want to ensure that another record exists and/or has a specific value before proceeding.
|
|
447
|
+
*/
|
|
448
|
+
conditionCheck(keyCondition) {
|
|
449
|
+
return new ConditionCheckBuilder(this.tableName, keyCondition);
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Performs a batch get operation to retrieve multiple items at once
|
|
453
|
+
*
|
|
454
|
+
* @param keys Array of primary keys to retrieve
|
|
455
|
+
* @returns A promise that resolves to the retrieved items
|
|
456
|
+
*/
|
|
457
|
+
async batchGet(keys) {
|
|
458
|
+
const allItems = [];
|
|
459
|
+
const allUnprocessedKeys = [];
|
|
460
|
+
for (const chunk of chunkArray(keys, DDB_BATCH_GET_LIMIT)) {
|
|
461
|
+
const formattedKeys = chunk.map((key) => ({
|
|
462
|
+
[this.partitionKey]: key.pk,
|
|
463
|
+
...this.sortKey ? { [this.sortKey]: key.sk } : {}
|
|
464
|
+
}));
|
|
465
|
+
const params = {
|
|
466
|
+
RequestItems: {
|
|
467
|
+
[this.tableName]: {
|
|
468
|
+
Keys: formattedKeys
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
try {
|
|
473
|
+
const result = await this.dynamoClient.batchGet(params);
|
|
474
|
+
if (result.Responses?.[this.tableName]) {
|
|
475
|
+
allItems.push(...result.Responses[this.tableName]);
|
|
476
|
+
}
|
|
477
|
+
const unprocessedKeysArray = result.UnprocessedKeys?.[this.tableName]?.Keys || [];
|
|
478
|
+
const unprocessedKeys = unprocessedKeysArray.map((key) => ({
|
|
479
|
+
pk: key[this.partitionKey],
|
|
480
|
+
sk: this.sortKey ? key[this.sortKey] : void 0
|
|
481
|
+
}));
|
|
482
|
+
if (unprocessedKeys.length > 0) {
|
|
483
|
+
allUnprocessedKeys.push(...unprocessedKeys);
|
|
484
|
+
}
|
|
485
|
+
} catch (error) {
|
|
486
|
+
console.error("Error in batch get operation:", error);
|
|
487
|
+
throw error;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return {
|
|
491
|
+
items: allItems,
|
|
492
|
+
unprocessedKeys: allUnprocessedKeys
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Performs a batch write operation to put or delete multiple items at once
|
|
497
|
+
*
|
|
498
|
+
* @param operations Array of put or delete operations
|
|
499
|
+
* @returns A promise that resolves to any unprocessed operations
|
|
500
|
+
*/
|
|
501
|
+
async batchWrite(operations) {
|
|
502
|
+
const allUnprocessedItems = [];
|
|
503
|
+
for (const chunk of chunkArray(operations, DDB_BATCH_WRITE_LIMIT)) {
|
|
504
|
+
const writeRequests = chunk.map((operation) => {
|
|
505
|
+
if (operation.type === "put") {
|
|
506
|
+
return {
|
|
507
|
+
PutRequest: {
|
|
508
|
+
Item: operation.item
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
return {
|
|
513
|
+
DeleteRequest: {
|
|
514
|
+
Key: this.createKeyForPrimaryIndex(operation.key)
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
});
|
|
518
|
+
const params = {
|
|
519
|
+
RequestItems: {
|
|
520
|
+
[this.tableName]: writeRequests
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
try {
|
|
524
|
+
const result = await this.dynamoClient.batchWrite(params);
|
|
525
|
+
const unprocessedRequestsArray = result.UnprocessedItems?.[this.tableName] || [];
|
|
526
|
+
if (unprocessedRequestsArray.length > 0) {
|
|
527
|
+
const unprocessedItems = unprocessedRequestsArray.map((request) => {
|
|
528
|
+
if (request?.PutRequest?.Item) {
|
|
529
|
+
return {
|
|
530
|
+
type: "put",
|
|
531
|
+
item: request.PutRequest.Item
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
if (request?.DeleteRequest?.Key) {
|
|
535
|
+
return {
|
|
536
|
+
type: "delete",
|
|
537
|
+
key: {
|
|
538
|
+
pk: request.DeleteRequest.Key[this.partitionKey],
|
|
539
|
+
sk: this.sortKey ? request.DeleteRequest.Key[this.sortKey] : void 0
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
throw new Error("Invalid unprocessed item format returned from DynamoDB");
|
|
544
|
+
});
|
|
545
|
+
allUnprocessedItems.push(...unprocessedItems);
|
|
546
|
+
}
|
|
547
|
+
} catch (error) {
|
|
548
|
+
console.error("Error in batch write operation:", error);
|
|
549
|
+
throw error;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return {
|
|
553
|
+
unprocessedItems: allUnprocessedItems
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
export { Table };
|