electrodb 1.6.1 → 1.6.2
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 +1 -1
- package/package.json +1 -1
- package/src/clauses.js +20 -2
- package/src/entity.js +11 -34
- package/src/operations.js +6 -1
- package/src/where.js +17 -1
package/CHANGELOG.md
CHANGED
|
@@ -138,4 +138,8 @@ All notable changes to this project will be documented in this file. Breaking ch
|
|
|
138
138
|
|
|
139
139
|
## [1.6.1] - 2021-12-05
|
|
140
140
|
### Fixed
|
|
141
|
-
- In some cases the `find()` and `match()` methods would incorrectly select an index without a complete partition key. This would result in validation exceptions preventing the user from querying if an index definition and provided attribute object aligned improperly. This was fixed and a slightly more robust mechanism for ranking indexes was made.
|
|
141
|
+
- In some cases the `find()` and `match()` methods would incorrectly select an index without a complete partition key. This would result in validation exceptions preventing the user from querying if an index definition and provided attribute object aligned improperly. This was fixed and a slightly more robust mechanism for ranking indexes was made.
|
|
142
|
+
|
|
143
|
+
## [1.6.2] = 2021-01-27
|
|
144
|
+
### Changed
|
|
145
|
+
- The methods `create`, `patch`, and `remove` will now refer to primary table keys through parameters via ExpressionAttributeNames when using `attribute_exists()`/`attribute_not_exists()` DynamoDB conditions. Prior to this they were referenced directly which would fail in cases where key names include illegal characters. Parameter implementation change only, non-breaking.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
[](https://coveralls.io/github/tywalch/electrodb?branch=master&kill_cache=please)
|
|
3
3
|
[](https://www.npmjs.com/package/electrodb)
|
|
4
4
|
 [](https://travis-ci.org/tywalch/electrodb)
|
|
5
|
-
[](https://runkit.com/tywalch/
|
|
5
|
+
[](https://runkit.com/tywalch/electrodb-building-queries)
|
|
6
6
|
|
|
7
7
|

|
|
8
8
|
***ElectroDB*** is a DynamoDB library to ease the use of having multiple entities and complex hierarchical relationships in a single DynamoDB table.
|
package/package.json
CHANGED
package/src/clauses.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const { QueryTypes, MethodTypes, ItemOperations, ExpressionTypes } = require("./types");
|
|
2
|
-
const {AttributeOperationProxy, UpdateOperations} = require("./operations");
|
|
1
|
+
const { QueryTypes, MethodTypes, ItemOperations, ExpressionTypes, TableIndex } = require("./types");
|
|
2
|
+
const {AttributeOperationProxy, UpdateOperations, FilterOperationNames} = require("./operations");
|
|
3
3
|
const {UpdateExpression} = require("./update");
|
|
4
4
|
const {FilterExpression} = require("./where");
|
|
5
5
|
const v = require("./validations");
|
|
@@ -134,6 +134,12 @@ let clauses = {
|
|
|
134
134
|
}
|
|
135
135
|
try {
|
|
136
136
|
const attributes = state.getCompositeAttributes();
|
|
137
|
+
const filter = state.query.filter[ExpressionTypes.ConditionExpression];
|
|
138
|
+
const {pk, sk} = entity._getPrimaryIndexFieldNames();
|
|
139
|
+
filter.unsafeSet(FilterOperationNames.exists, pk);
|
|
140
|
+
if (sk) {
|
|
141
|
+
filter.unsafeSet(FilterOperationNames.exists, sk);
|
|
142
|
+
}
|
|
137
143
|
return state
|
|
138
144
|
.setMethod(MethodTypes.delete)
|
|
139
145
|
.setType(QueryTypes.eq)
|
|
@@ -189,6 +195,12 @@ let clauses = {
|
|
|
189
195
|
try {
|
|
190
196
|
let record = entity.model.schema.checkCreate({...payload});
|
|
191
197
|
const attributes = state.getCompositeAttributes();
|
|
198
|
+
const filter = state.query.filter[ExpressionTypes.ConditionExpression];
|
|
199
|
+
const {pk, sk} = entity._getPrimaryIndexFieldNames();
|
|
200
|
+
filter.unsafeSet(FilterOperationNames.notExists, pk);
|
|
201
|
+
if (sk) {
|
|
202
|
+
filter.unsafeSet(FilterOperationNames.notExists, sk);
|
|
203
|
+
}
|
|
192
204
|
return state
|
|
193
205
|
.setMethod(MethodTypes.put)
|
|
194
206
|
.setType(QueryTypes.eq)
|
|
@@ -213,6 +225,12 @@ let clauses = {
|
|
|
213
225
|
}
|
|
214
226
|
try {
|
|
215
227
|
const attributes = state.getCompositeAttributes();
|
|
228
|
+
const filter = state.query.filter[ExpressionTypes.ConditionExpression];
|
|
229
|
+
const {pk, sk} = entity._getPrimaryIndexFieldNames();
|
|
230
|
+
filter.unsafeSet(FilterOperationNames.exists, pk);
|
|
231
|
+
if (sk) {
|
|
232
|
+
filter.unsafeSet(FilterOperationNames.exists, sk);
|
|
233
|
+
}
|
|
216
234
|
return state
|
|
217
235
|
.setMethod(MethodTypes.update)
|
|
218
236
|
.setType(QueryTypes.eq)
|
package/src/entity.js
CHANGED
|
@@ -198,11 +198,7 @@ class Entity {
|
|
|
198
198
|
|
|
199
199
|
create(attributes = {}) {
|
|
200
200
|
let index = TableIndex;
|
|
201
|
-
let options = {
|
|
202
|
-
params: {
|
|
203
|
-
ConditionExpression: this._makeItemDoesntExistConditions(index)
|
|
204
|
-
}
|
|
205
|
-
};
|
|
201
|
+
let options = {};
|
|
206
202
|
return this._makeChain(index, this._clausesWithFilters, clauses.index, options).create(attributes);
|
|
207
203
|
}
|
|
208
204
|
|
|
@@ -213,21 +209,13 @@ class Entity {
|
|
|
213
209
|
|
|
214
210
|
patch(facets = {}) {
|
|
215
211
|
let index = TableIndex;
|
|
216
|
-
let options = {
|
|
217
|
-
params: {
|
|
218
|
-
ConditionExpression: this._makeItemExistsConditions(index)
|
|
219
|
-
}
|
|
220
|
-
};
|
|
212
|
+
let options = {};
|
|
221
213
|
return this._makeChain(index, this._clausesWithFilters, clauses.index, options).patch(facets);
|
|
222
214
|
}
|
|
223
215
|
|
|
224
216
|
remove(facets = {}) {
|
|
225
217
|
let index = TableIndex;
|
|
226
|
-
let options = {
|
|
227
|
-
params: {
|
|
228
|
-
ConditionExpression: this._makeItemExistsConditions(index)
|
|
229
|
-
}
|
|
230
|
-
};
|
|
218
|
+
let options = {};
|
|
231
219
|
return this._makeChain(index, this._clausesWithFilters, clauses.index, options).remove(facets);
|
|
232
220
|
}
|
|
233
221
|
|
|
@@ -866,29 +854,18 @@ class Entity {
|
|
|
866
854
|
return {parameters, config};
|
|
867
855
|
}
|
|
868
856
|
|
|
869
|
-
|
|
870
|
-
let hasSortKey = this.model.lookup.indexHasSortKeys[
|
|
871
|
-
let accessPattern = this.model.translations.indexes.fromIndexToAccessPattern[
|
|
857
|
+
_getPrimaryIndexFieldNames() {
|
|
858
|
+
let hasSortKey = this.model.lookup.indexHasSortKeys[TableIndex];
|
|
859
|
+
let accessPattern = this.model.translations.indexes.fromIndexToAccessPattern[TableIndex];
|
|
872
860
|
let pkField = this.model.indexes[accessPattern].pk.field;
|
|
873
|
-
let
|
|
861
|
+
let skField;
|
|
874
862
|
if (hasSortKey) {
|
|
875
|
-
|
|
876
|
-
filter.push(`attribute_not_exists(${skField})`);
|
|
863
|
+
skField = this.model.indexes[accessPattern].sk.field;
|
|
877
864
|
}
|
|
878
|
-
return
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
_makeItemExistsConditions(index) {
|
|
882
|
-
let hasSortKey = this.model.lookup.indexHasSortKeys[index];
|
|
883
|
-
let accessPattern = this.model.translations.indexes.fromIndexToAccessPattern[index];
|
|
884
|
-
let pkField = this.model.indexes[accessPattern].pk.field;
|
|
885
|
-
|
|
886
|
-
let filter = [`attribute_exists(${pkField})`];
|
|
887
|
-
if (hasSortKey) {
|
|
888
|
-
let skField = this.model.indexes[accessPattern].sk.field;
|
|
889
|
-
filter.push(`attribute_exists(${skField})`);
|
|
865
|
+
return {
|
|
866
|
+
pk: pkField,
|
|
867
|
+
sk: skField
|
|
890
868
|
}
|
|
891
|
-
return filter.join(" AND ");
|
|
892
869
|
}
|
|
893
870
|
|
|
894
871
|
_applyParameterExpressionTypes(params, filter) {
|
package/src/operations.js
CHANGED
|
@@ -453,4 +453,9 @@ class AttributeOperationProxy {
|
|
|
453
453
|
}
|
|
454
454
|
}
|
|
455
455
|
|
|
456
|
-
|
|
456
|
+
const FilterOperationNames = Object.keys(FilterOperations).reduce((ops, name) => {
|
|
457
|
+
ops[name] = name;
|
|
458
|
+
return ops;
|
|
459
|
+
}, {});
|
|
460
|
+
|
|
461
|
+
module.exports = {UpdateOperations, FilterOperations, FilterOperationNames, ExpressionState, AttributeOperationProxy};
|
package/src/where.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const {MethodTypes, ExpressionTypes, BuilderTypes} = require("./types");
|
|
2
|
-
const {AttributeOperationProxy, ExpressionState} = require("./operations");
|
|
2
|
+
const {AttributeOperationProxy, ExpressionState, FilterOperations} = require("./operations");
|
|
3
3
|
const e = require("./errors");
|
|
4
4
|
|
|
5
5
|
class FilterExpression extends ExpressionState {
|
|
@@ -43,6 +43,22 @@ class FilterExpression extends ExpressionState {
|
|
|
43
43
|
this.expression = expression;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// applies operations without verifying them against known attributes. Used internally for key conditions.
|
|
47
|
+
unsafeSet(operation, name, ...values) {
|
|
48
|
+
const {template} = FilterOperations[operation] || {};
|
|
49
|
+
if (template === undefined) {
|
|
50
|
+
throw new Error(`Invalid operation: "${operation}". Please report`);
|
|
51
|
+
}
|
|
52
|
+
const names = this.setName({}, name, name);
|
|
53
|
+
if (values.length) {
|
|
54
|
+
for (const value of values) {
|
|
55
|
+
this.setValue(name, value);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const condition = template({}, name.expression, names.prop, ...values);
|
|
59
|
+
this.add(condition);
|
|
60
|
+
}
|
|
61
|
+
|
|
46
62
|
build() {
|
|
47
63
|
return this.expression;
|
|
48
64
|
}
|