electrodb 1.4.3 → 1.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -1
- package/README.md +94 -0
- package/package.json +1 -1
- package/src/entity.js +21 -1
package/CHANGELOG.md
CHANGED
|
@@ -88,4 +88,12 @@ All notable changes to this project will be documented in this file. Breaking ch
|
|
|
88
88
|
|
|
89
89
|
## [1.4.3] - 2021-10-03
|
|
90
90
|
### Fixed
|
|
91
|
-
- ElectroDB would throw when an `undefined` property was passed to query. This has been changed to not throw if a partial query on that index can be accomplished with the data provided.
|
|
91
|
+
- ElectroDB would throw when an `undefined` property was passed to query. This has been changed to not throw if a partial query on that index can be accomplished with the data provided.
|
|
92
|
+
|
|
93
|
+
## [1.4.4] - 2021-10-16
|
|
94
|
+
### Added
|
|
95
|
+
- Updates did not include composite attributes involved in primary index. Though these values cannot be changed, they should be `set` on update method calls in case the update results in an item insert. [[read more]](./README.md#updates-to-composite-attributes)
|
|
96
|
+
|
|
97
|
+
## [0.11.1] - 2021-10-17
|
|
98
|
+
### Patched
|
|
99
|
+
- Updates did not include composite attributes involved in primary index. Though these values cannot be changed, they should be `set` on update method calls in case the update results in an item insert. [[read more]](./README.md#updates-to-composite-attributes)
|
package/README.md
CHANGED
|
@@ -176,6 +176,7 @@ tasks
|
|
|
176
176
|
+ [Put Record](#put-record)
|
|
177
177
|
+ [Batch Write Put Records](#batch-write-put-records)
|
|
178
178
|
+ [Update Record](#update-record)
|
|
179
|
+
- [Updates to Composite Attributes](#updates-to-composite-attributes)
|
|
179
180
|
- [Update Method: Set](#update-method-set)
|
|
180
181
|
- [Update Method: Remove](#update-method-remove)
|
|
181
182
|
- [Update Method: Add](#update-method-add)
|
|
@@ -3129,6 +3130,99 @@ Update Method | Attribute Types
|
|
|
3129
3130
|
[delete](#update-method-delete) | `any` `set` | `object`
|
|
3130
3131
|
[data](#update-method-data) | `*` | `callback`
|
|
3131
3132
|
|
|
3133
|
+
#### Updates to Composite Attributes
|
|
3134
|
+
|
|
3135
|
+
ElectroDB adds some constraints to update calls to prevent the accidental loss of data. If an access pattern is defined with multiple composite attributes, then ElectroDB ensure the attributes cannot be updated individually. If an attribute involved in an index composite is updated, then the index key also must be updated, and if the whole key cannot be formed by the attributes supplied to the update, then it cannot create a composite key without overwriting the old data.
|
|
3136
|
+
|
|
3137
|
+
This example shows why a partial update to a composite key is prevented by ElectroDB:
|
|
3138
|
+
|
|
3139
|
+
```json
|
|
3140
|
+
{
|
|
3141
|
+
"index": "my-gsi",
|
|
3142
|
+
"pk": {
|
|
3143
|
+
"field": "gsi1pk",
|
|
3144
|
+
"composite": ["attr1"]
|
|
3145
|
+
},
|
|
3146
|
+
"sk": {
|
|
3147
|
+
"field": "gsi1sk",
|
|
3148
|
+
"composite": ["attr2", "attr3"]
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
3151
|
+
```
|
|
3152
|
+
|
|
3153
|
+
The above secondary index definition would generate the following index keys:
|
|
3154
|
+
|
|
3155
|
+
```json
|
|
3156
|
+
{
|
|
3157
|
+
"gsi1pk": "$service#attr1_value1",
|
|
3158
|
+
"gsi1sk": "$entity_version#attr2_value2#attr3_value6"
|
|
3159
|
+
}
|
|
3160
|
+
```
|
|
3161
|
+
|
|
3162
|
+
If a user attempts to update the attribute `attr2`, then ElectroDB has no way of knowing value of the attribute `attr3` or if forming the composite key without it would overwrite its value. The same problem exists if a user were to update `attr3`, ElectroDB cannot update the key without knowing each composite attribute's value.
|
|
3163
|
+
|
|
3164
|
+
In the event that a secondary index includes composite values from the table's primary index, ElectroDB will draw from the values supplied for the update key to address index gaps in the secondary index. For example:
|
|
3165
|
+
|
|
3166
|
+
For the defined indexes:
|
|
3167
|
+
|
|
3168
|
+
```json
|
|
3169
|
+
{
|
|
3170
|
+
"accessPattern1": {
|
|
3171
|
+
"pk": {
|
|
3172
|
+
"field": "pk",
|
|
3173
|
+
"composite": ["attr1"]
|
|
3174
|
+
},
|
|
3175
|
+
"sk": {
|
|
3176
|
+
"field": "sk",
|
|
3177
|
+
"composite": ["attr2"]
|
|
3178
|
+
}
|
|
3179
|
+
},
|
|
3180
|
+
"accessPattern2": {
|
|
3181
|
+
"index": "my-gsi",
|
|
3182
|
+
"pk": {
|
|
3183
|
+
"field": "gsi1pk",
|
|
3184
|
+
"composite": ["attr3"]
|
|
3185
|
+
},
|
|
3186
|
+
"sk": {
|
|
3187
|
+
"field": "gsi1sk",
|
|
3188
|
+
"composite": ["attr2", "attr4"]
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
```
|
|
3193
|
+
|
|
3194
|
+
A user could update `attr4` alone because ElectroDB is able to leverage the value for `attr2` from values supplied to the `update()` method:
|
|
3195
|
+
|
|
3196
|
+
```typescript
|
|
3197
|
+
entity.update({ attr1: "value1", attr2: "value2" })
|
|
3198
|
+
.set({ attr4: "value4" })
|
|
3199
|
+
.go();
|
|
3200
|
+
|
|
3201
|
+
{
|
|
3202
|
+
"UpdateExpression": "SET #attr4 = :attr4_u0, #gsi1sk = :gsi1sk_u0, #attr1 = :attr1_u0, #attr2 = :attr2_u0",
|
|
3203
|
+
"ExpressionAttributeNames": {
|
|
3204
|
+
"#attr4": "attr4",
|
|
3205
|
+
"#gsi1sk": "gsi1sk",
|
|
3206
|
+
"#attr1": "attr1",
|
|
3207
|
+
"#attr2": "attr2"
|
|
3208
|
+
},
|
|
3209
|
+
"ExpressionAttributeValues": {
|
|
3210
|
+
":attr4_u0": "value6",
|
|
3211
|
+
// This index was successfully built
|
|
3212
|
+
":gsi1sk_u0": "$update-edgecases_1#attr2_value2#attr4_value6",
|
|
3213
|
+
":attr1_u0": "value1",
|
|
3214
|
+
":attr2_u0": "value2"
|
|
3215
|
+
},
|
|
3216
|
+
"TableName": "test_table",
|
|
3217
|
+
"Key": {
|
|
3218
|
+
"pk": "$service#attr1_value1",
|
|
3219
|
+
"sk": "$entity_version#attr2_value2"
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
```
|
|
3223
|
+
|
|
3224
|
+
> Note: Included in the update are all attributes from the table's primary index. These values are automatically included on all updates in the event an update results in an insert.
|
|
3225
|
+
|
|
3132
3226
|
#### Update Method: Set
|
|
3133
3227
|
|
|
3134
3228
|
The `set()` method will accept all attributes defined on the model. Provide a value to apply or replace onto the item.
|
package/package.json
CHANGED
package/src/entity.js
CHANGED
|
@@ -1003,6 +1003,7 @@ class Entity {
|
|
|
1003
1003
|
}
|
|
1004
1004
|
|
|
1005
1005
|
_makeUpdateParams(update = {}, pk = {}, sk = {}) {
|
|
1006
|
+
let primaryIndexAttributes = {...pk, ...sk};
|
|
1006
1007
|
let modifiedAttributeValues = {};
|
|
1007
1008
|
let modifiedAttributeNames = {};
|
|
1008
1009
|
for (const path of Object.keys(update.paths)) {
|
|
@@ -1038,6 +1039,7 @@ class Entity {
|
|
|
1038
1039
|
const wasNotAlreadyModified = modifiedAttributeNames[indexKey] === undefined;
|
|
1039
1040
|
if (isNotTablePK && isNotTableSK && wasNotAlreadyModified) {
|
|
1040
1041
|
update.set(indexKey, updatedKeys[indexKey]);
|
|
1042
|
+
|
|
1041
1043
|
}
|
|
1042
1044
|
}
|
|
1043
1045
|
|
|
@@ -1050,6 +1052,24 @@ class Entity {
|
|
|
1050
1052
|
}
|
|
1051
1053
|
}
|
|
1052
1054
|
|
|
1055
|
+
// This loop adds the composite attributes to the Primary Index. This is important
|
|
1056
|
+
// in the case an update results in an "upsert". We want to add the Primary Index
|
|
1057
|
+
// composite attributes to the update so they will be included on the item when it
|
|
1058
|
+
// is created. It is done after all of the above because it is not a true "update"
|
|
1059
|
+
// so it should not be subject to the above "rules".
|
|
1060
|
+
for (const primaryIndexAttribute of Object.keys(primaryIndexAttributes)) {
|
|
1061
|
+
// isNotTablePK and isNotTableSK is important to check in case these properties
|
|
1062
|
+
// are not also the name of the index (you cannot modify the PK or SK of an item
|
|
1063
|
+
// after its creation)
|
|
1064
|
+
const attribute = this.model.schema.attributes[primaryIndexAttribute];
|
|
1065
|
+
const isNotTablePK = !!(attribute && attribute.field !== this.model.indexes[accessPattern].pk.field);
|
|
1066
|
+
const isNotTableSK = !!(attribute && attribute.field !== this.model.indexes[accessPattern].sk.field);
|
|
1067
|
+
const wasNotAlreadyModified = modifiedAttributeNames[primaryIndexAttribute] === undefined;
|
|
1068
|
+
if (isNotTablePK && isNotTableSK && wasNotAlreadyModified) {
|
|
1069
|
+
update.set(primaryIndexAttribute, primaryIndexAttributes[primaryIndexAttribute]);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1053
1073
|
return {
|
|
1054
1074
|
UpdateExpression: update.build(),
|
|
1055
1075
|
ExpressionAttributeNames: update.getNames(),
|
|
@@ -1429,7 +1449,7 @@ class Entity {
|
|
|
1429
1449
|
{ ...keyAttributes },
|
|
1430
1450
|
);
|
|
1431
1451
|
const removedKeyImpact = this._expectIndexFacets(
|
|
1432
|
-
{...removed},
|
|
1452
|
+
{ ...removed },
|
|
1433
1453
|
{...keyAttributes}
|
|
1434
1454
|
)
|
|
1435
1455
|
|