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 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
  [![Coverage Status](https://coveralls.io/repos/github/tywalch/electrodb/badge.svg?branch=master)](https://coveralls.io/github/tywalch/electrodb?branch=master&kill_cache=please)
3
3
  [![Coverage Status](https://img.shields.io/npm/dt/electrodb.svg)](https://www.npmjs.com/package/electrodb)
4
4
  ![npm bundle size](https://img.shields.io/bundlephobia/min/electrodb) [![Build Status](https://travis-ci.org/tywalch/electrodb.svg?branch=master)](https://travis-ci.org/tywalch/electrodb)
5
- [![Runkit Demo](https://img.shields.io/badge/runkit-electrodb-db4792)](https://runkit.com/tywalch/creating-and-querying-an-electrodb-service)
5
+ [![Runkit Demo](https://img.shields.io/badge/runkit-electrodb-db4792)](https://runkit.com/tywalch/electrodb-building-queries)
6
6
 
7
7
  ![ElectroDB](https://github.com/tywalch/electrodb/blob/master/assets/electrodb-drk.png?raw=true)
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "electrodb",
3
- "version": "1.6.1",
3
+ "version": "1.6.2",
4
4
  "description": "A library to more easily create and interact with multiple entities and heretical relationships in dynamodb",
5
5
  "main": "index.js",
6
6
  "scripts": {
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
- _makeItemDoesntExistConditions(index) {
870
- let hasSortKey = this.model.lookup.indexHasSortKeys[index];
871
- let accessPattern = this.model.translations.indexes.fromIndexToAccessPattern[index];
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 filter = [`attribute_not_exists(${pkField})`];
861
+ let skField;
874
862
  if (hasSortKey) {
875
- let skField = this.model.indexes[accessPattern].sk.field;
876
- filter.push(`attribute_not_exists(${skField})`);
863
+ skField = this.model.indexes[accessPattern].sk.field;
877
864
  }
878
- return filter.join(" AND ");
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
- module.exports = {UpdateOperations, FilterOperations, ExpressionState, AttributeOperationProxy};
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
  }