electrodb 1.11.1 → 2.0.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 +769 -672
- package/index.d.ts +323 -204
- package/index.js +2 -1
- package/package.json +1 -1
- package/src/clauses.js +15 -32
- package/src/entity.js +176 -89
- package/src/errors.js +6 -0
- package/src/filters.js +2 -2
- package/src/operations.js +7 -1
- package/src/schema.js +39 -11
- package/src/service.js +30 -111
- package/src/types.js +18 -2
- package/src/util.js +24 -0
- package/src/where.js +2 -2
package/README.md
CHANGED
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
|
|
12
12
|
------------
|
|
13
13
|
|
|
14
|
+
<h1 align="center">ElectroDB has now reached 2.0.0!</h1>
|
|
15
|
+
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.
|
|
16
|
+
|
|
17
|
+
------------
|
|
18
|
+
|
|
14
19
|
<a href="https://electrodb.fun"><h1 align="center">Introducing: The NEW ElectroDB Playground</h1></a>
|
|
15
20
|
|
|
16
21
|
<p align="center">
|
|
@@ -31,7 +36,7 @@
|
|
|
31
36
|
- [**Simplified Update Expression Composition**](#update-record) - Easily compose type safe update operations without having to format tedious `ExpressionAttributeNames`, `ExpressionAttributeValues`, and `UpdateExpressions`.
|
|
32
37
|
- [**Easily Query Across Entities**](#collections) - Define "collections" to create powerful/idiomatic queries that return multiple entities in a single request.
|
|
33
38
|
- [**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**](#
|
|
39
|
+
- [**Simplified Pagination API**](#entity-pagination) - ElectroDB generates url safe cursors for pagination, allows for fine grain automated pagination, and supports async iteration.
|
|
35
40
|
- [**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
41
|
- [**TypeScript Support**](#typescript-support) - Strong **TypeScript** support for both Entities and Services now in Beta.
|
|
37
42
|
- [**Query Directly via the Terminal**](#electro-cli) - Execute queries against your `Entities`, `Services`, `Models` directly from the command line.
|
|
@@ -97,28 +102,22 @@ tasks
|
|
|
97
102
|
## Table of Contents
|
|
98
103
|
- [ElectroDB](#electrodb)
|
|
99
104
|
* [Features](#features)
|
|
100
|
-
* [Table of Contents](#table-of-contents)
|
|
101
105
|
- [Project Goals](#project-goals)
|
|
102
106
|
- [Installation](#installation)
|
|
103
107
|
- [Usage](#usage)
|
|
104
108
|
- [Entities and Services](#entities-and-services)
|
|
109
|
+
- [Getting Started](#getting-started)
|
|
105
110
|
- [Entities](#entities)
|
|
106
111
|
- [Services](#services)
|
|
107
112
|
* [TypeScript Support](#typescript-support)
|
|
108
|
-
+ [
|
|
109
|
-
|
|
110
|
-
- [Independent Models](#independent-models)
|
|
111
|
-
- [Joining Entity instances to a Service](#joining-entity-instances-to-a-service)
|
|
112
|
-
- [Joining models to a Service](#joining-models-to-a-service)
|
|
113
|
-
- [Joining Entities or Models with an alias](#joining-entities-or-models-with-an-alias)
|
|
114
|
-
- [Joining Entities at Service construction for TypeScript](#joining-entities-at-service-construction-for-typescript)
|
|
113
|
+
+ [Services](#services-1)
|
|
114
|
+
- [Joining Entities together](#joining-entities-together)
|
|
115
115
|
* [Model](#model)
|
|
116
116
|
+ [Model Properties](#model-properties)
|
|
117
117
|
+ [Service Options](#service-options)
|
|
118
118
|
* [Attributes](#attributes)
|
|
119
|
-
+ [
|
|
120
|
-
|
|
121
|
-
- [Attribute Definition](#attribute-definition)
|
|
119
|
+
+ [Attribute Definition](#attribute-definition)
|
|
120
|
+
- [Attribute Definition](#attribute-definition-1)
|
|
122
121
|
- [Enum Attributes](#enum-attributes)
|
|
123
122
|
- [Map Attributes](#map-attributes)
|
|
124
123
|
- [List Attributes](#list-attributes)
|
|
@@ -136,7 +135,6 @@ tasks
|
|
|
136
135
|
+ [Indexes With Sort Keys](#indexes-with-sort-keys)
|
|
137
136
|
+ [Numeric Keys](#numeric-keys)
|
|
138
137
|
+ [Index Casing](#index-casing)
|
|
139
|
-
* [Facets](#facets)
|
|
140
138
|
* [Composite Attributes](#composite-attributes)
|
|
141
139
|
+ [Composite Attribute Arrays](#composite-attribute-arrays)
|
|
142
140
|
+ [Composite Attribute Templates](#composite-attribute-templates)
|
|
@@ -151,10 +149,6 @@ tasks
|
|
|
151
149
|
* [Index and Collection Naming Conventions](#index-and-collection-naming-conventions)
|
|
152
150
|
+ [Index Naming Conventions](#index-naming-conventions)
|
|
153
151
|
* [Collection Naming Conventions](#collection-naming-conventions)
|
|
154
|
-
* [Filters](#filters)
|
|
155
|
-
+ [Defined on the model](#defined-on-the-model)
|
|
156
|
-
+ [Defined via Filter method after query operators](#defined-via-filter-method-after-query-operators)
|
|
157
|
-
+ [Multiple Filters](#multiple-filters)
|
|
158
152
|
* [Where](#where)
|
|
159
153
|
+ [FilterExpressions](#filterexpressions)
|
|
160
154
|
+ [ConditionExpressions](#conditionexpressions)
|
|
@@ -168,7 +162,7 @@ tasks
|
|
|
168
162
|
+ [Query App Records](#query-app-records)
|
|
169
163
|
- [Partition Key Composite Attributes](#partition-key-composite-attributes)
|
|
170
164
|
+ [Sort Key Operations](#sort-key-operations)
|
|
171
|
-
* [
|
|
165
|
+
* [Performing Queries](#performing-queries)
|
|
172
166
|
+ [Query Method](#query-method)
|
|
173
167
|
+ [Get Method](#get-method)
|
|
174
168
|
+ [Batch Get](#batch-get)
|
|
@@ -194,26 +188,27 @@ tasks
|
|
|
194
188
|
+ [Match Records](#match-records)
|
|
195
189
|
+ [Access Pattern Queries](#access-pattern-queries)
|
|
196
190
|
- [Begins With Queries](#begins-with-queries)
|
|
197
|
-
* [Collection
|
|
198
|
-
* [
|
|
191
|
+
* [Collection Queries](#collection-queries)
|
|
192
|
+
* [Executing Queries](#executing-queries)
|
|
199
193
|
+ [Params](#params)
|
|
200
194
|
+ [Go](#go)
|
|
201
|
-
+ [Page](#page)
|
|
202
195
|
- [Entity Pagination](#entity-pagination)
|
|
196
|
+
* [Pagination Cursor](#pagination-cursor)
|
|
203
197
|
- [Service Pagination](#service-pagination)
|
|
204
|
-
- [
|
|
198
|
+
- [Pagination Query Options](#pagination-query-options)
|
|
199
|
+
* [Query Option Pager](#query-option-pager)
|
|
205
200
|
* [Pagination Example](#pagination-example)
|
|
206
201
|
* [Query Examples](#query-examples)
|
|
207
202
|
* [Query Options](#query-options)
|
|
208
203
|
- [AWS DynamoDB Client](#aws-dynamodb-client)
|
|
209
204
|
* [V2 Client](#v2-client)
|
|
210
205
|
* [V3 Client](#v3-client)
|
|
206
|
+
- [Logging](#logging)
|
|
211
207
|
- [Events](#events)
|
|
212
208
|
* [Query Event](#query-event)
|
|
213
209
|
* [Results Event](#results-event)
|
|
214
|
-
- [Logging](#logging)
|
|
215
210
|
- [Listeners](#listeners)
|
|
216
|
-
- [Errors
|
|
211
|
+
- [ElectroDB Errors](#electrodb-errors)
|
|
217
212
|
+ [No Client Defined On Model](#no-client-defined-on-model)
|
|
218
213
|
+ [Invalid Identifier](#invalid-identifier)
|
|
219
214
|
+ [Invalid Key Composite Attribute Template](#invalid-key-composite-attribute-template)
|
|
@@ -237,7 +232,6 @@ tasks
|
|
|
237
232
|
+ [Invalid Attribute](#invalid-attribute)
|
|
238
233
|
+ [AWS Error](#aws-error)
|
|
239
234
|
+ [Unknown Errors](#unknown-errors)
|
|
240
|
-
+ [Invalid Last Evaluated Key](#invalid-last-evaluated-key)
|
|
241
235
|
+ [No Owner For Pager](#no-owner-for-pager)
|
|
242
236
|
+ [Pager Not Unique](#pager-not-unique)
|
|
243
237
|
- [Examples](#examples)
|
|
@@ -273,24 +267,30 @@ tasks
|
|
|
273
267
|
- [Stores will renewals for Q4](#stores-will-renewals-for-q4)
|
|
274
268
|
- [Spite-stores with release renewals this year](#spite-stores-with-release-renewals-this-year)
|
|
275
269
|
- [All Latte Larrys in a particular mall building](#all-latte-larrys-in-a-particular-mall-building)
|
|
276
|
-
- [
|
|
277
|
-
* [
|
|
278
|
-
* [
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
270
|
+
- [TypeScript](#typescript)
|
|
271
|
+
* [Custom Attributes](#custom-attributes)
|
|
272
|
+
* [Exported Types](#exported-types)
|
|
273
|
+
+ [QueryResponse Type](#queryresponse-type)
|
|
274
|
+
+ [EntityRecord Type](#entityrecord-type)
|
|
275
|
+
+ [EntityItem Type](#entityitem-type)
|
|
276
|
+
+ [CollectionItem Type](#collectionitem-type)
|
|
277
|
+
+ [CollectionResponse](#collectionresponse)
|
|
278
|
+
+ [CreateEntityItem Type](#createentityitem-type)
|
|
279
|
+
+ [UpdateEntityItem Type](#updateentityitem-type)
|
|
280
|
+
+ [UpdateAddEntityItem Type](#updateaddentityitem-type)
|
|
281
|
+
+ [UpdateSubtractEntityItem Type](#updatesubtractentityitem-type)
|
|
282
|
+
+ [UpdateAppendEntityItem Type](#updateappendentityitem-type)
|
|
283
|
+
+ [UpdateRemoveEntityItem Type](#updateremoveentityitem-type)
|
|
284
|
+
+ [UpdateDeleteEntityItem Type](#updatedeleteentityitem-type)
|
|
287
285
|
- [Using ElectroDB With Existing Data](#using-electrodb-with-existing-data)
|
|
288
286
|
- [Electro CLI](#electro-cli)
|
|
287
|
+
- [Version 2 Migration](#version-2-migration)
|
|
288
|
+
* [New response format for all query methods.](#new-response-format-for-all-query-methods)
|
|
289
|
+
* [Unified Pagination APIs](#unified-pagination-apis)
|
|
289
290
|
- [Version 1 Migration](#version-1-migration)
|
|
290
291
|
* [New schema format/breaking key format change](#new-schema-format-breaking-key-format-change)
|
|
291
292
|
* [The renaming of index property Facets to Composite and Template](#the-renaming-of-index-property-facets-to-composite-and-template)
|
|
292
293
|
* [Get Method to Return null](#get-method-to-return-null)
|
|
293
|
-
- [Coming Soon](#coming-soon)
|
|
294
294
|
|
|
295
295
|
----------
|
|
296
296
|
|
|
@@ -327,6 +327,9 @@ import { Entity, Service } from "electrodb";
|
|
|
327
327
|
|
|
328
328
|
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.
|
|
329
329
|
|
|
330
|
+
# Getting Started
|
|
331
|
+
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.
|
|
332
|
+
|
|
330
333
|
# Entities
|
|
331
334
|
|
|
332
335
|
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.
|
|
@@ -365,7 +368,7 @@ If you experience any issues using TypeScript with ElectroDB, your feedback is v
|
|
|
365
368
|
|
|
366
369
|
See the section [Exported TypeScript Types](#exported-typescript-types) to read more about the useful types exported from ElectroDB.
|
|
367
370
|
|
|
368
|
-
###
|
|
371
|
+
### Services
|
|
369
372
|
|
|
370
373
|
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.
|
|
371
374
|
```typescript
|
|
@@ -381,48 +384,7 @@ Services take an optional second parameter, similar to Entities, with a `client`
|
|
|
381
384
|
|
|
382
385
|
While not yet typed, this pattern will also accept Models, or a mix of Entities and Models, in the same object literal format.
|
|
383
386
|
|
|
384
|
-
|
|
385
|
-
When using JavaScript, use `join` to add [Entities](#entities) or [Models](#model) onto a Service.
|
|
386
|
-
|
|
387
|
-
> _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._
|
|
388
|
-
|
|
389
|
-
#### Independent Models
|
|
390
|
-
|
|
391
|
-
```javascript
|
|
392
|
-
let table = "my_table_name";
|
|
393
|
-
let employees = new Entity(EmployeesModel, { client, table });
|
|
394
|
-
let tasks = new Entity(TasksModel, { client, table });
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
#### Joining Entity instances to a Service
|
|
398
|
-
|
|
399
|
-
```javascript
|
|
400
|
-
// Joining Entity instances to a Service
|
|
401
|
-
let TaskApp = new Service("TaskApp", { client, table });
|
|
402
|
-
TaskApp
|
|
403
|
-
.join(employees) // available at TaskApp.entities.employees
|
|
404
|
-
.join(tasks); // available at TaskApp.entities.tasks
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
#### Joining models to a Service
|
|
408
|
-
|
|
409
|
-
```javascript
|
|
410
|
-
let TaskApp = new Service("TaskApp", { client, table });
|
|
411
|
-
TaskApp
|
|
412
|
-
.join(EmployeesModel) // available at TaskApp.entities.employees (based on entity name in model)
|
|
413
|
-
.join(TasksModel); // available at TaskApp.entities.tasks (based on entity name in model)
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
#### Joining Entities or Models with an alias
|
|
417
|
-
|
|
418
|
-
```javascript
|
|
419
|
-
let TaskApp = new Service("TaskApp", { client, table });
|
|
420
|
-
TaskApp
|
|
421
|
-
.join("personnel", EmployeesModel) // available at TaskApp.entities.personnel
|
|
422
|
-
.join("directives", TasksModel); // available at TaskApp.entities.directives
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
#### Joining Entities at Service construction for TypeScript
|
|
387
|
+
#### Joining Entities together
|
|
426
388
|
|
|
427
389
|
```typescript
|
|
428
390
|
let TaskApp = new Service({
|
|
@@ -633,7 +595,7 @@ Property | Description
|
|
|
633
595
|
-------------- | -----------
|
|
634
596
|
model.service | Name of the application using the entity, used to namespace all entities
|
|
635
597
|
model.entity | Name of the entity that the schema represents
|
|
636
|
-
model.version |
|
|
598
|
+
model.version | The version number of the schema, used to namespace keys
|
|
637
599
|
attributes | An object containing each attribute that makes up the schema
|
|
638
600
|
indexes | An object containing table indexes, including the values for the table's default Partition Key and Sort Key
|
|
639
601
|
|
|
@@ -651,15 +613,7 @@ client | (optional) An instance of the `docClient` from the `aws-sdk` for use
|
|
|
651
613
|
> **Pro-Tip:**
|
|
652
614
|
> 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.
|
|
653
615
|
|
|
654
|
-
###
|
|
655
|
-
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.
|
|
656
|
-
```typescript
|
|
657
|
-
attributes: {
|
|
658
|
-
<AttributeName>: "string" | "number" | "boolean" | "list" | "map" | "set" | "any" | string[] | ReadonlyArray<string>
|
|
659
|
-
}
|
|
660
|
-
```
|
|
661
|
-
|
|
662
|
-
### Expanded Syntax
|
|
616
|
+
### Attribute Definition
|
|
663
617
|
Use the expanded syntax build out more robust attribute options.
|
|
664
618
|
```typescript
|
|
665
619
|
attributes: {
|
|
@@ -693,7 +647,6 @@ Property | Type | Req
|
|
|
693
647
|
`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.
|
|
694
648
|
`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.
|
|
695
649
|
`label` | `string` | no | all | Used in index composition to prefix key composite attributes. By default, the `AttributeName` is used as the label.
|
|
696
|
-
`cast` | `"number"`, `"string"`, `"boolean"` | no | all | Optionally cast attribute values when interacting with DynamoDB. Current options include: "number", "string", and "boolean".
|
|
697
650
|
`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
|
|
698
651
|
`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.
|
|
699
652
|
`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.
|
|
@@ -776,7 +729,7 @@ attributes: {
|
|
|
776
729
|
|
|
777
730
|
#### Set Attributes
|
|
778
731
|
|
|
779
|
-
The Set attribute is arguably DynamoDB's most powerful type. ElectroDB supports String and Number Sets using the `items` property set as either `"string"
|
|
732
|
+
The Set attribute is arguably DynamoDB's most powerful type. ElectroDB supports String and Number Sets using the `items` property set as either `"string"`, `"number"`, or an array of strings or numbers. When a ReadonlyArray is provided, ElectroDB will enforce those values as a finite list of acceptable values, similar to an [Enum Attribute](#enum-attributes)
|
|
780
733
|
|
|
781
734
|
In addition to having the same modeling benefits you get with other attributes, ElectroDB also simplifies the use of Sets by removing the need to use DynamoDB's special `createSet` class to work with Sets. ElectroDB Set Attributes accept Arrays, JavaScript native Sets, and objects from `createSet` as values. ElectroDB will manage the casting of values to a DynamoDB Set value prior to saving and ElectroDB will also convert Sets back to JavaScript arrays on retrieval.
|
|
782
735
|
|
|
@@ -791,6 +744,14 @@ attributes: {
|
|
|
791
744
|
myNumberSet: {
|
|
792
745
|
type: "set",
|
|
793
746
|
items: "number"
|
|
747
|
+
},
|
|
748
|
+
myEnumStringSet: {
|
|
749
|
+
type: "set",
|
|
750
|
+
items: ["RED", "GREEN", "BLUE"] as const // electrodb will only accept the included values "RED", "GREEN", and/or "BLUE"
|
|
751
|
+
},
|
|
752
|
+
myEnumNumberSet: {
|
|
753
|
+
type: "set",
|
|
754
|
+
items: [1, 2, 3] as const // electrodb will only accept the included values 1, 2, and/or 3
|
|
794
755
|
}
|
|
795
756
|
}
|
|
796
757
|
```
|
|
@@ -842,7 +803,7 @@ If your attributes needs to watch for any changes to an item, you can model this
|
|
|
842
803
|
```typescript
|
|
843
804
|
myAttr: {
|
|
844
805
|
type: "string",
|
|
845
|
-
watch: "*", // "watch all"
|
|
806
|
+
watch: "*", // <- "watch all"
|
|
846
807
|
set: (myAttr, allAttributes) => {
|
|
847
808
|
// Whenever an `update` or `patch` operation is performed, this callback will be fired.
|
|
848
809
|
// Note: myAttr or the attributes under `allAttributes` could be independently undefined because either attribute could have triggered this callback
|
|
@@ -1064,8 +1025,6 @@ signature | behavior
|
|
|
1064
1025
|
`(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.
|
|
1065
1026
|
`(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
|
|
1066
1027
|
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
1028
|
## Indexes
|
|
1070
1029
|
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"_.
|
|
1071
1030
|
|
|
@@ -1073,6 +1032,8 @@ All DynamoDB table start with at least a PartitionKey with an optional SortKey,
|
|
|
1073
1032
|
|
|
1074
1033
|
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.
|
|
1075
1034
|
|
|
1035
|
+
> _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_
|
|
1036
|
+
|
|
1076
1037
|
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.
|
|
1077
1038
|
|
|
1078
1039
|
```typescript
|
|
@@ -1097,17 +1058,17 @@ indexes: {
|
|
|
1097
1058
|
| Property | Type | Required | Description |
|
|
1098
1059
|
| -------------- | :------------------------------------: | :------: | ----------- |
|
|
1099
1060
|
| `pk` | `object` | yes | Configuration for the pk of that index or table
|
|
1100
|
-
| `pk.composite` | `string
|
|
1061
|
+
| `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).
|
|
1101
1062
|
| `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).
|
|
1102
|
-
| `pk.field` | `string` | yes | The name of the
|
|
1103
|
-
| `pk.casing` | `default
|
|
1063
|
+
| `pk.field` | `string` | yes | The name of the index Partition Key field as it exists in DynamoDB, if named differently in the schema attributes.
|
|
1064
|
+
| `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`.
|
|
1104
1065
|
| `sk` | `object` | no | Configuration for the sk of that index or table
|
|
1105
|
-
| `sk.composite` | `string
|
|
1066
|
+
| `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).
|
|
1106
1067
|
| `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).
|
|
1107
|
-
| `sk.field` | `string` | yes | The name of the
|
|
1108
|
-
| `pk.casing` | `default
|
|
1109
|
-
| `index` | `string` | no | Required when the `Index` defined is a *Secondary Index*; but is
|
|
1110
|
-
| `collection` | `string
|
|
1068
|
+
| `sk.field` | `string` | yes | The name of the index Sort Key field as it exists in DynamoDB, if named differently in the schema attributes.
|
|
1069
|
+
| `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`.
|
|
1070
|
+
| `index` | `string` | no | Required when the `Index` defined is a *Global/Local Secondary Index*; but is omitted for the table's primary index.
|
|
1071
|
+
| `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).
|
|
1111
1072
|
|
|
1112
1073
|
### Indexes Without Sort Keys
|
|
1113
1074
|
When using indexes without Sort Keys, that should be expressed as an index *without* an `sk` property at all. Indexes without an `sk` cannot have a collection, see [Collections](#collections) for more detail.
|
|
@@ -1228,14 +1189,6 @@ Casing Option | Effect
|
|
|
1228
1189
|
`upper` | Will convert the key to uppercase prior it its use
|
|
1229
1190
|
`none` | Will not perform any casing changes when building keys
|
|
1230
1191
|
|
|
1231
|
-
## Facets
|
|
1232
|
-
|
|
1233
|
-
As of version `0.11.1`, "Facets" have been renamed to "Composite Attributes", and all documentation has been updated to reflect that change.
|
|
1234
|
-
|
|
1235
|
-
- To learn about the latest syntax, checkout [Composite Attributes](#composite-attributes).
|
|
1236
|
-
- 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).
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
1192
|
## Composite Attributes
|
|
1240
1193
|
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`.
|
|
1241
1194
|
|
|
@@ -1465,7 +1418,7 @@ When your attribute's name, or [`field` property](#expanded-syntax) on an attrib
|
|
|
1465
1418
|
}
|
|
1466
1419
|
```
|
|
1467
1420
|
|
|
1468
|
-
[](
|
|
1421
|
+
[](https://electrodb.fun/?#code/JYWwDg9gTgLgBAbwKIDsbBgTwL5wGZQQhwDkApgDZkDGMhAJgEYkDcAUG9RCgM7zAoAbmTTRMcALxwUZAO5xU6LAAoEbOHBAR6lAFyJ1GuCKWZ9AIkwQArlAD6JjJjsoAhiDLmANIY08yUILA1GQWVrZ2-oHBZC7unj5GcMJQPMDcFgCM5obYiXCuMHTAjNYwZDz6akmu1FzWaACS9FW+RlhgoXDmfFACAOY5SXltYAzWtABy1iCMAa1JGh1d5igzc1BDRti5+QI6AB4VC0Zj2hMwlQaLcGAA1ic3eMCULd219U303m1JXOAQNLlfQAbXMHxsX3MAF1fnARjceA9rjd8C8KG9zGd6BdprMAj9UXB-pAgV0wdjcesCbCbjthrk2HlEDBXIwqPoSCBMAAVNlUEjYACU7E43D4BTqkJgzUk7zqMAAtAAWACsAGYAAzmdhcXjwSlTalQOUAJk1msyooEwlEUEwhgAdGAyqpJZ8ZfQvLdxkb8SbhU7+hBlCKODaTGJHQBHawBB1JZ2+y6qCENT2BxPB0PsIA)
|
|
1469
1422
|
|
|
1470
1423
|
**Using `template`**
|
|
1471
1424
|
|
|
@@ -1487,7 +1440,7 @@ Another approach allows you to use the `template` property, which allows you to
|
|
|
1487
1440
|
"your_access_pattern_name": {
|
|
1488
1441
|
pk: {
|
|
1489
1442
|
field: "accountId",
|
|
1490
|
-
composite: ["accountId"],
|
|
1443
|
+
composite: ["accountId"],
|
|
1491
1444
|
template: "${accountId}"
|
|
1492
1445
|
},
|
|
1493
1446
|
sk: {...}
|
|
@@ -1769,8 +1722,11 @@ let results = await TaskApp.collections
|
|
|
1769
1722
|
.go();
|
|
1770
1723
|
|
|
1771
1724
|
{
|
|
1772
|
-
|
|
1725
|
+
data: {
|
|
1726
|
+
tasks: [...], // tasks for employeeId "JExotic"
|
|
1773
1727
|
employees: [...] // employee record(s) with employeeId "JExotic"
|
|
1728
|
+
},
|
|
1729
|
+
cursor: null
|
|
1774
1730
|
}
|
|
1775
1731
|
```
|
|
1776
1732
|
|
|
@@ -1988,8 +1944,11 @@ const results = await TaskApp.collections
|
|
|
1988
1944
|
|
|
1989
1945
|
// results
|
|
1990
1946
|
{
|
|
1991
|
-
|
|
1992
|
-
|
|
1947
|
+
data: {
|
|
1948
|
+
tasks: [...], // tasks associated with projectId "SD-204
|
|
1949
|
+
projectMembers: [...] // employees of project "SD-204"
|
|
1950
|
+
},
|
|
1951
|
+
cursor: null,
|
|
1993
1952
|
}
|
|
1994
1953
|
|
|
1995
1954
|
// parameters
|
|
@@ -2017,9 +1976,12 @@ const results = await TaskApp.collections
|
|
|
2017
1976
|
|
|
2018
1977
|
// results
|
|
2019
1978
|
{
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
1979
|
+
data: {
|
|
1980
|
+
tasks: [...], // tasks assigned to employeeId "JExotic"
|
|
1981
|
+
projectMembers: [...], // projects with employeeId "JExotic"
|
|
1982
|
+
employees: [...] // employee record(s) with employeeId "JExotic"
|
|
1983
|
+
},
|
|
1984
|
+
cursor: null,
|
|
2023
1985
|
}
|
|
2024
1986
|
|
|
2025
1987
|
{
|
|
@@ -2041,8 +2003,11 @@ const results = await TaskApp.collections
|
|
|
2041
2003
|
|
|
2042
2004
|
// results
|
|
2043
2005
|
{
|
|
2044
|
-
|
|
2045
|
-
|
|
2006
|
+
data: {
|
|
2007
|
+
tasks: [...], // tasks assigned to employeeId "JExotic"
|
|
2008
|
+
projectMembers: [...], // projects with employeeId "JExotic"
|
|
2009
|
+
},
|
|
2010
|
+
cursor: null,
|
|
2046
2011
|
}
|
|
2047
2012
|
|
|
2048
2013
|
{
|
|
@@ -2149,195 +2114,6 @@ For example, the `contributions` collection is named such because when given an
|
|
|
2149
2114
|
|
|
2150
2115
|
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.
|
|
2151
2116
|
|
|
2152
|
-
## Filters
|
|
2153
|
-
|
|
2154
|
-
> 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.
|
|
2155
|
-
|
|
2156
|
-
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.
|
|
2157
|
-
|
|
2158
|
-
```json
|
|
2159
|
-
{
|
|
2160
|
-
"IndexName": "idx2",
|
|
2161
|
-
"TableName": "StoreDirectory",
|
|
2162
|
-
"ExpressionAttributeNames": {
|
|
2163
|
-
"#rent": "rent",
|
|
2164
|
-
"#discount": "discount",
|
|
2165
|
-
"#pk": "idx2pk",
|
|
2166
|
-
"#sk1": "idx2sk"
|
|
2167
|
-
},
|
|
2168
|
-
"ExpressionAttributeValues": {
|
|
2169
|
-
":rent1": "2000.00",
|
|
2170
|
-
":rent2": "5000.00",
|
|
2171
|
-
":discount1": "1000.00",
|
|
2172
|
-
":pk": "$mallstoredirectory_1#mallid_eastpointe",
|
|
2173
|
-
":sk1": "$mallstore#leaseenddate_2020-04-01#rent_",
|
|
2174
|
-
":sk2": "$mallstore#leaseenddate_2020-07-01#rent_"
|
|
2175
|
-
},
|
|
2176
|
-
"KeyConditionExpression": ",#pk = :pk and #sk1 BETWEEN :sk1 AND :sk2",
|
|
2177
|
-
"FilterExpression": "(#rent between :rent1 and :rent2) AND #discount <= :discount1"
|
|
2178
|
-
}
|
|
2179
|
-
```
|
|
2180
|
-
### Defined on the model
|
|
2181
|
-
|
|
2182
|
-
> Deprecated but functional with 1.x
|
|
2183
|
-
|
|
2184
|
-
Filters can be defined on the model and used in your query chain.
|
|
2185
|
-
|
|
2186
|
-
```javascript
|
|
2187
|
-
/**
|
|
2188
|
-
* Filter by low rent a specific mall or a leaseEnd withing a specific range
|
|
2189
|
-
* @param {Object} attributes - All attributes from the model with methods for each filter operation
|
|
2190
|
-
* @param {...*} values - Values passed when calling the filter in a query chain.
|
|
2191
|
-
**/
|
|
2192
|
-
filters: {
|
|
2193
|
-
rentPromotions: function(attributes, minRent, maxRent, promotion) {
|
|
2194
|
-
let {rent, discount} = attributes;
|
|
2195
|
-
return `
|
|
2196
|
-
${rent.between(minRent, maxRent)} AND ${discount.lte(promotion)}
|
|
2197
|
-
`
|
|
2198
|
-
}
|
|
2199
|
-
}
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
let StoreLocations = new Entity(model, {table: "StoreDirectory"});
|
|
2203
|
-
let maxRent = "5000.00";
|
|
2204
|
-
let minRent = "2000.00";
|
|
2205
|
-
let promotion = "1000.00";
|
|
2206
|
-
let stores = await MallStores.query
|
|
2207
|
-
.stores({ mallId: "EastPointe" })
|
|
2208
|
-
.between({ leaseEndDate: "2020-04-01" }, { leaseEndDate: "2020-07-01" })
|
|
2209
|
-
.rentPromotions(minRent, maxRent, promotion)
|
|
2210
|
-
.go();
|
|
2211
|
-
|
|
2212
|
-
// Equivalent Parameters
|
|
2213
|
-
{
|
|
2214
|
-
IndexName: 'idx2',
|
|
2215
|
-
TableName: 'StoreDirectory',
|
|
2216
|
-
ExpressionAttributeNames: {
|
|
2217
|
-
'#rent': 'rent',
|
|
2218
|
-
'#discount': 'discount',
|
|
2219
|
-
'#pk': 'idx2pk',
|
|
2220
|
-
'#sk1': 'idx2sk'
|
|
2221
|
-
},
|
|
2222
|
-
ExpressionAttributeValues: {
|
|
2223
|
-
':rent1': '2000.00',
|
|
2224
|
-
':rent2': '5000.00',
|
|
2225
|
-
':discount1': '1000.00',
|
|
2226
|
-
':pk': '$mallstoredirectory_1#mallid_eastpointe',
|
|
2227
|
-
':sk1': '$mallstore#leaseenddate_2020-04-01#rent_',
|
|
2228
|
-
':sk2': '$mallstore#leaseenddate_2020-07-01#rent_'
|
|
2229
|
-
},
|
|
2230
|
-
KeyConditionExpression: '#pk = :pk and #sk1 BETWEEN :sk1 AND :sk2',
|
|
2231
|
-
FilterExpression: '(#rent between :rent1 and :rent2) AND #discount <= :discount1'
|
|
2232
|
-
}
|
|
2233
|
-
```
|
|
2234
|
-
### Defined via Filter method after query operators
|
|
2235
|
-
|
|
2236
|
-
> 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.
|
|
2237
|
-
|
|
2238
|
-
The easiest way to use filters is to use them inline in your query chain.
|
|
2239
|
-
|
|
2240
|
-
```javascript
|
|
2241
|
-
let StoreLocations = new Entity(model, {table: "StoreDirectory"});
|
|
2242
|
-
let maxRent = "5000.00";
|
|
2243
|
-
let minRent = "2000.00";
|
|
2244
|
-
let promotion = "1000.00";
|
|
2245
|
-
let stores = await StoreLocations.query
|
|
2246
|
-
.leases({ mallId: "EastPointe" })
|
|
2247
|
-
.between({ leaseEndDate: "2020-04-01" }, { leaseEndDate: "2020-07-01" })
|
|
2248
|
-
.filter(({rent, discount}) => `
|
|
2249
|
-
${rent.between(minRent, maxRent)} AND ${discount.lte(promotion)}
|
|
2250
|
-
`)
|
|
2251
|
-
.go();
|
|
2252
|
-
|
|
2253
|
-
// Equivalent Parameters
|
|
2254
|
-
{
|
|
2255
|
-
IndexName: 'idx2',
|
|
2256
|
-
TableName: 'StoreDirectory',
|
|
2257
|
-
ExpressionAttributeNames: {
|
|
2258
|
-
'#rent': 'rent',
|
|
2259
|
-
'#discount': 'discount',
|
|
2260
|
-
'#pk': 'idx2pk',
|
|
2261
|
-
'#sk1': 'idx2sk'
|
|
2262
|
-
},
|
|
2263
|
-
ExpressionAttributeValues: {
|
|
2264
|
-
':rent1': '2000.00',
|
|
2265
|
-
':rent2': '5000.00',
|
|
2266
|
-
':discount1': '1000.00',
|
|
2267
|
-
':pk': '$mallstoredirectory_1#mallid_eastpointe',
|
|
2268
|
-
':sk1': '$mallstore#leaseenddate_2020-04-01#rent_',
|
|
2269
|
-
':sk2': '$mallstore#leaseenddate_2020-07-01#rent_'
|
|
2270
|
-
},
|
|
2271
|
-
KeyConditionExpression: '#pk = :pk and #sk1 BETWEEN :sk1 AND :sk2',
|
|
2272
|
-
FilterExpression: '(#rent between :rent1 and :rent2) AND #discount <= :discount1'
|
|
2273
|
-
}
|
|
2274
|
-
```
|
|
2275
|
-
|
|
2276
|
-
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:
|
|
2277
|
-
|
|
2278
|
-
operator | example | result
|
|
2279
|
-
| ----------- | -------------------------------- |
|
|
2280
|
-
`gte` | `rent.gte(maxRent)` | `#rent >= :rent1`
|
|
2281
|
-
`gt` | `rent.gt(maxRent)` | `#rent > :rent1`
|
|
2282
|
-
`lte` | `rent.lte(maxRent)` | `#rent <= :rent1`
|
|
2283
|
-
`lt` | `rent.lt(maxRent)` | `#rent < :rent1`
|
|
2284
|
-
`eq` | `rent.eq(maxRent)` | `#rent = :rent1`
|
|
2285
|
-
`ne` | `rent.ne(maxRent)` | `#rent <> :rent1`
|
|
2286
|
-
`begins` | `rent.begins(maxRent)` | `begins_with(#rent, :rent1)`
|
|
2287
|
-
`exists` | `rent.exists()` | `attribute_exists(#rent)`
|
|
2288
|
-
`notExists` | `rent.notExists()` | `attribute_not_exists(#rent)`
|
|
2289
|
-
`contains` | `rent.contains(maxRent)` | `contains(#rent = :rent1)`
|
|
2290
|
-
`notContains` | `rent.notContains(maxRent)` | `not contains(#rent = :rent1)`
|
|
2291
|
-
`between` | `rent.between(minRent, maxRent)` | `(#rent between :rent1 and :rent2)`
|
|
2292
|
-
`name` | `rent.name()` | `#rent`
|
|
2293
|
-
`value` | `rent.value(maxRent)` | `:rent1`
|
|
2294
|
-
|
|
2295
|
-
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`.
|
|
2296
|
-
|
|
2297
|
-
### Multiple Filters
|
|
2298
|
-
|
|
2299
|
-
> 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.
|
|
2300
|
-
|
|
2301
|
-
It is possible to chain together multiple filters. The resulting FilterExpressions are concatenated with an implicit `AND` operator.
|
|
2302
|
-
|
|
2303
|
-
```javascript
|
|
2304
|
-
let MallStores = new Entity(model, {table: "StoreDirectory"});
|
|
2305
|
-
let stores = await MallStores.query
|
|
2306
|
-
.leases({ mallId: "EastPointe" })
|
|
2307
|
-
.between({ leaseEndDate: "2020-04-01" }, { leaseEndDate: "2020-07-01" })
|
|
2308
|
-
.filter(({ rent, discount }) => `
|
|
2309
|
-
${rent.between("2000.00", "5000.00")} AND ${discount.eq("1000.00")}
|
|
2310
|
-
`)
|
|
2311
|
-
.filter(({ category }) => `
|
|
2312
|
-
${category.eq("food/coffee")}
|
|
2313
|
-
`)
|
|
2314
|
-
.go();
|
|
2315
|
-
|
|
2316
|
-
// Equivalent Parameters
|
|
2317
|
-
{
|
|
2318
|
-
TableName: 'StoreDirectory',
|
|
2319
|
-
ExpressionAttributeNames: {
|
|
2320
|
-
'#rent': 'rent',
|
|
2321
|
-
'#discount': 'discount',
|
|
2322
|
-
'#category': 'category',
|
|
2323
|
-
'#pk': 'idx2pk',
|
|
2324
|
-
'#sk1': 'idx2sk'
|
|
2325
|
-
},
|
|
2326
|
-
ExpressionAttributeValues: {
|
|
2327
|
-
':rent1': '2000.00',
|
|
2328
|
-
':rent2': '5000.00',
|
|
2329
|
-
':discount1': '1000.00',
|
|
2330
|
-
':category1': 'food/coffee',
|
|
2331
|
-
':pk': '$mallstoredirectory_1#mallid_eastpointe',
|
|
2332
|
-
':sk1': '$mallstore#leaseenddate_2020-04-01#storeid_',
|
|
2333
|
-
':sk2': '$mallstore#leaseenddate_2020-07-01#storeid_'
|
|
2334
|
-
},
|
|
2335
|
-
KeyConditionExpression: '#pk = :pk and #sk1 BETWEEN :sk1 AND :sk2',
|
|
2336
|
-
IndexName: 'idx2',
|
|
2337
|
-
FilterExpression: '(#rent between :rent1 and :rent2) AND (#discount = :discount1 AND #category = :category1)'
|
|
2338
|
-
}
|
|
2339
|
-
```
|
|
2340
|
-
|
|
2341
2117
|
## Where
|
|
2342
2118
|
|
|
2343
2119
|
> The `where()` method is an improvement on the `filter()` method. Unlike `filter`, `where` will be compatible with upcoming features related to complex types.
|
|
@@ -2617,7 +2393,7 @@ attributes | string[] | _(all attributes)_ | The `attributes` option allo
|
|
|
2617
2393
|
|
|
2618
2394
|
ElectroDB queries use DynamoDB's `query` method to find records based on your table's indexes.
|
|
2619
2395
|
|
|
2620
|
-
> _NOTE:
|
|
2396
|
+
> _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._
|
|
2621
2397
|
|
|
2622
2398
|
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**:
|
|
2623
2399
|
1. You must always supply the **Partition Key** in full for all queries to **DynamoDB**.
|
|
@@ -2818,7 +2594,7 @@ The `StoreLocations` entity above, using just the `stores` **Index** alone enabl
|
|
|
2818
2594
|
3. All `LatteLarrys` locations inside a specific *Mall*
|
|
2819
2595
|
4. A specific `LatteLarrys` inside of a *Mall* and *Building*
|
|
2820
2596
|
|
|
2821
|
-
##
|
|
2597
|
+
## Performing Queries
|
|
2822
2598
|
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.
|
|
2823
2599
|
|
|
2824
2600
|
> 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
|
|
@@ -2829,13 +2605,14 @@ The methods: Get (`get`), Create (`put`), Update (`update`), and Delete (`delete
|
|
|
2829
2605
|
|
|
2830
2606
|
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)
|
|
2831
2607
|
|
|
2832
|
-
> _NOTE:
|
|
2608
|
+
> _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._
|
|
2833
2609
|
|
|
2834
2610
|
### Get Method
|
|
2835
2611
|
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.
|
|
2836
2612
|
|
|
2837
2613
|
> _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._
|
|
2838
2614
|
|
|
2615
|
+
Example:
|
|
2839
2616
|
```javascript
|
|
2840
2617
|
let results = await StoreLocations.get({
|
|
2841
2618
|
storeId: "LatteLarrys",
|
|
@@ -2843,15 +2620,24 @@ let results = await StoreLocations.get({
|
|
|
2843
2620
|
buildingId: "F34",
|
|
2844
2621
|
cityId: "Atlanta1"
|
|
2845
2622
|
}).go();
|
|
2623
|
+
```
|
|
2624
|
+
Response Format:
|
|
2625
|
+
```typescript
|
|
2626
|
+
{
|
|
2627
|
+
data: Array<YOUR_SCHEMA>,
|
|
2628
|
+
cursor: string | undefined
|
|
2629
|
+
}
|
|
2630
|
+
```
|
|
2846
2631
|
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2632
|
+
Equivalent DocClient Parameters:
|
|
2633
|
+
```json
|
|
2634
|
+
{
|
|
2635
|
+
"Key": {
|
|
2636
|
+
"pk": "$mallstoredirectory#cityid_atlanta1#mallid_eastpointe",
|
|
2637
|
+
"sk": "$mallstore_1#buildingid_f34#storeid_lattelarrys"
|
|
2638
|
+
},
|
|
2639
|
+
"TableName": "YOUR_TABLE_NAME"
|
|
2640
|
+
}
|
|
2855
2641
|
```
|
|
2856
2642
|
|
|
2857
2643
|
### Batch Get
|
|
@@ -2870,6 +2656,7 @@ If you set the [Query Option](#query-options) `concurrent` to `2`, ElectroDB wil
|
|
|
2870
2656
|
|
|
2871
2657
|
It is important to consider your Table's throughput considerations when setting this value.
|
|
2872
2658
|
|
|
2659
|
+
Example:
|
|
2873
2660
|
```javascript
|
|
2874
2661
|
let [results, unprocessed] = await StoreLocations.get([
|
|
2875
2662
|
{
|
|
@@ -2885,24 +2672,34 @@ let [results, unprocessed] = await StoreLocations.get([
|
|
|
2885
2672
|
cityId: "Madison2"
|
|
2886
2673
|
}
|
|
2887
2674
|
]).go({concurrent: 1}); // `concurrent` value is optional and default's to `1`
|
|
2675
|
+
```
|
|
2676
|
+
|
|
2677
|
+
Response Format:
|
|
2678
|
+
```typescript
|
|
2679
|
+
{
|
|
2680
|
+
data: Array<YOUR_SCHEMA>,
|
|
2681
|
+
unprocessed: Array<YOUR_COMPOSITE_ATTRIBUTES>
|
|
2682
|
+
}
|
|
2683
|
+
```
|
|
2888
2684
|
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2685
|
+
Equivalent DocClient Parameters:
|
|
2686
|
+
```json
|
|
2687
|
+
{
|
|
2688
|
+
"RequestItems": {
|
|
2689
|
+
"YOUR_TABLE_NAME": {
|
|
2690
|
+
"Keys": [
|
|
2691
|
+
{
|
|
2692
|
+
"pk": "$mallstoredirectory#cityid_atlanta1#mallid_eastpointe",
|
|
2693
|
+
"sk": "$mallstore_1#buildingid_f34#storeid_lattelarrys"
|
|
2694
|
+
},
|
|
2695
|
+
{
|
|
2696
|
+
"pk": "$mallstoredirectory#cityid_madison2#mallid_westend",
|
|
2697
|
+
"sk": "$mallstore_1#buildingid_a21#storeid_mochajoes"
|
|
2698
|
+
}
|
|
2699
|
+
]
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2906
2703
|
```
|
|
2907
2704
|
|
|
2908
2705
|
The two-dimensional array returned by batch get most easily used when deconstructed into two variables, in the above case: `results` and `unprocessed`.
|
|
@@ -2916,6 +2713,7 @@ Elements of the `unprocessed` array are unlike results received from a query. In
|
|
|
2916
2713
|
### Delete Method
|
|
2917
2714
|
Provide all Table Index composite attributes in an object to the `delete` method to delete a record.
|
|
2918
2715
|
|
|
2716
|
+
Example:
|
|
2919
2717
|
```javascript
|
|
2920
2718
|
await StoreLocations.delete({
|
|
2921
2719
|
storeId: "LatteLarrys",
|
|
@@ -2923,15 +2721,24 @@ await StoreLocations.delete({
|
|
|
2923
2721
|
buildingId: "F34",
|
|
2924
2722
|
cityId: "Atlanta1"
|
|
2925
2723
|
}).go();
|
|
2724
|
+
```
|
|
2725
|
+
|
|
2726
|
+
Response Format:
|
|
2727
|
+
```typescript
|
|
2728
|
+
{
|
|
2729
|
+
data: { YOUR_SCHEMA }
|
|
2730
|
+
}
|
|
2731
|
+
```
|
|
2926
2732
|
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2733
|
+
Equivalent DocClient Parameters:
|
|
2734
|
+
```json
|
|
2735
|
+
{
|
|
2736
|
+
"Key": {
|
|
2737
|
+
"pk": "$mallstoredirectory#cityid_atlanta1#mallid_eastpointe",
|
|
2738
|
+
"sk": "$mallstore_1#buildingid_f34#storeid_lattelarrys"
|
|
2739
|
+
},
|
|
2740
|
+
"TableName": "YOUR_TABLE_NAME"
|
|
2741
|
+
}
|
|
2935
2742
|
```
|
|
2936
2743
|
|
|
2937
2744
|
### Batch Write Delete Records
|
|
@@ -2950,6 +2757,7 @@ If you set the [Query Option](#query-options) `concurrent` to `2`, ElectroDB wil
|
|
|
2950
2757
|
|
|
2951
2758
|
It is important to consider your Table's throughput considerations when setting this value.
|
|
2952
2759
|
|
|
2760
|
+
Example:
|
|
2953
2761
|
```javascript
|
|
2954
2762
|
let unprocessed = await StoreLocations.delete([
|
|
2955
2763
|
{
|
|
@@ -2965,8 +2773,17 @@ let unprocessed = await StoreLocations.delete([
|
|
|
2965
2773
|
cityId: "LosAngeles1"
|
|
2966
2774
|
}
|
|
2967
2775
|
]).go({concurrent: 1}); // `concurrent` value is optional and default's to `1`
|
|
2776
|
+
```
|
|
2777
|
+
|
|
2778
|
+
Response Format:
|
|
2779
|
+
```typescript
|
|
2780
|
+
{
|
|
2781
|
+
unprocessed: Array<YOUR_COMPOSITE_ATTRIBUTES>
|
|
2782
|
+
}
|
|
2783
|
+
```
|
|
2968
2784
|
|
|
2969
|
-
|
|
2785
|
+
Equivalent DocClient Parameters:
|
|
2786
|
+
```json
|
|
2970
2787
|
{
|
|
2971
2788
|
"RequestItems": {
|
|
2972
2789
|
"StoreDirectory": [
|
|
@@ -2996,7 +2813,9 @@ Elements of the `unprocessed` array are unlike results received from a query. In
|
|
|
2996
2813
|
### Put Record
|
|
2997
2814
|
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.
|
|
2998
2815
|
|
|
2999
|
-
This example includes an optional conditional expression
|
|
2816
|
+
Note: This example includes an optional conditional expression
|
|
2817
|
+
|
|
2818
|
+
Example:
|
|
3000
2819
|
```javascript
|
|
3001
2820
|
await StoreLocations
|
|
3002
2821
|
.put({
|
|
@@ -3011,8 +2830,17 @@ await StoreLocations
|
|
|
3011
2830
|
})
|
|
3012
2831
|
.where((attr, op) => op.eq(attr.rent, "4500.00"))
|
|
3013
2832
|
.go()
|
|
2833
|
+
```
|
|
2834
|
+
|
|
2835
|
+
Response Format:
|
|
2836
|
+
```typescript
|
|
2837
|
+
{
|
|
2838
|
+
data: { YOUR_SCHEMA }
|
|
2839
|
+
}
|
|
2840
|
+
```
|
|
3014
2841
|
|
|
3015
|
-
|
|
2842
|
+
Equivalent DocClient Parameters:
|
|
2843
|
+
```json
|
|
3016
2844
|
{
|
|
3017
2845
|
"Item": {
|
|
3018
2846
|
"cityId": "Atlanta1",
|
|
@@ -3060,6 +2888,7 @@ If you set the [Query Option](#query-options) `concurrent` to `2`, ElectroDB wil
|
|
|
3060
2888
|
|
|
3061
2889
|
It is important to consider your Table's throughput considerations when setting this value.
|
|
3062
2890
|
|
|
2891
|
+
Example:
|
|
3063
2892
|
```javascript
|
|
3064
2893
|
let unprocessed = await StoreLocations.put([
|
|
3065
2894
|
{
|
|
@@ -3083,8 +2912,17 @@ let unprocessed = await StoreLocations.put([
|
|
|
3083
2912
|
rent: "1500.00"
|
|
3084
2913
|
}
|
|
3085
2914
|
]).go({concurrent: 1}); // `concurrent` value is optional and default's to `1`
|
|
2915
|
+
```
|
|
2916
|
+
|
|
2917
|
+
Response Format:
|
|
2918
|
+
```typescript
|
|
2919
|
+
{
|
|
2920
|
+
unprocessed: Array<YOUR_COMPOSITE_ATTRIBUTES>
|
|
2921
|
+
}
|
|
2922
|
+
```
|
|
3086
2923
|
|
|
3087
|
-
|
|
2924
|
+
Equivalent DocClient Parameters:
|
|
2925
|
+
```json
|
|
3088
2926
|
{
|
|
3089
2927
|
"RequestItems": {
|
|
3090
2928
|
"StoreDirectory": [
|
|
@@ -3220,11 +3058,23 @@ For the defined indexes:
|
|
|
3220
3058
|
|
|
3221
3059
|
A user could update `attr4` alone because ElectroDB is able to leverage the value for `attr2` from values supplied to the `update()` method:
|
|
3222
3060
|
|
|
3061
|
+
|
|
3062
|
+
Example:
|
|
3223
3063
|
```typescript
|
|
3224
3064
|
entity.update({ attr1: "value1", attr2: "value2" })
|
|
3225
3065
|
.set({ attr4: "value4" })
|
|
3226
3066
|
.go();
|
|
3067
|
+
```
|
|
3068
|
+
|
|
3069
|
+
Response Format:
|
|
3070
|
+
```typescript
|
|
3071
|
+
{
|
|
3072
|
+
data: { YOUR_SCHEMA }
|
|
3073
|
+
}
|
|
3074
|
+
```
|
|
3227
3075
|
|
|
3076
|
+
Equivalent DocClient Parameters:
|
|
3077
|
+
```json
|
|
3228
3078
|
{
|
|
3229
3079
|
"UpdateExpression": "SET #attr4 = :attr4_u0, #gsi1sk = :gsi1sk_u0, #attr1 = :attr1_u0, #attr2 = :attr2_u0",
|
|
3230
3080
|
"ExpressionAttributeNames": {
|
|
@@ -3240,7 +3090,7 @@ entity.update({ attr1: "value1", attr2: "value2" })
|
|
|
3240
3090
|
":attr1_u0": "value1",
|
|
3241
3091
|
":attr2_u0": "value2"
|
|
3242
3092
|
},
|
|
3243
|
-
"TableName": "
|
|
3093
|
+
"TableName": "YOUR_TABLE_NAME",
|
|
3244
3094
|
"Key": {
|
|
3245
3095
|
"pk": "$service#attr1_value1",
|
|
3246
3096
|
"sk": "$entity_version#attr2_value2"
|
|
@@ -3254,14 +3104,24 @@ entity.update({ attr1: "value1", attr2: "value2" })
|
|
|
3254
3104
|
|
|
3255
3105
|
The `set()` method will accept all attributes defined on the model. Provide a value to apply or replace onto the item.
|
|
3256
3106
|
|
|
3107
|
+
Example:
|
|
3257
3108
|
```javascript
|
|
3258
3109
|
await StoreLocations
|
|
3259
3110
|
.update({cityId, mallId, storeId, buildingId})
|
|
3260
3111
|
.set({category: "food/meal"})
|
|
3261
3112
|
.where((attr, op) => op.eq(attr.category, "food/coffee"))
|
|
3262
3113
|
.go()
|
|
3114
|
+
```
|
|
3115
|
+
|
|
3116
|
+
Response Format:
|
|
3117
|
+
```typescript
|
|
3118
|
+
{
|
|
3119
|
+
data: { YOUR_SCHEMA }
|
|
3120
|
+
}
|
|
3121
|
+
```
|
|
3263
3122
|
|
|
3264
|
-
|
|
3123
|
+
Equivalent DocClient Parameters:
|
|
3124
|
+
```json
|
|
3265
3125
|
{
|
|
3266
3126
|
"UpdateExpression": "SET #category = :category",
|
|
3267
3127
|
"ExpressionAttributeNames": {
|
|
@@ -3286,14 +3146,24 @@ The `remove()` method will accept all attributes defined on the model. Unlike mo
|
|
|
3286
3146
|
|
|
3287
3147
|
> _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._
|
|
3288
3148
|
|
|
3149
|
+
Example:
|
|
3289
3150
|
```javascript
|
|
3290
3151
|
await StoreLocations
|
|
3291
3152
|
.update({cityId, mallId, storeId, buildingId})
|
|
3292
3153
|
.remove(["category"])
|
|
3293
3154
|
.where((attr, op) => op.eq(attr.category, "food/coffee"))
|
|
3294
3155
|
.go()
|
|
3156
|
+
```
|
|
3157
|
+
|
|
3158
|
+
Response Format:
|
|
3159
|
+
```typescript
|
|
3160
|
+
{
|
|
3161
|
+
data: { YOUR_SCHEMA }
|
|
3162
|
+
}
|
|
3163
|
+
```
|
|
3295
3164
|
|
|
3296
|
-
|
|
3165
|
+
Equivalent DocClient Parameters:
|
|
3166
|
+
```json
|
|
3297
3167
|
{
|
|
3298
3168
|
"UpdateExpression": "REMOVE #category",
|
|
3299
3169
|
"ExpressionAttributeNames": {
|
|
@@ -3317,6 +3187,7 @@ The `add()` method will accept attributes with type `number`, `set`, and `any` d
|
|
|
3317
3187
|
|
|
3318
3188
|
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`.
|
|
3319
3189
|
|
|
3190
|
+
Example:
|
|
3320
3191
|
```javascript
|
|
3321
3192
|
const newTenant = client.createSet("larry");
|
|
3322
3193
|
|
|
@@ -3328,8 +3199,17 @@ await StoreLocations
|
|
|
3328
3199
|
})
|
|
3329
3200
|
.where((attr, op) => op.eq(attr.category, "food/coffee"))
|
|
3330
3201
|
.go()
|
|
3202
|
+
```
|
|
3203
|
+
|
|
3204
|
+
Response Format:
|
|
3205
|
+
```typescript
|
|
3206
|
+
{
|
|
3207
|
+
data: { YOUR_SCHEMA }
|
|
3208
|
+
}
|
|
3209
|
+
```
|
|
3331
3210
|
|
|
3332
|
-
|
|
3211
|
+
Equivalent DocClient Parameters:
|
|
3212
|
+
```json
|
|
3333
3213
|
{
|
|
3334
3214
|
"UpdateExpression": "SET #rent = #rent + :rent0 ADD #tenant :tenant0",
|
|
3335
3215
|
"ExpressionAttributeNames": {
|
|
@@ -3355,14 +3235,24 @@ await StoreLocations
|
|
|
3355
3235
|
|
|
3356
3236
|
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.
|
|
3357
3237
|
|
|
3238
|
+
Example:
|
|
3358
3239
|
```javascript
|
|
3359
3240
|
await StoreLocations
|
|
3360
3241
|
.update({cityId, mallId, storeId, buildingId})
|
|
3361
3242
|
.subtract({deposit: 500})
|
|
3362
3243
|
.where((attr, op) => op.eq(attr.category, "food/coffee"))
|
|
3363
3244
|
.go()
|
|
3245
|
+
```
|
|
3246
|
+
|
|
3247
|
+
Response Format:
|
|
3248
|
+
```typescript
|
|
3249
|
+
{
|
|
3250
|
+
data: { YOUR_SCHEMA }
|
|
3251
|
+
}
|
|
3252
|
+
```
|
|
3364
3253
|
|
|
3365
|
-
|
|
3254
|
+
Equivalent DocClient Parameters:
|
|
3255
|
+
```json
|
|
3366
3256
|
{
|
|
3367
3257
|
"UpdateExpression": "SET #deposit = #deposit - :deposit0",
|
|
3368
3258
|
"ExpressionAttributeNames": {
|
|
@@ -3386,6 +3276,7 @@ await StoreLocations
|
|
|
3386
3276
|
|
|
3387
3277
|
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.
|
|
3388
3278
|
|
|
3279
|
+
Example:
|
|
3389
3280
|
```javascript
|
|
3390
3281
|
await StoreLocations
|
|
3391
3282
|
.update({cityId, mallId, storeId, buildingId})
|
|
@@ -3397,8 +3288,17 @@ await StoreLocations
|
|
|
3397
3288
|
})
|
|
3398
3289
|
.where((attr, op) => op.eq(attr.category, "food/coffee"))
|
|
3399
3290
|
.go()
|
|
3291
|
+
```
|
|
3400
3292
|
|
|
3401
|
-
|
|
3293
|
+
Response Format:
|
|
3294
|
+
```typescript
|
|
3295
|
+
{
|
|
3296
|
+
data: { YOUR_SCHEMA }
|
|
3297
|
+
}
|
|
3298
|
+
```
|
|
3299
|
+
|
|
3300
|
+
Equivalent DocClient Parameters:
|
|
3301
|
+
```json
|
|
3402
3302
|
{
|
|
3403
3303
|
"UpdateExpression": "SET #rentalAgreement = list_append(#rentalAgreement, :rentalAgreement0)",
|
|
3404
3304
|
"ExpressionAttributeNames": {
|
|
@@ -3427,14 +3327,24 @@ await StoreLocations
|
|
|
3427
3327
|
|
|
3428
3328
|
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.
|
|
3429
3329
|
|
|
3330
|
+
Example:
|
|
3430
3331
|
```javascript
|
|
3431
3332
|
await StoreLocations
|
|
3432
3333
|
.update({cityId, mallId, storeId, buildingId})
|
|
3433
3334
|
.delete({contact: ['555-345-2222']})
|
|
3434
3335
|
.where((attr, op) => op.eq(attr.category, "food/coffee"))
|
|
3435
3336
|
.go()
|
|
3337
|
+
```
|
|
3436
3338
|
|
|
3437
|
-
|
|
3339
|
+
Response Format:
|
|
3340
|
+
```typescript
|
|
3341
|
+
{
|
|
3342
|
+
data: { YOUR_SCHEMA }
|
|
3343
|
+
}
|
|
3344
|
+
```
|
|
3345
|
+
|
|
3346
|
+
Equivalent DocClient Parameters:
|
|
3347
|
+
```json
|
|
3438
3348
|
{
|
|
3439
3349
|
"UpdateExpression": "DELETE #contact :contact0",
|
|
3440
3350
|
"ExpressionAttributeNames": {
|
|
@@ -3476,6 +3386,7 @@ operation | example | result
|
|
|
3476
3386
|
`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
|
|
3477
3387
|
`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
|
|
3478
3388
|
|
|
3389
|
+
Example:
|
|
3479
3390
|
```javascript
|
|
3480
3391
|
await StoreLocations
|
|
3481
3392
|
.update({cityId, mallId, storeId, buildingId})
|
|
@@ -3497,8 +3408,17 @@ await StoreLocations
|
|
|
3497
3408
|
})
|
|
3498
3409
|
.where((attr, op) => op.eq(attr.category, "food/coffee"))
|
|
3499
3410
|
.go()
|
|
3411
|
+
```
|
|
3500
3412
|
|
|
3501
|
-
|
|
3413
|
+
Response Format:
|
|
3414
|
+
```typescript
|
|
3415
|
+
{
|
|
3416
|
+
data: { YOUR_SCHEMA }
|
|
3417
|
+
}
|
|
3418
|
+
```
|
|
3419
|
+
|
|
3420
|
+
Equivalent DocClient Parameters:
|
|
3421
|
+
```json
|
|
3502
3422
|
{
|
|
3503
3423
|
"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",
|
|
3504
3424
|
"ExpressionAttributeNames": {
|
|
@@ -3610,6 +3530,7 @@ When scanning for rows, you can use filters the same as you would any query. For
|
|
|
3610
3530
|
|
|
3611
3531
|
*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.*
|
|
3612
3532
|
|
|
3533
|
+
Example:
|
|
3613
3534
|
```javascript
|
|
3614
3535
|
await StoreLocations.scan
|
|
3615
3536
|
.where(({category}, {eq}) => `
|
|
@@ -3619,8 +3540,18 @@ await StoreLocations.scan
|
|
|
3619
3540
|
${between(leaseEndDate, "2020-03", "2020-04")}
|
|
3620
3541
|
`)
|
|
3621
3542
|
.go()
|
|
3543
|
+
```
|
|
3544
|
+
|
|
3545
|
+
Response Format:
|
|
3546
|
+
```typescript
|
|
3547
|
+
{
|
|
3548
|
+
data: Array<YOUR_SCHEMA>,
|
|
3549
|
+
cursor: string | undefined
|
|
3550
|
+
}
|
|
3551
|
+
```
|
|
3622
3552
|
|
|
3623
|
-
|
|
3553
|
+
Equivalent DocClient Parameters:
|
|
3554
|
+
```json
|
|
3624
3555
|
{
|
|
3625
3556
|
"TableName": "StoreDirectory",
|
|
3626
3557
|
"ExpressionAttributeNames": {
|
|
@@ -3655,20 +3586,68 @@ await StoreLocations.remove({
|
|
|
3655
3586
|
buildingId: "F34",
|
|
3656
3587
|
cityId: "Atlanta1"
|
|
3657
3588
|
}).go();
|
|
3589
|
+
```
|
|
3590
|
+
|
|
3591
|
+
Response Format:
|
|
3592
|
+
```typescript
|
|
3593
|
+
{
|
|
3594
|
+
data: { YOUR_SCHEMA }
|
|
3595
|
+
}
|
|
3596
|
+
```
|
|
3658
3597
|
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3598
|
+
Equivalent DocClient Parameters:
|
|
3599
|
+
```json
|
|
3600
|
+
{
|
|
3601
|
+
"Key": {
|
|
3602
|
+
"pk": "$mallstoredirectory#cityid_atlanta1#mallid_eastpointe",
|
|
3603
|
+
"sk": "$mallstore_1#buildingid_f34#storeid_lattelarrys"
|
|
3604
|
+
},
|
|
3605
|
+
"TableName": "YOUR_TABLE_TABLE"
|
|
3606
|
+
"ConditionExpression": "attribute_exists(pk) AND attribute_exists(sk)"
|
|
3607
|
+
}
|
|
3668
3608
|
```
|
|
3669
3609
|
|
|
3670
3610
|
### Patch Record
|
|
3671
3611
|
|
|
3612
|
+
```javascript
|
|
3613
|
+
await entity.update({ attr1: "value1", attr2: "value2" })
|
|
3614
|
+
.set({ attr4: "value4" })
|
|
3615
|
+
.go();
|
|
3616
|
+
```
|
|
3617
|
+
|
|
3618
|
+
Response Format:
|
|
3619
|
+
```typescript
|
|
3620
|
+
{
|
|
3621
|
+
data: { YOUR_SCHEMA }
|
|
3622
|
+
}
|
|
3623
|
+
```
|
|
3624
|
+
|
|
3625
|
+
Equivalent DocClient Parameters:
|
|
3626
|
+
```json
|
|
3627
|
+
{
|
|
3628
|
+
"UpdateExpression": "SET #attr4 = :attr4_u0, #gsi1sk = :gsi1sk_u0, #attr1 = :attr1_u0, #attr2 = :attr2_u0",
|
|
3629
|
+
"ExpressionAttributeNames": {
|
|
3630
|
+
"#attr4": "attr4",
|
|
3631
|
+
"#gsi1sk": "gsi1sk",
|
|
3632
|
+
"#attr1": "attr1",
|
|
3633
|
+
"#attr2": "attr2"
|
|
3634
|
+
},
|
|
3635
|
+
"ExpressionAttributeValues": {
|
|
3636
|
+
":attr4_u0": "value6",
|
|
3637
|
+
// This index was successfully built
|
|
3638
|
+
":gsi1sk_u0": "$update-edgecases_1#attr2_value2#attr4_value6",
|
|
3639
|
+
":attr1_u0": "value1",
|
|
3640
|
+
":attr2_u0": "value2"
|
|
3641
|
+
},
|
|
3642
|
+
"TableName": "YOUR_TABLE_NAME",
|
|
3643
|
+
"Key": {
|
|
3644
|
+
"pk": "$service#attr1_value1",
|
|
3645
|
+
"sk": "$entity_version#attr2_value2"
|
|
3646
|
+
},
|
|
3647
|
+
"ConditionExpression": "attribute_exists(pk) AND attribute_exists(sk)"
|
|
3648
|
+
}
|
|
3649
|
+
```
|
|
3650
|
+
|
|
3672
3651
|
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.
|
|
3673
3652
|
|
|
3674
3653
|
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()`.
|
|
@@ -3679,6 +3658,7 @@ In DynamoDB, `put` operations by default will overwrite a record if record being
|
|
|
3679
3658
|
|
|
3680
3659
|
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.
|
|
3681
3660
|
|
|
3661
|
+
Example:
|
|
3682
3662
|
```javascript
|
|
3683
3663
|
await StoreLocations
|
|
3684
3664
|
.create({
|
|
@@ -3693,8 +3673,17 @@ await StoreLocations
|
|
|
3693
3673
|
})
|
|
3694
3674
|
.where((attr, op) => op.eq(attr.rent, "4500.00"))
|
|
3695
3675
|
.go()
|
|
3676
|
+
```
|
|
3696
3677
|
|
|
3697
|
-
|
|
3678
|
+
Response Format:
|
|
3679
|
+
```typescript
|
|
3680
|
+
{
|
|
3681
|
+
data: { YOUR_SCHEMA }
|
|
3682
|
+
}
|
|
3683
|
+
```
|
|
3684
|
+
|
|
3685
|
+
Equivalent DocClient Parameters:
|
|
3686
|
+
```json
|
|
3698
3687
|
{
|
|
3699
3688
|
"Item": {
|
|
3700
3689
|
"cityId": "Atlanta1",
|
|
@@ -3734,13 +3723,24 @@ DynamoDB offers three methods to query records: `get`, `query`, and `scan`. In *
|
|
|
3734
3723
|
|
|
3735
3724
|
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.
|
|
3736
3725
|
|
|
3726
|
+
Example:
|
|
3737
3727
|
```javascript
|
|
3738
3728
|
await StoreLocations.find({
|
|
3739
3729
|
mallId: "EastPointe",
|
|
3740
3730
|
buildingId: "BuildingA1",
|
|
3741
3731
|
}).go()
|
|
3732
|
+
```
|
|
3742
3733
|
|
|
3743
|
-
|
|
3734
|
+
Response Format:
|
|
3735
|
+
```typescript
|
|
3736
|
+
{
|
|
3737
|
+
data: Array<YOUR_SCHEMA>,
|
|
3738
|
+
cursor: string | undefined
|
|
3739
|
+
}
|
|
3740
|
+
```
|
|
3741
|
+
|
|
3742
|
+
Equivalent DocClient Parameters:
|
|
3743
|
+
```json
|
|
3744
3744
|
{
|
|
3745
3745
|
"KeyConditionExpression": "#pk = :pk and begins_with(#sk1, :sk1)",
|
|
3746
3746
|
"TableName": "StoreDirectory",
|
|
@@ -3768,7 +3768,7 @@ Match is a convenience method based off of ElectroDB's [find](#find-records) met
|
|
|
3768
3768
|
|
|
3769
3769
|
Match differs from [Find](#find-records) in that it will also include all supplied values into a query filter.
|
|
3770
3770
|
|
|
3771
|
-
|
|
3771
|
+
Example:
|
|
3772
3772
|
```javascript
|
|
3773
3773
|
await StoreLocations.find({
|
|
3774
3774
|
mallId: "EastPointe",
|
|
@@ -3776,8 +3776,18 @@ await StoreLocations.find({
|
|
|
3776
3776
|
leaseEndDate: "2020-03-22",
|
|
3777
3777
|
rent: "1500.00"
|
|
3778
3778
|
}).go()
|
|
3779
|
+
```
|
|
3780
|
+
|
|
3781
|
+
Response Format:
|
|
3782
|
+
```typescript
|
|
3783
|
+
{
|
|
3784
|
+
data: Array<YOUR_SCHEMA>,
|
|
3785
|
+
cursor: string | undefined
|
|
3786
|
+
}
|
|
3787
|
+
```
|
|
3779
3788
|
|
|
3780
|
-
|
|
3789
|
+
Equivalent DocClient Parameters:
|
|
3790
|
+
```json
|
|
3781
3791
|
{
|
|
3782
3792
|
"KeyConditionExpression": "#pk = :pk and begins_with(#sk1, :sk1)",
|
|
3783
3793
|
"TableName": "StoreDirectory",
|
|
@@ -3867,7 +3877,7 @@ The second example allows you to make queries that do include buildings such as
|
|
|
3867
3877
|
|
|
3868
3878
|
For these reasons it is important to consider that attributes passed to the Access Pattern method are considered to be full, known, data.
|
|
3869
3879
|
|
|
3870
|
-
## Collection
|
|
3880
|
+
## Collection Queries
|
|
3871
3881
|
Collections allow you to query across Entities. They can be used on `Service` instance.
|
|
3872
3882
|
|
|
3873
3883
|
```javascript
|
|
@@ -4010,7 +4020,7 @@ TaskApp.collections
|
|
|
4010
4020
|
}
|
|
4011
4021
|
```
|
|
4012
4022
|
|
|
4013
|
-
##
|
|
4023
|
+
## Executing Queries
|
|
4014
4024
|
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()`).
|
|
4015
4025
|
|
|
4016
4026
|
Both `.params()` and `.go()` take a query configuration object which is detailed more in the section [Query Options](#query-options).
|
|
@@ -4063,126 +4073,73 @@ let stores = MallStores.query
|
|
|
4063
4073
|
|
|
4064
4074
|
```
|
|
4065
4075
|
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
> _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._
|
|
4069
|
-
|
|
4070
|
-
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.
|
|
4071
|
-
|
|
4072
|
-
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.
|
|
4073
|
-
|
|
4074
|
-
The "pager" includes the associated entity's Identifiers.
|
|
4076
|
+
#### Entity Pagination
|
|
4075
4077
|
|
|
4076
|
-
|
|
4078
|
+
##### Pagination Cursor
|
|
4077
4079
|
|
|
4078
|
-
|
|
4080
|
+
All ElectroDB `query` and `scan` operations return a `cursor`, which is a stringified and copy of DynamoDB's `LastEvaluatedKey` with a `base64url` encoding.
|
|
4079
4081
|
|
|
4080
|
-
|
|
4082
|
+
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.
|
|
4081
4083
|
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
```javascript
|
|
4085
|
-
let [next, stores] = await MallStores.query
|
|
4084
|
+
```typescript
|
|
4085
|
+
const results1 = await MallStores.query
|
|
4086
4086
|
.leases({ mallId })
|
|
4087
|
-
.
|
|
4087
|
+
.go(); // no "cursor" passed to `.go()`
|
|
4088
4088
|
|
|
4089
|
-
|
|
4089
|
+
const results2 = await MallStores.query
|
|
4090
4090
|
.leases({ mallId })
|
|
4091
|
-
.
|
|
4092
|
-
|
|
4093
|
-
// page:
|
|
4094
|
-
// {
|
|
4095
|
-
// storeId: "LatteLarrys",
|
|
4096
|
-
// mallId: "EastPointe",
|
|
4097
|
-
// buildingId: "BuildingA1",
|
|
4098
|
-
// unitId: "B47"
|
|
4099
|
-
// __edb_e__: "MallStore",
|
|
4100
|
-
// __edb_v__: "version"
|
|
4101
|
-
// }
|
|
4102
|
-
|
|
4103
|
-
// stores
|
|
4104
|
-
// [{
|
|
4105
|
-
// mall: '3010aa0d-5591-4664-8385-3503ece58b1c',
|
|
4106
|
-
// leaseEnd: '2020-01-20',
|
|
4107
|
-
// sector: '7d0f5c19-ec1d-4c1e-b613-a4cc07eb4db5',
|
|
4108
|
-
// store: 'MNO',
|
|
4109
|
-
// unit: 'B5',
|
|
4110
|
-
// id: 'e0705325-d735-4fe4-906e-74091a551a04',
|
|
4111
|
-
// building: 'BuildingE',
|
|
4112
|
-
// category: 'food/coffee',
|
|
4113
|
-
// rent: '0.00'
|
|
4114
|
-
// },
|
|
4115
|
-
// {
|
|
4116
|
-
// mall: '3010aa0d-5591-4664-8385-3503ece58b1c',
|
|
4117
|
-
// leaseEnd: '2020-01-20',
|
|
4118
|
-
// sector: '7d0f5c19-ec1d-4c1e-b613-a4cc07eb4db5',
|
|
4119
|
-
// store: 'ZYX',
|
|
4120
|
-
// unit: 'B9',
|
|
4121
|
-
// id: 'f201a1d3-2126-46a2-aec9-758ade8ab2ab',
|
|
4122
|
-
// building: 'BuildingI',
|
|
4123
|
-
// category: 'food/coffee',
|
|
4124
|
-
// rent: '0.00'
|
|
4125
|
-
// }]
|
|
4126
|
-
```
|
|
4127
|
-
|
|
4128
|
-
#### Service Pagination
|
|
4129
|
-
|
|
4130
|
-
> _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._
|
|
4131
|
-
|
|
4132
|
-
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.
|
|
4091
|
+
.go({cursor: results1.cursor}); // Paginate by querying with the "cursor" from your first query
|
|
4133
4092
|
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
The `.page()` method also accepts [Query Options](#query-options) just like the `.go()` and `.params()` methods. Unlike those methods, however, the `.page()` method accepts Query Options as the _second_ parameter (the first parameter is reserved for the "pager").
|
|
4137
|
-
|
|
4138
|
-
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.
|
|
4139
|
-
|
|
4140
|
-
> _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._
|
|
4141
|
-
|
|
4142
|
-
The three options for the query option `pager` are as follows:
|
|
4143
|
-
|
|
4144
|
-
```javascript
|
|
4145
|
-
// LastEvaluatedKey
|
|
4093
|
+
// results1
|
|
4146
4094
|
{
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4095
|
+
cursor: '...'
|
|
4096
|
+
data: [{
|
|
4097
|
+
mall: '3010aa0d-5591-4664-8385-3503ece58b1c',
|
|
4098
|
+
leaseEnd: '2020-01-20',
|
|
4099
|
+
sector: '7d0f5c19-ec1d-4c1e-b613-a4cc07eb4db5',
|
|
4100
|
+
store: 'MNO',
|
|
4101
|
+
unit: 'B5',
|
|
4102
|
+
id: 'e0705325-d735-4fe4-906e-74091a551a04',
|
|
4103
|
+
building: 'BuildingE',
|
|
4104
|
+
category: 'food/coffee',
|
|
4105
|
+
rent: '0.00'
|
|
4106
|
+
},
|
|
4107
|
+
{
|
|
4108
|
+
mall: '3010aa0d-5591-4664-8385-3503ece58b1c',
|
|
4109
|
+
leaseEnd: '2020-01-20',
|
|
4110
|
+
sector: '7d0f5c19-ec1d-4c1e-b613-a4cc07eb4db5',
|
|
4111
|
+
store: 'ZYX',
|
|
4112
|
+
unit: 'B9',
|
|
4113
|
+
id: 'f201a1d3-2126-46a2-aec9-758ade8ab2ab',
|
|
4114
|
+
building: 'BuildingI',
|
|
4115
|
+
category: 'food/coffee',
|
|
4116
|
+
rent: '0.00'
|
|
4117
|
+
}]
|
|
4151
4118
|
}
|
|
4152
4119
|
```
|
|
4153
4120
|
|
|
4154
|
-
|
|
4121
|
+
#### Service Pagination
|
|
4122
|
+
|
|
4123
|
+
Pagination with services is also possible. Similar to [Entity Pagination](#entity-pagination), calling the `.go()` method returns the following structure:
|
|
4155
4124
|
|
|
4156
|
-
```
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
"zip": "34706",
|
|
4163
|
-
"office": "mobile branch",
|
|
4164
|
-
"__edb_e__": "offices",
|
|
4165
|
-
"__edb_v__": "1"
|
|
4125
|
+
```typescript
|
|
4126
|
+
type GoResults = {
|
|
4127
|
+
cursor: string | null;
|
|
4128
|
+
data: {
|
|
4129
|
+
[entityName: string]: { /** EntityItem */ }[]
|
|
4130
|
+
}
|
|
4166
4131
|
}
|
|
4167
4132
|
```
|
|
4168
4133
|
|
|
4169
|
-
|
|
4134
|
+
#### Pagination Query Options
|
|
4170
4135
|
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
{
|
|
4174
|
-
"city": "power",
|
|
4175
|
-
"country": "united states of america",
|
|
4176
|
-
"state": "oregon",
|
|
4177
|
-
"zip": "34706",
|
|
4178
|
-
"office": "mobile branch",
|
|
4179
|
-
}
|
|
4180
|
-
```
|
|
4136
|
+
##### Query Option Pager
|
|
4137
|
+
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.
|
|
4181
4138
|
|
|
4182
4139
|
**"raw":** The `"raw"` option returns the LastEvaluatedKey as it was returned by the DynamoDB DocClient.
|
|
4183
4140
|
|
|
4184
|
-
```
|
|
4185
|
-
// {pager: "raw"}
|
|
4141
|
+
```typescript
|
|
4142
|
+
// {pager: "raw"}
|
|
4186
4143
|
{
|
|
4187
4144
|
pk: '$taskapp#country_united states of america#state_oregon',
|
|
4188
4145
|
sk: '$offices_1#city_power#zip_34706#office_mobile branch',
|
|
@@ -4198,14 +4155,14 @@ Simple pagination example:
|
|
|
4198
4155
|
```javascript
|
|
4199
4156
|
async function getAllStores(mallId) {
|
|
4200
4157
|
let stores = [];
|
|
4201
|
-
let
|
|
4158
|
+
let cursor = null;
|
|
4202
4159
|
|
|
4203
4160
|
do {
|
|
4204
|
-
|
|
4161
|
+
const results = await MallStores.query
|
|
4205
4162
|
.leases({ mallId })
|
|
4206
|
-
.
|
|
4207
|
-
stores = [...stores, ...results];
|
|
4208
|
-
|
|
4163
|
+
.go({ pager });
|
|
4164
|
+
stores = [...stores, ...results.data];
|
|
4165
|
+
cursor = results.cursor;
|
|
4209
4166
|
} while(pager !== null);
|
|
4210
4167
|
|
|
4211
4168
|
return stores;
|
|
@@ -4280,7 +4237,7 @@ await StoreLocations.query
|
|
|
4280
4237
|
```
|
|
4281
4238
|
|
|
4282
4239
|
## Query Options
|
|
4283
|
-
Query options can be added the `.params()
|
|
4240
|
+
Query options can be added the `.params()` and `.go()`` to change query behavior or add customer parameters to a query.
|
|
4284
4241
|
|
|
4285
4242
|
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:
|
|
4286
4243
|
|
|
@@ -4288,16 +4245,15 @@ By default, **ElectroDB** enables you to work with records as the names and prop
|
|
|
4288
4245
|
{
|
|
4289
4246
|
params?: object;
|
|
4290
4247
|
table?: string;
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
pager?: "raw" | "named" | "item";
|
|
4248
|
+
data?: 'raw' | 'includeKeys' | 'attributes';
|
|
4249
|
+
pager?: 'raw' | 'cursor';
|
|
4294
4250
|
originalErr?: boolean;
|
|
4295
4251
|
concurrent?: number;
|
|
4296
4252
|
unprocessed?: "raw" | "item";
|
|
4297
4253
|
response?: "default" | "none" | "all_old" | "updated_old" | "all_new" | "updated_new";
|
|
4298
4254
|
ignoreOwnership?: boolean;
|
|
4299
4255
|
limit?: number;
|
|
4300
|
-
pages?: number;
|
|
4256
|
+
pages?: number | 'all';
|
|
4301
4257
|
logger?: (event) => void;
|
|
4302
4258
|
listeners Array<(event) => void>;
|
|
4303
4259
|
preserveBatchOrder?: boolean;
|
|
@@ -4310,16 +4266,16 @@ Option | Default | Description
|
|
|
4310
4266
|
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.
|
|
4311
4267
|
table | _(from constructor)_ | Use a different table than the one defined in the [Service Options](#service-options)
|
|
4312
4268
|
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.
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
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).
|
|
4269
|
+
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.
|
|
4270
|
+
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).
|
|
4316
4271
|
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.
|
|
4317
|
-
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
|
|
4272
|
+
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.
|
|
4318
4273
|
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).
|
|
4319
4274
|
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.
|
|
4320
4275
|
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`.
|
|
4321
4276
|
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.
|
|
4322
|
-
pages |
|
|
4277
|
+
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'`.
|
|
4278
|
+
sort | '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)]
|
|
4323
4279
|
listeners | `[]` | An array of callbacks that are invoked when [internal ElectroDB events](#events) occur.
|
|
4324
4280
|
logger | _none_ | A convenience option for a single event listener that semantically can be used for logging.
|
|
4325
4281
|
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.
|
|
@@ -4468,7 +4424,7 @@ task.query
|
|
|
4468
4424
|
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!
|
|
4469
4425
|
|
|
4470
4426
|
## Query Event
|
|
4471
|
-
The `query` event occurs when a query is made via the terminal
|
|
4427
|
+
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.
|
|
4472
4428
|
|
|
4473
4429
|
*Type:*
|
|
4474
4430
|
```typescript
|
|
@@ -4488,7 +4444,7 @@ const prop3 = "3ec9ed0c-7497-4d05-bdb8-86c09a618047";
|
|
|
4488
4444
|
|
|
4489
4445
|
entity.update({ prop1, prop2 })
|
|
4490
4446
|
.set({ prop3 })
|
|
4491
|
-
.go()
|
|
4447
|
+
.go();
|
|
4492
4448
|
```
|
|
4493
4449
|
|
|
4494
4450
|
*Example Output:*
|
|
@@ -4649,7 +4605,7 @@ task.query
|
|
|
4649
4605
|
.go({ listeners: [listener1, listener2] });
|
|
4650
4606
|
```
|
|
4651
4607
|
|
|
4652
|
-
# Errors
|
|
4608
|
+
# ElectroDB Errors
|
|
4653
4609
|
|
|
4654
4610
|
Error Code | Description
|
|
4655
4611
|
:--------: | --------------------
|
|
@@ -4983,19 +4939,6 @@ By default ElectroDB tries to keep the stack trace close to your code, ideally t
|
|
|
4983
4939
|
|
|
4984
4940
|
### Unknown Errors
|
|
4985
4941
|
|
|
4986
|
-
### Invalid Last Evaluated Key
|
|
4987
|
-
*Code: 5003*
|
|
4988
|
-
|
|
4989
|
-
*Why this occurred:*
|
|
4990
|
-
_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.
|
|
4991
|
-
|
|
4992
|
-
*What to do about it:*
|
|
4993
|
-
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.
|
|
4994
|
-
```javascript
|
|
4995
|
-
// example
|
|
4996
|
-
myModel.scan.page(null, {pager: "raw"});
|
|
4997
|
-
```
|
|
4998
|
-
|
|
4999
4942
|
### No Owner For Pager
|
|
5000
4943
|
*Code: 5004*
|
|
5001
4944
|
|
|
@@ -5121,16 +5064,7 @@ const EmployeesModel = {
|
|
|
5121
5064
|
composite: ["team", "office", "employee"],
|
|
5122
5065
|
},
|
|
5123
5066
|
},
|
|
5124
|
-
}
|
|
5125
|
-
filters: {
|
|
5126
|
-
upcomingCelebrations: (attributes, startDate, endDate) => {
|
|
5127
|
-
let { dateHired, birthday } = attributes;
|
|
5128
|
-
return `${dateHired.between(startDate, endDate)} OR ${birthday.between(
|
|
5129
|
-
startDate,
|
|
5130
|
-
endDate,
|
|
5131
|
-
)}`;
|
|
5132
|
-
},
|
|
5133
|
-
},
|
|
5067
|
+
}
|
|
5134
5068
|
};
|
|
5135
5069
|
|
|
5136
5070
|
const TasksModel = {
|
|
@@ -5228,12 +5162,13 @@ const DynamoDB = require("aws-sdk/clients/dynamodb");
|
|
|
5228
5162
|
const client = new DynamoDB.DocumentClient({region: "us-east-1"});
|
|
5229
5163
|
const { Service } = require("electrodb");
|
|
5230
5164
|
const table = "projectmanagement";
|
|
5231
|
-
const EmployeeApp = new Service("EmployeeApp", { client, table });
|
|
5232
5165
|
|
|
5233
|
-
EmployeeApp
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5166
|
+
const EmployeeApp = new Service({
|
|
5167
|
+
employees: EmployeesModel,
|
|
5168
|
+
tasks: TasksModel,
|
|
5169
|
+
offices: OfficesModel,
|
|
5170
|
+
}, { client, table });
|
|
5171
|
+
|
|
5237
5172
|
```
|
|
5238
5173
|
### Query Records
|
|
5239
5174
|
#### All tasks and employee information for a given employee
|
|
@@ -5245,29 +5180,32 @@ EmployeeApp.collections.assignements({employee: "CBaskin"}).go();
|
|
|
5245
5180
|
Returns the following:
|
|
5246
5181
|
```javascript
|
|
5247
5182
|
{
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5183
|
+
data: {
|
|
5184
|
+
employees: [{
|
|
5185
|
+
employee: "cbaskin",
|
|
5186
|
+
firstName: "carol",
|
|
5187
|
+
lastName: "baskin",
|
|
5188
|
+
office: "big cat rescue",
|
|
5189
|
+
title: "owner",
|
|
5190
|
+
team: "cool cats and kittens",
|
|
5191
|
+
salary: "1,000,000",
|
|
5192
|
+
manager: "",
|
|
5193
|
+
dateHired: "1992-11-04",
|
|
5194
|
+
birthday: "1961-06-06",
|
|
5195
|
+
}],
|
|
5196
|
+
tasks: [{
|
|
5197
|
+
task: "Feed tigers",
|
|
5198
|
+
description: "Prepare food for tigers to eat",
|
|
5199
|
+
project: "Keep tigers alive",
|
|
5200
|
+
employee: "cbaskin"
|
|
5201
|
+
}, {
|
|
5202
|
+
task: "Fill water bowls",
|
|
5203
|
+
description: "Ensure the tigers have enough water",
|
|
5204
|
+
project: "Keep tigers alive",
|
|
5205
|
+
employee: "cbaskin"
|
|
5206
|
+
}]
|
|
5207
|
+
},
|
|
5208
|
+
cursor: '...'
|
|
5271
5209
|
}
|
|
5272
5210
|
```
|
|
5273
5211
|
|
|
@@ -5280,26 +5218,29 @@ EmployeeApp.collections.workplaces({office: "big cat rescue"}).go()
|
|
|
5280
5218
|
Returns the following:
|
|
5281
5219
|
```javascript
|
|
5282
5220
|
{
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5221
|
+
data: {
|
|
5222
|
+
employees: [{
|
|
5223
|
+
employee: "cbaskin",
|
|
5224
|
+
firstName: "carol",
|
|
5225
|
+
lastName: "baskin",
|
|
5226
|
+
office: "big cat rescue",
|
|
5227
|
+
title: "owner",
|
|
5228
|
+
team: "cool cats and kittens",
|
|
5229
|
+
salary: "1,000,000",
|
|
5230
|
+
manager: "",
|
|
5231
|
+
dateHired: "1992-11-04",
|
|
5232
|
+
birthday: "1961-06-06",
|
|
5233
|
+
}],
|
|
5234
|
+
offices: [{
|
|
5235
|
+
office: "big cat rescue",
|
|
5236
|
+
country: "usa",
|
|
5237
|
+
state: "florida",
|
|
5238
|
+
city: "tampa",
|
|
5239
|
+
zip: "12345",
|
|
5240
|
+
address: "123 Kitty Cat Lane"
|
|
5241
|
+
}]
|
|
5242
|
+
},
|
|
5243
|
+
cursor: '...'
|
|
5303
5244
|
}
|
|
5304
5245
|
```
|
|
5305
5246
|
|
|
@@ -5311,19 +5252,22 @@ EmployeeApp.entities.tasks.query.assigned({employee: "cbaskin"}).go();
|
|
|
5311
5252
|
```
|
|
5312
5253
|
Returns the following:
|
|
5313
5254
|
```javascript
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5255
|
+
{
|
|
5256
|
+
data: [
|
|
5257
|
+
{
|
|
5258
|
+
task: "Feed tigers",
|
|
5259
|
+
description: "Prepare food for tigers to eat",
|
|
5260
|
+
project: "Keep tigers alive",
|
|
5261
|
+
employee: "cbaskin"
|
|
5262
|
+
}, {
|
|
5263
|
+
task: "Fill water bowls",
|
|
5264
|
+
description: "Ensure the tigers have enough water",
|
|
5265
|
+
project: "Keep tigers alive",
|
|
5266
|
+
employee: "cbaskin"
|
|
5267
|
+
}
|
|
5268
|
+
],
|
|
5269
|
+
cursor: '...',
|
|
5270
|
+
}
|
|
5327
5271
|
```
|
|
5328
5272
|
#### Tasks for a given project
|
|
5329
5273
|
Fulfilling [Requirement #4](#employee-app-requirements).
|
|
@@ -5332,14 +5276,17 @@ EmployeeApp.entities.tasks.query.project({project: "Murder Carol"}).go();
|
|
|
5332
5276
|
```
|
|
5333
5277
|
Returns the following:
|
|
5334
5278
|
```javascript
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5279
|
+
{
|
|
5280
|
+
data: [
|
|
5281
|
+
{
|
|
5282
|
+
task: "Hire hitman",
|
|
5283
|
+
description: "Find someone to murder Carol",
|
|
5284
|
+
project: "Murder Carol",
|
|
5285
|
+
employee: "jexotic"
|
|
5286
|
+
}
|
|
5287
|
+
],
|
|
5288
|
+
cursor: '...'
|
|
5289
|
+
}
|
|
5343
5290
|
```
|
|
5344
5291
|
|
|
5345
5292
|
#### Find office locations
|
|
@@ -5349,16 +5296,19 @@ EmployeeApp.entities.office.locations({country: "usa", state: "florida"}).go()
|
|
|
5349
5296
|
```
|
|
5350
5297
|
Returns the following:
|
|
5351
5298
|
```javascript
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5299
|
+
{
|
|
5300
|
+
data: [
|
|
5301
|
+
{
|
|
5302
|
+
office: "big cat rescue",
|
|
5303
|
+
country: "usa",
|
|
5304
|
+
state: "florida",
|
|
5305
|
+
city: "tampa",
|
|
5306
|
+
zip: "12345",
|
|
5307
|
+
address: "123 Kitty Cat Lane"
|
|
5308
|
+
}
|
|
5309
|
+
],
|
|
5310
|
+
cursor: '...'
|
|
5311
|
+
}
|
|
5362
5312
|
```
|
|
5363
5313
|
|
|
5364
5314
|
#### Find employee salaries and titles
|
|
@@ -5371,70 +5321,87 @@ EmployeeApp.entities.employees
|
|
|
5371
5321
|
```
|
|
5372
5322
|
Returns the following:
|
|
5373
5323
|
```javascript
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5324
|
+
{
|
|
5325
|
+
data: [
|
|
5326
|
+
{
|
|
5327
|
+
employee: "ssaffery",
|
|
5328
|
+
firstName: "saff",
|
|
5329
|
+
lastName: "saffery",
|
|
5330
|
+
office: "gw zoo",
|
|
5331
|
+
title: "animal wrangler",
|
|
5332
|
+
team: "keepers",
|
|
5333
|
+
salary: "105.00",
|
|
5334
|
+
manager: "jexotic",
|
|
5335
|
+
dateHired: "1999-02-23",
|
|
5336
|
+
birthday: "1960-07-11",
|
|
5337
|
+
}
|
|
5338
|
+
],
|
|
5339
|
+
cursor: '...'
|
|
5340
|
+
}
|
|
5388
5341
|
```
|
|
5389
5342
|
|
|
5390
5343
|
#### Find employee birthdays or anniversaries
|
|
5391
5344
|
Fulfilling [Requirement #7](#employee-app-requirements).
|
|
5392
5345
|
```javascript
|
|
5346
|
+
const startDate = "2020-05-01";
|
|
5347
|
+
const endDate = "2020-06-01";
|
|
5348
|
+
|
|
5393
5349
|
EmployeeApp.entities.employees
|
|
5394
5350
|
.workplaces({office: "gw zoo"})
|
|
5351
|
+
.where(({ birthday, dateHired }, { between }) => `
|
|
5352
|
+
${between(dateHired, startDate, endDate)} OR
|
|
5353
|
+
${between(birthday, startDate, endDate)}
|
|
5354
|
+
`)
|
|
5395
5355
|
.upcomingCelebrations("2020-05-01", "2020-06-01")
|
|
5396
5356
|
.go()
|
|
5397
5357
|
```
|
|
5398
5358
|
Returns the following:
|
|
5399
5359
|
```javascript
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5360
|
+
{
|
|
5361
|
+
data: [
|
|
5362
|
+
{
|
|
5363
|
+
employee: "jexotic",
|
|
5364
|
+
firstName: "joe",
|
|
5365
|
+
lastName: "maldonado-passage",
|
|
5366
|
+
office: "gw zoo",
|
|
5367
|
+
title: "tiger king",
|
|
5368
|
+
team: "founders",
|
|
5369
|
+
salary: "10000.00",
|
|
5370
|
+
manager: "jlowe",
|
|
5371
|
+
dateHired: "1999-02-23",
|
|
5372
|
+
birthday: "1963-03-05",
|
|
5373
|
+
}
|
|
5374
|
+
],
|
|
5375
|
+
cursor: '...'
|
|
5376
|
+
}
|
|
5414
5377
|
```
|
|
5415
5378
|
#### Find direct reports
|
|
5416
5379
|
Fulfilling [Requirement #8](#employee-app-requirements).
|
|
5417
5380
|
```javascript
|
|
5381
|
+
|
|
5418
5382
|
EmployeeApp.entities.employees
|
|
5419
5383
|
.reports({manager: "jlowe"})
|
|
5420
5384
|
.go()
|
|
5421
5385
|
```
|
|
5422
5386
|
Returns the following:
|
|
5423
5387
|
```javascript
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5388
|
+
{
|
|
5389
|
+
data: [
|
|
5390
|
+
{
|
|
5391
|
+
employee: "jexotic",
|
|
5392
|
+
firstName: "joe",
|
|
5393
|
+
lastName: "maldonado-passage",
|
|
5394
|
+
office: "gw zoo",
|
|
5395
|
+
title: "tiger king",
|
|
5396
|
+
team: "founders",
|
|
5397
|
+
salary: "10000.00",
|
|
5398
|
+
manager: "jlowe",
|
|
5399
|
+
dateHired: "1999-02-23",
|
|
5400
|
+
birthday: "1963-03-05",
|
|
5401
|
+
}
|
|
5402
|
+
],
|
|
5403
|
+
cursor: '...'
|
|
5404
|
+
}
|
|
5438
5405
|
```
|
|
5439
5406
|
|
|
5440
5407
|
## Shopping Mall Property Management App
|
|
@@ -5471,14 +5438,16 @@ await StoreLocations.create({
|
|
|
5471
5438
|
Returns the following:
|
|
5472
5439
|
```json
|
|
5473
5440
|
{
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5441
|
+
"data": {
|
|
5442
|
+
"mallId": "EastPointe",
|
|
5443
|
+
"storeId": "LatteLarrys",
|
|
5444
|
+
"buildingId": "BuildingA1",
|
|
5445
|
+
"unitId": "B47",
|
|
5446
|
+
"category": "spite store",
|
|
5447
|
+
"leaseEndDate": "2020-02-29",
|
|
5448
|
+
"rent": "5000.00",
|
|
5449
|
+
"discount": "0.00"
|
|
5450
|
+
}
|
|
5482
5451
|
}
|
|
5483
5452
|
```
|
|
5484
5453
|
---
|
|
@@ -5497,7 +5466,9 @@ await StoreLocations.update({storeId, mallId, buildingId, unitId}).set({
|
|
|
5497
5466
|
Returns the following:
|
|
5498
5467
|
```json
|
|
5499
5468
|
{
|
|
5500
|
-
|
|
5469
|
+
"data": {
|
|
5470
|
+
"leaseEndDate": "2021-02-28"
|
|
5471
|
+
}
|
|
5501
5472
|
}
|
|
5502
5473
|
```
|
|
5503
5474
|
|
|
@@ -5537,8 +5508,9 @@ let storeId = "LatteLarrys";
|
|
|
5537
5508
|
await StoreLocations.delete({storeId, mallId, buildingId, unitId}).go();
|
|
5538
5509
|
```
|
|
5539
5510
|
Returns the following:
|
|
5540
|
-
|
|
5541
|
-
|
|
5511
|
+
|
|
5512
|
+
```json
|
|
5513
|
+
{ "data": {} }
|
|
5542
5514
|
```
|
|
5543
5515
|
|
|
5544
5516
|
### Query Mall Records
|
|
@@ -5616,11 +5588,90 @@ let storeId = "LatteLarrys";
|
|
|
5616
5588
|
let stores = await StoreLocations.malls({mallId}).query({buildingId, storeId}).go();
|
|
5617
5589
|
```
|
|
5618
5590
|
|
|
5619
|
-
#
|
|
5591
|
+
# TypeScript
|
|
5592
|
+
ElectroDB using advanced dynamic typing techniques to automatically create types based on the configurations in your model. Changes to your model will automatically change the types returned by ElectroDB.
|
|
5593
|
+
|
|
5594
|
+
## Custom Attributes
|
|
5595
|
+
If you have a need for a custom attribute type (beyond those supported by ElectroDB) you can use the the export function `createCustomAttribute`. This function takes an attribute definition and allows you to specify a custom typed attribute with ElectroDB:
|
|
5596
|
+
|
|
5597
|
+
> _NOTE: creating a custom type, ElectroDB will enforce attribute constraints based on the attribute definition provided, but will yield typing control to the user. This may result in some mismatches between your typing and the constraints enforced by ElectroDB._
|
|
5598
|
+
|
|
5599
|
+
```typescript
|
|
5600
|
+
import { Entity, createCustomAttribute } from 'electrodb';
|
|
5601
|
+
|
|
5602
|
+
const table = 'workplace_table';
|
|
5603
|
+
|
|
5604
|
+
type PersonnelRole = {
|
|
5605
|
+
type: 'employee';
|
|
5606
|
+
startDate: number;
|
|
5607
|
+
endDate?: number;
|
|
5608
|
+
} | {
|
|
5609
|
+
type: 'contractor';
|
|
5610
|
+
contractStartDate: number;
|
|
5611
|
+
contractEndDate: number;
|
|
5612
|
+
};
|
|
5613
|
+
|
|
5620
5614
|
|
|
5621
|
-
|
|
5615
|
+
const person = new Entity({
|
|
5616
|
+
model: {
|
|
5617
|
+
entity: 'personnel',
|
|
5618
|
+
service: 'workplace',
|
|
5619
|
+
version: '1'
|
|
5620
|
+
},
|
|
5621
|
+
attributes: {
|
|
5622
|
+
id: {
|
|
5623
|
+
type: 'string'
|
|
5624
|
+
},
|
|
5625
|
+
role: createCustomAttribute<PersonnelRole>({
|
|
5626
|
+
required: true,
|
|
5627
|
+
}),
|
|
5628
|
+
},
|
|
5629
|
+
indexes: {
|
|
5630
|
+
record: {
|
|
5631
|
+
pk: {
|
|
5632
|
+
field: 'pk',
|
|
5633
|
+
composite: ['id']
|
|
5634
|
+
},
|
|
5635
|
+
sk: {
|
|
5636
|
+
field: 'sk',
|
|
5637
|
+
composite: [],
|
|
5638
|
+
}
|
|
5639
|
+
}
|
|
5640
|
+
}
|
|
5641
|
+
}, { table });
|
|
5642
|
+
```
|
|
5622
5643
|
|
|
5623
|
-
##
|
|
5644
|
+
## Exported Types
|
|
5645
|
+
|
|
5646
|
+
The following types are exported for easier use while using ElectroDB with TypeScript. The naming convention for the types include three different kinds:
|
|
5647
|
+
|
|
5648
|
+
- `xResponse` -- Types with the postfix `Response` represent the returned interfaces directly from ElectroDB.
|
|
5649
|
+
|
|
5650
|
+
- `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.
|
|
5651
|
+
|
|
5652
|
+
- `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.
|
|
5653
|
+
|
|
5654
|
+
The follow highlight many of the types exported utility types from ElectroDB:
|
|
5655
|
+
|
|
5656
|
+
### QueryResponse Type
|
|
5657
|
+
|
|
5658
|
+
The QueryResponse type is the same type returned by an ElectroDB Query.
|
|
5659
|
+
|
|
5660
|
+
_Definition:_
|
|
5661
|
+
|
|
5662
|
+
```typescript
|
|
5663
|
+
export type QueryResponse<E extends Entity<any, any, any, any>> = {
|
|
5664
|
+
data: EntityItem<E>;
|
|
5665
|
+
cursor: string | null;
|
|
5666
|
+
}
|
|
5667
|
+
```
|
|
5668
|
+
|
|
5669
|
+
_Use:_
|
|
5670
|
+
```typescript
|
|
5671
|
+
type EntitySchema = QueryResponse<typeof MyEntity>
|
|
5672
|
+
```
|
|
5673
|
+
|
|
5674
|
+
### EntityRecord Type
|
|
5624
5675
|
|
|
5625
5676
|
The EntityRecord type is an object containing every attribute an Entity's model.
|
|
5626
5677
|
|
|
@@ -5635,10 +5686,10 @@ type EntityRecord<E extends Entity<any, any, any, any>> =
|
|
|
5635
5686
|
|
|
5636
5687
|
_Use:_
|
|
5637
5688
|
```typescript
|
|
5638
|
-
type
|
|
5689
|
+
type Item = EntityRecord<typeof MyEntity>
|
|
5639
5690
|
```
|
|
5640
5691
|
|
|
5641
|
-
|
|
5692
|
+
### EntityItem Type
|
|
5642
5693
|
|
|
5643
5694
|
This type represents an item as it is returned from a query. This is different from the `EntityRecord` in that this type reflects the `required`, `hidden`, `default`, etc properties defined on the attribute.
|
|
5644
5695
|
|
|
@@ -5654,20 +5705,57 @@ export type EntityItem<E extends Entity<any, any, any, any>> =
|
|
|
5654
5705
|
_Use:_
|
|
5655
5706
|
|
|
5656
5707
|
```typescript
|
|
5657
|
-
type
|
|
5708
|
+
type Item = EntityItem<typeof MyEntityInstance>;
|
|
5658
5709
|
```
|
|
5659
5710
|
|
|
5660
|
-
|
|
5711
|
+
### CollectionItem Type
|
|
5661
5712
|
|
|
5662
|
-
This type represents
|
|
5713
|
+
This type represents an item returned from a collection query, and is similar to EntityItem.
|
|
5714
|
+
|
|
5715
|
+
_Definition:_
|
|
5716
|
+
```typescript
|
|
5717
|
+
export type CollectionItem<SERVICE extends Service<any>, COLLECTION extends keyof SERVICE["collections"]> =
|
|
5718
|
+
SERVICE extends Service<infer E>
|
|
5719
|
+
? Pick<{
|
|
5720
|
+
[EntityName in keyof E]: E[EntityName] extends Entity<infer A, infer F, infer C, infer S>
|
|
5721
|
+
? COLLECTION extends keyof CollectionAssociations<E>
|
|
5722
|
+
? EntityName extends CollectionAssociations<E>[COLLECTION]
|
|
5723
|
+
? ResponseItem<A,F,C,S>[]
|
|
5724
|
+
: never
|
|
5725
|
+
: never
|
|
5726
|
+
: never
|
|
5727
|
+
}, COLLECTION extends keyof CollectionAssociations<E>
|
|
5728
|
+
? CollectionAssociations<E>[COLLECTION]
|
|
5729
|
+
: never>
|
|
5730
|
+
: never
|
|
5731
|
+
```
|
|
5663
5732
|
|
|
5664
5733
|
_Use:_
|
|
5665
5734
|
|
|
5666
|
-
```
|
|
5735
|
+
```typescript
|
|
5667
5736
|
type CollectionResults = CollectionItem<typeof MyServiceInstance, "collectionName">
|
|
5668
5737
|
```
|
|
5669
5738
|
|
|
5670
|
-
|
|
5739
|
+
### CollectionResponse
|
|
5740
|
+
|
|
5741
|
+
This type represents the value returned the collection query itself
|
|
5742
|
+
|
|
5743
|
+
_Definition:_
|
|
5744
|
+
|
|
5745
|
+
```typescript
|
|
5746
|
+
export type CollectionResponse<SERVICE extends Service<any>, COLLECTION extends keyof SERVICE["collections"]> = {
|
|
5747
|
+
data: CollectionItem<SERVICE, COLLECTION>;
|
|
5748
|
+
cursor: string | null;
|
|
5749
|
+
}
|
|
5750
|
+
```
|
|
5751
|
+
|
|
5752
|
+
_Use:_
|
|
5753
|
+
|
|
5754
|
+
```typescript
|
|
5755
|
+
type CollectionResults = CollectionResponse<typeof MyServiceInstance, "collectionName">
|
|
5756
|
+
```
|
|
5757
|
+
|
|
5758
|
+
### CreateEntityItem Type
|
|
5671
5759
|
|
|
5672
5760
|
This type represents an item that you would pass your entity's `put` or `create` method
|
|
5673
5761
|
|
|
@@ -5686,7 +5774,7 @@ _Use:_
|
|
|
5686
5774
|
type NewThing = CreateEntityItem<typeof MyEntityInstance>;
|
|
5687
5775
|
```
|
|
5688
5776
|
|
|
5689
|
-
|
|
5777
|
+
### UpdateEntityItem Type
|
|
5690
5778
|
|
|
5691
5779
|
This type represents an item that you would pass your entity's `set` method when using `create` or `update`.
|
|
5692
5780
|
|
|
@@ -5705,8 +5793,7 @@ _Use:_
|
|
|
5705
5793
|
type UpdateProperties = UpdateEntityItem<typeof MyEntityInstance>;
|
|
5706
5794
|
```
|
|
5707
5795
|
|
|
5708
|
-
|
|
5709
|
-
## UpdateAddEntityItem Type
|
|
5796
|
+
### UpdateAddEntityItem Type
|
|
5710
5797
|
|
|
5711
5798
|
This type represents an item that you would pass your entity's `add` method when using `create` or `update`.
|
|
5712
5799
|
|
|
@@ -5719,7 +5806,7 @@ export type UpdateAddEntityItem<E extends Entity<any, any, any, any>> =
|
|
|
5719
5806
|
|
|
5720
5807
|
`````
|
|
5721
5808
|
|
|
5722
|
-
|
|
5809
|
+
### UpdateSubtractEntityItem Type
|
|
5723
5810
|
|
|
5724
5811
|
This type represents an item that you would pass your entity's `subtract` method when using `create` or `update`.
|
|
5725
5812
|
|
|
@@ -5731,7 +5818,7 @@ export type UpdateSubtractEntityItem<E extends Entity<any, any, any, any>> =
|
|
|
5731
5818
|
: never;
|
|
5732
5819
|
```
|
|
5733
5820
|
|
|
5734
|
-
|
|
5821
|
+
### UpdateAppendEntityItem Type
|
|
5735
5822
|
|
|
5736
5823
|
This type represents an item that you would pass your entity's `append` method when using `create` or `update`.
|
|
5737
5824
|
|
|
@@ -5743,7 +5830,7 @@ export type UpdateAppendEntityItem<E extends Entity<any, any, any, any>> =
|
|
|
5743
5830
|
: never;
|
|
5744
5831
|
```
|
|
5745
5832
|
|
|
5746
|
-
|
|
5833
|
+
### UpdateRemoveEntityItem Type
|
|
5747
5834
|
|
|
5748
5835
|
This type represents an item that you would pass your entity's `remove` method when using `create` or `update`.
|
|
5749
5836
|
|
|
@@ -5755,7 +5842,7 @@ export type UpdateRemoveEntityItem<E extends Entity<any, any, any, any>> =
|
|
|
5755
5842
|
: never;
|
|
5756
5843
|
```
|
|
5757
5844
|
|
|
5758
|
-
|
|
5845
|
+
### UpdateDeleteEntityItem Type
|
|
5759
5846
|
|
|
5760
5847
|
This type represents an item that you would pass your entity's `delete` method when using `create` or `update`.
|
|
5761
5848
|
|
|
@@ -5777,8 +5864,6 @@ Whenever using ElectroDB with existing tables/data, it is best to use the [Query
|
|
|
5777
5864
|
.params({ignoreOwnership: true})
|
|
5778
5865
|
// when querying the table
|
|
5779
5866
|
.go({ignoreOwnership: true})
|
|
5780
|
-
// when using pagination
|
|
5781
|
-
.page(null, {ignoreOwnership: true})
|
|
5782
5867
|
```
|
|
5783
5868
|
|
|
5784
5869
|
**Your existing index fields have values with mixed case:**
|
|
@@ -5803,6 +5888,22 @@ Electro is a CLI utility toolbox for extending the functionality of **ElectroDB*
|
|
|
5803
5888
|
|
|
5804
5889
|
For usage and installation details you can learn more [here](https://github.com/tywalch/electrocli).
|
|
5805
5890
|
|
|
5891
|
+
# Version 2 Migration
|
|
5892
|
+
## New response format for all query methods.
|
|
5893
|
+
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.
|
|
5894
|
+
|
|
5895
|
+
## Unified pagination APIs
|
|
5896
|
+
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.
|
|
5897
|
+
|
|
5898
|
+
Note: It is still possible to return the native DynamoDB LastEvaluatedKey using the `pager` and/or `data` [query options](#query-options). This new `cursor`
|
|
5899
|
+
|
|
5900
|
+
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.
|
|
5901
|
+
|
|
5902
|
+
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.
|
|
5903
|
+
|
|
5904
|
+
## Pagination with a string cursor
|
|
5905
|
+
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.
|
|
5906
|
+
|
|
5806
5907
|
# Version 1 Migration
|
|
5807
5908
|
This section is to detail any breaking changes made on the journey to a stable 1.0 product.
|
|
5808
5909
|
|
|
@@ -5846,6 +5947,7 @@ let old_schema = {
|
|
|
5846
5947
|
attributes: {...},
|
|
5847
5948
|
indexes: {...}
|
|
5848
5949
|
};
|
|
5950
|
+
|
|
5849
5951
|
new Entity(old_schema, {client});
|
|
5850
5952
|
|
|
5851
5953
|
// new way
|
|
@@ -5858,6 +5960,7 @@ let new_schema = {
|
|
|
5858
5960
|
attributes: {...},
|
|
5859
5961
|
indexes: {...}
|
|
5860
5962
|
};
|
|
5963
|
+
|
|
5861
5964
|
new Entity(new_schema, {client, table});
|
|
5862
5965
|
```
|
|
5863
5966
|
|
|
@@ -5876,9 +5979,6 @@ new Service({
|
|
|
5876
5979
|
}, {client});
|
|
5877
5980
|
|
|
5878
5981
|
// new way
|
|
5879
|
-
new Service("service_name", {client, table});
|
|
5880
|
-
|
|
5881
|
-
// new way (for better TypeScript support)
|
|
5882
5982
|
new Service({entity1, entity2, ...})
|
|
5883
5983
|
```
|
|
5884
5984
|
|
|
@@ -5895,6 +5995,3 @@ This change stems from the fact the `facets` is already a defined term in the Dy
|
|
|
5895
5995
|
## Get Method to Return null
|
|
5896
5996
|
|
|
5897
5997
|
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.
|
|
5898
|
-
|
|
5899
|
-
# Coming Soon
|
|
5900
|
-
- Default query options defined on the `model` to give more general control of interactions with the Entity.
|