betterddb 0.4.6 → 0.4.7

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.
@@ -6,6 +6,7 @@ import { CreateBuilder } from './builders/create-builder';
6
6
  import { GetBuilder } from './builders/get-builder';
7
7
  import { DeleteBuilder } from './builders/delete-builder';
8
8
  import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
9
+ import { BatchGetBuilder } from './builders/batch-get-builder';
9
10
  export type PrimaryKeyValue = string | number;
10
11
  /**
11
12
  * A key definition can be either a simple key (a property name)
@@ -110,6 +111,10 @@ export declare class BetterDDB<T> {
110
111
  * Get an item by its primary key.
111
112
  */
112
113
  get(rawKey: Partial<T>): GetBuilder<T>;
114
+ /**
115
+ * Get multiple items by their primary keys.
116
+ */
117
+ batchGet(rawKeys: Partial<T>[]): BatchGetBuilder<T>;
113
118
  /**
114
119
  * Update an item.
115
120
  */
package/lib/betterddb.js CHANGED
@@ -7,6 +7,7 @@ const update_builder_1 = require("./builders/update-builder");
7
7
  const create_builder_1 = require("./builders/create-builder");
8
8
  const get_builder_1 = require("./builders/get-builder");
9
9
  const delete_builder_1 = require("./builders/delete-builder");
10
+ const batch_get_builder_1 = require("./builders/batch-get-builder");
10
11
  /**
11
12
  * BetterDDB is a definition-based DynamoDB wrapper library.
12
13
  */
@@ -114,6 +115,12 @@ class BetterDDB {
114
115
  get(rawKey) {
115
116
  return new get_builder_1.GetBuilder(this, rawKey);
116
117
  }
118
+ /**
119
+ * Get multiple items by their primary keys.
120
+ */
121
+ batchGet(rawKeys) {
122
+ return new batch_get_builder_1.BatchGetBuilder(this, rawKeys);
123
+ }
117
124
  /**
118
125
  * Update an item.
119
126
  */
@@ -0,0 +1,21 @@
1
+ import { BetterDDB } from '../betterddb';
2
+ export declare class BatchGetBuilder<T> {
3
+ private parent;
4
+ private keys;
5
+ private projectionExpression?;
6
+ private expressionAttributeNames;
7
+ /**
8
+ * @param parent - The BetterDDB instance for the table.
9
+ * @param keys - An array of partial keys for the items you wish to retrieve.
10
+ */
11
+ constructor(parent: BetterDDB<T>, keys: Partial<T>[]);
12
+ /**
13
+ * Specify a projection by providing an array of attribute names.
14
+ */
15
+ withProjection(attributes: (keyof T)[]): this;
16
+ /**
17
+ * Executes the batch get operation.
18
+ * Returns an array of parsed items of type T.
19
+ */
20
+ execute(): Promise<T[]>;
21
+ }
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BatchGetBuilder = void 0;
4
+ const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
5
+ class BatchGetBuilder {
6
+ /**
7
+ * @param parent - The BetterDDB instance for the table.
8
+ * @param keys - An array of partial keys for the items you wish to retrieve.
9
+ */
10
+ constructor(parent, keys) {
11
+ this.parent = parent;
12
+ this.keys = keys;
13
+ this.expressionAttributeNames = {};
14
+ }
15
+ /**
16
+ * Specify a projection by providing an array of attribute names.
17
+ */
18
+ withProjection(attributes) {
19
+ this.projectionExpression = attributes.map(attr => `#${String(attr)}`).join(', ');
20
+ for (const attr of attributes) {
21
+ this.expressionAttributeNames[`#${String(attr)}`] = String(attr);
22
+ }
23
+ return this;
24
+ }
25
+ /**
26
+ * Executes the batch get operation.
27
+ * Returns an array of parsed items of type T.
28
+ */
29
+ async execute() {
30
+ const tableName = this.parent.getTableName();
31
+ // Build an array of keys using the parent's key builder.
32
+ const keysArray = this.keys.map(key => this.parent.buildKey(key));
33
+ // Construct the BatchGet parameters.
34
+ const params = {
35
+ RequestItems: {
36
+ [tableName]: {
37
+ Keys: keysArray,
38
+ ...(this.projectionExpression && {
39
+ ProjectionExpression: this.projectionExpression,
40
+ ExpressionAttributeNames: this.expressionAttributeNames,
41
+ }),
42
+ },
43
+ },
44
+ };
45
+ const result = await this.parent.getClient().send(new lib_dynamodb_1.BatchGetCommand(params));
46
+ const responses = result.Responses ? result.Responses[tableName] : [];
47
+ if (!responses) {
48
+ return [];
49
+ }
50
+ return this.parent.getSchema().array().parse(responses);
51
+ }
52
+ }
53
+ exports.BatchGetBuilder = BatchGetBuilder;
@@ -8,7 +8,4 @@ export declare class CreateBuilder<T> {
8
8
  execute(): Promise<T>;
9
9
  transactWrite(ops: TransactWriteItem[] | TransactWriteItem): this;
10
10
  toTransactPut(): TransactWriteItem;
11
- then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
12
- catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<T | TResult>;
13
- finally(onfinally?: (() => void) | null): Promise<T>;
14
11
  }
@@ -62,14 +62,5 @@ class CreateBuilder {
62
62
  };
63
63
  return { Put: putItem };
64
64
  }
65
- then(onfulfilled, onrejected) {
66
- return this.execute().then(onfulfilled, onrejected);
67
- }
68
- catch(onrejected) {
69
- return this.execute().catch(onrejected);
70
- }
71
- finally(onfinally) {
72
- return this.execute().finally(onfinally);
73
- }
74
65
  }
75
66
  exports.CreateBuilder = CreateBuilder;
@@ -13,7 +13,4 @@ export declare class DeleteBuilder<T> {
13
13
  execute(): Promise<void>;
14
14
  transactWrite(ops: TransactWriteItem[] | TransactWriteItem): this;
15
15
  toTransactDelete(): TransactWriteItem;
16
- then<TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
17
- catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<void | TResult>;
18
- finally(onfinally?: (() => void) | null): Promise<void>;
19
16
  }
@@ -68,14 +68,5 @@ class DeleteBuilder {
68
68
  }
69
69
  return { Delete: deleteItem };
70
70
  }
71
- then(onfulfilled, onrejected) {
72
- return this.execute().then(onfulfilled, onrejected);
73
- }
74
- catch(onrejected) {
75
- return this.execute().catch(onrejected);
76
- }
77
- finally(onfinally) {
78
- return this.execute().finally(onfinally);
79
- }
80
71
  }
81
72
  exports.DeleteBuilder = DeleteBuilder;
@@ -14,7 +14,4 @@ export declare class GetBuilder<T> {
14
14
  execute(): Promise<T | null>;
15
15
  transactGet(ops: TransactGetItem[] | TransactGetItem): this;
16
16
  toTransactGet(): TransactGetItem;
17
- then<TResult1 = T | null, TResult2 = never>(onfulfilled?: ((value: T | null) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
18
- catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<T | null | TResult>;
19
- finally(onfinally?: (() => void) | null): Promise<T | null>;
20
17
  }
@@ -67,14 +67,5 @@ class GetBuilder {
67
67
  }
68
68
  return { Get: getItem };
69
69
  }
70
- then(onfulfilled, onrejected) {
71
- return this.execute().then(onfulfilled, onrejected);
72
- }
73
- catch(onrejected) {
74
- return this.execute().catch(onrejected);
75
- }
76
- finally(onfinally) {
77
- return this.execute().finally(onfinally);
78
- }
79
70
  }
80
71
  exports.GetBuilder = GetBuilder;
@@ -5,7 +5,7 @@ export declare class QueryBuilder<T> {
5
5
  private filters;
6
6
  private expressionAttributeNames;
7
7
  private expressionAttributeValues;
8
- private indexName?;
8
+ private index?;
9
9
  private sortKeyCondition?;
10
10
  private limit?;
11
11
  private lastKey?;
@@ -21,7 +21,4 @@ export declare class QueryBuilder<T> {
21
21
  * Executes the query and returns a Promise that resolves with an array of items.
22
22
  */
23
23
  execute(): Promise<T[]>;
24
- then<TResult1 = T[], TResult2 = never>(onfulfilled?: ((value: T[]) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
25
- catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<T[] | TResult>;
26
- finally(onfinally?: (() => void) | null): Promise<T[]>;
27
24
  }
@@ -12,7 +12,13 @@ class QueryBuilder {
12
12
  this.ascending = true;
13
13
  }
14
14
  usingIndex(indexName) {
15
- this.indexName = indexName;
15
+ if (!this.parent.getKeys().gsis) {
16
+ throw new Error('No global secondary indexes defined for this table');
17
+ }
18
+ if (!(indexName in this.parent.getKeys().gsis)) {
19
+ throw new Error('index does not exist');
20
+ }
21
+ this.index = this.parent.getKeys().gsis[indexName];
16
22
  return this;
17
23
  }
18
24
  sortAscending() {
@@ -64,19 +70,20 @@ class QueryBuilder {
64
70
  * Executes the query and returns a Promise that resolves with an array of items.
65
71
  */
66
72
  async execute() {
67
- // Build a simple key condition for the partition key.
73
+ var _a, _b;
68
74
  const keys = this.parent.getKeys();
69
- const pkName = keys.primary.name;
75
+ let pkName = keys.primary.name;
76
+ let builtKey = this.parent.buildKey(this.key);
77
+ if (this.index) {
78
+ pkName = this.index.primary.name;
79
+ builtKey = this.parent.buildIndexes(this.key);
80
+ }
70
81
  this.expressionAttributeNames['#pk'] = pkName;
71
- // Cast the built key to a record so that we can index by string.
72
- const builtKey = this.parent.buildKey(this.key);
73
- this.expressionAttributeValues[':pk_value'] = builtKey[pkName];
74
82
  let keyConditionExpression = `#pk = :pk_value`;
75
- // If a sortKeyCondition was set via another fluent method, append it.
76
83
  if (this.sortKeyCondition) {
77
84
  keyConditionExpression += ` AND ${this.sortKeyCondition}`;
78
85
  }
79
- // If any filters were added, set them as FilterExpression.
86
+ this.expressionAttributeValues[':pk_value'] = builtKey[pkName];
80
87
  const params = {
81
88
  TableName: this.parent.getTableName(),
82
89
  KeyConditionExpression: keyConditionExpression,
@@ -85,7 +92,7 @@ class QueryBuilder {
85
92
  ScanIndexForward: this.ascending,
86
93
  Limit: this.limit,
87
94
  ExclusiveStartKey: this.lastKey,
88
- IndexName: this.indexName
95
+ IndexName: (_b = (_a = this.index) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : undefined
89
96
  };
90
97
  if (this.filters.length > 0) {
91
98
  params.FilterExpression = this.filters.join(' AND ');
@@ -93,15 +100,5 @@ class QueryBuilder {
93
100
  const result = await this.parent.getClient().send(new lib_dynamodb_1.QueryCommand(params));
94
101
  return this.parent.getSchema().array().parse(result.Items);
95
102
  }
96
- // Thenable implementation.
97
- then(onfulfilled, onrejected) {
98
- return this.execute().then(onfulfilled, onrejected);
99
- }
100
- catch(onrejected) {
101
- return this.execute().catch(onrejected);
102
- }
103
- finally(onfinally) {
104
- return this.execute().finally(onfinally);
105
- }
106
103
  }
107
104
  exports.QueryBuilder = QueryBuilder;
@@ -14,7 +14,4 @@ export declare class ScanBuilder<T> {
14
14
  * Executes the scan and returns a Promise that resolves with an array of items.
15
15
  */
16
16
  execute(): Promise<T[]>;
17
- then<TResult1 = T[], TResult2 = never>(onfulfilled?: ((value: T[]) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
18
- catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null): Promise<T[] | TResult>;
19
- finally(onfinally?: (() => void) | null): Promise<T[]>;
20
17
  }
@@ -63,15 +63,5 @@ class ScanBuilder {
63
63
  const result = await this.parent.getClient().send(new lib_dynamodb_1.ScanCommand(params));
64
64
  return this.parent.getSchema().array().parse(result.Items);
65
65
  }
66
- // Thenable implementation.
67
- then(onfulfilled, onrejected) {
68
- return this.execute().then(onfulfilled, onrejected);
69
- }
70
- catch(onrejected) {
71
- return this.execute().catch(onrejected);
72
- }
73
- finally(onfinally) {
74
- return this.execute().finally(onfinally);
75
- }
76
66
  }
77
67
  exports.ScanBuilder = ScanBuilder;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "betterddb",
3
- "version": "0.4.6",
3
+ "version": "0.4.7",
4
4
  "description": "A definition-based DynamoDB wrapper library that provides a schema-driven and fully typesafe DAL.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
package/src/betterddb.ts CHANGED
@@ -6,6 +6,7 @@ import { CreateBuilder } from './builders/create-builder';
6
6
  import { GetBuilder } from './builders/get-builder';
7
7
  import { DeleteBuilder } from './builders/delete-builder';
8
8
  import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
9
+ import { BatchGetBuilder } from './builders/batch-get-builder';
9
10
 
10
11
  export type PrimaryKeyValue = string | number;
11
12
 
@@ -212,6 +213,13 @@ export class BetterDDB<T> {
212
213
  return new GetBuilder<T>(this, rawKey);
213
214
  }
214
215
 
216
+ /**
217
+ * Get multiple items by their primary keys.
218
+ */
219
+ public batchGet(rawKeys: Partial<T>[]): BatchGetBuilder<T> {
220
+ return new BatchGetBuilder<T>(this, rawKeys);
221
+ }
222
+
215
223
  /**
216
224
  * Update an item.
217
225
  */
@@ -0,0 +1,56 @@
1
+ import { BetterDDB } from '../betterddb';
2
+ import { BatchGetCommand } from '@aws-sdk/lib-dynamodb';
3
+ import { BatchGetItemInput } from '@aws-sdk/client-dynamodb';
4
+
5
+ export class BatchGetBuilder<T> {
6
+ private projectionExpression?: string;
7
+ private expressionAttributeNames: Record<string, string> = {};
8
+
9
+ /**
10
+ * @param parent - The BetterDDB instance for the table.
11
+ * @param keys - An array of partial keys for the items you wish to retrieve.
12
+ */
13
+ constructor(private parent: BetterDDB<T>, private keys: Partial<T>[]) {}
14
+
15
+ /**
16
+ * Specify a projection by providing an array of attribute names.
17
+ */
18
+ public withProjection(attributes: (keyof T)[]): this {
19
+ this.projectionExpression = attributes.map(attr => `#${String(attr)}`).join(', ');
20
+ for (const attr of attributes) {
21
+ this.expressionAttributeNames[`#${String(attr)}`] = String(attr);
22
+ }
23
+ return this;
24
+ }
25
+
26
+ /**
27
+ * Executes the batch get operation.
28
+ * Returns an array of parsed items of type T.
29
+ */
30
+ public async execute(): Promise<T[]> {
31
+ const tableName = this.parent.getTableName();
32
+ // Build an array of keys using the parent's key builder.
33
+ const keysArray = this.keys.map(key => this.parent.buildKey(key));
34
+
35
+ // Construct the BatchGet parameters.
36
+ const params: BatchGetItemInput = {
37
+ RequestItems: {
38
+ [tableName]: {
39
+ Keys: keysArray,
40
+ ...(this.projectionExpression && {
41
+ ProjectionExpression: this.projectionExpression,
42
+ ExpressionAttributeNames: this.expressionAttributeNames,
43
+ }),
44
+ },
45
+ },
46
+ };
47
+
48
+ const result = await this.parent.getClient().send(new BatchGetCommand(params));
49
+ const responses = result.Responses ? result.Responses[tableName] : [];
50
+ if (!responses) {
51
+ return [];
52
+ }
53
+
54
+ return this.parent.getSchema().array().parse(responses) as T[];
55
+ }
56
+ }
@@ -66,19 +66,4 @@ export class CreateBuilder<T> {
66
66
  };
67
67
  return { Put: putItem };
68
68
  }
69
-
70
- public then<TResult1 = T, TResult2 = never>(
71
- onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
72
- onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
73
- ): Promise<TResult1 | TResult2> {
74
- return this.execute().then(onfulfilled, onrejected);
75
- }
76
- public catch<TResult = never>(
77
- onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
78
- ): Promise<T | TResult> {
79
- return this.execute().catch(onrejected);
80
- }
81
- public finally(onfinally?: (() => void) | null): Promise<T> {
82
- return this.execute().finally(onfinally);
83
- }
84
69
  }
@@ -66,19 +66,4 @@ export class DeleteBuilder<T> {
66
66
  }
67
67
  return { Delete: deleteItem };
68
68
  }
69
-
70
- public then<TResult1 = void, TResult2 = never>(
71
- onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null,
72
- onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
73
- ): Promise<TResult1 | TResult2> {
74
- return this.execute().then(onfulfilled, onrejected);
75
- }
76
- public catch<TResult = never>(
77
- onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
78
- ): Promise<void | TResult> {
79
- return this.execute().catch(onrejected);
80
- }
81
- public finally(onfinally?: (() => void) | null): Promise<void> {
82
- return this.execute().finally(onfinally);
83
- }
84
69
  }
@@ -65,19 +65,4 @@ export class GetBuilder<T> {
65
65
  }
66
66
  return { Get: getItem };
67
67
  }
68
-
69
- public then<TResult1 = T | null, TResult2 = never>(
70
- onfulfilled?: ((value: T | null) => TResult1 | PromiseLike<TResult1>) | null,
71
- onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
72
- ): Promise<TResult1 | TResult2> {
73
- return this.execute().then(onfulfilled, onrejected);
74
- }
75
- public catch<TResult = never>(
76
- onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
77
- ): Promise<T | null | TResult> {
78
- return this.execute().catch(onrejected);
79
- }
80
- public finally(onfinally?: (() => void) | null): Promise<T | null> {
81
- return this.execute().finally(onfinally);
82
- }
83
68
  }
@@ -1,12 +1,12 @@
1
1
  import { QueryCommand, QueryCommandInput } from '@aws-sdk/lib-dynamodb';
2
- import { BetterDDB } from '../betterddb';
2
+ import { BetterDDB, GSIConfig } from '../betterddb';
3
3
 
4
4
  export class QueryBuilder<T> {
5
5
  private filters: string[] = [];
6
6
  private expressionAttributeNames: Record<string, string> = {};
7
7
  private expressionAttributeValues: Record<string, any> = {};
8
- private indexName?: string;
9
- private sortKeyCondition?: string; // You can extend this to support a fluent sort builder.
8
+ private index?: GSIConfig<T>;
9
+ private sortKeyCondition?: string;
10
10
  private limit?: number;
11
11
  private lastKey?: Record<string, any>;
12
12
  private ascending: boolean = true;
@@ -14,7 +14,14 @@ export class QueryBuilder<T> {
14
14
  constructor(private parent: BetterDDB<T>, private key: Partial<T>) {}
15
15
 
16
16
  public usingIndex(indexName: string): this {
17
- this.indexName = indexName;
17
+ if (!this.parent.getKeys().gsis) {
18
+ throw new Error('No global secondary indexes defined for this table');
19
+ }
20
+ if (!(indexName in this.parent.getKeys().gsis!)) {
21
+ throw new Error('index does not exist')
22
+ }
23
+
24
+ this.index = this.parent.getKeys().gsis![indexName];
18
25
  return this;
19
26
  }
20
27
 
@@ -74,22 +81,22 @@ export class QueryBuilder<T> {
74
81
  * Executes the query and returns a Promise that resolves with an array of items.
75
82
  */
76
83
  public async execute(): Promise<T[]> {
77
- // Build a simple key condition for the partition key.
78
84
  const keys = this.parent.getKeys();
79
- const pkName = keys.primary.name;
85
+ let pkName = keys.primary.name;
86
+ let builtKey = this.parent.buildKey(this.key) as Record<string, any>;
87
+ if (this.index) {
88
+ pkName = this.index.primary.name;
89
+ builtKey = this.parent.buildIndexes(this.key);
90
+ }
80
91
  this.expressionAttributeNames['#pk'] = pkName;
81
92
 
82
- // Cast the built key to a record so that we can index by string.
83
- const builtKey = this.parent.buildKey(this.key) as Record<string, any>;
84
- this.expressionAttributeValues[':pk_value'] = builtKey[pkName];
85
-
86
93
  let keyConditionExpression = `#pk = :pk_value`;
87
- // If a sortKeyCondition was set via another fluent method, append it.
88
94
  if (this.sortKeyCondition) {
89
95
  keyConditionExpression += ` AND ${this.sortKeyCondition}`;
90
96
  }
91
97
 
92
- // If any filters were added, set them as FilterExpression.
98
+ this.expressionAttributeValues[':pk_value'] = builtKey[pkName];
99
+
93
100
  const params: QueryCommandInput = {
94
101
  TableName: this.parent.getTableName(),
95
102
  KeyConditionExpression: keyConditionExpression,
@@ -98,7 +105,7 @@ export class QueryBuilder<T> {
98
105
  ScanIndexForward: this.ascending,
99
106
  Limit: this.limit,
100
107
  ExclusiveStartKey: this.lastKey,
101
- IndexName: this.indexName
108
+ IndexName: this.index?.name ?? undefined
102
109
  };
103
110
 
104
111
  if (this.filters.length > 0) {
@@ -108,20 +115,4 @@ export class QueryBuilder<T> {
108
115
  const result = await this.parent.getClient().send(new QueryCommand(params));
109
116
  return this.parent.getSchema().array().parse(result.Items) as T[];
110
117
  }
111
-
112
- // Thenable implementation.
113
- public then<TResult1 = T[], TResult2 = never>(
114
- onfulfilled?: ((value: T[]) => TResult1 | PromiseLike<TResult1>) | null,
115
- onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
116
- ): Promise<TResult1 | TResult2> {
117
- return this.execute().then(onfulfilled, onrejected);
118
- }
119
- public catch<TResult = never>(
120
- onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
121
- ): Promise<T[] | TResult> {
122
- return this.execute().catch(onrejected);
123
- }
124
- public finally(onfinally?: (() => void) | null): Promise<T[]> {
125
- return this.execute().finally(onfinally);
126
- }
127
118
  }
@@ -71,20 +71,4 @@ export class ScanBuilder<T> {
71
71
  const result = await this.parent.getClient().send(new ScanCommand(params));
72
72
  return this.parent.getSchema().array().parse(result.Items) as T[];
73
73
  }
74
-
75
- // Thenable implementation.
76
- public then<TResult1 = T[], TResult2 = never>(
77
- onfulfilled?: ((value: T[]) => TResult1 | PromiseLike<TResult1>) | null,
78
- onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
79
- ): Promise<TResult1 | TResult2> {
80
- return this.execute().then(onfulfilled, onrejected);
81
- }
82
- public catch<TResult = never>(
83
- onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null
84
- ): Promise<T[] | TResult> {
85
- return this.execute().catch(onrejected);
86
- }
87
- public finally(onfinally?: (() => void) | null): Promise<T[]> {
88
- return this.execute().finally(onfinally);
89
- }
90
74
  }
@@ -30,7 +30,7 @@ const UserSchema = z.object({
30
30
 
31
31
  type User = z.infer<typeof UserSchema>;
32
32
 
33
- const userDdb = new BetterDDB<User>({
33
+ const userDdb = new BetterDDB({
34
34
  schema: UserSchema,
35
35
  tableName: TEST_TABLE,
36
36
  entityName: ENTITY_NAME,
@@ -30,7 +30,7 @@ const UserSchema = z.object({
30
30
 
31
31
  type User = z.infer<typeof UserSchema>;
32
32
 
33
- const userDdb = new BetterDDB<User>({
33
+ const userDdb = new BetterDDB({
34
34
  schema: UserSchema,
35
35
  tableName: TEST_TABLE,
36
36
  entityName: ENTITY_NAME,
package/test/get.test.ts CHANGED
@@ -31,7 +31,7 @@ const UserSchema = z.object({
31
31
 
32
32
  type User = z.infer<typeof UserSchema>;
33
33
 
34
- const userDdb = new BetterDDB<User>({
34
+ const userDdb = new BetterDDB({
35
35
  schema: UserSchema,
36
36
  tableName: TEST_TABLE,
37
37
  entityName: ENTITY_NAME,
@@ -29,7 +29,7 @@ const UserSchema = z.object({
29
29
 
30
30
  type User = z.infer<typeof UserSchema>;
31
31
 
32
- const userDdb = new BetterDDB<User>({
32
+ const userDdb = new BetterDDB({
33
33
  schema: UserSchema,
34
34
  tableName: TEST_TABLE,
35
35
  entityName: ENTITY_NAME,
@@ -48,7 +48,7 @@ const userDdb = new BetterDDB<User>({
48
48
 
49
49
  beforeAll(async () => {
50
50
  await createTestTable(TEST_TABLE, KEY_SCHEMA, ATTRIBUTE_DEFINITIONS);
51
- // Insert multiple items.
51
+
52
52
  const items = [
53
53
  { id: 'user-1', name: 'Alice', email: 'alice@example.com' },
54
54
  { id: 'user-2', name: 'Alice B', email: 'alice@example.com' },
@@ -67,10 +67,21 @@ describe('BetterDDB - Query Operation', () => {
67
67
  it('should query items using QueryBuilder', async () => {
68
68
  const results = await userDdb.query({ id: 'user-1' })
69
69
  .where('name', 'begins_with', 'Alice')
70
- .limitResults(5);
70
+ .limitResults(5).execute();
71
71
  expect(results.length).toBeGreaterThanOrEqual(1);
72
72
  results.forEach(result => {
73
73
  expect(result.name).toMatch(/^Alice/);
74
74
  });
75
75
  });
76
+
77
+ it('should query items using QueryBuilder with index', async () => {
78
+ const results = await userDdb.query({ email: 'alice@example.com' })
79
+ .usingIndex('EmailIndex')
80
+ .limitResults(1)
81
+ .execute();
82
+ expect(results.length).toEqual(1);
83
+ results.forEach(result => {
84
+ expect(result.email).toEqual('alice@example.com');
85
+ });
86
+ });
76
87
  });
package/test/scan.test.ts CHANGED
@@ -30,7 +30,7 @@ const UserSchema = z.object({
30
30
 
31
31
  type User = z.infer<typeof UserSchema>;
32
32
 
33
- const userDdb = new BetterDDB<User>({
33
+ const userDdb = new BetterDDB({
34
34
  schema: UserSchema,
35
35
  tableName: TEST_TABLE,
36
36
  entityName: ENTITY_NAME,
@@ -61,7 +61,7 @@ describe('BetterDDB - Scan Operation', () => {
61
61
  it('should scan items using ScanBuilder', async () => {
62
62
  const results = await userDdb.scan()
63
63
  .where('email', 'begins_with', 'char')
64
- .limitResults(10);
64
+ .limitResults(10).execute();
65
65
  expect(results.length).toBeGreaterThanOrEqual(1);
66
66
  results.forEach(result => {
67
67
  expect(result.email).toMatch(/^char/i);
@@ -31,7 +31,7 @@ const UserSchema = z.object({
31
31
 
32
32
  type User = z.infer<typeof UserSchema>;
33
33
 
34
- const userDdb = new BetterDDB<User>({
34
+ const userDdb = new BetterDDB({
35
35
  schema: UserSchema,
36
36
  tableName: TEST_TABLE,
37
37
  entityName: ENTITY_NAME,
@@ -14,6 +14,15 @@ export const createTestTable = async (tableName: string, keySchema: CreateTableC
14
14
  KeySchema: keySchema,
15
15
  AttributeDefinitions: attributeDefinitions,
16
16
  BillingMode: 'PAY_PER_REQUEST',
17
+ GlobalSecondaryIndexes: [
18
+ {
19
+ IndexName: 'EmailIndex',
20
+ KeySchema: [{ AttributeName: 'email', KeyType: 'HASH' }],
21
+ Projection: {
22
+ ProjectionType: 'ALL',
23
+ },
24
+ },
25
+ ],
17
26
  });
18
27
  } catch (error: any) {
19
28
  if (error.code === 'ResourceInUseException') {