electrodb 1.12.1 → 2.1.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/.travis.yml +1 -1
- package/README.md +712 -668
- package/index.d.ts +192 -179
- package/package.json +1 -1
- package/src/clauses.js +15 -32
- package/src/entity.js +184 -92
- package/src/errors.js +6 -0
- package/src/filters.js +2 -2
- package/src/operations.js +1 -1
- package/src/schema.js +68 -12
- package/src/service.js +48 -117
- package/src/types.js +15 -2
- package/src/util.js +63 -0
- package/src/validations.js +12 -0
- package/src/where.js +2 -2
- package/notes +0 -45
- package/output +0 -106
- package/taskdata.json +0 -1
- package/typez.ts +0 -1736
package/README.md
CHANGED
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
|
|
12
12
|
------------
|
|
13
13
|
|
|
14
|
+
<h1 align="center">ElectroDB has now reached 2.0.0!</h1>
|
|
15
|
+
|
|
16
|
+
For existing users, checkout the [CHANGELOG](./CHANGELOG.md) and/or the section [Version 2 Migration](#version-2-migration) to learn more about the recent move to 2.0.0 and the changes neccessary to move to the newest version.
|
|
17
|
+
|
|
18
|
+
------------
|
|
19
|
+
|
|
14
20
|
<a href="https://electrodb.fun"><h1 align="center">Introducing: The NEW ElectroDB Playground</h1></a>
|
|
15
21
|
|
|
16
22
|
<p align="center">
|
|
@@ -31,7 +37,7 @@
|
|
|
31
37
|
- [**Simplified Update Expression Composition**](#update-record) - Easily compose type safe update operations without having to format tedious `ExpressionAttributeNames`, `ExpressionAttributeValues`, and `UpdateExpressions`.
|
|
32
38
|
- [**Easily Query Across Entities**](#collections) - Define "collections" to create powerful/idiomatic queries that return multiple entities in a single request.
|
|
33
39
|
- [**Automatic Index Selection**](#find-records) - Use `.find()` or `.match()` methods to dynamically and efficiently query based on defined sort key structures.
|
|
34
|
-
- [**Simplified Pagination API**](#
|
|
40
|
+
- [**Simplified Pagination API**](#entity-pagination) - ElectroDB generates url safe cursors for pagination, allows for fine grain automated pagination, and supports async iteration.
|
|
35
41
|
- [**Use With Your Existing Solution**](#composite-attribute-templates) - If you are already using DynamoDB, and want to use ElectroDB, use custom Composite Attribute Templates to leverage your existing key structures.
|
|
36
42
|
- [**TypeScript Support**](#typescript-support) - Strong **TypeScript** support for both Entities and Services now in Beta.
|
|
37
43
|
- [**Query Directly via the Terminal**](#electro-cli) - Execute queries against your `Entities`, `Services`, `Models` directly from the command line.
|
|
@@ -94,30 +100,25 @@ tasks
|
|
|
94
100
|
|
|
95
101
|
------------
|
|
96
102
|
|
|
103
|
+
## Table of Contents
|
|
97
104
|
- [ElectroDB](#electrodb)
|
|
98
105
|
* [Features](#features)
|
|
99
|
-
* [Table of Contents](#table-of-contents)
|
|
100
106
|
- [Project Goals](#project-goals)
|
|
101
107
|
- [Installation](#installation)
|
|
102
108
|
- [Usage](#usage)
|
|
103
109
|
- [Entities and Services](#entities-and-services)
|
|
110
|
+
- [Getting Started](#getting-started)
|
|
104
111
|
- [Entities](#entities)
|
|
105
112
|
- [Services](#services)
|
|
106
113
|
* [TypeScript Support](#typescript-support)
|
|
107
|
-
+ [
|
|
108
|
-
|
|
109
|
-
- [Independent Models](#independent-models)
|
|
110
|
-
- [Joining Entity instances to a Service](#joining-entity-instances-to-a-service)
|
|
111
|
-
- [Joining models to a Service](#joining-models-to-a-service)
|
|
112
|
-
- [Joining Entities or Models with an alias](#joining-entities-or-models-with-an-alias)
|
|
113
|
-
- [Joining Entities at Service construction for TypeScript](#joining-entities-at-service-construction-for-typescript)
|
|
114
|
+
+ [Services](#services-1)
|
|
115
|
+
- [Joining Entities together](#joining-entities-together)
|
|
114
116
|
* [Model](#model)
|
|
115
117
|
+ [Model Properties](#model-properties)
|
|
116
118
|
+ [Service Options](#service-options)
|
|
117
119
|
* [Attributes](#attributes)
|
|
118
|
-
+ [
|
|
119
|
-
|
|
120
|
-
- [Attribute Definition](#attribute-definition)
|
|
120
|
+
+ [Attribute Definition](#attribute-definition)
|
|
121
|
+
- [Attribute Definition](#attribute-definition-1)
|
|
121
122
|
- [Enum Attributes](#enum-attributes)
|
|
122
123
|
- [Map Attributes](#map-attributes)
|
|
123
124
|
- [List Attributes](#list-attributes)
|
|
@@ -135,7 +136,6 @@ tasks
|
|
|
135
136
|
+ [Indexes With Sort Keys](#indexes-with-sort-keys)
|
|
136
137
|
+ [Numeric Keys](#numeric-keys)
|
|
137
138
|
+ [Index Casing](#index-casing)
|
|
138
|
-
* [Facets](#facets)
|
|
139
139
|
* [Composite Attributes](#composite-attributes)
|
|
140
140
|
+ [Composite Attribute Arrays](#composite-attribute-arrays)
|
|
141
141
|
+ [Composite Attribute Templates](#composite-attribute-templates)
|
|
@@ -150,10 +150,6 @@ tasks
|
|
|
150
150
|
* [Index and Collection Naming Conventions](#index-and-collection-naming-conventions)
|
|
151
151
|
+ [Index Naming Conventions](#index-naming-conventions)
|
|
152
152
|
* [Collection Naming Conventions](#collection-naming-conventions)
|
|
153
|
-
* [Filters](#filters)
|
|
154
|
-
+ [Defined on the model](#defined-on-the-model)
|
|
155
|
-
+ [Defined via Filter method after query operators](#defined-via-filter-method-after-query-operators)
|
|
156
|
-
+ [Multiple Filters](#multiple-filters)
|
|
157
153
|
* [Where](#where)
|
|
158
154
|
+ [FilterExpressions](#filterexpressions)
|
|
159
155
|
+ [ConditionExpressions](#conditionexpressions)
|
|
@@ -167,7 +163,7 @@ tasks
|
|
|
167
163
|
+ [Query App Records](#query-app-records)
|
|
168
164
|
- [Partition Key Composite Attributes](#partition-key-composite-attributes)
|
|
169
165
|
+ [Sort Key Operations](#sort-key-operations)
|
|
170
|
-
* [
|
|
166
|
+
* [Performing Queries](#performing-queries)
|
|
171
167
|
+ [Query Method](#query-method)
|
|
172
168
|
+ [Get Method](#get-method)
|
|
173
169
|
+ [Batch Get](#batch-get)
|
|
@@ -193,14 +189,15 @@ tasks
|
|
|
193
189
|
+ [Match Records](#match-records)
|
|
194
190
|
+ [Access Pattern Queries](#access-pattern-queries)
|
|
195
191
|
- [Begins With Queries](#begins-with-queries)
|
|
196
|
-
* [Collection
|
|
197
|
-
* [
|
|
192
|
+
* [Collection Queries](#collection-queries)
|
|
193
|
+
* [Executing Queries](#executing-queries)
|
|
198
194
|
+ [Params](#params)
|
|
199
195
|
+ [Go](#go)
|
|
200
|
-
+ [Page](#page)
|
|
201
196
|
- [Entity Pagination](#entity-pagination)
|
|
197
|
+
* [Pagination Cursor](#pagination-cursor)
|
|
202
198
|
- [Service Pagination](#service-pagination)
|
|
203
|
-
- [
|
|
199
|
+
- [Pagination Query Options](#pagination-query-options)
|
|
200
|
+
* [Query Option Pager](#query-option-pager)
|
|
204
201
|
* [Pagination Example](#pagination-example)
|
|
205
202
|
* [Query Examples](#query-examples)
|
|
206
203
|
* [Query Options](#query-options)
|
|
@@ -212,7 +209,7 @@ tasks
|
|
|
212
209
|
* [Query Event](#query-event)
|
|
213
210
|
* [Results Event](#results-event)
|
|
214
211
|
- [Listeners](#listeners)
|
|
215
|
-
- [Errors
|
|
212
|
+
- [ElectroDB Errors](#electrodb-errors)
|
|
216
213
|
+ [No Client Defined On Model](#no-client-defined-on-model)
|
|
217
214
|
+ [Invalid Identifier](#invalid-identifier)
|
|
218
215
|
+ [Invalid Key Composite Attribute Template](#invalid-key-composite-attribute-template)
|
|
@@ -236,7 +233,6 @@ tasks
|
|
|
236
233
|
+ [Invalid Attribute](#invalid-attribute)
|
|
237
234
|
+ [AWS Error](#aws-error)
|
|
238
235
|
+ [Unknown Errors](#unknown-errors)
|
|
239
|
-
+ [Invalid Last Evaluated Key](#invalid-last-evaluated-key)
|
|
240
236
|
+ [No Owner For Pager](#no-owner-for-pager)
|
|
241
237
|
+ [Pager Not Unique](#pager-not-unique)
|
|
242
238
|
- [Examples](#examples)
|
|
@@ -275,9 +271,11 @@ tasks
|
|
|
275
271
|
- [TypeScript](#typescript)
|
|
276
272
|
* [Custom Attributes](#custom-attributes)
|
|
277
273
|
* [Exported Types](#exported-types)
|
|
274
|
+
+ [QueryResponse Type](#queryresponse-type)
|
|
278
275
|
+ [EntityRecord Type](#entityrecord-type)
|
|
279
276
|
+ [EntityItem Type](#entityitem-type)
|
|
280
277
|
+ [CollectionItem Type](#collectionitem-type)
|
|
278
|
+
+ [CollectionResponse](#collectionresponse)
|
|
281
279
|
+ [CreateEntityItem Type](#createentityitem-type)
|
|
282
280
|
+ [UpdateEntityItem Type](#updateentityitem-type)
|
|
283
281
|
+ [UpdateAddEntityItem Type](#updateaddentityitem-type)
|
|
@@ -287,11 +285,13 @@ tasks
|
|
|
287
285
|
+ [UpdateDeleteEntityItem Type](#updatedeleteentityitem-type)
|
|
288
286
|
- [Using ElectroDB With Existing Data](#using-electrodb-with-existing-data)
|
|
289
287
|
- [Electro CLI](#electro-cli)
|
|
288
|
+
- [Version 2 Migration](#version-2-migration)
|
|
289
|
+
* [New response format for all query methods.](#new-response-format-for-all-query-methods)
|
|
290
|
+
* [Unified Pagination APIs](#unified-pagination-apis)
|
|
290
291
|
- [Version 1 Migration](#version-1-migration)
|
|
291
292
|
* [New schema format/breaking key format change](#new-schema-format-breaking-key-format-change)
|
|
292
293
|
* [The renaming of index property Facets to Composite and Template](#the-renaming-of-index-property-facets-to-composite-and-template)
|
|
293
294
|
* [Get Method to Return null](#get-method-to-return-null)
|
|
294
|
-
- [Coming Soon](#coming-soon)
|
|
295
295
|
|
|
296
296
|
----------
|
|
297
297
|
|
|
@@ -328,6 +328,9 @@ import { Entity, Service } from "electrodb";
|
|
|
328
328
|
|
|
329
329
|
You can use Entities independent of Services, you do not need to import models into a Service to use them individually. However, If you intend to make queries that `join` or span multiple Entities you will need to use a Service.
|
|
330
330
|
|
|
331
|
+
# Getting Started
|
|
332
|
+
If you're looking to get started right away with ElectroDB, checkout code examples in the `/examples` directory, or for [guided examples](#examples) in this document below. Additionally the section [Building Queries](#building-queries) shows examples of every and has descriptions of all methods available in ElectroDB. If you use TypeScript, the section [TypeScript](#typescript) contains useful exported types to use in your project.
|
|
333
|
+
|
|
331
334
|
# Entities
|
|
332
335
|
|
|
333
336
|
In ***ElectroDB*** an `Entity` is represents a single business object. For example, in a simple task tracking application, one Entity could represent an Employee and or a Task that is assigned to an employee.
|
|
@@ -342,7 +345,7 @@ import { Entity } from "electrodb";
|
|
|
342
345
|
> When using TypeScript, for strong type checking, be sure to either add your model as an object literal to the Entity constructor or create your model using const assertions with the `as const` syntax.
|
|
343
346
|
|
|
344
347
|
# Services
|
|
345
|
-
In ***ElectroDB*** a `Service` represents a collection of related Entities. Services allow you to build queries span across Entities. Similar to Entities, Services can coexist on a single table without collision. You can use Entities independent of Services, you do not need to import models into a Service to use them individually. However, you do you need to use a Service if you intend make queries that `join` multiple Entities.
|
|
348
|
+
In ***ElectroDB*** a `Service` represents a collection of related Entities. Services allow you to build queries that span across Entities. Similar to Entities, Services can coexist on a single table without collision. You can use Entities independent of Services, you do not need to import models into a Service to use them individually. However, you do you need to use a Service if you intend make queries that `join` multiple Entities.
|
|
346
349
|
|
|
347
350
|
Require:
|
|
348
351
|
```javascript
|
|
@@ -366,7 +369,7 @@ If you experience any issues using TypeScript with ElectroDB, your feedback is v
|
|
|
366
369
|
|
|
367
370
|
See the section [Exported TypeScript Types](#exported-typescript-types) to read more about the useful types exported from ElectroDB.
|
|
368
371
|
|
|
369
|
-
###
|
|
372
|
+
### Services
|
|
370
373
|
|
|
371
374
|
New with version `0.10.0` is TypeScript support. To ensure accurate types with, TypeScript users should create their services by passing an Object literal or const object that maps Entity alias names to Entity instances.
|
|
372
375
|
```typescript
|
|
@@ -382,48 +385,7 @@ Services take an optional second parameter, similar to Entities, with a `client`
|
|
|
382
385
|
|
|
383
386
|
While not yet typed, this pattern will also accept Models, or a mix of Entities and Models, in the same object literal format.
|
|
384
387
|
|
|
385
|
-
|
|
386
|
-
When using JavaScript, use `join` to add [Entities](#entities) or [Models](#model) onto a Service.
|
|
387
|
-
|
|
388
|
-
> _NOTE: If using TypeScript, see [Joining Entities at Service construction for TypeScript](#joining-entities-at-service-construction-for-typescript) to learn how to "join" entities for use in a TypeScript project._
|
|
389
|
-
|
|
390
|
-
#### Independent Models
|
|
391
|
-
|
|
392
|
-
```javascript
|
|
393
|
-
let table = "my_table_name";
|
|
394
|
-
let employees = new Entity(EmployeesModel, { client, table });
|
|
395
|
-
let tasks = new Entity(TasksModel, { client, table });
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
#### Joining Entity instances to a Service
|
|
399
|
-
|
|
400
|
-
```javascript
|
|
401
|
-
// Joining Entity instances to a Service
|
|
402
|
-
let TaskApp = new Service("TaskApp", { client, table });
|
|
403
|
-
TaskApp
|
|
404
|
-
.join(employees) // available at TaskApp.entities.employees
|
|
405
|
-
.join(tasks); // available at TaskApp.entities.tasks
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
#### Joining models to a Service
|
|
409
|
-
|
|
410
|
-
```javascript
|
|
411
|
-
let TaskApp = new Service("TaskApp", { client, table });
|
|
412
|
-
TaskApp
|
|
413
|
-
.join(EmployeesModel) // available at TaskApp.entities.employees (based on entity name in model)
|
|
414
|
-
.join(TasksModel); // available at TaskApp.entities.tasks (based on entity name in model)
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
#### Joining Entities or Models with an alias
|
|
418
|
-
|
|
419
|
-
```javascript
|
|
420
|
-
let TaskApp = new Service("TaskApp", { client, table });
|
|
421
|
-
TaskApp
|
|
422
|
-
.join("personnel", EmployeesModel) // available at TaskApp.entities.personnel
|
|
423
|
-
.join("directives", TasksModel); // available at TaskApp.entities.directives
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
#### Joining Entities at Service construction for TypeScript
|
|
388
|
+
#### Joining Entities together
|
|
427
389
|
|
|
428
390
|
```typescript
|
|
429
391
|
let TaskApp = new Service({
|
|
@@ -634,7 +596,7 @@ Property | Description
|
|
|
634
596
|
-------------- | -----------
|
|
635
597
|
model.service | Name of the application using the entity, used to namespace all entities
|
|
636
598
|
model.entity | Name of the entity that the schema represents
|
|
637
|
-
model.version |
|
|
599
|
+
model.version | The version number of the schema, used to namespace keys
|
|
638
600
|
attributes | An object containing each attribute that makes up the schema
|
|
639
601
|
indexes | An object containing table indexes, including the values for the table's default Partition Key and Sort Key
|
|
640
602
|
|
|
@@ -652,15 +614,7 @@ client | (optional) An instance of the `docClient` from the `aws-sdk` for use
|
|
|
652
614
|
> **Pro-Tip:**
|
|
653
615
|
> Using the `field` property, you can map an `AttributeName` to a different field name in your table. This can be useful to utilize existing tables, existing models, or even to reduce record sizes via shorter field names. For example, you may refer to an attribute as `organization` but want to save the attribute with a field name of `org` in DynamoDB.
|
|
654
616
|
|
|
655
|
-
###
|
|
656
|
-
Assign just the `type` of the attribute directly to the attribute name. Types currently supported options are "string", "number", "boolean", an array of strings representing a fixed set of possible values, or "any" which disables value type checking on that attribute.
|
|
657
|
-
```typescript
|
|
658
|
-
attributes: {
|
|
659
|
-
<AttributeName>: "string" | "number" | "boolean" | "list" | "map" | "set" | "any" | string[] | ReadonlyArray<string>
|
|
660
|
-
}
|
|
661
|
-
```
|
|
662
|
-
|
|
663
|
-
### Expanded Syntax
|
|
617
|
+
### Attribute Definition
|
|
664
618
|
Use the expanded syntax build out more robust attribute options.
|
|
665
619
|
```typescript
|
|
666
620
|
attributes: {
|
|
@@ -675,7 +629,11 @@ attributes: {
|
|
|
675
629
|
cast?: "number"|"string"|"boolean";
|
|
676
630
|
get?: (attribute: <type>, schema: any) => <type> | void | undefined;
|
|
677
631
|
set?: (attribute?: <type>, schema?: any) => <type> | void | undefined;
|
|
678
|
-
watch
|
|
632
|
+
watch?: "*" | string[];
|
|
633
|
+
padding?: {
|
|
634
|
+
length: number;
|
|
635
|
+
char: string;
|
|
636
|
+
}
|
|
679
637
|
}
|
|
680
638
|
}
|
|
681
639
|
```
|
|
@@ -684,23 +642,23 @@ attributes: {
|
|
|
684
642
|
|
|
685
643
|
#### Attribute Definition
|
|
686
644
|
|
|
687
|
-
Property | Type | Required | Types
|
|
688
|
-
------------ | :--------------------------------------------------------: | :------: |
|
|
689
|
-
`type` | `string`, `ReadonlyArray<string>`, `string[]` | yes | all
|
|
690
|
-
`required` | `boolean` | no | all
|
|
691
|
-
`hidden` | `boolean` | no | all
|
|
692
|
-
`default` | `value`, `() => value` | no | all
|
|
693
|
-
`validate` | `RegExp`, `(value: any) => void`, `(value: any) => string` | no | all
|
|
694
|
-
`field` | `string` | no | all
|
|
695
|
-
`readOnly` | `boolean` | no | all
|
|
696
|
-
`label` | `string` | no | all
|
|
697
|
-
`
|
|
698
|
-
`set` | `(attribute, schema) => value` | no | all
|
|
699
|
-
`get` | `(attribute, schema) => value` | no | all
|
|
700
|
-
`watch` | `Attribute[], "*"` | no | root-only
|
|
701
|
-
`properties` | `{[key: string]: Attribute}` | yes* | map
|
|
702
|
-
`items` | `Attribute` | yes* | list
|
|
703
|
-
`items` | "string" | "number" | yes* | set
|
|
645
|
+
Property | Type | Required | Types | Description
|
|
646
|
+
------------ | :--------------------------------------------------------: | :------: | :------------: | -----------
|
|
647
|
+
`type` | `string`, `ReadonlyArray<string>`, `string[]` | yes | all | Accepts the values: `"string"`, `"number"` `"boolean"`, `"map"`, `"list"`, `"set"`, an array of strings representing a finite list of acceptable values: `["option1", "option2", "option3"]`, or `"any"` which disables value type checking on that attribute.
|
|
648
|
+
`required` | `boolean` | no | all | Flag an attribute as required to be present when creating a record. This attribute also acts as a type of `NOT NULL` flag, preventing it from being removed directly. When applied to nested properties, be mindful that default map values can cause required child attributes to fail validation.
|
|
649
|
+
`hidden` | `boolean` | no | all | Flag an attribute as hidden to remove the property from results before they are returned.
|
|
650
|
+
`default` | `value`, `() => value` | no | all | Either the default value itself or a synchronous function that returns the desired value. Applied before `set` and before `required` check. In the case of nested attributes, default values will apply defaults to children attributes until an undefined value is reached
|
|
651
|
+
`validate` | `RegExp`, `(value: any) => void`, `(value: any) => string` | no | all | Either regex or a synchronous callback to return an error string (will result in exception using the string as the error's message), or thrown exception in the event of an error.
|
|
652
|
+
`field` | `string` | no | all | The name of the attribute as it exists in DynamoDB, if named differently in the schema attributes. Defaults to the `AttributeName` as defined in the schema.
|
|
653
|
+
`readOnly` | `boolean` | no | all | Prevents an attribute from being updated after the record has been created. Attributes used in the composition of the table's primary Partition Key and Sort Key are read-only by default. The one exception to `readOnly` is for properties that also use the `watch` property, read [attribute watching](#attribute-watching) for more detail.
|
|
654
|
+
`label` | `string` | no | all | Used in index key composition to prefix key composite attributes. By default, the `AttributeName` is used as the label.
|
|
655
|
+
`padding` | `{ length: number; char: string; }` | no | string, number | Similar to `label`, this property only impacts the attribute's value during index key composition. Padding allows you to define a string pattern to left pad your attribute when ElectroDB builds your partition or sort key. This can be helpful to implementing zero-padding patterns with numbers and strings in sort keys. Note, this will _not_ impact your attribute's stored value, if you want to transform the attribute's field value, use the `set` callback described below.
|
|
656
|
+
`set` | `(attribute, schema) => value` | no | all | A synchronous callback allowing you to apply changes to a value before it is set in params or applied to the database. First value represents the value passed to ElectroDB, second value are the attributes passed on that update/put
|
|
657
|
+
`get` | `(attribute, schema) => value` | no | all | A synchronous callback allowing you to apply changes to a value after it is retrieved from the database. First value represents the value passed to ElectroDB, second value are the attributes retrieved from the database.
|
|
658
|
+
`watch` | `Attribute[], "*"` | no | root-only | Define other attributes that will always trigger your attribute's getter and setter callback after their getter/setter callbacks are executed. Only available on root level attributes.
|
|
659
|
+
`properties` | `{[key: string]: Attribute}` | yes* | map | Define the properties available on a `"map"` attribute, required if your attribute is a map. Syntax for map properties is the same as root level attributes.
|
|
660
|
+
`items` | `Attribute` | yes* | list | Define the attribute type your list attribute will contain, required if your attribute is a list. Syntax for list items is the same as a single attribute.
|
|
661
|
+
`items` | "string" | "number" | yes* | set | Define the primitive type your set attribute will contain, required if your attribute is a set. Unlike lists, a set defines it's items with a string of either "string" or "number".
|
|
704
662
|
|
|
705
663
|
#### Enum Attributes
|
|
706
664
|
|
|
@@ -851,7 +809,7 @@ If your attributes needs to watch for any changes to an item, you can model this
|
|
|
851
809
|
```typescript
|
|
852
810
|
myAttr: {
|
|
853
811
|
type: "string",
|
|
854
|
-
watch: "*", // "watch all"
|
|
812
|
+
watch: "*", // <- "watch all"
|
|
855
813
|
set: (myAttr, allAttributes) => {
|
|
856
814
|
// Whenever an `update` or `patch` operation is performed, this callback will be fired.
|
|
857
815
|
// Note: myAttr or the attributes under `allAttributes` could be independently undefined because either attribute could have triggered this callback
|
|
@@ -1073,8 +1031,6 @@ signature | behavior
|
|
|
1073
1031
|
`(value: T) => boolean` | If a boolean value is returned, `true` or truthy values will signify than a value is invalid while `false` or falsey will be considered valid.
|
|
1074
1032
|
`(value: T) => void` | A void or `undefined` value is returned, will be treated as successful, in this scenario you can throw an Error yourself to interrupt the query
|
|
1075
1033
|
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
1034
|
## Indexes
|
|
1079
1035
|
When using ElectroDB, indexes are referenced by their `AccessPatternName`. This allows you to maintain generic index names on your DynamoDB table, but reference domain specific names while using your ElectroDB Entity. These will often be referenced as _"Access Patterns"_.
|
|
1080
1036
|
|
|
@@ -1082,6 +1038,8 @@ All DynamoDB table start with at least a PartitionKey with an optional SortKey,
|
|
|
1082
1038
|
|
|
1083
1039
|
In your model, the _Table Index_ this is expressed as an _Access Pattern_ *without* an `index` property. For Secondary Indexes (both GSIs and LSIs), use the `index` property to define the name of the index as defined on your DynamoDB table.
|
|
1084
1040
|
|
|
1041
|
+
> _NOTE: The 'index' property is simply a mapping of your AccessPatternName to your DynamoDB index name. ElectroDB does not create or alter DynamoDB tables, so your indexes will need to be created prior to use_
|
|
1042
|
+
|
|
1085
1043
|
Within these _AccessPatterns_, you define the PartitionKey and (optionally) SortKeys that are present on your DynamoDB table and map the key's name on the table with the `field` property.
|
|
1086
1044
|
|
|
1087
1045
|
```typescript
|
|
@@ -1108,14 +1066,14 @@ indexes: {
|
|
|
1108
1066
|
| `pk` | `object` | yes | Configuration for the pk of that index or table
|
|
1109
1067
|
| `pk.composite` | `string[]` | yes | An array that represents the order in which attributes are concatenated to composite attributes the key (see [Composite Attributes](#composite-attributes) below for more on this functionality).
|
|
1110
1068
|
| `pk.template` | `string` | no | A string that represents the template in which attributes composed to form a key (see [Composite Attribute Templates](#composite-attribute-templates) below for more on this functionality).
|
|
1111
|
-
| `pk.field` | `string` | yes | The name of the
|
|
1069
|
+
| `pk.field` | `string` | yes | The name of the index Partition Key field as it exists in DynamoDB, if named differently in the schema attributes.
|
|
1112
1070
|
| `pk.casing` | `default`, `upper`, `lower`, `none` | no | Choose a case for ElectroDB to convert your keys to, to avoid casing pitfalls when querying data. Default: `lower`.
|
|
1113
1071
|
| `sk` | `object` | no | Configuration for the sk of that index or table
|
|
1114
1072
|
| `sk.composite` | `string[]` | no | Either an Array that represents the order in which attributes are concatenated to composite attributes the key, or a String for a composite attribute template. (see [Composite Attributes](#composite-attributes) below for more on this functionality).
|
|
1115
1073
|
| `sk.template` | `string` | no | A string that represents the template in which attributes composed to form a key (see [Composite Attribute Templates](#composite-attribute-templates) below for more on this functionality).
|
|
1116
|
-
| `sk.field` | `string` | yes | The name of the
|
|
1074
|
+
| `sk.field` | `string` | yes | The name of the index Sort Key field as it exists in DynamoDB, if named differently in the schema attributes.
|
|
1117
1075
|
| `pk.casing` | `default`, `upper`, `lower`, `none`, | no | Choose a case for ElectroDB to convert your keys to, to avoid casing pitfalls when querying data. Default: `lower`.
|
|
1118
|
-
| `index` | `string` | no | Required when the `Index` defined is a *Secondary Index*; but is
|
|
1076
|
+
| `index` | `string` | no | Required when the `Index` defined is a *Global/Local Secondary Index*; but is omitted for the table's primary index.
|
|
1119
1077
|
| `collection` | `string`, `string[]` | no | Used when models are joined to a `Service`. When two entities share a `collection` on the same `index`, they can be queried with one request to DynamoDB. The name of the collection should represent what the query would return as a pseudo `Entity`. (see [Collections](#collections) below for more on this functionality).
|
|
1120
1078
|
|
|
1121
1079
|
### Indexes Without Sort Keys
|
|
@@ -1237,14 +1195,6 @@ Casing Option | Effect
|
|
|
1237
1195
|
`upper` | Will convert the key to uppercase prior it its use
|
|
1238
1196
|
`none` | Will not perform any casing changes when building keys
|
|
1239
1197
|
|
|
1240
|
-
## Facets
|
|
1241
|
-
|
|
1242
|
-
As of version `0.11.1`, "Facets" have been renamed to "Composite Attributes", and all documentation has been updated to reflect that change.
|
|
1243
|
-
|
|
1244
|
-
- To learn about the latest syntax, checkout [Composite Attributes](#composite-attributes).
|
|
1245
|
-
- To learn about why this change was made in preparation for 1.0 checkout [Renaming Facets](#the-renaming-of-index-property-facets-to-composite-and-template).
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
1198
|
## Composite Attributes
|
|
1249
1199
|
A **Composite Attribute** is a segment of a key based on one of the attributes. **Composite Attributes** are concatenated together from either a **Partition Key**, or a **Sort Key** key, which define an `index`.
|
|
1250
1200
|
|
|
@@ -1496,7 +1446,7 @@ Another approach allows you to use the `template` property, which allows you to
|
|
|
1496
1446
|
"your_access_pattern_name": {
|
|
1497
1447
|
pk: {
|
|
1498
1448
|
field: "accountId",
|
|
1499
|
-
composite: ["accountId"],
|
|
1449
|
+
composite: ["accountId"],
|
|
1500
1450
|
template: "${accountId}"
|
|
1501
1451
|
},
|
|
1502
1452
|
sk: {...}
|
|
@@ -1778,8 +1728,11 @@ let results = await TaskApp.collections
|
|
|
1778
1728
|
.go();
|
|
1779
1729
|
|
|
1780
1730
|
{
|
|
1781
|
-
|
|
1731
|
+
data: {
|
|
1732
|
+
tasks: [...], // tasks for employeeId "JExotic"
|
|
1782
1733
|
employees: [...] // employee record(s) with employeeId "JExotic"
|
|
1734
|
+
},
|
|
1735
|
+
cursor: null
|
|
1783
1736
|
}
|
|
1784
1737
|
```
|
|
1785
1738
|
|
|
@@ -1997,8 +1950,11 @@ const results = await TaskApp.collections
|
|
|
1997
1950
|
|
|
1998
1951
|
// results
|
|
1999
1952
|
{
|
|
2000
|
-
|
|
2001
|
-
|
|
1953
|
+
data: {
|
|
1954
|
+
tasks: [...], // tasks associated with projectId "SD-204
|
|
1955
|
+
projectMembers: [...] // employees of project "SD-204"
|
|
1956
|
+
},
|
|
1957
|
+
cursor: null,
|
|
2002
1958
|
}
|
|
2003
1959
|
|
|
2004
1960
|
// parameters
|
|
@@ -2026,9 +1982,12 @@ const results = await TaskApp.collections
|
|
|
2026
1982
|
|
|
2027
1983
|
// results
|
|
2028
1984
|
{
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
1985
|
+
data: {
|
|
1986
|
+
tasks: [...], // tasks assigned to employeeId "JExotic"
|
|
1987
|
+
projectMembers: [...], // projects with employeeId "JExotic"
|
|
1988
|
+
employees: [...] // employee record(s) with employeeId "JExotic"
|
|
1989
|
+
},
|
|
1990
|
+
cursor: null,
|
|
2032
1991
|
}
|
|
2033
1992
|
|
|
2034
1993
|
{
|
|
@@ -2050,8 +2009,11 @@ const results = await TaskApp.collections
|
|
|
2050
2009
|
|
|
2051
2010
|
// results
|
|
2052
2011
|
{
|
|
2053
|
-
|
|
2054
|
-
|
|
2012
|
+
data: {
|
|
2013
|
+
tasks: [...], // tasks assigned to employeeId "JExotic"
|
|
2014
|
+
projectMembers: [...], // projects with employeeId "JExotic"
|
|
2015
|
+
},
|
|
2016
|
+
cursor: null,
|
|
2055
2017
|
}
|
|
2056
2018
|
|
|
2057
2019
|
{
|
|
@@ -2158,195 +2120,6 @@ For example, the `contributions` collection is named such because when given an
|
|
|
2158
2120
|
|
|
2159
2121
|
In the case of `assignments`, we receive a subset of `contributions` when supplying an `employeeId`: Only the tasks and projects they are "assigned" are returned.
|
|
2160
2122
|
|
|
2161
|
-
## Filters
|
|
2162
|
-
|
|
2163
|
-
> Filters are no longer the preferred way to add FilterExpressions. Checkout the [Where](#where) section to find out about how to apply FilterExpressions and ConditionExpressions.
|
|
2164
|
-
|
|
2165
|
-
Building thoughtful indexes can make queries simple and performant. Sometimes you need to filter results down further. By adding Filters to your model, you can extend your queries with custom filters. Below is the traditional way you would add a filter to Dynamo's DocumentClient directly alongside how you would accomplish the same using a Filter function.
|
|
2166
|
-
|
|
2167
|
-
```json
|
|
2168
|
-
{
|
|
2169
|
-
"IndexName": "idx2",
|
|
2170
|
-
"TableName": "StoreDirectory",
|
|
2171
|
-
"ExpressionAttributeNames": {
|
|
2172
|
-
"#rent": "rent",
|
|
2173
|
-
"#discount": "discount",
|
|
2174
|
-
"#pk": "idx2pk",
|
|
2175
|
-
"#sk1": "idx2sk"
|
|
2176
|
-
},
|
|
2177
|
-
"ExpressionAttributeValues": {
|
|
2178
|
-
":rent1": "2000.00",
|
|
2179
|
-
":rent2": "5000.00",
|
|
2180
|
-
":discount1": "1000.00",
|
|
2181
|
-
":pk": "$mallstoredirectory_1#mallid_eastpointe",
|
|
2182
|
-
":sk1": "$mallstore#leaseenddate_2020-04-01#rent_",
|
|
2183
|
-
":sk2": "$mallstore#leaseenddate_2020-07-01#rent_"
|
|
2184
|
-
},
|
|
2185
|
-
"KeyConditionExpression": ",#pk = :pk and #sk1 BETWEEN :sk1 AND :sk2",
|
|
2186
|
-
"FilterExpression": "(#rent between :rent1 and :rent2) AND #discount <= :discount1"
|
|
2187
|
-
}
|
|
2188
|
-
```
|
|
2189
|
-
### Defined on the model
|
|
2190
|
-
|
|
2191
|
-
> Deprecated but functional with 1.x
|
|
2192
|
-
|
|
2193
|
-
Filters can be defined on the model and used in your query chain.
|
|
2194
|
-
|
|
2195
|
-
```javascript
|
|
2196
|
-
/**
|
|
2197
|
-
* Filter by low rent a specific mall or a leaseEnd withing a specific range
|
|
2198
|
-
* @param {Object} attributes - All attributes from the model with methods for each filter operation
|
|
2199
|
-
* @param {...*} values - Values passed when calling the filter in a query chain.
|
|
2200
|
-
**/
|
|
2201
|
-
filters: {
|
|
2202
|
-
rentPromotions: function(attributes, minRent, maxRent, promotion) {
|
|
2203
|
-
let {rent, discount} = attributes;
|
|
2204
|
-
return `
|
|
2205
|
-
${rent.between(minRent, maxRent)} AND ${discount.lte(promotion)}
|
|
2206
|
-
`
|
|
2207
|
-
}
|
|
2208
|
-
}
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
let StoreLocations = new Entity(model, {table: "StoreDirectory"});
|
|
2212
|
-
let maxRent = "5000.00";
|
|
2213
|
-
let minRent = "2000.00";
|
|
2214
|
-
let promotion = "1000.00";
|
|
2215
|
-
let stores = await MallStores.query
|
|
2216
|
-
.stores({ mallId: "EastPointe" })
|
|
2217
|
-
.between({ leaseEndDate: "2020-04-01" }, { leaseEndDate: "2020-07-01" })
|
|
2218
|
-
.rentPromotions(minRent, maxRent, promotion)
|
|
2219
|
-
.go();
|
|
2220
|
-
|
|
2221
|
-
// Equivalent Parameters
|
|
2222
|
-
{
|
|
2223
|
-
IndexName: 'idx2',
|
|
2224
|
-
TableName: 'StoreDirectory',
|
|
2225
|
-
ExpressionAttributeNames: {
|
|
2226
|
-
'#rent': 'rent',
|
|
2227
|
-
'#discount': 'discount',
|
|
2228
|
-
'#pk': 'idx2pk',
|
|
2229
|
-
'#sk1': 'idx2sk'
|
|
2230
|
-
},
|
|
2231
|
-
ExpressionAttributeValues: {
|
|
2232
|
-
':rent1': '2000.00',
|
|
2233
|
-
':rent2': '5000.00',
|
|
2234
|
-
':discount1': '1000.00',
|
|
2235
|
-
':pk': '$mallstoredirectory_1#mallid_eastpointe',
|
|
2236
|
-
':sk1': '$mallstore#leaseenddate_2020-04-01#rent_',
|
|
2237
|
-
':sk2': '$mallstore#leaseenddate_2020-07-01#rent_'
|
|
2238
|
-
},
|
|
2239
|
-
KeyConditionExpression: '#pk = :pk and #sk1 BETWEEN :sk1 AND :sk2',
|
|
2240
|
-
FilterExpression: '(#rent between :rent1 and :rent2) AND #discount <= :discount1'
|
|
2241
|
-
}
|
|
2242
|
-
```
|
|
2243
|
-
### Defined via Filter method after query operators
|
|
2244
|
-
|
|
2245
|
-
> Filters are no longer the preferred way to add FilterExpressions. Checkout the [Where](#where) section to find out about how to apply FilterExpressions and ConditionExpressions.
|
|
2246
|
-
|
|
2247
|
-
The easiest way to use filters is to use them inline in your query chain.
|
|
2248
|
-
|
|
2249
|
-
```javascript
|
|
2250
|
-
let StoreLocations = new Entity(model, {table: "StoreDirectory"});
|
|
2251
|
-
let maxRent = "5000.00";
|
|
2252
|
-
let minRent = "2000.00";
|
|
2253
|
-
let promotion = "1000.00";
|
|
2254
|
-
let stores = await StoreLocations.query
|
|
2255
|
-
.leases({ mallId: "EastPointe" })
|
|
2256
|
-
.between({ leaseEndDate: "2020-04-01" }, { leaseEndDate: "2020-07-01" })
|
|
2257
|
-
.filter(({rent, discount}) => `
|
|
2258
|
-
${rent.between(minRent, maxRent)} AND ${discount.lte(promotion)}
|
|
2259
|
-
`)
|
|
2260
|
-
.go();
|
|
2261
|
-
|
|
2262
|
-
// Equivalent Parameters
|
|
2263
|
-
{
|
|
2264
|
-
IndexName: 'idx2',
|
|
2265
|
-
TableName: 'StoreDirectory',
|
|
2266
|
-
ExpressionAttributeNames: {
|
|
2267
|
-
'#rent': 'rent',
|
|
2268
|
-
'#discount': 'discount',
|
|
2269
|
-
'#pk': 'idx2pk',
|
|
2270
|
-
'#sk1': 'idx2sk'
|
|
2271
|
-
},
|
|
2272
|
-
ExpressionAttributeValues: {
|
|
2273
|
-
':rent1': '2000.00',
|
|
2274
|
-
':rent2': '5000.00',
|
|
2275
|
-
':discount1': '1000.00',
|
|
2276
|
-
':pk': '$mallstoredirectory_1#mallid_eastpointe',
|
|
2277
|
-
':sk1': '$mallstore#leaseenddate_2020-04-01#rent_',
|
|
2278
|
-
':sk2': '$mallstore#leaseenddate_2020-07-01#rent_'
|
|
2279
|
-
},
|
|
2280
|
-
KeyConditionExpression: '#pk = :pk and #sk1 BETWEEN :sk1 AND :sk2',
|
|
2281
|
-
FilterExpression: '(#rent between :rent1 and :rent2) AND #discount <= :discount1'
|
|
2282
|
-
}
|
|
2283
|
-
```
|
|
2284
|
-
|
|
2285
|
-
Filter functions allow you to write a `FilterExpression` without having to worry about the complexities of expression attributes. To accomplish this, ElectroDB injects an object `attributes` as the first parameter to all Filter Functions. This object contains every Attribute defined in the Entity's Model with the following operators as methods:
|
|
2286
|
-
|
|
2287
|
-
operator | example | result
|
|
2288
|
-
| ----------- | -------------------------------- |
|
|
2289
|
-
`gte` | `rent.gte(maxRent)` | `#rent >= :rent1`
|
|
2290
|
-
`gt` | `rent.gt(maxRent)` | `#rent > :rent1`
|
|
2291
|
-
`lte` | `rent.lte(maxRent)` | `#rent <= :rent1`
|
|
2292
|
-
`lt` | `rent.lt(maxRent)` | `#rent < :rent1`
|
|
2293
|
-
`eq` | `rent.eq(maxRent)` | `#rent = :rent1`
|
|
2294
|
-
`ne` | `rent.ne(maxRent)` | `#rent <> :rent1`
|
|
2295
|
-
`begins` | `rent.begins(maxRent)` | `begins_with(#rent, :rent1)`
|
|
2296
|
-
`exists` | `rent.exists()` | `attribute_exists(#rent)`
|
|
2297
|
-
`notExists` | `rent.notExists()` | `attribute_not_exists(#rent)`
|
|
2298
|
-
`contains` | `rent.contains(maxRent)` | `contains(#rent = :rent1)`
|
|
2299
|
-
`notContains` | `rent.notContains(maxRent)` | `not contains(#rent = :rent1)`
|
|
2300
|
-
`between` | `rent.between(minRent, maxRent)` | `(#rent between :rent1 and :rent2)`
|
|
2301
|
-
`name` | `rent.name()` | `#rent`
|
|
2302
|
-
`value` | `rent.value(maxRent)` | `:rent1`
|
|
2303
|
-
|
|
2304
|
-
This functionality allows you to write the remaining logic of your `FilterExpression` with ease. Add complex nested `and`/`or` conditions or other `FilterExpression` logic while ElectroDB handles the `ExpressionAttributeNames` and `ExpressionAttributeValues`.
|
|
2305
|
-
|
|
2306
|
-
### Multiple Filters
|
|
2307
|
-
|
|
2308
|
-
> Filters are no longer the preferred way to add FilterExpressions. Checkout the [Where](#where) section to find out about how to apply FilterExpressions and ConditionExpressions.
|
|
2309
|
-
|
|
2310
|
-
It is possible to chain together multiple filters. The resulting FilterExpressions are concatenated with an implicit `AND` operator.
|
|
2311
|
-
|
|
2312
|
-
```javascript
|
|
2313
|
-
let MallStores = new Entity(model, {table: "StoreDirectory"});
|
|
2314
|
-
let stores = await MallStores.query
|
|
2315
|
-
.leases({ mallId: "EastPointe" })
|
|
2316
|
-
.between({ leaseEndDate: "2020-04-01" }, { leaseEndDate: "2020-07-01" })
|
|
2317
|
-
.filter(({ rent, discount }) => `
|
|
2318
|
-
${rent.between("2000.00", "5000.00")} AND ${discount.eq("1000.00")}
|
|
2319
|
-
`)
|
|
2320
|
-
.filter(({ category }) => `
|
|
2321
|
-
${category.eq("food/coffee")}
|
|
2322
|
-
`)
|
|
2323
|
-
.go();
|
|
2324
|
-
|
|
2325
|
-
// Equivalent Parameters
|
|
2326
|
-
{
|
|
2327
|
-
TableName: 'StoreDirectory',
|
|
2328
|
-
ExpressionAttributeNames: {
|
|
2329
|
-
'#rent': 'rent',
|
|
2330
|
-
'#discount': 'discount',
|
|
2331
|
-
'#category': 'category',
|
|
2332
|
-
'#pk': 'idx2pk',
|
|
2333
|
-
'#sk1': 'idx2sk'
|
|
2334
|
-
},
|
|
2335
|
-
ExpressionAttributeValues: {
|
|
2336
|
-
':rent1': '2000.00',
|
|
2337
|
-
':rent2': '5000.00',
|
|
2338
|
-
':discount1': '1000.00',
|
|
2339
|
-
':category1': 'food/coffee',
|
|
2340
|
-
':pk': '$mallstoredirectory_1#mallid_eastpointe',
|
|
2341
|
-
':sk1': '$mallstore#leaseenddate_2020-04-01#storeid_',
|
|
2342
|
-
':sk2': '$mallstore#leaseenddate_2020-07-01#storeid_'
|
|
2343
|
-
},
|
|
2344
|
-
KeyConditionExpression: '#pk = :pk and #sk1 BETWEEN :sk1 AND :sk2',
|
|
2345
|
-
IndexName: 'idx2',
|
|
2346
|
-
FilterExpression: '(#rent between :rent1 and :rent2) AND (#discount = :discount1 AND #category = :category1)'
|
|
2347
|
-
}
|
|
2348
|
-
```
|
|
2349
|
-
|
|
2350
2123
|
## Where
|
|
2351
2124
|
|
|
2352
2125
|
> The `where()` method is an improvement on the `filter()` method. Unlike `filter`, `where` will be compatible with upcoming features related to complex types.
|
|
@@ -2626,7 +2399,7 @@ attributes | string[] | _(all attributes)_ | The `attributes` option allo
|
|
|
2626
2399
|
|
|
2627
2400
|
ElectroDB queries use DynamoDB's `query` method to find records based on your table's indexes.
|
|
2628
2401
|
|
|
2629
|
-
> _NOTE:
|
|
2402
|
+
> _NOTE: To limit the number of items ElectroDB will retrieve, read more about the [Query Options](#query-options) `pages` and `limit`, or use the ElectroDB [Pagination API](#page) for fine-grain pagination support._
|
|
2630
2403
|
|
|
2631
2404
|
Forming a composite **Partition Key** and **Sort Key** is a critical step in planning **Access Patterns** in **DynamoDB**. When planning composite keys, it is crucial to consider the order in which they are *composed*. As of the time of writing this documentation, **DynamoDB** has the following constraints that should be taken into account when planning your **Access Patterns**:
|
|
2632
2405
|
1. You must always supply the **Partition Key** in full for all queries to **DynamoDB**.
|
|
@@ -2827,7 +2600,7 @@ The `StoreLocations` entity above, using just the `stores` **Index** alone enabl
|
|
|
2827
2600
|
3. All `LatteLarrys` locations inside a specific *Mall*
|
|
2828
2601
|
4. A specific `LatteLarrys` inside of a *Mall* and *Building*
|
|
2829
2602
|
|
|
2830
|
-
##
|
|
2603
|
+
## Performing Queries
|
|
2831
2604
|
Queries in ***ElectroDB*** are built around the **Access Patterns** defined in the Schema and are capable of using partial key **Composite Attributes** to create performant lookups. To accomplish this, ***ElectroDB*** offers a predictable chainable API.
|
|
2832
2605
|
|
|
2833
2606
|
> Examples in this section using the `StoreLocations` schema defined [above](#shopping-mall-stores) and can be directly experiment with on runkit: https://runkit.com/tywalch/electrodb-building-queries
|
|
@@ -2838,13 +2611,14 @@ The methods: Get (`get`), Create (`put`), Update (`update`), and Delete (`delete
|
|
|
2838
2611
|
|
|
2839
2612
|
ElectroDB queries use DynamoDB's `query` method to find records based on your table's indexes. To read more about queries checkout the section [Building Queries](#building-queries)
|
|
2840
2613
|
|
|
2841
|
-
> _NOTE:
|
|
2614
|
+
> _NOTE: To limit the number of items ElectroDB will retrieve, read more about the [Query Options](#query-options) `pages` and `limit`, or use the ElectroDB [Pagination API](#page) for fine-grain pagination support._
|
|
2842
2615
|
|
|
2843
2616
|
### Get Method
|
|
2844
2617
|
Provide all Table Index composite attributes in an object to the `get` method. In the event no record is found, a value of `null` will be returned.
|
|
2845
2618
|
|
|
2846
2619
|
> _NOTE: As part of ElectroDB's roll out of 1.0.0, a breaking change was made to the `get` method. Prior to 1.0.0, the `get` method would return an empty object if a record was not found. This has been changed to now return a value of `null` in this case._
|
|
2847
2620
|
|
|
2621
|
+
Example:
|
|
2848
2622
|
```javascript
|
|
2849
2623
|
let results = await StoreLocations.get({
|
|
2850
2624
|
storeId: "LatteLarrys",
|
|
@@ -2852,22 +2626,30 @@ let results = await StoreLocations.get({
|
|
|
2852
2626
|
buildingId: "F34",
|
|
2853
2627
|
cityId: "Atlanta1"
|
|
2854
2628
|
}).go();
|
|
2629
|
+
```
|
|
2630
|
+
Response Format:
|
|
2631
|
+
```typescript
|
|
2632
|
+
{
|
|
2633
|
+
data: Array<YOUR_SCHEMA>,
|
|
2634
|
+
cursor: string | undefined
|
|
2635
|
+
}
|
|
2636
|
+
```
|
|
2855
2637
|
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2638
|
+
Equivalent DocClient Parameters:
|
|
2639
|
+
```json
|
|
2640
|
+
{
|
|
2641
|
+
"Key": {
|
|
2642
|
+
"pk": "$mallstoredirectory#cityid_atlanta1#mallid_eastpointe",
|
|
2643
|
+
"sk": "$mallstore_1#buildingid_f34#storeid_lattelarrys"
|
|
2644
|
+
},
|
|
2645
|
+
"TableName": "YOUR_TABLE_NAME"
|
|
2646
|
+
}
|
|
2864
2647
|
```
|
|
2865
2648
|
|
|
2866
2649
|
### Batch Get
|
|
2867
2650
|
Provide all Table Index composite attributes in an array of objects to the `get` method to perform a BatchGet query.
|
|
2868
2651
|
|
|
2869
|
-
> _NOTE:
|
|
2870
|
-
> Additionally, when performing a BatchGet the `.params()` method will return an _array_ of parameters, rather than just the parameters for one docClient query. This is because ElectroDB BatchGet queries larger than the docClient's limit of 100 records.
|
|
2652
|
+
> _NOTE: When performing a BatchGet the `.params()` method will return an _array_ of parameters, rather than just the parameters for one docClient query. This is because ElectroDB BatchGet allows queries larger than the docClient's limit of 100 records.
|
|
2871
2653
|
|
|
2872
2654
|
If the number of records you are requesting is above the BatchGet threshold of 100 records, ElectroDB will make multiple requests to DynamoDB and return the results in a single array. By default, ElectroDB will make these requests in series, one after another. If you are confident your table can handle the throughput, you can use the [Query Option](#query-options) `concurrent`. This value can be set to any number greater than zero, and will execute that number of requests simultaneously.
|
|
2873
2655
|
|
|
@@ -2879,6 +2661,7 @@ If you set the [Query Option](#query-options) `concurrent` to `2`, ElectroDB wil
|
|
|
2879
2661
|
|
|
2880
2662
|
It is important to consider your Table's throughput considerations when setting this value.
|
|
2881
2663
|
|
|
2664
|
+
Example:
|
|
2882
2665
|
```javascript
|
|
2883
2666
|
let [results, unprocessed] = await StoreLocations.get([
|
|
2884
2667
|
{
|
|
@@ -2894,24 +2677,34 @@ let [results, unprocessed] = await StoreLocations.get([
|
|
|
2894
2677
|
cityId: "Madison2"
|
|
2895
2678
|
}
|
|
2896
2679
|
]).go({concurrent: 1}); // `concurrent` value is optional and default's to `1`
|
|
2680
|
+
```
|
|
2897
2681
|
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2682
|
+
Response Format:
|
|
2683
|
+
```typescript
|
|
2684
|
+
{
|
|
2685
|
+
data: Array<YOUR_SCHEMA>,
|
|
2686
|
+
unprocessed: Array<YOUR_COMPOSITE_ATTRIBUTES>
|
|
2687
|
+
}
|
|
2688
|
+
```
|
|
2689
|
+
|
|
2690
|
+
Equivalent DocClient Parameters:
|
|
2691
|
+
```json
|
|
2692
|
+
{
|
|
2693
|
+
"RequestItems": {
|
|
2694
|
+
"YOUR_TABLE_NAME": {
|
|
2695
|
+
"Keys": [
|
|
2696
|
+
{
|
|
2697
|
+
"pk": "$mallstoredirectory#cityid_atlanta1#mallid_eastpointe",
|
|
2698
|
+
"sk": "$mallstore_1#buildingid_f34#storeid_lattelarrys"
|
|
2699
|
+
},
|
|
2700
|
+
{
|
|
2701
|
+
"pk": "$mallstoredirectory#cityid_madison2#mallid_westend",
|
|
2702
|
+
"sk": "$mallstore_1#buildingid_a21#storeid_mochajoes"
|
|
2703
|
+
}
|
|
2704
|
+
]
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
2915
2708
|
```
|
|
2916
2709
|
|
|
2917
2710
|
The two-dimensional array returned by batch get most easily used when deconstructed into two variables, in the above case: `results` and `unprocessed`.
|
|
@@ -2925,6 +2718,7 @@ Elements of the `unprocessed` array are unlike results received from a query. In
|
|
|
2925
2718
|
### Delete Method
|
|
2926
2719
|
Provide all Table Index composite attributes in an object to the `delete` method to delete a record.
|
|
2927
2720
|
|
|
2721
|
+
Example:
|
|
2928
2722
|
```javascript
|
|
2929
2723
|
await StoreLocations.delete({
|
|
2930
2724
|
storeId: "LatteLarrys",
|
|
@@ -2932,15 +2726,24 @@ await StoreLocations.delete({
|
|
|
2932
2726
|
buildingId: "F34",
|
|
2933
2727
|
cityId: "Atlanta1"
|
|
2934
2728
|
}).go();
|
|
2729
|
+
```
|
|
2935
2730
|
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2731
|
+
Response Format:
|
|
2732
|
+
```typescript
|
|
2733
|
+
{
|
|
2734
|
+
data: { YOUR_SCHEMA }
|
|
2735
|
+
}
|
|
2736
|
+
```
|
|
2737
|
+
|
|
2738
|
+
Equivalent DocClient Parameters:
|
|
2739
|
+
```json
|
|
2740
|
+
{
|
|
2741
|
+
"Key": {
|
|
2742
|
+
"pk": "$mallstoredirectory#cityid_atlanta1#mallid_eastpointe",
|
|
2743
|
+
"sk": "$mallstore_1#buildingid_f34#storeid_lattelarrys"
|
|
2744
|
+
},
|
|
2745
|
+
"TableName": "YOUR_TABLE_NAME"
|
|
2746
|
+
}
|
|
2944
2747
|
```
|
|
2945
2748
|
|
|
2946
2749
|
### Batch Write Delete Records
|
|
@@ -2959,6 +2762,7 @@ If you set the [Query Option](#query-options) `concurrent` to `2`, ElectroDB wil
|
|
|
2959
2762
|
|
|
2960
2763
|
It is important to consider your Table's throughput considerations when setting this value.
|
|
2961
2764
|
|
|
2765
|
+
Example:
|
|
2962
2766
|
```javascript
|
|
2963
2767
|
let unprocessed = await StoreLocations.delete([
|
|
2964
2768
|
{
|
|
@@ -2974,8 +2778,17 @@ let unprocessed = await StoreLocations.delete([
|
|
|
2974
2778
|
cityId: "LosAngeles1"
|
|
2975
2779
|
}
|
|
2976
2780
|
]).go({concurrent: 1}); // `concurrent` value is optional and default's to `1`
|
|
2781
|
+
```
|
|
2782
|
+
|
|
2783
|
+
Response Format:
|
|
2784
|
+
```typescript
|
|
2785
|
+
{
|
|
2786
|
+
unprocessed: Array<YOUR_COMPOSITE_ATTRIBUTES>
|
|
2787
|
+
}
|
|
2788
|
+
```
|
|
2977
2789
|
|
|
2978
|
-
|
|
2790
|
+
Equivalent DocClient Parameters:
|
|
2791
|
+
```json
|
|
2979
2792
|
{
|
|
2980
2793
|
"RequestItems": {
|
|
2981
2794
|
"StoreDirectory": [
|
|
@@ -3005,7 +2818,9 @@ Elements of the `unprocessed` array are unlike results received from a query. In
|
|
|
3005
2818
|
### Put Record
|
|
3006
2819
|
Provide all *required* Attributes as defined in the model to create a new record. **ElectroDB** will enforce any defined validations, defaults, casting, and field aliasing. A Put operation will trigger the `default`, and `set` attribute callbacks when writing to DynamoDB. By default, after performing a `put()` or `create()` operation, ElectroDB will format and return the record through the same process as a Get/Query. This process will invoke the `get` callback on all included attributes. If this behaviour is not desired, use the [Query Option](#query-options) `response:"none"` to return a null value.
|
|
3007
2820
|
|
|
3008
|
-
This example includes an optional conditional expression
|
|
2821
|
+
Note: This example includes an optional conditional expression
|
|
2822
|
+
|
|
2823
|
+
Example:
|
|
3009
2824
|
```javascript
|
|
3010
2825
|
await StoreLocations
|
|
3011
2826
|
.put({
|
|
@@ -3020,8 +2835,17 @@ await StoreLocations
|
|
|
3020
2835
|
})
|
|
3021
2836
|
.where((attr, op) => op.eq(attr.rent, "4500.00"))
|
|
3022
2837
|
.go()
|
|
2838
|
+
```
|
|
3023
2839
|
|
|
3024
|
-
|
|
2840
|
+
Response Format:
|
|
2841
|
+
```typescript
|
|
2842
|
+
{
|
|
2843
|
+
data: { YOUR_SCHEMA }
|
|
2844
|
+
}
|
|
2845
|
+
```
|
|
2846
|
+
|
|
2847
|
+
Equivalent DocClient Parameters:
|
|
2848
|
+
```json
|
|
3025
2849
|
{
|
|
3026
2850
|
"Item": {
|
|
3027
2851
|
"cityId": "Atlanta1",
|
|
@@ -3069,6 +2893,7 @@ If you set the [Query Option](#query-options) `concurrent` to `2`, ElectroDB wil
|
|
|
3069
2893
|
|
|
3070
2894
|
It is important to consider your Table's throughput considerations when setting this value.
|
|
3071
2895
|
|
|
2896
|
+
Example:
|
|
3072
2897
|
```javascript
|
|
3073
2898
|
let unprocessed = await StoreLocations.put([
|
|
3074
2899
|
{
|
|
@@ -3092,8 +2917,17 @@ let unprocessed = await StoreLocations.put([
|
|
|
3092
2917
|
rent: "1500.00"
|
|
3093
2918
|
}
|
|
3094
2919
|
]).go({concurrent: 1}); // `concurrent` value is optional and default's to `1`
|
|
2920
|
+
```
|
|
3095
2921
|
|
|
3096
|
-
|
|
2922
|
+
Response Format:
|
|
2923
|
+
```typescript
|
|
2924
|
+
{
|
|
2925
|
+
unprocessed: Array<YOUR_COMPOSITE_ATTRIBUTES>
|
|
2926
|
+
}
|
|
2927
|
+
```
|
|
2928
|
+
|
|
2929
|
+
Equivalent DocClient Parameters:
|
|
2930
|
+
```json
|
|
3097
2931
|
{
|
|
3098
2932
|
"RequestItems": {
|
|
3099
2933
|
"StoreDirectory": [
|
|
@@ -3229,11 +3063,23 @@ For the defined indexes:
|
|
|
3229
3063
|
|
|
3230
3064
|
A user could update `attr4` alone because ElectroDB is able to leverage the value for `attr2` from values supplied to the `update()` method:
|
|
3231
3065
|
|
|
3066
|
+
|
|
3067
|
+
Example:
|
|
3232
3068
|
```typescript
|
|
3233
3069
|
entity.update({ attr1: "value1", attr2: "value2" })
|
|
3234
3070
|
.set({ attr4: "value4" })
|
|
3235
3071
|
.go();
|
|
3072
|
+
```
|
|
3073
|
+
|
|
3074
|
+
Response Format:
|
|
3075
|
+
```typescript
|
|
3076
|
+
{
|
|
3077
|
+
data: { YOUR_SCHEMA }
|
|
3078
|
+
}
|
|
3079
|
+
```
|
|
3236
3080
|
|
|
3081
|
+
Equivalent DocClient Parameters:
|
|
3082
|
+
```json
|
|
3237
3083
|
{
|
|
3238
3084
|
"UpdateExpression": "SET #attr4 = :attr4_u0, #gsi1sk = :gsi1sk_u0, #attr1 = :attr1_u0, #attr2 = :attr2_u0",
|
|
3239
3085
|
"ExpressionAttributeNames": {
|
|
@@ -3249,7 +3095,7 @@ entity.update({ attr1: "value1", attr2: "value2" })
|
|
|
3249
3095
|
":attr1_u0": "value1",
|
|
3250
3096
|
":attr2_u0": "value2"
|
|
3251
3097
|
},
|
|
3252
|
-
"TableName": "
|
|
3098
|
+
"TableName": "YOUR_TABLE_NAME",
|
|
3253
3099
|
"Key": {
|
|
3254
3100
|
"pk": "$service#attr1_value1",
|
|
3255
3101
|
"sk": "$entity_version#attr2_value2"
|
|
@@ -3263,14 +3109,24 @@ entity.update({ attr1: "value1", attr2: "value2" })
|
|
|
3263
3109
|
|
|
3264
3110
|
The `set()` method will accept all attributes defined on the model. Provide a value to apply or replace onto the item.
|
|
3265
3111
|
|
|
3112
|
+
Example:
|
|
3266
3113
|
```javascript
|
|
3267
3114
|
await StoreLocations
|
|
3268
3115
|
.update({cityId, mallId, storeId, buildingId})
|
|
3269
3116
|
.set({category: "food/meal"})
|
|
3270
3117
|
.where((attr, op) => op.eq(attr.category, "food/coffee"))
|
|
3271
3118
|
.go()
|
|
3119
|
+
```
|
|
3120
|
+
|
|
3121
|
+
Response Format:
|
|
3122
|
+
```typescript
|
|
3123
|
+
{
|
|
3124
|
+
data: { YOUR_SCHEMA }
|
|
3125
|
+
}
|
|
3126
|
+
```
|
|
3272
3127
|
|
|
3273
|
-
|
|
3128
|
+
Equivalent DocClient Parameters:
|
|
3129
|
+
```json
|
|
3274
3130
|
{
|
|
3275
3131
|
"UpdateExpression": "SET #category = :category",
|
|
3276
3132
|
"ExpressionAttributeNames": {
|
|
@@ -3295,14 +3151,24 @@ The `remove()` method will accept all attributes defined on the model. Unlike mo
|
|
|
3295
3151
|
|
|
3296
3152
|
> _NOTE that the attribute property `required` functions as a sort of `NOT NULL` flag. Because of this, if a property exists as `required:true` it will not be possible to _remove_ that property in particular. If the attribute is a property is on "map", and the "map" is not required, then the "map" _can_ be removed._
|
|
3297
3153
|
|
|
3154
|
+
Example:
|
|
3298
3155
|
```javascript
|
|
3299
3156
|
await StoreLocations
|
|
3300
3157
|
.update({cityId, mallId, storeId, buildingId})
|
|
3301
3158
|
.remove(["category"])
|
|
3302
3159
|
.where((attr, op) => op.eq(attr.category, "food/coffee"))
|
|
3303
3160
|
.go()
|
|
3161
|
+
```
|
|
3304
3162
|
|
|
3305
|
-
|
|
3163
|
+
Response Format:
|
|
3164
|
+
```typescript
|
|
3165
|
+
{
|
|
3166
|
+
data: { YOUR_SCHEMA }
|
|
3167
|
+
}
|
|
3168
|
+
```
|
|
3169
|
+
|
|
3170
|
+
Equivalent DocClient Parameters:
|
|
3171
|
+
```json
|
|
3306
3172
|
{
|
|
3307
3173
|
"UpdateExpression": "REMOVE #category",
|
|
3308
3174
|
"ExpressionAttributeNames": {
|
|
@@ -3326,6 +3192,7 @@ The `add()` method will accept attributes with type `number`, `set`, and `any` d
|
|
|
3326
3192
|
|
|
3327
3193
|
If the attribute is defined as `any`, the syntax compatible with the attribute type `set` will be used. For this reason, do not use the attribute type `any` to represent a `number`.
|
|
3328
3194
|
|
|
3195
|
+
Example:
|
|
3329
3196
|
```javascript
|
|
3330
3197
|
const newTenant = client.createSet("larry");
|
|
3331
3198
|
|
|
@@ -3337,8 +3204,17 @@ await StoreLocations
|
|
|
3337
3204
|
})
|
|
3338
3205
|
.where((attr, op) => op.eq(attr.category, "food/coffee"))
|
|
3339
3206
|
.go()
|
|
3207
|
+
```
|
|
3340
3208
|
|
|
3341
|
-
|
|
3209
|
+
Response Format:
|
|
3210
|
+
```typescript
|
|
3211
|
+
{
|
|
3212
|
+
data: { YOUR_SCHEMA }
|
|
3213
|
+
}
|
|
3214
|
+
```
|
|
3215
|
+
|
|
3216
|
+
Equivalent DocClient Parameters:
|
|
3217
|
+
```json
|
|
3342
3218
|
{
|
|
3343
3219
|
"UpdateExpression": "SET #rent = #rent + :rent0 ADD #tenant :tenant0",
|
|
3344
3220
|
"ExpressionAttributeNames": {
|
|
@@ -3364,14 +3240,24 @@ await StoreLocations
|
|
|
3364
3240
|
|
|
3365
3241
|
The `subtract()` method will accept attributes with type `number`. In the case of a `number` attribute, provide a number to _subtract_ from the existing attribute's value on the item.
|
|
3366
3242
|
|
|
3243
|
+
Example:
|
|
3367
3244
|
```javascript
|
|
3368
3245
|
await StoreLocations
|
|
3369
3246
|
.update({cityId, mallId, storeId, buildingId})
|
|
3370
3247
|
.subtract({deposit: 500})
|
|
3371
3248
|
.where((attr, op) => op.eq(attr.category, "food/coffee"))
|
|
3372
3249
|
.go()
|
|
3250
|
+
```
|
|
3251
|
+
|
|
3252
|
+
Response Format:
|
|
3253
|
+
```typescript
|
|
3254
|
+
{
|
|
3255
|
+
data: { YOUR_SCHEMA }
|
|
3256
|
+
}
|
|
3257
|
+
```
|
|
3373
3258
|
|
|
3374
|
-
|
|
3259
|
+
Equivalent DocClient Parameters:
|
|
3260
|
+
```json
|
|
3375
3261
|
{
|
|
3376
3262
|
"UpdateExpression": "SET #deposit = #deposit - :deposit0",
|
|
3377
3263
|
"ExpressionAttributeNames": {
|
|
@@ -3395,6 +3281,7 @@ await StoreLocations
|
|
|
3395
3281
|
|
|
3396
3282
|
The `append()` method will accept attributes with type `any`. This is a convenience method for working with DynamoDB lists, and is notably different that [`set`](#update-method-set) because it will add an element to an existing array, rather than overwrite the existing value.
|
|
3397
3283
|
|
|
3284
|
+
Example:
|
|
3398
3285
|
```javascript
|
|
3399
3286
|
await StoreLocations
|
|
3400
3287
|
.update({cityId, mallId, storeId, buildingId})
|
|
@@ -3406,8 +3293,17 @@ await StoreLocations
|
|
|
3406
3293
|
})
|
|
3407
3294
|
.where((attr, op) => op.eq(attr.category, "food/coffee"))
|
|
3408
3295
|
.go()
|
|
3296
|
+
```
|
|
3297
|
+
|
|
3298
|
+
Response Format:
|
|
3299
|
+
```typescript
|
|
3300
|
+
{
|
|
3301
|
+
data: { YOUR_SCHEMA }
|
|
3302
|
+
}
|
|
3303
|
+
```
|
|
3409
3304
|
|
|
3410
|
-
|
|
3305
|
+
Equivalent DocClient Parameters:
|
|
3306
|
+
```json
|
|
3411
3307
|
{
|
|
3412
3308
|
"UpdateExpression": "SET #rentalAgreement = list_append(#rentalAgreement, :rentalAgreement0)",
|
|
3413
3309
|
"ExpressionAttributeNames": {
|
|
@@ -3436,14 +3332,24 @@ await StoreLocations
|
|
|
3436
3332
|
|
|
3437
3333
|
The `delete()` method will accept attributes with type `any` or `set` . This operation removes items from a the `contract` attribute, defined as a `set` attribute.
|
|
3438
3334
|
|
|
3335
|
+
Example:
|
|
3439
3336
|
```javascript
|
|
3440
3337
|
await StoreLocations
|
|
3441
3338
|
.update({cityId, mallId, storeId, buildingId})
|
|
3442
3339
|
.delete({contact: ['555-345-2222']})
|
|
3443
3340
|
.where((attr, op) => op.eq(attr.category, "food/coffee"))
|
|
3444
3341
|
.go()
|
|
3342
|
+
```
|
|
3343
|
+
|
|
3344
|
+
Response Format:
|
|
3345
|
+
```typescript
|
|
3346
|
+
{
|
|
3347
|
+
data: { YOUR_SCHEMA }
|
|
3348
|
+
}
|
|
3349
|
+
```
|
|
3445
3350
|
|
|
3446
|
-
|
|
3351
|
+
Equivalent DocClient Parameters:
|
|
3352
|
+
```json
|
|
3447
3353
|
{
|
|
3448
3354
|
"UpdateExpression": "DELETE #contact :contact0",
|
|
3449
3355
|
"ExpressionAttributeNames": {
|
|
@@ -3485,6 +3391,7 @@ operation | example | result
|
|
|
3485
3391
|
`value` | `value(rent, amount)` | `:rent1` | Create a reference to a particular value, can be passed to other operation that allows leveraging existing attribute values in calculating new values
|
|
3486
3392
|
`ifNotExists` | `ifNotExists(rent, amount)` | `#rent = if_not_exists(#rent, :rent0)` | Update a property's value only if that property doesn't yet exist on the record
|
|
3487
3393
|
|
|
3394
|
+
Example:
|
|
3488
3395
|
```javascript
|
|
3489
3396
|
await StoreLocations
|
|
3490
3397
|
.update({cityId, mallId, storeId, buildingId})
|
|
@@ -3506,8 +3413,17 @@ await StoreLocations
|
|
|
3506
3413
|
})
|
|
3507
3414
|
.where((attr, op) => op.eq(attr.category, "food/coffee"))
|
|
3508
3415
|
.go()
|
|
3416
|
+
```
|
|
3509
3417
|
|
|
3510
|
-
|
|
3418
|
+
Response Format:
|
|
3419
|
+
```typescript
|
|
3420
|
+
{
|
|
3421
|
+
data: { YOUR_SCHEMA }
|
|
3422
|
+
}
|
|
3423
|
+
```
|
|
3424
|
+
|
|
3425
|
+
Equivalent DocClient Parameters:
|
|
3426
|
+
```json
|
|
3511
3427
|
{
|
|
3512
3428
|
"UpdateExpression": "SET #category = :category_u0, #rent = #rent + :rent_u0, #deposit = #deposit - :deposit_u0, #rentalAgreement = list_append(#rentalAgreement, :rentalAgreement_u0), #totalFees = #totalFees + #petFee REMOVE #leaseEndDate, #gsi2sk ADD #tenant :tenant_u0, #leaseHolders :tenant_u0 DELETE #tags :tags_u0, #contact :contact_u0",
|
|
3513
3429
|
"ExpressionAttributeNames": {
|
|
@@ -3619,6 +3535,7 @@ When scanning for rows, you can use filters the same as you would any query. For
|
|
|
3619
3535
|
|
|
3620
3536
|
*Note: `Scan` functionality will be scoped to your Entity. This means your results will only include records that match the Entity defined in the model.*
|
|
3621
3537
|
|
|
3538
|
+
Example:
|
|
3622
3539
|
```javascript
|
|
3623
3540
|
await StoreLocations.scan
|
|
3624
3541
|
.where(({category}, {eq}) => `
|
|
@@ -3628,8 +3545,18 @@ await StoreLocations.scan
|
|
|
3628
3545
|
${between(leaseEndDate, "2020-03", "2020-04")}
|
|
3629
3546
|
`)
|
|
3630
3547
|
.go()
|
|
3548
|
+
```
|
|
3549
|
+
|
|
3550
|
+
Response Format:
|
|
3551
|
+
```typescript
|
|
3552
|
+
{
|
|
3553
|
+
data: Array<YOUR_SCHEMA>,
|
|
3554
|
+
cursor: string | undefined
|
|
3555
|
+
}
|
|
3556
|
+
```
|
|
3631
3557
|
|
|
3632
|
-
|
|
3558
|
+
Equivalent DocClient Parameters:
|
|
3559
|
+
```json
|
|
3633
3560
|
{
|
|
3634
3561
|
"TableName": "StoreDirectory",
|
|
3635
3562
|
"ExpressionAttributeNames": {
|
|
@@ -3664,20 +3591,68 @@ await StoreLocations.remove({
|
|
|
3664
3591
|
buildingId: "F34",
|
|
3665
3592
|
cityId: "Atlanta1"
|
|
3666
3593
|
}).go();
|
|
3594
|
+
```
|
|
3667
3595
|
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3596
|
+
Response Format:
|
|
3597
|
+
```typescript
|
|
3598
|
+
{
|
|
3599
|
+
data: { YOUR_SCHEMA }
|
|
3600
|
+
}
|
|
3601
|
+
```
|
|
3602
|
+
|
|
3603
|
+
Equivalent DocClient Parameters:
|
|
3604
|
+
```json
|
|
3605
|
+
{
|
|
3606
|
+
"Key": {
|
|
3607
|
+
"pk": "$mallstoredirectory#cityid_atlanta1#mallid_eastpointe",
|
|
3608
|
+
"sk": "$mallstore_1#buildingid_f34#storeid_lattelarrys"
|
|
3609
|
+
},
|
|
3610
|
+
"TableName": "YOUR_TABLE_TABLE"
|
|
3611
|
+
"ConditionExpression": "attribute_exists(pk) AND attribute_exists(sk)"
|
|
3612
|
+
}
|
|
3677
3613
|
```
|
|
3678
3614
|
|
|
3679
3615
|
### Patch Record
|
|
3680
3616
|
|
|
3617
|
+
```javascript
|
|
3618
|
+
await entity.update({ attr1: "value1", attr2: "value2" })
|
|
3619
|
+
.set({ attr4: "value4" })
|
|
3620
|
+
.go();
|
|
3621
|
+
```
|
|
3622
|
+
|
|
3623
|
+
Response Format:
|
|
3624
|
+
```typescript
|
|
3625
|
+
{
|
|
3626
|
+
data: { YOUR_SCHEMA }
|
|
3627
|
+
}
|
|
3628
|
+
```
|
|
3629
|
+
|
|
3630
|
+
Equivalent DocClient Parameters:
|
|
3631
|
+
```json
|
|
3632
|
+
{
|
|
3633
|
+
"UpdateExpression": "SET #attr4 = :attr4_u0, #gsi1sk = :gsi1sk_u0, #attr1 = :attr1_u0, #attr2 = :attr2_u0",
|
|
3634
|
+
"ExpressionAttributeNames": {
|
|
3635
|
+
"#attr4": "attr4",
|
|
3636
|
+
"#gsi1sk": "gsi1sk",
|
|
3637
|
+
"#attr1": "attr1",
|
|
3638
|
+
"#attr2": "attr2"
|
|
3639
|
+
},
|
|
3640
|
+
"ExpressionAttributeValues": {
|
|
3641
|
+
":attr4_u0": "value6",
|
|
3642
|
+
// This index was successfully built
|
|
3643
|
+
":gsi1sk_u0": "$update-edgecases_1#attr2_value2#attr4_value6",
|
|
3644
|
+
":attr1_u0": "value1",
|
|
3645
|
+
":attr2_u0": "value2"
|
|
3646
|
+
},
|
|
3647
|
+
"TableName": "YOUR_TABLE_NAME",
|
|
3648
|
+
"Key": {
|
|
3649
|
+
"pk": "$service#attr1_value1",
|
|
3650
|
+
"sk": "$entity_version#attr2_value2"
|
|
3651
|
+
},
|
|
3652
|
+
"ConditionExpression": "attribute_exists(pk) AND attribute_exists(sk)"
|
|
3653
|
+
}
|
|
3654
|
+
```
|
|
3655
|
+
|
|
3681
3656
|
In DynamoDB, `update` operations by default will insert a record if record being updated does not exist. In **_ElectroDB_**, the `patch` method will utilize the `attribute_exists()` parameter dynamically to ensure records are only "patched" and not inserted when updating.
|
|
3682
3657
|
|
|
3683
3658
|
For more detail on how to use the `patch()` method, see the section [Update Record](#update-record) to see all the transferable requirements and capabilities available to `patch()`.
|
|
@@ -3688,6 +3663,7 @@ In DynamoDB, `put` operations by default will overwrite a record if record being
|
|
|
3688
3663
|
|
|
3689
3664
|
A Put operation will trigger the `default`, and `set` attribute callbacks when writing to DynamoDB. By default, after writing to DynamoDB, ElectroDB will format and return the record through the same process as a Get/Query, which will invoke the `get` callback on all included attributes. If this behaviour is not desired, use the [Query Option](#query-options) `response:"none"` to return a null value.
|
|
3690
3665
|
|
|
3666
|
+
Example:
|
|
3691
3667
|
```javascript
|
|
3692
3668
|
await StoreLocations
|
|
3693
3669
|
.create({
|
|
@@ -3702,8 +3678,17 @@ await StoreLocations
|
|
|
3702
3678
|
})
|
|
3703
3679
|
.where((attr, op) => op.eq(attr.rent, "4500.00"))
|
|
3704
3680
|
.go()
|
|
3681
|
+
```
|
|
3705
3682
|
|
|
3706
|
-
|
|
3683
|
+
Response Format:
|
|
3684
|
+
```typescript
|
|
3685
|
+
{
|
|
3686
|
+
data: { YOUR_SCHEMA }
|
|
3687
|
+
}
|
|
3688
|
+
```
|
|
3689
|
+
|
|
3690
|
+
Equivalent DocClient Parameters:
|
|
3691
|
+
```json
|
|
3707
3692
|
{
|
|
3708
3693
|
"Item": {
|
|
3709
3694
|
"cityId": "Atlanta1",
|
|
@@ -3743,13 +3728,24 @@ DynamoDB offers three methods to query records: `get`, `query`, and `scan`. In *
|
|
|
3743
3728
|
|
|
3744
3729
|
The Find method is useful when the index chosen does not matter or is not known. If your secondary indexes do not contain all attributes then this method might not be right for you. The mechanism that picks the best index for a given payload is subject to improvement and change without triggering a breaking change release version.
|
|
3745
3730
|
|
|
3731
|
+
Example:
|
|
3746
3732
|
```javascript
|
|
3747
3733
|
await StoreLocations.find({
|
|
3748
3734
|
mallId: "EastPointe",
|
|
3749
3735
|
buildingId: "BuildingA1",
|
|
3750
3736
|
}).go()
|
|
3737
|
+
```
|
|
3738
|
+
|
|
3739
|
+
Response Format:
|
|
3740
|
+
```typescript
|
|
3741
|
+
{
|
|
3742
|
+
data: Array<YOUR_SCHEMA>,
|
|
3743
|
+
cursor: string | undefined
|
|
3744
|
+
}
|
|
3745
|
+
```
|
|
3751
3746
|
|
|
3752
|
-
|
|
3747
|
+
Equivalent DocClient Parameters:
|
|
3748
|
+
```json
|
|
3753
3749
|
{
|
|
3754
3750
|
"KeyConditionExpression": "#pk = :pk and begins_with(#sk1, :sk1)",
|
|
3755
3751
|
"TableName": "StoreDirectory",
|
|
@@ -3777,7 +3773,7 @@ Match is a convenience method based off of ElectroDB's [find](#find-records) met
|
|
|
3777
3773
|
|
|
3778
3774
|
Match differs from [Find](#find-records) in that it will also include all supplied values into a query filter.
|
|
3779
3775
|
|
|
3780
|
-
|
|
3776
|
+
Example:
|
|
3781
3777
|
```javascript
|
|
3782
3778
|
await StoreLocations.find({
|
|
3783
3779
|
mallId: "EastPointe",
|
|
@@ -3785,8 +3781,18 @@ await StoreLocations.find({
|
|
|
3785
3781
|
leaseEndDate: "2020-03-22",
|
|
3786
3782
|
rent: "1500.00"
|
|
3787
3783
|
}).go()
|
|
3784
|
+
```
|
|
3785
|
+
|
|
3786
|
+
Response Format:
|
|
3787
|
+
```typescript
|
|
3788
|
+
{
|
|
3789
|
+
data: Array<YOUR_SCHEMA>,
|
|
3790
|
+
cursor: string | undefined
|
|
3791
|
+
}
|
|
3792
|
+
```
|
|
3788
3793
|
|
|
3789
|
-
|
|
3794
|
+
Equivalent DocClient Parameters:
|
|
3795
|
+
```json
|
|
3790
3796
|
{
|
|
3791
3797
|
"KeyConditionExpression": "#pk = :pk and begins_with(#sk1, :sk1)",
|
|
3792
3798
|
"TableName": "StoreDirectory",
|
|
@@ -3876,7 +3882,7 @@ The second example allows you to make queries that do include buildings such as
|
|
|
3876
3882
|
|
|
3877
3883
|
For these reasons it is important to consider that attributes passed to the Access Pattern method are considered to be full, known, data.
|
|
3878
3884
|
|
|
3879
|
-
## Collection
|
|
3885
|
+
## Collection Queries
|
|
3880
3886
|
Collections allow you to query across Entities. They can be used on `Service` instance.
|
|
3881
3887
|
|
|
3882
3888
|
```javascript
|
|
@@ -4019,7 +4025,7 @@ TaskApp.collections
|
|
|
4019
4025
|
}
|
|
4020
4026
|
```
|
|
4021
4027
|
|
|
4022
|
-
##
|
|
4028
|
+
## Executing Queries
|
|
4023
4029
|
Lastly, all query chains end with either a `.go()`, `.params()`, or `page()` method invocation. These terminal methods will either execute the query to DynamoDB (`.go()`) or return formatted parameters for use with the DynamoDB docClient (`.params()`).
|
|
4024
4030
|
|
|
4025
4031
|
Both `.params()` and `.go()` take a query configuration object which is detailed more in the section [Query Options](#query-options).
|
|
@@ -4072,126 +4078,73 @@ let stores = MallStores.query
|
|
|
4072
4078
|
|
|
4073
4079
|
```
|
|
4074
4080
|
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
> _NOTE: By Default, ElectroDB queries will paginate through all results with the [`go()`](#building-queries) method. ElectroDB's `page()` method can be used to manually iterate through DynamoDB query results._
|
|
4078
|
-
|
|
4079
|
-
The `page` method _ends_ a query chain, and asynchronously queries DynamoDB with the `client` provided in the model. Unlike the `.go()`, the `.page()` method returns a tuple.
|
|
4080
|
-
|
|
4081
|
-
The first element for a page query is the "pager": an object contains the composite attributes that make up the `ExclusiveStartKey` that is returned by the DynamoDB client. This is very useful in multi-tenant applications where only some composite attributes are exposed to the client, or there is a need to prevent leaking keys between entities. If there is no `ExclusiveStartKey` this value will be null. On subsequent calls to `.page()`, pass the results returned from the previous call to `.page()` or construct the composite attributes yourself.
|
|
4082
|
-
|
|
4083
|
-
The "pager" includes the associated entity's Identifiers.
|
|
4084
|
-
|
|
4085
|
-
> _NOTE: It is *highly recommended* to use the [query option](#query-options) `pager: "raw""` flag when using `.page()` with `scan` operations. This is because when using scan on large tables the docClient may return an `ExclusiveStartKey` for a record that does not belong to entity making the query (regardless of the filters set). In these cases ElectroDB will return null (to avoid leaking the keys of other entities) when further pagination may be needed to find your records._
|
|
4081
|
+
#### Entity Pagination
|
|
4086
4082
|
|
|
4087
|
-
|
|
4083
|
+
##### Pagination Cursor
|
|
4088
4084
|
|
|
4089
|
-
|
|
4085
|
+
All ElectroDB `query` and `scan` operations return a `cursor`, which is a stringified and copy of DynamoDB's `LastEvaluatedKey` with a `base64url` encoding.
|
|
4090
4086
|
|
|
4091
|
-
|
|
4087
|
+
The terminal method `go()` accepts a `cursor` when executing a `query` or `scan` to continue paginating for more results. Pass the cursor from the previous query to your next query and ElectroDB will continue its pagination where it left off.
|
|
4092
4088
|
|
|
4093
|
-
```
|
|
4094
|
-
|
|
4089
|
+
```typescript
|
|
4090
|
+
const results1 = await MallStores.query
|
|
4095
4091
|
.leases({ mallId })
|
|
4096
|
-
.
|
|
4092
|
+
.go(); // no "cursor" passed to `.go()`
|
|
4097
4093
|
|
|
4098
|
-
|
|
4094
|
+
const results2 = await MallStores.query
|
|
4099
4095
|
.leases({ mallId })
|
|
4100
|
-
.
|
|
4101
|
-
|
|
4102
|
-
// page:
|
|
4103
|
-
// {
|
|
4104
|
-
// storeId: "LatteLarrys",
|
|
4105
|
-
// mallId: "EastPointe",
|
|
4106
|
-
// buildingId: "BuildingA1",
|
|
4107
|
-
// unitId: "B47"
|
|
4108
|
-
// __edb_e__: "MallStore",
|
|
4109
|
-
// __edb_v__: "version"
|
|
4110
|
-
// }
|
|
4111
|
-
|
|
4112
|
-
// stores
|
|
4113
|
-
// [{
|
|
4114
|
-
// mall: '3010aa0d-5591-4664-8385-3503ece58b1c',
|
|
4115
|
-
// leaseEnd: '2020-01-20',
|
|
4116
|
-
// sector: '7d0f5c19-ec1d-4c1e-b613-a4cc07eb4db5',
|
|
4117
|
-
// store: 'MNO',
|
|
4118
|
-
// unit: 'B5',
|
|
4119
|
-
// id: 'e0705325-d735-4fe4-906e-74091a551a04',
|
|
4120
|
-
// building: 'BuildingE',
|
|
4121
|
-
// category: 'food/coffee',
|
|
4122
|
-
// rent: '0.00'
|
|
4123
|
-
// },
|
|
4124
|
-
// {
|
|
4125
|
-
// mall: '3010aa0d-5591-4664-8385-3503ece58b1c',
|
|
4126
|
-
// leaseEnd: '2020-01-20',
|
|
4127
|
-
// sector: '7d0f5c19-ec1d-4c1e-b613-a4cc07eb4db5',
|
|
4128
|
-
// store: 'ZYX',
|
|
4129
|
-
// unit: 'B9',
|
|
4130
|
-
// id: 'f201a1d3-2126-46a2-aec9-758ade8ab2ab',
|
|
4131
|
-
// building: 'BuildingI',
|
|
4132
|
-
// category: 'food/coffee',
|
|
4133
|
-
// rent: '0.00'
|
|
4134
|
-
// }]
|
|
4135
|
-
```
|
|
4136
|
-
|
|
4137
|
-
#### Service Pagination
|
|
4138
|
-
|
|
4139
|
-
> _NOTE: By Default, ElectroDB will paginate through all results with the [`query()`](#building-queries) method. ElectroDB's `page()` method can be used to manually iterate through DynamoDB query results._
|
|
4140
|
-
|
|
4141
|
-
Pagination with services is also possible. Similar to [Entity Pagination](#entity-pagination), calling the `.page()` method returns a `[pager, results]` tuple. Also, similar to pagination on Entities, the pager object returned by default is a deconstruction of the returned LastEvaluatedKey.
|
|
4142
|
-
|
|
4143
|
-
#### Pager Query Options
|
|
4096
|
+
.go({cursor: results1.cursor}); // Paginate by querying with the "cursor" from your first query
|
|
4144
4097
|
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
A notable Query Option, that is available only to the `.page()` method, is an option called `pager`. This property defines the post-processing ElectroDB should perform on a returned `LastEvaluatedKey`, as well as how ElectroDB should interpret an _incoming_ pager, to use as an ExclusiveStartKey.
|
|
4148
|
-
|
|
4149
|
-
> _NOTE: Because the "pager" object is destructured from the keys DynamoDB returns as the `LastEvaluatedKey`, these composite attributes differ from the record's actual attribute values in one important way: Their string values will all be lowercase. If you intend to use these attributes in ways where their casing _will_ matter (e.g. in a `where` filter), keep in mind this may result in unexpected outcomes._
|
|
4150
|
-
|
|
4151
|
-
The three options for the query option `pager` are as follows:
|
|
4152
|
-
|
|
4153
|
-
```javascript
|
|
4154
|
-
// LastEvaluatedKey
|
|
4098
|
+
// results1
|
|
4155
4099
|
{
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4100
|
+
cursor: '...'
|
|
4101
|
+
data: [{
|
|
4102
|
+
mall: '3010aa0d-5591-4664-8385-3503ece58b1c',
|
|
4103
|
+
leaseEnd: '2020-01-20',
|
|
4104
|
+
sector: '7d0f5c19-ec1d-4c1e-b613-a4cc07eb4db5',
|
|
4105
|
+
store: 'MNO',
|
|
4106
|
+
unit: 'B5',
|
|
4107
|
+
id: 'e0705325-d735-4fe4-906e-74091a551a04',
|
|
4108
|
+
building: 'BuildingE',
|
|
4109
|
+
category: 'food/coffee',
|
|
4110
|
+
rent: '0.00'
|
|
4111
|
+
},
|
|
4112
|
+
{
|
|
4113
|
+
mall: '3010aa0d-5591-4664-8385-3503ece58b1c',
|
|
4114
|
+
leaseEnd: '2020-01-20',
|
|
4115
|
+
sector: '7d0f5c19-ec1d-4c1e-b613-a4cc07eb4db5',
|
|
4116
|
+
store: 'ZYX',
|
|
4117
|
+
unit: 'B9',
|
|
4118
|
+
id: 'f201a1d3-2126-46a2-aec9-758ade8ab2ab',
|
|
4119
|
+
building: 'BuildingI',
|
|
4120
|
+
category: 'food/coffee',
|
|
4121
|
+
rent: '0.00'
|
|
4122
|
+
}]
|
|
4160
4123
|
}
|
|
4161
4124
|
```
|
|
4162
4125
|
|
|
4163
|
-
|
|
4126
|
+
#### Service Pagination
|
|
4164
4127
|
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
"__edb_e__": "offices",
|
|
4174
|
-
"__edb_v__": "1"
|
|
4128
|
+
Pagination with services is also possible. Similar to [Entity Pagination](#entity-pagination), calling the `.go()` method returns the following structure:
|
|
4129
|
+
|
|
4130
|
+
```typescript
|
|
4131
|
+
type GoResults = {
|
|
4132
|
+
cursor: string | null;
|
|
4133
|
+
data: {
|
|
4134
|
+
[entityName: string]: { /** EntityItem */ }[]
|
|
4135
|
+
}
|
|
4175
4136
|
}
|
|
4176
4137
|
```
|
|
4177
4138
|
|
|
4178
|
-
|
|
4139
|
+
#### Pagination Query Options
|
|
4179
4140
|
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
{
|
|
4183
|
-
"city": "power",
|
|
4184
|
-
"country": "united states of america",
|
|
4185
|
-
"state": "oregon",
|
|
4186
|
-
"zip": "34706",
|
|
4187
|
-
"office": "mobile branch",
|
|
4188
|
-
}
|
|
4189
|
-
```
|
|
4141
|
+
##### Query Option Pager
|
|
4142
|
+
A notable Pagination Option is `pager`. This property defines the post-processing ElectroDB should perform on a returned `LastEvaluatedKey`, as well as how ElectroDB should interpret an _incoming_ pager, to use as an ExclusiveStartKey.
|
|
4190
4143
|
|
|
4191
4144
|
**"raw":** The `"raw"` option returns the LastEvaluatedKey as it was returned by the DynamoDB DocClient.
|
|
4192
4145
|
|
|
4193
|
-
```
|
|
4194
|
-
// {pager: "raw"}
|
|
4146
|
+
```typescript
|
|
4147
|
+
// {pager: "raw"}
|
|
4195
4148
|
{
|
|
4196
4149
|
pk: '$taskapp#country_united states of america#state_oregon',
|
|
4197
4150
|
sk: '$offices_1#city_power#zip_34706#office_mobile branch',
|
|
@@ -4207,14 +4160,14 @@ Simple pagination example:
|
|
|
4207
4160
|
```javascript
|
|
4208
4161
|
async function getAllStores(mallId) {
|
|
4209
4162
|
let stores = [];
|
|
4210
|
-
let
|
|
4163
|
+
let cursor = null;
|
|
4211
4164
|
|
|
4212
4165
|
do {
|
|
4213
|
-
|
|
4166
|
+
const results = await MallStores.query
|
|
4214
4167
|
.leases({ mallId })
|
|
4215
|
-
.
|
|
4216
|
-
stores = [...stores, ...results];
|
|
4217
|
-
|
|
4168
|
+
.go({ pager });
|
|
4169
|
+
stores = [...stores, ...results.data];
|
|
4170
|
+
cursor = results.cursor;
|
|
4218
4171
|
} while(pager !== null);
|
|
4219
4172
|
|
|
4220
4173
|
return stores;
|
|
@@ -4289,7 +4242,7 @@ await StoreLocations.query
|
|
|
4289
4242
|
```
|
|
4290
4243
|
|
|
4291
4244
|
## Query Options
|
|
4292
|
-
Query options can be added the `.params()
|
|
4245
|
+
Query options can be added the `.params()` and `.go()`` to change query behavior or add customer parameters to a query.
|
|
4293
4246
|
|
|
4294
4247
|
By default, **ElectroDB** enables you to work with records as the names and properties defined in the model. Additionally, it removes the need to deal directly with the docClient parameters which can be complex for a team without as much experience with DynamoDB. The Query Options object can be passed to both the `.params()` and `.go()` methods when building you query. Below are the options available:
|
|
4295
4248
|
|
|
@@ -4297,20 +4250,20 @@ By default, **ElectroDB** enables you to work with records as the names and prop
|
|
|
4297
4250
|
{
|
|
4298
4251
|
params?: object;
|
|
4299
4252
|
table?: string;
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
pager?: "raw" | "named" | "item";
|
|
4253
|
+
data?: 'raw' | 'includeKeys' | 'attributes';
|
|
4254
|
+
pager?: 'raw' | 'cursor';
|
|
4303
4255
|
originalErr?: boolean;
|
|
4304
4256
|
concurrent?: number;
|
|
4305
4257
|
unprocessed?: "raw" | "item";
|
|
4306
4258
|
response?: "default" | "none" | "all_old" | "updated_old" | "all_new" | "updated_new";
|
|
4307
4259
|
ignoreOwnership?: boolean;
|
|
4308
4260
|
limit?: number;
|
|
4309
|
-
pages?: number;
|
|
4261
|
+
pages?: number | 'all';
|
|
4310
4262
|
logger?: (event) => void;
|
|
4311
4263
|
listeners Array<(event) => void>;
|
|
4312
4264
|
preserveBatchOrder?: boolean;
|
|
4313
4265
|
attributes?: string[];
|
|
4266
|
+
order?: 'asc' | 'desc';
|
|
4314
4267
|
};
|
|
4315
4268
|
```
|
|
4316
4269
|
|
|
@@ -4319,16 +4272,16 @@ Option | Default | Description
|
|
|
4319
4272
|
params | `{}` | Properties added to this object will be merged onto the params sent to the document client. Any conflicts with **ElectroDB** will favor the params specified here.
|
|
4320
4273
|
table | _(from constructor)_ | Use a different table than the one defined in the [Service Options](#service-options)
|
|
4321
4274
|
attributes | _(all attributes)_ | The `attributes` query option allows you to specify ProjectionExpression Attributes for your `get` or `query` operation. As of `1.11.0` only root attributes are allowed to be specified.
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
pager | `"named"` | Used in with pagination (`.pages()`) calls to override ElectroDBs default behaviour to break apart `LastEvaluatedKeys` records into composite attributes. See more detail about this in the sections for [Pager Query Options](#pager-query-options).
|
|
4275
|
+
data | `"attributes"` | Accepts the values `'raw'`, `'includeKeys'`, `'attributes'` or `undefined`. Use `'raw'` to return query results as they were returned by the docClient. Use `'includeKeys'` to include item partition and sort key values in your return object. By default, **ElectroDB** does not return partition, sort, or global keys in its response.
|
|
4276
|
+
pager | `cursor` | Used in with pagination calls to override ElectroDBs default behaviour to return a serialized string cursor. See more detail about this in the sections for [Pager Query Options](#pager-query-options).
|
|
4325
4277
|
originalErr | `false` | By default, **ElectroDB** alters the stacktrace of any exceptions thrown by the DynamoDB client to give better visibility to the developer. Set this value equal to `true` to turn off this functionality and return the error unchanged.
|
|
4326
|
-
concurrent | `1` | When performing batch operations, how many requests (1 batch operation == 1 request) to DynamoDB should ElectroDB make at one time. Be mindful of your DynamoDB throughput configurations
|
|
4278
|
+
concurrent | `1` | When performing batch operations, how many requests (1 batch operation == 1 request) to DynamoDB should ElectroDB make at one time. Be mindful of your DynamoDB throughput configurations.
|
|
4327
4279
|
unprocessed | `"item"` | Used in batch processing to override ElectroDBs default behaviour to break apart DynamoDBs `Unprocessed` records into composite attributes. See more detail about this in the sections for [BatchGet](#batch-get), [BatchDelete](#batch-write-delete-records), and [BatchPut](#batch-write-put-records).
|
|
4328
4280
|
response | `"default"` | Used as a convenience for applying the DynamoDB parameter `ReturnValues`. The options here are the same as the parameter values for the DocumentClient except lowercase. The `"none"` option will cause the method to return null and will bypass ElectroDB's response formatting -- useful if formatting performance is a concern.
|
|
4329
4281
|
ignoreOwnership | `false` | By default, **ElectroDB** interrogates items returned from a query for the presence of matching entity "identifiers". This helps to ensure other entities, or other versions of an entity, are filtered from your results. If you are using ElectroDB with an existing table/dataset you can turn off this feature by setting this property to `true`.
|
|
4330
4282
|
limit | _none_ | A target for the number of items to return from DynamoDB. If this option is passed, Queries on entities and through collections will paginate DynamoDB until this limit is reached or all items for that query have been returned.
|
|
4331
|
-
pages |
|
|
4283
|
+
pages | 1 | How many DynamoDB pages should a query iterate through before stopping. To have ElectroDB automatically paginate through all results, pass the string value `'all'`.
|
|
4284
|
+
order | 'asc' | Convenience option for `ScanIndexForward`, to the change the order of queries based on your index's Sort Key -- valid options include 'asc' and 'desc'. [[read more](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html)]
|
|
4332
4285
|
listeners | `[]` | An array of callbacks that are invoked when [internal ElectroDB events](#events) occur.
|
|
4333
4286
|
logger | _none_ | A convenience option for a single event listener that semantically can be used for logging.
|
|
4334
4287
|
preserveBatchOrder | `false` | When used with a [batchGet](#batch-get) operation, ElectroDB will ensure the order returned by a batchGet will be the same as the order provided. When enabled, if a record is returned from DynamoDB as "unprocessed" ([read more here](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html)), ElectroDB will return a null value at that index.
|
|
@@ -4477,7 +4430,7 @@ task.query
|
|
|
4477
4430
|
ElectroDB can be supplied with callbacks (see: [logging](#logging) and [listeners](#listeners) to learn how) to be invoked after certain request lifecycles. This can be useful for logging, analytics, expanding functionality, and more. The following are events currently supported by ElectroDB -- if you would like to see additional events feel free to create a github issue to discuss your concept/need!
|
|
4478
4431
|
|
|
4479
4432
|
## Query Event
|
|
4480
|
-
The `query` event occurs when a query is made via the terminal
|
|
4433
|
+
The `query` event occurs when a query is made via the terminal method [`go()`](#go) . The event includes the exact parameters given to the provided client, the ElectroDB method used, and the ElectroDB configuration provided.
|
|
4481
4434
|
|
|
4482
4435
|
*Type:*
|
|
4483
4436
|
```typescript
|
|
@@ -4497,7 +4450,7 @@ const prop3 = "3ec9ed0c-7497-4d05-bdb8-86c09a618047";
|
|
|
4497
4450
|
|
|
4498
4451
|
entity.update({ prop1, prop2 })
|
|
4499
4452
|
.set({ prop3 })
|
|
4500
|
-
.go()
|
|
4453
|
+
.go();
|
|
4501
4454
|
```
|
|
4502
4455
|
|
|
4503
4456
|
*Example Output:*
|
|
@@ -4658,7 +4611,7 @@ task.query
|
|
|
4658
4611
|
.go({ listeners: [listener1, listener2] });
|
|
4659
4612
|
```
|
|
4660
4613
|
|
|
4661
|
-
# Errors
|
|
4614
|
+
# ElectroDB Errors
|
|
4662
4615
|
|
|
4663
4616
|
Error Code | Description
|
|
4664
4617
|
:--------: | --------------------
|
|
@@ -4992,19 +4945,6 @@ By default ElectroDB tries to keep the stack trace close to your code, ideally t
|
|
|
4992
4945
|
|
|
4993
4946
|
### Unknown Errors
|
|
4994
4947
|
|
|
4995
|
-
### Invalid Last Evaluated Key
|
|
4996
|
-
*Code: 5003*
|
|
4997
|
-
|
|
4998
|
-
*Why this occurred:*
|
|
4999
|
-
_Likely_ you were calling `.page()` on a `scan`. If you weren't please make an issue and include as much detail about your query as possible.
|
|
5000
|
-
|
|
5001
|
-
*What to do about it:*
|
|
5002
|
-
When paginating with *scan* queries, it is highly recommended that the query option, `{pager: "raw"}`. This is because when using scan on large tables the docClient may return an ExclusiveStartKey for a record that does not belong to entity making the query (regardless of the filters set). In these cases ElectroDB will return null (to avoid leaking the keys of other entities) when further pagination may be needed to find your records.
|
|
5003
|
-
```javascript
|
|
5004
|
-
// example
|
|
5005
|
-
myModel.scan.page(null, {pager: "raw"});
|
|
5006
|
-
```
|
|
5007
|
-
|
|
5008
4948
|
### No Owner For Pager
|
|
5009
4949
|
*Code: 5004*
|
|
5010
4950
|
|
|
@@ -5130,16 +5070,7 @@ const EmployeesModel = {
|
|
|
5130
5070
|
composite: ["team", "office", "employee"],
|
|
5131
5071
|
},
|
|
5132
5072
|
},
|
|
5133
|
-
}
|
|
5134
|
-
filters: {
|
|
5135
|
-
upcomingCelebrations: (attributes, startDate, endDate) => {
|
|
5136
|
-
let { dateHired, birthday } = attributes;
|
|
5137
|
-
return `${dateHired.between(startDate, endDate)} OR ${birthday.between(
|
|
5138
|
-
startDate,
|
|
5139
|
-
endDate,
|
|
5140
|
-
)}`;
|
|
5141
|
-
},
|
|
5142
|
-
},
|
|
5073
|
+
}
|
|
5143
5074
|
};
|
|
5144
5075
|
|
|
5145
5076
|
const TasksModel = {
|
|
@@ -5237,12 +5168,13 @@ const DynamoDB = require("aws-sdk/clients/dynamodb");
|
|
|
5237
5168
|
const client = new DynamoDB.DocumentClient({region: "us-east-1"});
|
|
5238
5169
|
const { Service } = require("electrodb");
|
|
5239
5170
|
const table = "projectmanagement";
|
|
5240
|
-
const EmployeeApp = new Service("EmployeeApp", { client, table });
|
|
5241
5171
|
|
|
5242
|
-
EmployeeApp
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5172
|
+
const EmployeeApp = new Service({
|
|
5173
|
+
employees: EmployeesModel,
|
|
5174
|
+
tasks: TasksModel,
|
|
5175
|
+
offices: OfficesModel,
|
|
5176
|
+
}, { client, table });
|
|
5177
|
+
|
|
5246
5178
|
```
|
|
5247
5179
|
### Query Records
|
|
5248
5180
|
#### All tasks and employee information for a given employee
|
|
@@ -5254,29 +5186,32 @@ EmployeeApp.collections.assignements({employee: "CBaskin"}).go();
|
|
|
5254
5186
|
Returns the following:
|
|
5255
5187
|
```javascript
|
|
5256
5188
|
{
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5189
|
+
data: {
|
|
5190
|
+
employees: [{
|
|
5191
|
+
employee: "cbaskin",
|
|
5192
|
+
firstName: "carol",
|
|
5193
|
+
lastName: "baskin",
|
|
5194
|
+
office: "big cat rescue",
|
|
5195
|
+
title: "owner",
|
|
5196
|
+
team: "cool cats and kittens",
|
|
5197
|
+
salary: "1,000,000",
|
|
5198
|
+
manager: "",
|
|
5199
|
+
dateHired: "1992-11-04",
|
|
5200
|
+
birthday: "1961-06-06",
|
|
5201
|
+
}],
|
|
5202
|
+
tasks: [{
|
|
5203
|
+
task: "Feed tigers",
|
|
5204
|
+
description: "Prepare food for tigers to eat",
|
|
5205
|
+
project: "Keep tigers alive",
|
|
5206
|
+
employee: "cbaskin"
|
|
5207
|
+
}, {
|
|
5208
|
+
task: "Fill water bowls",
|
|
5209
|
+
description: "Ensure the tigers have enough water",
|
|
5210
|
+
project: "Keep tigers alive",
|
|
5211
|
+
employee: "cbaskin"
|
|
5212
|
+
}]
|
|
5213
|
+
},
|
|
5214
|
+
cursor: '...'
|
|
5280
5215
|
}
|
|
5281
5216
|
```
|
|
5282
5217
|
|
|
@@ -5289,26 +5224,29 @@ EmployeeApp.collections.workplaces({office: "big cat rescue"}).go()
|
|
|
5289
5224
|
Returns the following:
|
|
5290
5225
|
```javascript
|
|
5291
5226
|
{
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5227
|
+
data: {
|
|
5228
|
+
employees: [{
|
|
5229
|
+
employee: "cbaskin",
|
|
5230
|
+
firstName: "carol",
|
|
5231
|
+
lastName: "baskin",
|
|
5232
|
+
office: "big cat rescue",
|
|
5233
|
+
title: "owner",
|
|
5234
|
+
team: "cool cats and kittens",
|
|
5235
|
+
salary: "1,000,000",
|
|
5236
|
+
manager: "",
|
|
5237
|
+
dateHired: "1992-11-04",
|
|
5238
|
+
birthday: "1961-06-06",
|
|
5239
|
+
}],
|
|
5240
|
+
offices: [{
|
|
5241
|
+
office: "big cat rescue",
|
|
5242
|
+
country: "usa",
|
|
5243
|
+
state: "florida",
|
|
5244
|
+
city: "tampa",
|
|
5245
|
+
zip: "12345",
|
|
5246
|
+
address: "123 Kitty Cat Lane"
|
|
5247
|
+
}]
|
|
5248
|
+
},
|
|
5249
|
+
cursor: '...'
|
|
5312
5250
|
}
|
|
5313
5251
|
```
|
|
5314
5252
|
|
|
@@ -5320,19 +5258,22 @@ EmployeeApp.entities.tasks.query.assigned({employee: "cbaskin"}).go();
|
|
|
5320
5258
|
```
|
|
5321
5259
|
Returns the following:
|
|
5322
5260
|
```javascript
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5261
|
+
{
|
|
5262
|
+
data: [
|
|
5263
|
+
{
|
|
5264
|
+
task: "Feed tigers",
|
|
5265
|
+
description: "Prepare food for tigers to eat",
|
|
5266
|
+
project: "Keep tigers alive",
|
|
5267
|
+
employee: "cbaskin"
|
|
5268
|
+
}, {
|
|
5269
|
+
task: "Fill water bowls",
|
|
5270
|
+
description: "Ensure the tigers have enough water",
|
|
5271
|
+
project: "Keep tigers alive",
|
|
5272
|
+
employee: "cbaskin"
|
|
5273
|
+
}
|
|
5274
|
+
],
|
|
5275
|
+
cursor: '...',
|
|
5276
|
+
}
|
|
5336
5277
|
```
|
|
5337
5278
|
#### Tasks for a given project
|
|
5338
5279
|
Fulfilling [Requirement #4](#employee-app-requirements).
|
|
@@ -5341,14 +5282,17 @@ EmployeeApp.entities.tasks.query.project({project: "Murder Carol"}).go();
|
|
|
5341
5282
|
```
|
|
5342
5283
|
Returns the following:
|
|
5343
5284
|
```javascript
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5285
|
+
{
|
|
5286
|
+
data: [
|
|
5287
|
+
{
|
|
5288
|
+
task: "Hire hitman",
|
|
5289
|
+
description: "Find someone to murder Carol",
|
|
5290
|
+
project: "Murder Carol",
|
|
5291
|
+
employee: "jexotic"
|
|
5292
|
+
}
|
|
5293
|
+
],
|
|
5294
|
+
cursor: '...'
|
|
5295
|
+
}
|
|
5352
5296
|
```
|
|
5353
5297
|
|
|
5354
5298
|
#### Find office locations
|
|
@@ -5358,16 +5302,19 @@ EmployeeApp.entities.office.locations({country: "usa", state: "florida"}).go()
|
|
|
5358
5302
|
```
|
|
5359
5303
|
Returns the following:
|
|
5360
5304
|
```javascript
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5305
|
+
{
|
|
5306
|
+
data: [
|
|
5307
|
+
{
|
|
5308
|
+
office: "big cat rescue",
|
|
5309
|
+
country: "usa",
|
|
5310
|
+
state: "florida",
|
|
5311
|
+
city: "tampa",
|
|
5312
|
+
zip: "12345",
|
|
5313
|
+
address: "123 Kitty Cat Lane"
|
|
5314
|
+
}
|
|
5315
|
+
],
|
|
5316
|
+
cursor: '...'
|
|
5317
|
+
}
|
|
5371
5318
|
```
|
|
5372
5319
|
|
|
5373
5320
|
#### Find employee salaries and titles
|
|
@@ -5380,70 +5327,87 @@ EmployeeApp.entities.employees
|
|
|
5380
5327
|
```
|
|
5381
5328
|
Returns the following:
|
|
5382
5329
|
```javascript
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5330
|
+
{
|
|
5331
|
+
data: [
|
|
5332
|
+
{
|
|
5333
|
+
employee: "ssaffery",
|
|
5334
|
+
firstName: "saff",
|
|
5335
|
+
lastName: "saffery",
|
|
5336
|
+
office: "gw zoo",
|
|
5337
|
+
title: "animal wrangler",
|
|
5338
|
+
team: "keepers",
|
|
5339
|
+
salary: "105.00",
|
|
5340
|
+
manager: "jexotic",
|
|
5341
|
+
dateHired: "1999-02-23",
|
|
5342
|
+
birthday: "1960-07-11",
|
|
5343
|
+
}
|
|
5344
|
+
],
|
|
5345
|
+
cursor: '...'
|
|
5346
|
+
}
|
|
5397
5347
|
```
|
|
5398
5348
|
|
|
5399
5349
|
#### Find employee birthdays or anniversaries
|
|
5400
5350
|
Fulfilling [Requirement #7](#employee-app-requirements).
|
|
5401
5351
|
```javascript
|
|
5352
|
+
const startDate = "2020-05-01";
|
|
5353
|
+
const endDate = "2020-06-01";
|
|
5354
|
+
|
|
5402
5355
|
EmployeeApp.entities.employees
|
|
5403
5356
|
.workplaces({office: "gw zoo"})
|
|
5357
|
+
.where(({ birthday, dateHired }, { between }) => `
|
|
5358
|
+
${between(dateHired, startDate, endDate)} OR
|
|
5359
|
+
${between(birthday, startDate, endDate)}
|
|
5360
|
+
`)
|
|
5404
5361
|
.upcomingCelebrations("2020-05-01", "2020-06-01")
|
|
5405
5362
|
.go()
|
|
5406
5363
|
```
|
|
5407
5364
|
Returns the following:
|
|
5408
5365
|
```javascript
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5366
|
+
{
|
|
5367
|
+
data: [
|
|
5368
|
+
{
|
|
5369
|
+
employee: "jexotic",
|
|
5370
|
+
firstName: "joe",
|
|
5371
|
+
lastName: "maldonado-passage",
|
|
5372
|
+
office: "gw zoo",
|
|
5373
|
+
title: "tiger king",
|
|
5374
|
+
team: "founders",
|
|
5375
|
+
salary: "10000.00",
|
|
5376
|
+
manager: "jlowe",
|
|
5377
|
+
dateHired: "1999-02-23",
|
|
5378
|
+
birthday: "1963-03-05",
|
|
5379
|
+
}
|
|
5380
|
+
],
|
|
5381
|
+
cursor: '...'
|
|
5382
|
+
}
|
|
5423
5383
|
```
|
|
5424
5384
|
#### Find direct reports
|
|
5425
5385
|
Fulfilling [Requirement #8](#employee-app-requirements).
|
|
5426
5386
|
```javascript
|
|
5387
|
+
|
|
5427
5388
|
EmployeeApp.entities.employees
|
|
5428
5389
|
.reports({manager: "jlowe"})
|
|
5429
5390
|
.go()
|
|
5430
5391
|
```
|
|
5431
5392
|
Returns the following:
|
|
5432
5393
|
```javascript
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5394
|
+
{
|
|
5395
|
+
data: [
|
|
5396
|
+
{
|
|
5397
|
+
employee: "jexotic",
|
|
5398
|
+
firstName: "joe",
|
|
5399
|
+
lastName: "maldonado-passage",
|
|
5400
|
+
office: "gw zoo",
|
|
5401
|
+
title: "tiger king",
|
|
5402
|
+
team: "founders",
|
|
5403
|
+
salary: "10000.00",
|
|
5404
|
+
manager: "jlowe",
|
|
5405
|
+
dateHired: "1999-02-23",
|
|
5406
|
+
birthday: "1963-03-05",
|
|
5407
|
+
}
|
|
5408
|
+
],
|
|
5409
|
+
cursor: '...'
|
|
5410
|
+
}
|
|
5447
5411
|
```
|
|
5448
5412
|
|
|
5449
5413
|
## Shopping Mall Property Management App
|
|
@@ -5480,14 +5444,16 @@ await StoreLocations.create({
|
|
|
5480
5444
|
Returns the following:
|
|
5481
5445
|
```json
|
|
5482
5446
|
{
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5447
|
+
"data": {
|
|
5448
|
+
"mallId": "EastPointe",
|
|
5449
|
+
"storeId": "LatteLarrys",
|
|
5450
|
+
"buildingId": "BuildingA1",
|
|
5451
|
+
"unitId": "B47",
|
|
5452
|
+
"category": "spite store",
|
|
5453
|
+
"leaseEndDate": "2020-02-29",
|
|
5454
|
+
"rent": "5000.00",
|
|
5455
|
+
"discount": "0.00"
|
|
5456
|
+
}
|
|
5491
5457
|
}
|
|
5492
5458
|
```
|
|
5493
5459
|
---
|
|
@@ -5506,7 +5472,9 @@ await StoreLocations.update({storeId, mallId, buildingId, unitId}).set({
|
|
|
5506
5472
|
Returns the following:
|
|
5507
5473
|
```json
|
|
5508
5474
|
{
|
|
5509
|
-
|
|
5475
|
+
"data": {
|
|
5476
|
+
"leaseEndDate": "2021-02-28"
|
|
5477
|
+
}
|
|
5510
5478
|
}
|
|
5511
5479
|
```
|
|
5512
5480
|
|
|
@@ -5546,8 +5514,9 @@ let storeId = "LatteLarrys";
|
|
|
5546
5514
|
await StoreLocations.delete({storeId, mallId, buildingId, unitId}).go();
|
|
5547
5515
|
```
|
|
5548
5516
|
Returns the following:
|
|
5549
|
-
|
|
5550
|
-
|
|
5517
|
+
|
|
5518
|
+
```json
|
|
5519
|
+
{ "data": {} }
|
|
5551
5520
|
```
|
|
5552
5521
|
|
|
5553
5522
|
### Query Mall Records
|
|
@@ -5636,17 +5605,19 @@ If you have a need for a custom attribute type (beyond those supported by Electr
|
|
|
5636
5605
|
```typescript
|
|
5637
5606
|
import { Entity, createCustomAttribute } from 'electrodb';
|
|
5638
5607
|
|
|
5608
|
+
const table = 'workplace_table';
|
|
5609
|
+
|
|
5639
5610
|
type PersonnelRole = {
|
|
5640
5611
|
type: 'employee';
|
|
5641
|
-
startDate:
|
|
5642
|
-
endDate?:
|
|
5612
|
+
startDate: number;
|
|
5613
|
+
endDate?: number;
|
|
5643
5614
|
} | {
|
|
5644
5615
|
type: 'contractor';
|
|
5645
|
-
contractStartDate:
|
|
5646
|
-
contractEndDate:
|
|
5616
|
+
contractStartDate: number;
|
|
5617
|
+
contractEndDate: number;
|
|
5647
5618
|
};
|
|
5648
5619
|
|
|
5649
|
-
|
|
5620
|
+
|
|
5650
5621
|
const person = new Entity({
|
|
5651
5622
|
model: {
|
|
5652
5623
|
entity: 'personnel',
|
|
@@ -5665,11 +5636,11 @@ const person = new Entity({
|
|
|
5665
5636
|
record: {
|
|
5666
5637
|
pk: {
|
|
5667
5638
|
field: 'pk',
|
|
5668
|
-
|
|
5639
|
+
composite: ['id']
|
|
5669
5640
|
},
|
|
5670
5641
|
sk: {
|
|
5671
5642
|
field: 'sk',
|
|
5672
|
-
|
|
5643
|
+
composite: [],
|
|
5673
5644
|
}
|
|
5674
5645
|
}
|
|
5675
5646
|
}
|
|
@@ -5678,7 +5649,33 @@ const person = new Entity({
|
|
|
5678
5649
|
|
|
5679
5650
|
## Exported Types
|
|
5680
5651
|
|
|
5681
|
-
The following types are exported for easier use while using ElectroDB with TypeScript:
|
|
5652
|
+
The following types are exported for easier use while using ElectroDB with TypeScript. The naming convention for the types include three different kinds:
|
|
5653
|
+
|
|
5654
|
+
- `xResponse` -- Types with the postfix `Response` represent the returned interfaces directly from ElectroDB.
|
|
5655
|
+
|
|
5656
|
+
- `xItem` -- Types with the postfix `Item` represent an Entity row. Queries return multiple items, a get returns a single item, etc. The type for an item is inferred based on the attributes and index definitions within your model. For example if your attribute is marked as `required` then that attribute will never be undefined, if your attribute has a default value then it won't be required to be supplied on `put`, `list` attributes must be an array, etc.
|
|
5657
|
+
|
|
5658
|
+
- `xRecord` -- In some cases it is helpful to have a type that respresents all attributes of an item without nullable properties. Types with the postfix `Record` contain all properties in a non-nullable format.
|
|
5659
|
+
|
|
5660
|
+
The follow highlight many of the types exported utility types from ElectroDB:
|
|
5661
|
+
|
|
5662
|
+
### QueryResponse Type
|
|
5663
|
+
|
|
5664
|
+
The QueryResponse type is the same type returned by an ElectroDB Query.
|
|
5665
|
+
|
|
5666
|
+
_Definition:_
|
|
5667
|
+
|
|
5668
|
+
```typescript
|
|
5669
|
+
export type QueryResponse<E extends Entity<any, any, any, any>> = {
|
|
5670
|
+
data: EntityItem<E>;
|
|
5671
|
+
cursor: string | null;
|
|
5672
|
+
}
|
|
5673
|
+
```
|
|
5674
|
+
|
|
5675
|
+
_Use:_
|
|
5676
|
+
```typescript
|
|
5677
|
+
type EntitySchema = QueryResponse<typeof MyEntity>
|
|
5678
|
+
```
|
|
5682
5679
|
|
|
5683
5680
|
### EntityRecord Type
|
|
5684
5681
|
|
|
@@ -5695,7 +5692,7 @@ type EntityRecord<E extends Entity<any, any, any, any>> =
|
|
|
5695
5692
|
|
|
5696
5693
|
_Use:_
|
|
5697
5694
|
```typescript
|
|
5698
|
-
type
|
|
5695
|
+
type Item = EntityRecord<typeof MyEntity>
|
|
5699
5696
|
```
|
|
5700
5697
|
|
|
5701
5698
|
### EntityItem Type
|
|
@@ -5714,19 +5711,56 @@ export type EntityItem<E extends Entity<any, any, any, any>> =
|
|
|
5714
5711
|
_Use:_
|
|
5715
5712
|
|
|
5716
5713
|
```typescript
|
|
5717
|
-
type
|
|
5714
|
+
type Item = EntityItem<typeof MyEntityInstance>;
|
|
5718
5715
|
```
|
|
5719
5716
|
|
|
5720
5717
|
### CollectionItem Type
|
|
5721
5718
|
|
|
5722
|
-
This type represents
|
|
5719
|
+
This type represents an item returned from a collection query, and is similar to EntityItem.
|
|
5720
|
+
|
|
5721
|
+
_Definition:_
|
|
5722
|
+
```typescript
|
|
5723
|
+
export type CollectionItem<SERVICE extends Service<any>, COLLECTION extends keyof SERVICE["collections"]> =
|
|
5724
|
+
SERVICE extends Service<infer E>
|
|
5725
|
+
? Pick<{
|
|
5726
|
+
[EntityName in keyof E]: E[EntityName] extends Entity<infer A, infer F, infer C, infer S>
|
|
5727
|
+
? COLLECTION extends keyof CollectionAssociations<E>
|
|
5728
|
+
? EntityName extends CollectionAssociations<E>[COLLECTION]
|
|
5729
|
+
? ResponseItem<A,F,C,S>[]
|
|
5730
|
+
: never
|
|
5731
|
+
: never
|
|
5732
|
+
: never
|
|
5733
|
+
}, COLLECTION extends keyof CollectionAssociations<E>
|
|
5734
|
+
? CollectionAssociations<E>[COLLECTION]
|
|
5735
|
+
: never>
|
|
5736
|
+
: never
|
|
5737
|
+
```
|
|
5723
5738
|
|
|
5724
5739
|
_Use:_
|
|
5725
5740
|
|
|
5726
|
-
```
|
|
5741
|
+
```typescript
|
|
5727
5742
|
type CollectionResults = CollectionItem<typeof MyServiceInstance, "collectionName">
|
|
5728
5743
|
```
|
|
5729
5744
|
|
|
5745
|
+
### CollectionResponse
|
|
5746
|
+
|
|
5747
|
+
This type represents the value returned the collection query itself
|
|
5748
|
+
|
|
5749
|
+
_Definition:_
|
|
5750
|
+
|
|
5751
|
+
```typescript
|
|
5752
|
+
export type CollectionResponse<SERVICE extends Service<any>, COLLECTION extends keyof SERVICE["collections"]> = {
|
|
5753
|
+
data: CollectionItem<SERVICE, COLLECTION>;
|
|
5754
|
+
cursor: string | null;
|
|
5755
|
+
}
|
|
5756
|
+
```
|
|
5757
|
+
|
|
5758
|
+
_Use:_
|
|
5759
|
+
|
|
5760
|
+
```typescript
|
|
5761
|
+
type CollectionResults = CollectionResponse<typeof MyServiceInstance, "collectionName">
|
|
5762
|
+
```
|
|
5763
|
+
|
|
5730
5764
|
### CreateEntityItem Type
|
|
5731
5765
|
|
|
5732
5766
|
This type represents an item that you would pass your entity's `put` or `create` method
|
|
@@ -5836,8 +5870,6 @@ Whenever using ElectroDB with existing tables/data, it is best to use the [Query
|
|
|
5836
5870
|
.params({ignoreOwnership: true})
|
|
5837
5871
|
// when querying the table
|
|
5838
5872
|
.go({ignoreOwnership: true})
|
|
5839
|
-
// when using pagination
|
|
5840
|
-
.page(null, {ignoreOwnership: true})
|
|
5841
5873
|
```
|
|
5842
5874
|
|
|
5843
5875
|
**Your existing index fields have values with mixed case:**
|
|
@@ -5862,6 +5894,22 @@ Electro is a CLI utility toolbox for extending the functionality of **ElectroDB*
|
|
|
5862
5894
|
|
|
5863
5895
|
For usage and installation details you can learn more [here](https://github.com/tywalch/electrocli).
|
|
5864
5896
|
|
|
5897
|
+
# Version 2 Migration
|
|
5898
|
+
## New response format for all query methods.
|
|
5899
|
+
Prior to 2.0.0, ElectroDB had multiple unique response signatures depending on the method used. Queries now return responses within an envelope object with results typically on a property called `data`. The section [Building Queries](#building-queries) now has response format examples for all methods, and the section [Exported Types](#exported-types) has new utility types you can use to express response types in your code.
|
|
5900
|
+
|
|
5901
|
+
## Unified pagination APIs
|
|
5902
|
+
Version 2.0.0 removes the `.page()` terminal function and unifies pagination under the `.go()` method. The response signature for queries, scans, finds, and matches now include a cursor string that can be passed back into the go method as a query option (e.g. `go({cursor})`. This new cursor is a departure from the destructure object ElectroDB returned prior for pagination, and is a `base64url` type string making it url safe.
|
|
5903
|
+
|
|
5904
|
+
Note: It is still possible to return the native DynamoDB LastEvaluatedKey using the `pager` and/or `data` [query options](#query-options). This new `cursor`
|
|
5905
|
+
|
|
5906
|
+
Another change to pagination involves the "auto-pagination" used with the `.go()` method. Prior to 2.0.0 the `.go()` method would paginate through all _query_ results automatically. This was not the behavior for `scan` which caused some confusion. All queries and and query-like methods (scan, find, match, etc) now query a single page by default. You can use the [query options](#query-options) `pages` and `limit` to instruct electrodb to automatically iterate through multiple pages, or use `pages: 'all'` to have electrodb automatically exhaust pagination.
|
|
5907
|
+
|
|
5908
|
+
Checkout the section [#pagination-query-options] to read more on this topic and to find an example of how to perform pagination with ElectroDB 2.0.0.
|
|
5909
|
+
|
|
5910
|
+
## Pagination with a string cursor
|
|
5911
|
+
All ElectroDB `query` and `scan` operations return a `cursor`, which is a stringified and copy of DynamoDB's `LastEvaluatedKey` with a `base64url` encoding. Read the section [Pagination Cursor](#pagination-cursor) to learn more about how the cursor is formed and how to use it to accomplish pagination in ElectroDB.
|
|
5912
|
+
|
|
5865
5913
|
# Version 1 Migration
|
|
5866
5914
|
This section is to detail any breaking changes made on the journey to a stable 1.0 product.
|
|
5867
5915
|
|
|
@@ -5905,6 +5953,7 @@ let old_schema = {
|
|
|
5905
5953
|
attributes: {...},
|
|
5906
5954
|
indexes: {...}
|
|
5907
5955
|
};
|
|
5956
|
+
|
|
5908
5957
|
new Entity(old_schema, {client});
|
|
5909
5958
|
|
|
5910
5959
|
// new way
|
|
@@ -5917,6 +5966,7 @@ let new_schema = {
|
|
|
5917
5966
|
attributes: {...},
|
|
5918
5967
|
indexes: {...}
|
|
5919
5968
|
};
|
|
5969
|
+
|
|
5920
5970
|
new Entity(new_schema, {client, table});
|
|
5921
5971
|
```
|
|
5922
5972
|
|
|
@@ -5935,9 +5985,6 @@ new Service({
|
|
|
5935
5985
|
}, {client});
|
|
5936
5986
|
|
|
5937
5987
|
// new way
|
|
5938
|
-
new Service("service_name", {client, table});
|
|
5939
|
-
|
|
5940
|
-
// new way (for better TypeScript support)
|
|
5941
5988
|
new Service({entity1, entity2, ...})
|
|
5942
5989
|
```
|
|
5943
5990
|
|
|
@@ -5954,6 +6001,3 @@ This change stems from the fact the `facets` is already a defined term in the Dy
|
|
|
5954
6001
|
## Get Method to Return null
|
|
5955
6002
|
|
|
5956
6003
|
1.0.0 brings back a `null` response from the `get()` method when a record could not be found. Prior to `1.0.0` ElectroDB returned an empty object.
|
|
5957
|
-
|
|
5958
|
-
# Coming Soon
|
|
5959
|
-
- Default query options defined on the `model` to give more general control of interactions with the Entity.
|