betterddb 0.6.1 → 0.6.4
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/.eslintrc.cjs +41 -0
- package/lib/betterddb.js +26 -18
- package/lib/builders/batch-get-builder.js +6 -2
- package/lib/builders/create-builder.js +14 -6
- package/lib/builders/delete-builder.js +8 -5
- package/lib/builders/get-builder.js +11 -6
- package/lib/builders/query-builder.js +38 -31
- package/lib/builders/scan-builder.js +17 -11
- package/lib/builders/update-builder.js +21 -16
- package/lib/operator.js +10 -10
- package/lib/{betterddb.d.ts → types/betterddb.d.ts} +10 -9
- package/lib/types/betterddb.d.ts.map +1 -0
- package/lib/{builders → types/builders}/batch-get-builder.d.ts +2 -1
- package/lib/types/builders/batch-get-builder.d.ts.map +1 -0
- package/lib/{builders → types/builders}/create-builder.d.ts +3 -2
- package/lib/types/builders/create-builder.d.ts.map +1 -0
- package/lib/{builders → types/builders}/delete-builder.d.ts +3 -2
- package/lib/types/builders/delete-builder.d.ts.map +1 -0
- package/lib/{builders → types/builders}/get-builder.d.ts +3 -2
- package/lib/types/builders/get-builder.d.ts.map +1 -0
- package/lib/{builders → types/builders}/query-builder.d.ts +4 -3
- package/lib/types/builders/query-builder.d.ts.map +1 -0
- package/lib/{builders → types/builders}/scan-builder.d.ts +4 -3
- package/lib/types/builders/scan-builder.d.ts.map +1 -0
- package/lib/{builders → types/builders}/update-builder.d.ts +3 -2
- package/lib/types/builders/update-builder.d.ts.map +1 -0
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/operator.d.ts +3 -0
- package/lib/types/operator.d.ts.map +1 -0
- package/lib/types/{paginated-result.d.ts → types/paginated-result.d.ts} +1 -0
- package/lib/types/types/paginated-result.d.ts.map +1 -0
- package/package.json +15 -5
- package/prettier.config.js +6 -0
- package/src/betterddb.ts +39 -35
- package/src/builders/batch-get-builder.ts +11 -6
- package/src/builders/create-builder.ts +42 -27
- package/src/builders/delete-builder.ts +28 -17
- package/src/builders/get-builder.ts +26 -19
- package/src/builders/query-builder.ts +64 -44
- package/src/builders/scan-builder.ts +18 -14
- package/src/builders/update-builder.ts +49 -28
- package/src/index.ts +1 -1
- package/src/operator.ts +21 -21
- package/src/types/paginated-result.ts +1 -1
- package/tsconfig.json +25 -11
- package/lib/index.d.ts +0 -1
- package/lib/operator.d.ts +0 -2
package/.eslintrc.cjs
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** @type {import("eslint").Linter.Config} */
|
|
2
|
+
const config = {
|
|
3
|
+
"parser": "@typescript-eslint/parser",
|
|
4
|
+
"parserOptions": {
|
|
5
|
+
"project": true
|
|
6
|
+
},
|
|
7
|
+
"plugins": [
|
|
8
|
+
"@typescript-eslint"
|
|
9
|
+
],
|
|
10
|
+
"extends": [
|
|
11
|
+
"plugin:@typescript-eslint/recommended-type-checked",
|
|
12
|
+
"plugin:@typescript-eslint/stylistic-type-checked"
|
|
13
|
+
],
|
|
14
|
+
"rules": {
|
|
15
|
+
"@typescript-eslint/array-type": "off",
|
|
16
|
+
"@typescript-eslint/consistent-type-definitions": "off",
|
|
17
|
+
"@typescript-eslint/consistent-type-imports": [
|
|
18
|
+
"warn",
|
|
19
|
+
{
|
|
20
|
+
"prefer": "type-imports",
|
|
21
|
+
"fixStyle": "inline-type-imports"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"@typescript-eslint/no-unused-vars": [
|
|
25
|
+
"warn",
|
|
26
|
+
{
|
|
27
|
+
"argsIgnorePattern": "^_"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"@typescript-eslint/require-await": "off",
|
|
31
|
+
"@typescript-eslint/no-misused-promises": [
|
|
32
|
+
"error",
|
|
33
|
+
{
|
|
34
|
+
"checksVoidReturn": {
|
|
35
|
+
"attributes": false
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
module.exports = config;
|
package/lib/betterddb.js
CHANGED
|
@@ -12,15 +12,21 @@ const batch_get_builder_1 = require("./builders/batch-get-builder");
|
|
|
12
12
|
* BetterDDB is a definition-based DynamoDB wrapper library.
|
|
13
13
|
*/
|
|
14
14
|
class BetterDDB {
|
|
15
|
+
schema;
|
|
16
|
+
tableName;
|
|
17
|
+
entityType;
|
|
18
|
+
client;
|
|
19
|
+
keys;
|
|
20
|
+
timestamps;
|
|
21
|
+
counter;
|
|
15
22
|
constructor(options) {
|
|
16
|
-
var _a, _b, _c;
|
|
17
23
|
this.schema = options.schema;
|
|
18
24
|
this.tableName = options.tableName;
|
|
19
|
-
this.entityType =
|
|
25
|
+
this.entityType = options.entityType?.toUpperCase();
|
|
20
26
|
this.keys = options.keys;
|
|
21
27
|
this.client = options.client;
|
|
22
|
-
this.timestamps =
|
|
23
|
-
this.counter =
|
|
28
|
+
this.timestamps = options.timestamps ?? false;
|
|
29
|
+
this.counter = options.counter ?? false;
|
|
24
30
|
}
|
|
25
31
|
getCounter() {
|
|
26
32
|
return this.counter;
|
|
@@ -45,7 +51,9 @@ class BetterDDB {
|
|
|
45
51
|
}
|
|
46
52
|
// Helper: Retrieve the key value from a KeyDefinition.
|
|
47
53
|
getKeyValue(def, rawKey) {
|
|
48
|
-
if (typeof def ===
|
|
54
|
+
if (typeof def === "string" ||
|
|
55
|
+
typeof def === "number" ||
|
|
56
|
+
typeof def === "symbol") {
|
|
49
57
|
return String(rawKey[def]);
|
|
50
58
|
}
|
|
51
59
|
else {
|
|
@@ -60,18 +68,18 @@ class BetterDDB {
|
|
|
60
68
|
// For primary (partition) key:
|
|
61
69
|
const pkConfig = this.keys.primary;
|
|
62
70
|
keyObj[pkConfig.name] =
|
|
63
|
-
|
|
64
|
-
typeof pkConfig.definition ===
|
|
65
|
-
typeof pkConfig.definition ===
|
|
71
|
+
typeof pkConfig.definition === "string" ||
|
|
72
|
+
typeof pkConfig.definition === "number" ||
|
|
73
|
+
typeof pkConfig.definition === "symbol"
|
|
66
74
|
? String(rawKey[pkConfig.definition])
|
|
67
75
|
: pkConfig.definition.build(rawKey);
|
|
68
76
|
// For sort key, if defined:
|
|
69
77
|
if (this.keys.sort) {
|
|
70
78
|
const skConfig = this.keys.sort;
|
|
71
79
|
keyObj[skConfig.name] =
|
|
72
|
-
|
|
73
|
-
typeof skConfig.definition ===
|
|
74
|
-
typeof skConfig.definition ===
|
|
80
|
+
typeof skConfig.definition === "string" ||
|
|
81
|
+
typeof skConfig.definition === "number" ||
|
|
82
|
+
typeof skConfig.definition === "symbol"
|
|
75
83
|
? String(rawKey[skConfig.definition])
|
|
76
84
|
: skConfig.definition.build(rawKey);
|
|
77
85
|
}
|
|
@@ -88,18 +96,18 @@ class BetterDDB {
|
|
|
88
96
|
// Compute primary index attribute.
|
|
89
97
|
const primaryConfig = gsiConfig.primary;
|
|
90
98
|
indexAttributes[primaryConfig.name] =
|
|
91
|
-
|
|
92
|
-
typeof primaryConfig.definition ===
|
|
93
|
-
typeof primaryConfig.definition ===
|
|
99
|
+
typeof primaryConfig.definition === "string" ||
|
|
100
|
+
typeof primaryConfig.definition === "number" ||
|
|
101
|
+
typeof primaryConfig.definition === "symbol"
|
|
94
102
|
? String(rawItem[primaryConfig.definition])
|
|
95
103
|
: primaryConfig.definition.build(rawItem);
|
|
96
104
|
// Compute sort index attribute if provided.
|
|
97
|
-
if (gsiConfig
|
|
105
|
+
if (gsiConfig?.sort) {
|
|
98
106
|
const sortConfig = gsiConfig.sort;
|
|
99
107
|
indexAttributes[sortConfig.name] =
|
|
100
|
-
|
|
101
|
-
typeof sortConfig.definition ===
|
|
102
|
-
typeof sortConfig.definition ===
|
|
108
|
+
typeof sortConfig.definition === "string" ||
|
|
109
|
+
typeof sortConfig.definition === "number" ||
|
|
110
|
+
typeof sortConfig.definition === "symbol"
|
|
103
111
|
? String(rawItem[sortConfig.definition])
|
|
104
112
|
: sortConfig.definition.build(rawItem);
|
|
105
113
|
}
|
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BatchGetBuilder = void 0;
|
|
4
4
|
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
5
5
|
class BatchGetBuilder {
|
|
6
|
+
parent;
|
|
7
|
+
keys;
|
|
6
8
|
/**
|
|
7
9
|
* @param parent - The BetterDDB instance for the table.
|
|
8
10
|
* @param keys - An array of partial keys for the items you wish to retrieve.
|
|
@@ -30,7 +32,7 @@ class BatchGetBuilder {
|
|
|
30
32
|
});
|
|
31
33
|
const tableName = this.parent.getTableName();
|
|
32
34
|
// Build an array of keys using the parent's key builder.
|
|
33
|
-
const keysArray = deduplicatedKeys.map(key => this.parent.buildKey(key));
|
|
35
|
+
const keysArray = deduplicatedKeys.map((key) => this.parent.buildKey(key));
|
|
34
36
|
// Construct the BatchGet parameters.
|
|
35
37
|
const params = {
|
|
36
38
|
RequestItems: {
|
|
@@ -39,7 +41,9 @@ class BatchGetBuilder {
|
|
|
39
41
|
},
|
|
40
42
|
},
|
|
41
43
|
};
|
|
42
|
-
const result = await this.parent
|
|
44
|
+
const result = await this.parent
|
|
45
|
+
.getClient()
|
|
46
|
+
.send(new lib_dynamodb_1.BatchGetCommand(params));
|
|
43
47
|
const responses = result.Responses ? result.Responses[tableName] : [];
|
|
44
48
|
if (!responses) {
|
|
45
49
|
return [];
|
|
@@ -3,10 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CreateBuilder = void 0;
|
|
4
4
|
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
5
5
|
class CreateBuilder {
|
|
6
|
+
parent;
|
|
7
|
+
item;
|
|
8
|
+
extraTransactItems = [];
|
|
6
9
|
constructor(parent, item) {
|
|
7
10
|
this.parent = parent;
|
|
8
11
|
this.item = item;
|
|
9
|
-
this.extraTransactItems = [];
|
|
10
12
|
}
|
|
11
13
|
async execute() {
|
|
12
14
|
const validated = this.parent.getSchema().parse(this.item);
|
|
@@ -16,17 +18,20 @@ class CreateBuilder {
|
|
|
16
18
|
// Combine with extra transaction items.
|
|
17
19
|
const allItems = [...this.extraTransactItems, myTransactItem];
|
|
18
20
|
await this.parent.getClient().send(new lib_dynamodb_1.TransactWriteCommand({
|
|
19
|
-
TransactItems: allItems
|
|
21
|
+
TransactItems: allItems,
|
|
20
22
|
}));
|
|
21
23
|
// After transaction, retrieve the updated item.
|
|
22
24
|
const result = await this.parent.get(this.item).execute();
|
|
23
25
|
if (result === null) {
|
|
24
|
-
throw new Error(
|
|
26
|
+
throw new Error("Item not found after transaction create");
|
|
25
27
|
}
|
|
26
28
|
return result;
|
|
27
29
|
}
|
|
28
30
|
else {
|
|
29
|
-
let finalItem = {
|
|
31
|
+
let finalItem = {
|
|
32
|
+
...this.item,
|
|
33
|
+
entityType: this.parent.getEntityType(),
|
|
34
|
+
};
|
|
30
35
|
if (this.parent.getTimestamps()) {
|
|
31
36
|
const now = new Date().toISOString();
|
|
32
37
|
finalItem = { ...finalItem, createdAt: now, updatedAt: now };
|
|
@@ -39,7 +44,7 @@ class CreateBuilder {
|
|
|
39
44
|
finalItem = { ...finalItem, ...indexAttributes };
|
|
40
45
|
await this.parent.getClient().send(new lib_dynamodb_1.PutCommand({
|
|
41
46
|
TableName: this.parent.getTableName(),
|
|
42
|
-
Item: finalItem
|
|
47
|
+
Item: finalItem,
|
|
43
48
|
}));
|
|
44
49
|
return validated;
|
|
45
50
|
}
|
|
@@ -55,7 +60,10 @@ class CreateBuilder {
|
|
|
55
60
|
}
|
|
56
61
|
toTransactPut() {
|
|
57
62
|
const validated = this.parent.getSchema().parse(this.item);
|
|
58
|
-
let finalItem = {
|
|
63
|
+
let finalItem = {
|
|
64
|
+
...this.item,
|
|
65
|
+
entityType: this.parent.getEntityType(),
|
|
66
|
+
};
|
|
59
67
|
if (this.parent.getTimestamps()) {
|
|
60
68
|
const now = new Date().toISOString();
|
|
61
69
|
finalItem = { ...finalItem, createdAt: now, updatedAt: now };
|
|
@@ -3,10 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DeleteBuilder = void 0;
|
|
4
4
|
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
5
5
|
class DeleteBuilder {
|
|
6
|
+
parent;
|
|
7
|
+
key;
|
|
8
|
+
condition;
|
|
9
|
+
extraTransactItems = [];
|
|
6
10
|
constructor(parent, key) {
|
|
7
11
|
this.parent = parent;
|
|
8
12
|
this.key = key;
|
|
9
|
-
this.extraTransactItems = [];
|
|
10
13
|
}
|
|
11
14
|
/**
|
|
12
15
|
* Specify a condition expression for the delete operation.
|
|
@@ -28,18 +31,18 @@ class DeleteBuilder {
|
|
|
28
31
|
// Combine with extra transaction items.
|
|
29
32
|
const allItems = [...this.extraTransactItems, myTransactItem];
|
|
30
33
|
await this.parent.getClient().send(new lib_dynamodb_1.TransactWriteCommand({
|
|
31
|
-
TransactItems: allItems
|
|
34
|
+
TransactItems: allItems,
|
|
32
35
|
}));
|
|
33
36
|
// After transaction, retrieve the updated item.
|
|
34
37
|
const result = await this.parent.get(this.key).execute();
|
|
35
38
|
if (result === null) {
|
|
36
|
-
throw new Error(
|
|
39
|
+
throw new Error("Item not found after transaction delete");
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
else {
|
|
40
43
|
const params = {
|
|
41
44
|
TableName: this.parent.getTableName(),
|
|
42
|
-
Key: this.parent.buildKey(this.key)
|
|
45
|
+
Key: this.parent.buildKey(this.key),
|
|
43
46
|
};
|
|
44
47
|
if (this.condition) {
|
|
45
48
|
params.ConditionExpression = this.condition.expression;
|
|
@@ -60,7 +63,7 @@ class DeleteBuilder {
|
|
|
60
63
|
toTransactDelete() {
|
|
61
64
|
const deleteItem = {
|
|
62
65
|
TableName: this.parent.getTableName(),
|
|
63
|
-
Key: this.parent.buildKey(this.key)
|
|
66
|
+
Key: this.parent.buildKey(this.key),
|
|
64
67
|
};
|
|
65
68
|
if (this.condition) {
|
|
66
69
|
deleteItem.ConditionExpression = this.condition.expression;
|
|
@@ -3,17 +3,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.GetBuilder = void 0;
|
|
4
4
|
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
5
5
|
class GetBuilder {
|
|
6
|
+
parent;
|
|
7
|
+
key;
|
|
8
|
+
projectionExpression;
|
|
9
|
+
expressionAttributeNames = {};
|
|
10
|
+
extraTransactItems = [];
|
|
6
11
|
constructor(parent, key) {
|
|
7
12
|
this.parent = parent;
|
|
8
13
|
this.key = key;
|
|
9
|
-
this.expressionAttributeNames = {};
|
|
10
|
-
this.extraTransactItems = [];
|
|
11
14
|
}
|
|
12
15
|
/**
|
|
13
16
|
* Specify a projection by providing an array of attribute names.
|
|
14
17
|
*/
|
|
15
18
|
withProjection(attributes) {
|
|
16
|
-
this.projectionExpression = attributes
|
|
19
|
+
this.projectionExpression = attributes
|
|
20
|
+
.map((attr) => `#${String(attr)}`)
|
|
21
|
+
.join(", ");
|
|
17
22
|
for (const attr of attributes) {
|
|
18
23
|
this.expressionAttributeNames[`#${String(attr)}`] = String(attr);
|
|
19
24
|
}
|
|
@@ -26,7 +31,7 @@ class GetBuilder {
|
|
|
26
31
|
// Combine with extra transaction items.
|
|
27
32
|
const allItems = [...this.extraTransactItems, myTransactItem];
|
|
28
33
|
await this.parent.getClient().send(new lib_dynamodb_1.TransactGetCommand({
|
|
29
|
-
TransactItems: allItems
|
|
34
|
+
TransactItems: allItems,
|
|
30
35
|
}));
|
|
31
36
|
// After transaction, retrieve the updated item.
|
|
32
37
|
const result = await this.parent.get(this.key).execute();
|
|
@@ -35,7 +40,7 @@ class GetBuilder {
|
|
|
35
40
|
else {
|
|
36
41
|
const params = {
|
|
37
42
|
TableName: this.parent.getTableName(),
|
|
38
|
-
Key: this.parent.buildKey(this.key)
|
|
43
|
+
Key: this.parent.buildKey(this.key),
|
|
39
44
|
};
|
|
40
45
|
if (this.projectionExpression) {
|
|
41
46
|
params.ProjectionExpression = this.projectionExpression;
|
|
@@ -59,7 +64,7 @@ class GetBuilder {
|
|
|
59
64
|
toTransactGet() {
|
|
60
65
|
const getItem = {
|
|
61
66
|
TableName: this.parent.getTableName(),
|
|
62
|
-
Key: this.parent.buildKey(this.key)
|
|
67
|
+
Key: this.parent.buildKey(this.key),
|
|
63
68
|
};
|
|
64
69
|
if (this.projectionExpression) {
|
|
65
70
|
getItem.ProjectionExpression = this.projectionExpression;
|
|
@@ -4,32 +4,37 @@ exports.QueryBuilder = void 0;
|
|
|
4
4
|
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
5
5
|
const operator_1 = require("../operator");
|
|
6
6
|
class QueryBuilder {
|
|
7
|
+
parent;
|
|
8
|
+
key;
|
|
9
|
+
keyConditions = [];
|
|
10
|
+
filterConditions = [];
|
|
11
|
+
expressionAttributeNames = {};
|
|
12
|
+
expressionAttributeValues = {};
|
|
13
|
+
index;
|
|
14
|
+
limit;
|
|
15
|
+
lastKey;
|
|
16
|
+
ascending = true;
|
|
7
17
|
constructor(parent, key) {
|
|
8
18
|
this.parent = parent;
|
|
9
19
|
this.key = key;
|
|
10
|
-
this.keyConditions = [];
|
|
11
|
-
this.filterConditions = [];
|
|
12
|
-
this.expressionAttributeNames = {};
|
|
13
|
-
this.expressionAttributeValues = {};
|
|
14
|
-
this.ascending = true;
|
|
15
20
|
const keys = this.parent.getKeys();
|
|
16
21
|
let pkName = keys.primary.name;
|
|
17
22
|
let builtKey = this.parent.buildKey(this.key);
|
|
18
|
-
this.expressionAttributeNames[
|
|
19
|
-
this.expressionAttributeValues[
|
|
23
|
+
this.expressionAttributeNames["#pk"] = pkName;
|
|
24
|
+
this.expressionAttributeValues[":pk_value"] = builtKey[pkName];
|
|
20
25
|
}
|
|
21
26
|
usingIndex(indexName) {
|
|
22
27
|
if (!this.parent.getKeys().gsis) {
|
|
23
|
-
throw new Error(
|
|
28
|
+
throw new Error("No global secondary indexes defined for this table");
|
|
24
29
|
}
|
|
25
30
|
if (!(indexName in this.parent.getKeys().gsis)) {
|
|
26
|
-
throw new Error(
|
|
31
|
+
throw new Error("index does not exist");
|
|
27
32
|
}
|
|
28
33
|
this.index = this.parent.getKeys().gsis[indexName];
|
|
29
34
|
const pkName = this.index.primary.name;
|
|
30
35
|
const builtKey = this.parent.buildIndexes(this.key);
|
|
31
|
-
this.expressionAttributeNames[
|
|
32
|
-
this.expressionAttributeValues[
|
|
36
|
+
this.expressionAttributeNames["#pk"] = pkName;
|
|
37
|
+
this.expressionAttributeValues[":pk_value"] = builtKey[pkName];
|
|
33
38
|
return this;
|
|
34
39
|
}
|
|
35
40
|
sortAscending() {
|
|
@@ -41,25 +46,24 @@ class QueryBuilder {
|
|
|
41
46
|
return this;
|
|
42
47
|
}
|
|
43
48
|
where(operator, values) {
|
|
44
|
-
var _a, _b;
|
|
45
49
|
const keys = this.parent.getKeys();
|
|
46
50
|
// Determine the sort key name from either the index or the primary keys.
|
|
47
|
-
const sortKeyName = this.index ?
|
|
51
|
+
const sortKeyName = this.index ? this.index.sort?.name : keys.sort?.name;
|
|
48
52
|
if (!sortKeyName) {
|
|
49
|
-
throw new Error(
|
|
53
|
+
throw new Error("Sort key is not defined for this table/index.");
|
|
50
54
|
}
|
|
51
|
-
const nameKey =
|
|
55
|
+
const nameKey = "#sk";
|
|
52
56
|
this.expressionAttributeNames[nameKey] = sortKeyName;
|
|
53
57
|
// Enforce that a complex sort key requires an object input.
|
|
54
|
-
if (typeof values !==
|
|
58
|
+
if (typeof values !== "object" || values === null) {
|
|
55
59
|
throw new Error(`For complex sort keys, please provide an object with all necessary properties.`);
|
|
56
60
|
}
|
|
57
|
-
if (operator ===
|
|
61
|
+
if (operator === "between") {
|
|
58
62
|
if (!Array.isArray(values) || values.length !== 2) {
|
|
59
63
|
throw new Error(`For 'between' operator, values must be a tuple of two objects`);
|
|
60
64
|
}
|
|
61
|
-
const valueKeyStart =
|
|
62
|
-
const valueKeyEnd =
|
|
65
|
+
const valueKeyStart = ":sk_start";
|
|
66
|
+
const valueKeyEnd = ":sk_end";
|
|
63
67
|
// Use the key definition's build function to build the key from the full object.
|
|
64
68
|
this.expressionAttributeValues[valueKeyStart] = this.index
|
|
65
69
|
? this.parent.buildIndexes(values[0])[sortKeyName]
|
|
@@ -69,8 +73,8 @@ class QueryBuilder {
|
|
|
69
73
|
: this.parent.buildKey(values[1])[sortKeyName];
|
|
70
74
|
this.keyConditions.push(`${nameKey} BETWEEN ${valueKeyStart} AND ${valueKeyEnd}`);
|
|
71
75
|
}
|
|
72
|
-
else if (operator ===
|
|
73
|
-
const valueKey =
|
|
76
|
+
else if (operator === "begins_with") {
|
|
77
|
+
const valueKey = ":sk_value";
|
|
74
78
|
this.expressionAttributeValues[valueKey] = this.index
|
|
75
79
|
? this.parent.buildIndexes(values)[sortKeyName]
|
|
76
80
|
: this.parent.buildKey(values)[sortKeyName];
|
|
@@ -78,7 +82,7 @@ class QueryBuilder {
|
|
|
78
82
|
}
|
|
79
83
|
else {
|
|
80
84
|
// For eq, lt, lte, gt, gte:
|
|
81
|
-
const valueKey =
|
|
85
|
+
const valueKey = ":sk_value";
|
|
82
86
|
this.expressionAttributeValues[valueKey] = this.index
|
|
83
87
|
? this.parent.buildIndexes(values)[sortKeyName]
|
|
84
88
|
: this.parent.buildKey(values)[sortKeyName];
|
|
@@ -92,7 +96,7 @@ class QueryBuilder {
|
|
|
92
96
|
const randomString = Math.random().toString(36).substring(2, 15);
|
|
93
97
|
const placeholderName = `#attr_${attrStr}_${randomString}`;
|
|
94
98
|
this.expressionAttributeNames[placeholderName] = attrStr;
|
|
95
|
-
if (operator ===
|
|
99
|
+
if (operator === "between") {
|
|
96
100
|
if (!Array.isArray(values) || values.length !== 2) {
|
|
97
101
|
throw new Error("For 'between' operator, values must be a tuple of two items");
|
|
98
102
|
}
|
|
@@ -102,7 +106,7 @@ class QueryBuilder {
|
|
|
102
106
|
this.expressionAttributeValues[placeholderValueEnd] = values[1];
|
|
103
107
|
this.filterConditions.push(`${placeholderName} BETWEEN ${placeholderValueStart} AND ${placeholderValueEnd}`);
|
|
104
108
|
}
|
|
105
|
-
else if (operator ===
|
|
109
|
+
else if (operator === "begins_with" || operator === "contains") {
|
|
106
110
|
const placeholderValue = `:val_${attrStr}_${randomString}`;
|
|
107
111
|
this.expressionAttributeValues[placeholderValue] = values;
|
|
108
112
|
this.filterConditions.push(`${operator}(${placeholderName}, ${placeholderValue})`);
|
|
@@ -127,9 +131,8 @@ class QueryBuilder {
|
|
|
127
131
|
* Executes the query and returns a Promise that resolves with an array of items.
|
|
128
132
|
*/
|
|
129
133
|
async execute() {
|
|
130
|
-
var _a, _b, _c;
|
|
131
134
|
this.keyConditions.unshift(`#pk = :pk_value`);
|
|
132
|
-
const keyConditionExpression = this.keyConditions.join(
|
|
135
|
+
const keyConditionExpression = this.keyConditions.join(" AND ");
|
|
133
136
|
const params = {
|
|
134
137
|
TableName: this.parent.getTableName(),
|
|
135
138
|
KeyConditionExpression: keyConditionExpression,
|
|
@@ -138,16 +141,20 @@ class QueryBuilder {
|
|
|
138
141
|
ScanIndexForward: this.ascending,
|
|
139
142
|
Limit: this.limit,
|
|
140
143
|
ExclusiveStartKey: this.lastKey,
|
|
141
|
-
IndexName:
|
|
144
|
+
IndexName: this.index?.name ?? undefined,
|
|
142
145
|
};
|
|
143
146
|
if (this.parent.getEntityType()) {
|
|
144
147
|
this.filterConditions.push(`#entity = :entity_value`);
|
|
145
|
-
this.expressionAttributeNames[
|
|
146
|
-
this.expressionAttributeValues[
|
|
148
|
+
this.expressionAttributeNames["#entity"] = "entityType";
|
|
149
|
+
this.expressionAttributeValues[":entity_value"] =
|
|
150
|
+
this.parent.getEntityType();
|
|
147
151
|
}
|
|
148
|
-
params.FilterExpression = this.filterConditions.join(
|
|
152
|
+
params.FilterExpression = this.filterConditions.join(" AND ");
|
|
149
153
|
const result = await this.parent.getClient().send(new lib_dynamodb_1.QueryCommand(params));
|
|
150
|
-
return {
|
|
154
|
+
return {
|
|
155
|
+
items: this.parent.getSchema().array().parse(result.Items),
|
|
156
|
+
lastKey: result.LastEvaluatedKey ?? undefined,
|
|
157
|
+
};
|
|
151
158
|
}
|
|
152
159
|
}
|
|
153
160
|
exports.QueryBuilder = QueryBuilder;
|
|
@@ -4,17 +4,20 @@ exports.ScanBuilder = void 0;
|
|
|
4
4
|
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
5
5
|
const operator_1 = require("../operator");
|
|
6
6
|
class ScanBuilder {
|
|
7
|
+
parent;
|
|
8
|
+
filters = [];
|
|
9
|
+
expressionAttributeNames = {};
|
|
10
|
+
expressionAttributeValues = {};
|
|
11
|
+
limit;
|
|
12
|
+
lastKey;
|
|
7
13
|
constructor(parent) {
|
|
8
14
|
this.parent = parent;
|
|
9
|
-
this.filters = [];
|
|
10
|
-
this.expressionAttributeNames = {};
|
|
11
|
-
this.expressionAttributeValues = {};
|
|
12
15
|
}
|
|
13
16
|
where(attribute, operator, values) {
|
|
14
17
|
const attrStr = String(attribute);
|
|
15
18
|
const nameKey = `#attr_${attrStr}`;
|
|
16
19
|
this.expressionAttributeNames[nameKey] = attrStr;
|
|
17
|
-
if (operator ===
|
|
20
|
+
if (operator === "between") {
|
|
18
21
|
if (!Array.isArray(values) || values.length !== 2) {
|
|
19
22
|
throw new Error(`For 'between' operator, values must be a tuple of two items`);
|
|
20
23
|
}
|
|
@@ -24,7 +27,7 @@ class ScanBuilder {
|
|
|
24
27
|
this.expressionAttributeValues[valueKeyEnd] = values[1];
|
|
25
28
|
this.filters.push(`${nameKey} BETWEEN ${valueKeyStart} AND ${valueKeyEnd}`);
|
|
26
29
|
}
|
|
27
|
-
else if (operator ===
|
|
30
|
+
else if (operator === "begins_with" || operator === "contains") {
|
|
28
31
|
const valueKey = `:val_${attrStr}`;
|
|
29
32
|
this.expressionAttributeValues[valueKey] = values;
|
|
30
33
|
this.filters.push(`${operator}(${nameKey}, ${valueKey})`);
|
|
@@ -49,22 +52,25 @@ class ScanBuilder {
|
|
|
49
52
|
* Executes the scan and returns a Promise that resolves with an array of items.
|
|
50
53
|
*/
|
|
51
54
|
async execute() {
|
|
52
|
-
var _a;
|
|
53
55
|
const params = {
|
|
54
56
|
TableName: this.parent.getTableName(),
|
|
55
57
|
ExpressionAttributeNames: this.expressionAttributeNames,
|
|
56
58
|
ExpressionAttributeValues: this.expressionAttributeValues,
|
|
57
59
|
Limit: this.limit,
|
|
58
|
-
ExclusiveStartKey: this.lastKey
|
|
60
|
+
ExclusiveStartKey: this.lastKey,
|
|
59
61
|
};
|
|
60
62
|
if (this.parent.getEntityType()) {
|
|
61
63
|
this.filters.push(`#entity = :entity_value`);
|
|
62
|
-
this.expressionAttributeNames[
|
|
63
|
-
this.expressionAttributeValues[
|
|
64
|
+
this.expressionAttributeNames["#entity"] = "entityType";
|
|
65
|
+
this.expressionAttributeValues[":entity_value"] =
|
|
66
|
+
this.parent.getEntityType();
|
|
64
67
|
}
|
|
65
|
-
params.FilterExpression = this.filters.join(
|
|
68
|
+
params.FilterExpression = this.filters.join(" AND ");
|
|
66
69
|
const result = await this.parent.getClient().send(new lib_dynamodb_1.ScanCommand(params));
|
|
67
|
-
return {
|
|
70
|
+
return {
|
|
71
|
+
items: this.parent.getSchema().array().parse(result.Items),
|
|
72
|
+
lastKey: result.LastEvaluatedKey ?? undefined,
|
|
73
|
+
};
|
|
68
74
|
}
|
|
69
75
|
}
|
|
70
76
|
exports.ScanBuilder = ScanBuilder;
|
|
@@ -3,13 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.UpdateBuilder = void 0;
|
|
4
4
|
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
5
5
|
class UpdateBuilder {
|
|
6
|
+
parent;
|
|
7
|
+
key;
|
|
8
|
+
actions = {};
|
|
9
|
+
condition;
|
|
10
|
+
// When using transaction mode, we store extra transaction items.
|
|
11
|
+
extraTransactItems = [];
|
|
6
12
|
// Reference to the parent BetterDDB instance and key.
|
|
7
13
|
constructor(parent, key) {
|
|
8
14
|
this.parent = parent;
|
|
9
15
|
this.key = key;
|
|
10
|
-
this.actions = {};
|
|
11
|
-
// When using transaction mode, we store extra transaction items.
|
|
12
|
-
this.extraTransactItems = [];
|
|
13
16
|
}
|
|
14
17
|
// Chainable methods:
|
|
15
18
|
set(attrs) {
|
|
@@ -76,17 +79,17 @@ class UpdateBuilder {
|
|
|
76
79
|
setParts.push(`${nameKey} = ${valueKey}`);
|
|
77
80
|
}
|
|
78
81
|
if (setParts.length > 0) {
|
|
79
|
-
clauses.push(`SET ${setParts.join(
|
|
82
|
+
clauses.push(`SET ${setParts.join(", ")}`);
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
85
|
// Build REMOVE clause.
|
|
83
86
|
if (this.actions.remove && this.actions.remove.length > 0) {
|
|
84
|
-
const removeParts = this.actions.remove.map(attr => {
|
|
87
|
+
const removeParts = this.actions.remove.map((attr) => {
|
|
85
88
|
const nameKey = `#remove_${String(attr)}`;
|
|
86
89
|
ExpressionAttributeNames[nameKey] = String(attr);
|
|
87
90
|
return nameKey;
|
|
88
91
|
});
|
|
89
|
-
clauses.push(`REMOVE ${removeParts.join(
|
|
92
|
+
clauses.push(`REMOVE ${removeParts.join(", ")}`);
|
|
90
93
|
}
|
|
91
94
|
// Build ADD clause.
|
|
92
95
|
if (this.actions.add) {
|
|
@@ -99,7 +102,7 @@ class UpdateBuilder {
|
|
|
99
102
|
addParts.push(`${nameKey} ${valueKey}`);
|
|
100
103
|
}
|
|
101
104
|
if (addParts.length > 0) {
|
|
102
|
-
clauses.push(`ADD ${addParts.join(
|
|
105
|
+
clauses.push(`ADD ${addParts.join(", ")}`);
|
|
103
106
|
}
|
|
104
107
|
}
|
|
105
108
|
// Build DELETE clause.
|
|
@@ -113,7 +116,7 @@ class UpdateBuilder {
|
|
|
113
116
|
deleteParts.push(`${nameKey} ${valueKey}`);
|
|
114
117
|
}
|
|
115
118
|
if (deleteParts.length > 0) {
|
|
116
|
-
clauses.push(`DELETE ${deleteParts.join(
|
|
119
|
+
clauses.push(`DELETE ${deleteParts.join(", ")}`);
|
|
117
120
|
}
|
|
118
121
|
}
|
|
119
122
|
// Merge any provided condition attribute values.
|
|
@@ -121,9 +124,9 @@ class UpdateBuilder {
|
|
|
121
124
|
Object.assign(ExpressionAttributeValues, this.condition.attributeValues);
|
|
122
125
|
}
|
|
123
126
|
return {
|
|
124
|
-
updateExpression: clauses.join(
|
|
127
|
+
updateExpression: clauses.join(" "),
|
|
125
128
|
attributeNames: ExpressionAttributeNames,
|
|
126
|
-
attributeValues: ExpressionAttributeValues
|
|
129
|
+
attributeValues: ExpressionAttributeValues,
|
|
127
130
|
};
|
|
128
131
|
}
|
|
129
132
|
/**
|
|
@@ -136,7 +139,7 @@ class UpdateBuilder {
|
|
|
136
139
|
Key: this.parent.buildKey(this.key),
|
|
137
140
|
UpdateExpression: updateExpression,
|
|
138
141
|
ExpressionAttributeNames: attributeNames,
|
|
139
|
-
ExpressionAttributeValues: attributeValues
|
|
142
|
+
ExpressionAttributeValues: attributeValues,
|
|
140
143
|
};
|
|
141
144
|
if (this.condition && this.condition.expression) {
|
|
142
145
|
updateItem.ConditionExpression = this.condition.expression;
|
|
@@ -153,12 +156,12 @@ class UpdateBuilder {
|
|
|
153
156
|
// Combine with extra transaction items.
|
|
154
157
|
const allItems = [...this.extraTransactItems, myTransactItem];
|
|
155
158
|
await this.parent.getClient().send(new lib_dynamodb_1.TransactWriteCommand({
|
|
156
|
-
TransactItems: allItems
|
|
159
|
+
TransactItems: allItems,
|
|
157
160
|
}));
|
|
158
161
|
// After transaction, retrieve the updated item.
|
|
159
162
|
const result = await this.parent.get(this.key).execute();
|
|
160
163
|
if (result === null) {
|
|
161
|
-
throw new Error(
|
|
164
|
+
throw new Error("Item not found after transaction update");
|
|
162
165
|
}
|
|
163
166
|
return result;
|
|
164
167
|
}
|
|
@@ -171,14 +174,16 @@ class UpdateBuilder {
|
|
|
171
174
|
UpdateExpression: updateExpression,
|
|
172
175
|
ExpressionAttributeNames: attributeNames,
|
|
173
176
|
ExpressionAttributeValues: attributeValues,
|
|
174
|
-
ReturnValues:
|
|
177
|
+
ReturnValues: "ALL_NEW",
|
|
175
178
|
};
|
|
176
179
|
if (this.condition && this.condition.expression) {
|
|
177
180
|
params.ConditionExpression = this.condition.expression;
|
|
178
181
|
}
|
|
179
|
-
const result = await this.parent
|
|
182
|
+
const result = await this.parent
|
|
183
|
+
.getClient()
|
|
184
|
+
.send(new lib_dynamodb_1.UpdateCommand(params));
|
|
180
185
|
if (!result.Attributes) {
|
|
181
|
-
throw new Error(
|
|
186
|
+
throw new Error("No attributes returned after update");
|
|
182
187
|
}
|
|
183
188
|
return this.parent.getSchema().parse(result.Attributes);
|
|
184
189
|
}
|