electrodb 1.8.3 → 1.8.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 +5 -1
- package/README.md +3 -3
- package/package.json +1 -1
- package/src/entity.js +31 -16
- package/src/errors.js +6 -0
- package/src/util.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -178,4 +178,8 @@ All notable changes to this project will be documented in this file. Breaking ch
|
|
|
178
178
|
|
|
179
179
|
## [1.8.3] - 2022-05-14
|
|
180
180
|
### Changed
|
|
181
|
-
- Removing validation that requires at least one attribute to be provided in a PK composite. This opens the door to static PKs if the user so chooses
|
|
181
|
+
- Removing validation that requires at least one attribute to be provided in a PK composite. This opens the door to static PKs if the user so chooses
|
|
182
|
+
|
|
183
|
+
## [1.8.4] - 2022-05-18
|
|
184
|
+
### Changed
|
|
185
|
+
- Removing validation that an attribute used for one index cannot be used by another. ElectroDB will now simply validate that all composite attributes associated with an indexed field are identical, and that a field is not used as both a PK and SK in separate indexes. This change allows for LSIs to be used with ElectroDB
|
package/README.md
CHANGED
|
@@ -1071,7 +1071,7 @@ When using ElectroDB, indexes are referenced by their `AccessPatternName`. This
|
|
|
1071
1071
|
|
|
1072
1072
|
All DynamoDB table start with at least a PartitionKey with an optional SortKey, this can be referred to as the _"Table Index"_. The `indexes` object requires at least the definition of this _Table Index_ **Partition Key** and (if applicable) **Sort Key**.
|
|
1073
1073
|
|
|
1074
|
-
In your model, the _Table Index_ this is expressed as an _Access Pattern_ *without* an `index` property. For Secondary Indexes, use the `index` property to define the name of the index as defined on your DynamoDB table.
|
|
1074
|
+
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
1075
|
|
|
1076
1076
|
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
1077
|
|
|
@@ -2739,7 +2739,7 @@ const MallStore = new Entity(schema, {table: "StoreDirectory"});
|
|
|
2739
2739
|
#### Partition Key Composite Attributes
|
|
2740
2740
|
All queries require (*at minimum*) the **Composite Attributes** included in its defined **Partition Key**. **Composite Attributes** you define on the **Sort Key** can be partially supplied, but must be supplied in the order they are defined.
|
|
2741
2741
|
|
|
2742
|
-
> *
|
|
2742
|
+
> *IMPORTANT: Composite Attributes must be supplied in the order they are composed when invoking the **Access Pattern***. This is because composite attributes are used to form a concatenated key string, and if attributes supplied out of order, it is not possible to fill the gaps in that concatenation.
|
|
2743
2743
|
|
|
2744
2744
|
```javascript
|
|
2745
2745
|
const MallStore = new Entity({
|
|
@@ -3245,7 +3245,7 @@ entity.update({ attr1: "value1", attr2: "value2" })
|
|
|
3245
3245
|
}
|
|
3246
3246
|
```
|
|
3247
3247
|
|
|
3248
|
-
>
|
|
3248
|
+
> _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.__
|
|
3249
3249
|
|
|
3250
3250
|
#### Update Method: Set
|
|
3251
3251
|
|
package/package.json
CHANGED
package/src/entity.js
CHANGED
|
@@ -2257,22 +2257,6 @@ class Entity {
|
|
|
2257
2257
|
facets.fields.push(sk.field);
|
|
2258
2258
|
}
|
|
2259
2259
|
|
|
2260
|
-
if (seenIndexFields[pk.field] !== undefined) {
|
|
2261
|
-
throw new e.ElectroError(e.ErrorCodes.DuplicateIndexFields, `Partition Key (pk) on Access Pattern '${accessPattern}' references the field '${pk.field}' which is already referenced by the Access Pattern '${seenIndexFields[pk.field]}'. Fields used for indexes need to be unique to avoid conflicts.`);
|
|
2262
|
-
} else {
|
|
2263
|
-
seenIndexFields[pk.field] = accessPattern;
|
|
2264
|
-
}
|
|
2265
|
-
|
|
2266
|
-
if (sk.field) {
|
|
2267
|
-
if (sk.field === pk.field) {
|
|
2268
|
-
throw new e.ElectroError(e.ErrorCodes.DuplicateIndexFields, `The Access Pattern '${accessPattern}' references the field '${sk.field}' as the field name for both the PK and SK. Fields used for indexes need to be unique to avoid conflicts.`);
|
|
2269
|
-
} else if (seenIndexFields[sk.field] !== undefined) {
|
|
2270
|
-
throw new e.ElectroError(e.ErrorCodes.DuplicateIndexFields, `Sort Key (sk) on Access Pattern '${accessPattern}' references the field '${sk.field}' which is already referenced by the Access Pattern '${seenIndexFields[sk.field]}'. Fields used for indexes need to be unique to avoid conflicts.`);
|
|
2271
|
-
}else {
|
|
2272
|
-
seenIndexFields[sk.field] = accessPattern;
|
|
2273
|
-
}
|
|
2274
|
-
}
|
|
2275
|
-
|
|
2276
2260
|
if (Array.isArray(sk.facets)) {
|
|
2277
2261
|
let duplicates = pk.facets.filter(facet => sk.facets.includes(facet));
|
|
2278
2262
|
if (duplicates.length !== 0) {
|
|
@@ -2362,6 +2346,37 @@ class Entity {
|
|
|
2362
2346
|
facets.byField[sk.field][indexName] = sk;
|
|
2363
2347
|
}
|
|
2364
2348
|
|
|
2349
|
+
if (seenIndexFields[pk.field] !== undefined) {
|
|
2350
|
+
const definition = Object.values(facets.byField[pk.field]).find(definition => definition.index !== indexName)
|
|
2351
|
+
const definitionsMatch = validations.stringArrayMatch(pk.facets, definition.facets);
|
|
2352
|
+
if (!definitionsMatch) {
|
|
2353
|
+
throw new e.ElectroError(e.ErrorCodes.InconsistentIndexDefinition, `Partition Key (pk) on Access Pattern '${u.formatIndexNameForDisplay(accessPattern)}' is defined with the composite attribute(s) ${u.commaSeparatedString(pk.facets)}, but the accessPattern '${u.formatIndexNameForDisplay(definition.index)}' defines this field with the composite attributes ${u.commaSeparatedString(definition.facets)}'. Key fields must have the same composite attribute definitions across all indexes they are involved with`);
|
|
2354
|
+
}
|
|
2355
|
+
seenIndexFields[pk.field].push({accessPattern, type: 'pk'});
|
|
2356
|
+
} else {
|
|
2357
|
+
seenIndexFields[pk.field] = [];
|
|
2358
|
+
seenIndexFields[pk.field].push({accessPattern, type: 'pk'});
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
if (sk.field) {
|
|
2362
|
+
if (sk.field === pk.field) {
|
|
2363
|
+
throw new e.ElectroError(e.ErrorCodes.DuplicateIndexFields, `The Access Pattern '${u.formatIndexNameForDisplay(accessPattern)}' references the field '${sk.field}' as the field name for both the PK and SK. Fields used for indexes need to be unique to avoid conflicts.`);
|
|
2364
|
+
} else if (seenIndexFields[sk.field] !== undefined) {
|
|
2365
|
+
const isAlsoDefinedAsPK = seenIndexFields[sk.field].find(field => field.type === "pk");
|
|
2366
|
+
if (isAlsoDefinedAsPK) {
|
|
2367
|
+
throw new e.ElectroError(e.ErrorCodes.InconsistentIndexDefinition, `The Sort Key (sk) on Access Pattern '${u.formatIndexNameForDisplay(accessPattern)}' references the field '${pk.field}' which is already referenced by the Access Pattern(s) '${u.formatIndexNameForDisplay(isAlsoDefinedAsPK.accessPattern)}' as a Partition Key. Fields mapped to Partition Keys cannot be also mapped to Sort Keys.`);
|
|
2368
|
+
}
|
|
2369
|
+
const definition = Object.values(facets.byField[sk.field]).find(definition => definition.index !== indexName)
|
|
2370
|
+
const definitionsMatch = validations.stringArrayMatch(sk.facets, definition.facets);
|
|
2371
|
+
if (!definitionsMatch) {
|
|
2372
|
+
throw new e.ElectroError(e.ErrorCodes.DuplicateIndexFields, `Sort Key (sk) on Access Pattern '${u.formatIndexNameForDisplay(accessPattern)}' is defined with the composite attribute(s) ${u.commaSeparatedString(sk.facets)}, but the accessPattern '${u.formatIndexNameForDisplay(definition.index)}' defines this field with the composite attributes ${u.commaSeparatedString(definition.facets)}'. Key fields must have the same composite attribute definitions across all indexes they are involved with`);
|
|
2373
|
+
}
|
|
2374
|
+
seenIndexFields[sk.field].push({accessPattern, type: 'sk'});
|
|
2375
|
+
} else {
|
|
2376
|
+
seenIndexFields[sk.field] = [];
|
|
2377
|
+
seenIndexFields[sk.field].push({accessPattern, type: 'sk'});
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2365
2380
|
|
|
2366
2381
|
attributes.forEach(({index, type, name}, j) => {
|
|
2367
2382
|
let next = attributes[j + 1] !== undefined ? attributes[j + 1].name : "";
|
package/src/errors.js
CHANGED
|
@@ -139,6 +139,12 @@ const ErrorCodes = {
|
|
|
139
139
|
name: "InvalidClientProvided",
|
|
140
140
|
sym: ErrorCode,
|
|
141
141
|
},
|
|
142
|
+
InconsistentIndexDefinition: {
|
|
143
|
+
code: 1022,
|
|
144
|
+
section: "inconsistent-index-definition",
|
|
145
|
+
name: "InvalidClientProvided",
|
|
146
|
+
sym: ErrorCode,
|
|
147
|
+
},
|
|
142
148
|
MissingAttribute: {
|
|
143
149
|
code: 2001,
|
|
144
150
|
section: "missing-attribute",
|
package/src/util.js
CHANGED
|
@@ -75,8 +75,8 @@ function batchItems(arr = [], size) {
|
|
|
75
75
|
return batched;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
function commaSeparatedString(array = []) {
|
|
79
|
-
return array.map(value =>
|
|
78
|
+
function commaSeparatedString(array = [], prefix = '"', postfix = '"') {
|
|
79
|
+
return array.map(value => `${prefix}${value}${postfix}`).join(", ");
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
function formatStringCasing(str, casing, defaultCase) {
|