electrodb 2.9.2 → 2.10.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.
@@ -0,0 +1,126 @@
1
+ const FilterOperations = {
2
+ escape: {
3
+ template: function escape(options, attr) {
4
+ return `${attr}`;
5
+ },
6
+ rawValue: true,
7
+ },
8
+ size: {
9
+ template: function size(options, attr, name) {
10
+ return `size(${name})`
11
+ },
12
+ strict: false,
13
+ },
14
+ type: {
15
+ template: function attributeType(options, attr, name, value) {
16
+ return `attribute_type(${name}, ${value})`;
17
+ },
18
+ strict: false
19
+ },
20
+ ne: {
21
+ template: function ne(options, attr, name, value) {
22
+ return `${name} <> ${value}`;
23
+ },
24
+ strict: false,
25
+ },
26
+ eq: {
27
+ template: function eq(options, attr, name, value) {
28
+ return `${name} = ${value}`;
29
+ },
30
+ strict: false,
31
+ },
32
+ gt: {
33
+ template: function gt(options, attr, name, value) {
34
+ return `${name} > ${value}`;
35
+ },
36
+ strict: false
37
+ },
38
+ lt: {
39
+ template: function lt(options, attr, name, value) {
40
+ return `${name} < ${value}`;
41
+ },
42
+ strict: false
43
+ },
44
+ gte: {
45
+ template: function gte(options, attr, name, value) {
46
+ return `${name} >= ${value}`;
47
+ },
48
+ strict: false
49
+ },
50
+ lte: {
51
+ template: function lte(options, attr, name, value) {
52
+ return `${name} <= ${value}`;
53
+ },
54
+ strict: false
55
+ },
56
+ between: {
57
+ template: function between(options, attr, name, value1, value2) {
58
+ return `(${name} between ${value1} and ${value2})`;
59
+ },
60
+ strict: false
61
+ },
62
+ begins: {
63
+ template: function begins(options, attr, name, value) {
64
+ return `begins_with(${name}, ${value})`;
65
+ },
66
+ strict: false
67
+ },
68
+ exists: {
69
+ template: function exists(options, attr, name) {
70
+ return `attribute_exists(${name})`;
71
+ },
72
+ strict: false
73
+ },
74
+ notExists: {
75
+ template: function notExists(options, attr, name) {
76
+ return `attribute_not_exists(${name})`;
77
+ },
78
+ strict: false
79
+ },
80
+ contains: {
81
+ template: function contains(options, attr, name, value) {
82
+ return `contains(${name}, ${value})`;
83
+ },
84
+ strict: false
85
+ },
86
+ notContains: {
87
+ template: function notContains(options, attr, name, value) {
88
+ return `not contains(${name}, ${value})`;
89
+ },
90
+ strict: false
91
+ },
92
+ value: {
93
+ template: function(options, attr, name, value) {
94
+ return value;
95
+ },
96
+ strict: false,
97
+ canNest: true,
98
+ },
99
+ name: {
100
+ template: function(options, attr, name) {
101
+ return name;
102
+ },
103
+ strict: false,
104
+ canNest: true,
105
+ },
106
+ eqOrNotExists: {
107
+ template: function eq(options, attr, name, value) {
108
+ return `(${name} = ${value} OR attribute_not_exists(${name}))`;
109
+ },
110
+ strict: false,
111
+ },
112
+ field: {
113
+ template: function(options, _, fieldName) {
114
+ return fieldName !== undefined
115
+ ? `${fieldName}`
116
+ : '';
117
+ },
118
+ strict: false,
119
+ canNest: true,
120
+ rawField: true,
121
+ }
122
+ };
123
+
124
+ module.exports = {
125
+ FilterOperations
126
+ }
package/src/operations.js CHANGED
@@ -1,276 +1,9 @@
1
- const {AttributeTypes, ItemOperations, AttributeProxySymbol, BuilderTypes, DynamoDBAttributeTypes} = require("./types");
1
+ const { AttributeTypes, ItemOperations, AttributeProxySymbol, BuilderTypes} = require("./types");
2
+ const { UpdateOperations } = require('./updateOperations');
3
+ const { FilterOperations } = require('./filterOperations');
2
4
  const e = require("./errors");
3
5
  const u = require("./util");
4
6
 
5
- const deleteOperations = {
6
- canNest: false,
7
- template: function del(options, attr, path, value) {
8
- let operation = "";
9
- let expression = "";
10
- switch(attr.type) {
11
- case AttributeTypes.any:
12
- case AttributeTypes.set:
13
- operation = ItemOperations.delete;
14
- expression = `${path} ${value}`;
15
- break;
16
- default:
17
- throw new Error(`Invalid Update Attribute Operation: "DELETE" Operation can only be performed on attributes with type "set" or "any".`);
18
- }
19
- return {operation, expression};
20
- },
21
- };
22
-
23
- const UpdateOperations = {
24
- ifNotExists: {
25
- template: function if_not_exists(options, attr, path, value) {
26
- const operation = ItemOperations.set;
27
- const expression = `${path} = if_not_exists(${path}, ${value})`;
28
- return {operation, expression};
29
- }
30
- },
31
- name: {
32
- canNest: true,
33
- template: function name(options, attr, path) {
34
- return path;
35
- }
36
- },
37
- value: {
38
- canNest: true,
39
- template: function value(options, attr, path, value) {
40
- return value;
41
- }
42
- },
43
- append: {
44
- canNest: false,
45
- template: function append(options, attr, path, value) {
46
- let operation = "";
47
- let expression = "";
48
- switch(attr.type) {
49
- case AttributeTypes.any:
50
- case AttributeTypes.list:
51
- operation = ItemOperations.set;
52
- expression = `${path} = list_append(${path}, ${value})`;
53
- break;
54
- default:
55
- throw new Error(`Invalid Update Attribute Operation: "APPEND" Operation can only be performed on attributes with type "list" or "any".`);
56
- }
57
- return {operation, expression};
58
- }
59
- },
60
- add: {
61
- canNest: false,
62
- template: function add(options, attr, path, value) {
63
- let operation = "";
64
- let expression = "";
65
- let type = attr.type;
66
- if (type === AttributeTypes.any) {
67
- type = typeof value === 'number'
68
- ? AttributeTypes.number
69
- : AttributeTypes.any;
70
- }
71
- switch(type) {
72
- case AttributeTypes.any:
73
- case AttributeTypes.set:
74
- operation = ItemOperations.add;
75
- expression = `${path} ${value}`;
76
- break;
77
- case AttributeTypes.number:
78
- if (options.nestedValue) {
79
- operation = ItemOperations.set;
80
- expression = `${path} = ${path} + ${value}`;
81
- } else {
82
- operation = ItemOperations.add;
83
- expression = `${path} ${value}`;
84
- }
85
- break;
86
- default:
87
- throw new Error(`Invalid Update Attribute Operation: "ADD" Operation can only be performed on attributes with type "number", "set", or "any".`);
88
- }
89
- return {operation, expression};
90
- }
91
- },
92
- subtract: {
93
- canNest: false,
94
- template: function subtract(options, attr, path, value) {
95
- let operation = "";
96
- let expression = "";
97
- switch(attr.type) {
98
- case AttributeTypes.any:
99
- case AttributeTypes.number:
100
- operation = ItemOperations.set;
101
- expression = `${path} = ${path} - ${value}`;
102
- break;
103
- default:
104
- throw new Error(`Invalid Update Attribute Operation: "SUBTRACT" Operation can only be performed on attributes with type "number" or "any".`);
105
- }
106
-
107
- return {operation, expression};
108
- }
109
- },
110
- set: {
111
- canNest: false,
112
- template: function set(options, attr, path, value) {
113
- let operation = "";
114
- let expression = "";
115
- switch(attr.type) {
116
- case AttributeTypes.set:
117
- case AttributeTypes.list:
118
- case AttributeTypes.map:
119
- case AttributeTypes.enum:
120
- case AttributeTypes.string:
121
- case AttributeTypes.number:
122
- case AttributeTypes.boolean:
123
- case AttributeTypes.any:
124
- operation = ItemOperations.set;
125
- expression = `${path} = ${value}`;
126
- break;
127
- default:
128
- throw new Error(`Invalid Update Attribute Operation: "SET" Operation can only be performed on attributes with type "list", "map", "string", "number", "boolean", or "any".`);
129
- }
130
- return {operation, expression};
131
- }
132
- },
133
- remove: {
134
- canNest: false,
135
- template: function remove(options, attr, ...paths) {
136
- let operation = "";
137
- let expression = "";
138
- switch(attr.type) {
139
- case AttributeTypes.set:
140
- case AttributeTypes.any:
141
- case AttributeTypes.list:
142
- case AttributeTypes.map:
143
- case AttributeTypes.string:
144
- case AttributeTypes.number:
145
- case AttributeTypes.boolean:
146
- case AttributeTypes.enum:
147
- operation = ItemOperations.remove;
148
- expression = paths.join(", ");
149
- break;
150
- default: {
151
- throw new Error(`Invalid Update Attribute Operation: "REMOVE" Operation can only be performed on attributes with type "map", "list", "string", "number", "boolean", or "any".`);
152
- }
153
- }
154
- return {operation, expression};
155
- }
156
- },
157
- del: deleteOperations,
158
- delete: deleteOperations
159
- }
160
-
161
- const FilterOperations = {
162
- escape: {
163
- template: function escape(options, attr) {
164
- return `${attr}`;
165
- },
166
- noAttribute: true,
167
- },
168
- size: {
169
- template: function size(options, attr, name) {
170
- return `size(${name})`
171
- },
172
- strict: false,
173
- },
174
- type: {
175
- template: function attributeType(options, attr, name, value) {
176
- return `attribute_type(${name}, ${value})`;
177
- },
178
- strict: false
179
- },
180
- ne: {
181
- template: function ne(options, attr, name, value) {
182
- return `${name} <> ${value}`;
183
- },
184
- strict: false,
185
- },
186
- eq: {
187
- template: function eq(options, attr, name, value) {
188
- return `${name} = ${value}`;
189
- },
190
- strict: false,
191
- },
192
- gt: {
193
- template: function gt(options, attr, name, value) {
194
- return `${name} > ${value}`;
195
- },
196
- strict: false
197
- },
198
- lt: {
199
- template: function lt(options, attr, name, value) {
200
- return `${name} < ${value}`;
201
- },
202
- strict: false
203
- },
204
- gte: {
205
- template: function gte(options, attr, name, value) {
206
- return `${name} >= ${value}`;
207
- },
208
- strict: false
209
- },
210
- lte: {
211
- template: function lte(options, attr, name, value) {
212
- return `${name} <= ${value}`;
213
- },
214
- strict: false
215
- },
216
- between: {
217
- template: function between(options, attr, name, value1, value2) {
218
- return `(${name} between ${value1} and ${value2})`;
219
- },
220
- strict: false
221
- },
222
- begins: {
223
- template: function begins(options, attr, name, value) {
224
- return `begins_with(${name}, ${value})`;
225
- },
226
- strict: false
227
- },
228
- exists: {
229
- template: function exists(options, attr, name) {
230
- return `attribute_exists(${name})`;
231
- },
232
- strict: false
233
- },
234
- notExists: {
235
- template: function notExists(options, attr, name) {
236
- return `attribute_not_exists(${name})`;
237
- },
238
- strict: false
239
- },
240
- contains: {
241
- template: function contains(options, attr, name, value) {
242
- return `contains(${name}, ${value})`;
243
- },
244
- strict: false
245
- },
246
- notContains: {
247
- template: function notContains(options, attr, name, value) {
248
- return `not contains(${name}, ${value})`;
249
- },
250
- strict: false
251
- },
252
- value: {
253
- template: function(options, attr, name, value) {
254
- return value;
255
- },
256
- strict: false,
257
- canNest: true,
258
- },
259
- name: {
260
- template: function(options, attr, name) {
261
- return name;
262
- },
263
- strict: false,
264
- canNest: true,
265
- },
266
- eqOrNotExists: {
267
- template: function eq(options, attr, name, value) {
268
- return `(${name} = ${value} OR attribute_not_exists(${name}))`;
269
- },
270
- strict: false,
271
- }
272
- };
273
-
274
7
  class ExpressionState {
275
8
  constructor({prefix} = {}) {
276
9
  this.names = {};
@@ -304,10 +37,10 @@ class ExpressionState {
304
37
  expression = `${paths.expression}.${prop}`;
305
38
  this.names[prop] = value;
306
39
  } else {
307
- json = `${paths.json}[*]`;
40
+ json = `${paths.json}[${name}]`;
308
41
  expression = `${paths.expression}[${name}]`;
309
42
  }
310
- return {json, expression, prop};
43
+ return { json, expression, prop };
311
44
  }
312
45
 
313
46
  getNames() {
@@ -361,24 +94,31 @@ class AttributeOperationProxy {
361
94
  return op(this.attributes, this.operations, ...params);
362
95
  }
363
96
 
97
+ performOperation({operation, path, value, force = false}) {
98
+ if (value === undefined) {
99
+ return;
100
+ }
101
+ const parts = u.parseJSONPath(path);
102
+ let attribute = this.attributes;
103
+ for (let part of parts) {
104
+ attribute = attribute[part];
105
+ }
106
+ if (attribute) {
107
+ this.operations[operation](attribute, value);
108
+ const {target} = attribute();
109
+ if (target.readOnly && !force) {
110
+ throw new Error(`Attribute "${target.path}" is Read-Only and cannot be updated`);
111
+ }
112
+ }
113
+ }
114
+
364
115
  fromObject(operation, record) {
365
116
  for (let path of Object.keys(record)) {
366
- if (record[path] === undefined) {
367
- continue;
368
- }
369
- const value = record[path];
370
- const parts = u.parseJSONPath(path);
371
- let attribute = this.attributes;
372
- for (let part of parts) {
373
- attribute = attribute[part];
374
- }
375
- if (attribute) {
376
- this.operations[operation](attribute, value);
377
- const {target} = attribute();
378
- if (target.readOnly) {
379
- throw new Error(`Attribute "${target.path}" is Read-Only and cannot be updated`);
380
- }
381
- }
117
+ this.performOperation({
118
+ operation,
119
+ path,
120
+ value: record[path]
121
+ });
382
122
  }
383
123
  }
384
124
 
@@ -405,7 +145,7 @@ class AttributeOperationProxy {
405
145
  let ops = {};
406
146
  let seen = new Map();
407
147
  for (let operation of Object.keys(operations)) {
408
- let {template, canNest, noAttribute} = operations[operation];
148
+ let {template, canNest, rawValue, rawField} = operations[operation];
409
149
  Object.defineProperty(ops, operation, {
410
150
  get: () => {
411
151
  return (property, ...values) => {
@@ -462,7 +202,8 @@ class AttributeOperationProxy {
462
202
  }
463
203
 
464
204
  const options = {
465
- nestedValue: hasNestedValue
205
+ nestedValue: hasNestedValue,
206
+ createValue: (name, value) => builder.setValue(`${target.name}_${name}`, value),
466
207
  }
467
208
 
468
209
  const formatted = template(options, target, paths.expression, ...attributeValues);
@@ -478,7 +219,7 @@ class AttributeOperationProxy {
478
219
  }
479
220
 
480
221
  return formatted;
481
- } else if (noAttribute) {
222
+ } else if (rawValue) {
482
223
  // const {json, expression} = builder.setName({}, property, property);
483
224
  let attributeValueName = builder.setValue(property, property);
484
225
  builder.setPath(property, {
@@ -489,6 +230,12 @@ class AttributeOperationProxy {
489
230
  seen.set(attributeValueName, [property]);
490
231
  seen.set(formatted, [property]);
491
232
  return formatted;
233
+ } else if (rawField) {
234
+ const { prop, expression } = builder.setName({}, property, property);
235
+ const formatted = template({}, null, prop);
236
+ seen.set(expression, [property]);
237
+ seen.set(formatted, [property]);
238
+ return formatted;
492
239
  } else {
493
240
  throw new e.ElectroError(e.ErrorCodes.InvalidWhere, `Invalid Attribute in where clause passed to operation '${operation}'. Use injected attributes only.`);
494
241
  }
package/src/types.js CHANGED
@@ -220,6 +220,14 @@ const ItemOperations = {
220
220
  "ifNotExists": "ifNotExists"
221
221
  };
222
222
 
223
+ const UpsertOperations = {
224
+ "set": "set",
225
+ "add": "add",
226
+ "subtract": "subtract",
227
+ "append": "append",
228
+ "ifNotExists": "ifNotExists"
229
+ };
230
+
223
231
  const AttributeProxySymbol = Symbol("attribute_proxy");
224
232
  const TransactionCommitSymbol = Symbol('transaction_commit');
225
233
 
@@ -358,4 +366,5 @@ module.exports = {
358
366
  TransactionCommitSymbol,
359
367
  TransactionOperations,
360
368
  TransactionMethods,
369
+ UpsertOperations,
361
370
  };
package/src/update.js CHANGED
@@ -1,3 +1,4 @@
1
+ const { UpdateOperations } = require('./updateOperations');
1
2
  const {AttributeOperationProxy, ExpressionState} = require("./operations");
2
3
  const {ItemOperations, BuilderTypes} = require("./types");
3
4
 
@@ -33,7 +34,7 @@ class UpdateExpression extends ExpressionState {
33
34
  this.operations[type].delete(expression);
34
35
  }
35
36
 
36
- set(name, value, operation = ItemOperations.set) {
37
+ set(name, value, operation = ItemOperations.set, attribute) {
37
38
  let operationToApply = operation;
38
39
  if (operation === ItemOperations.ifNotExists) {
39
40
  operationToApply = ItemOperations.set;
@@ -87,7 +88,7 @@ class UpdateEntity {
87
88
 
88
89
  buildCallbackHandler(entity, state) {
89
90
  const proxy = new AttributeOperationProxy({
90
- builder: state.query.updates,
91
+ builder: state.query.update,
91
92
  attributes: this.attributes,
92
93
  operations: this.operations,
93
94
  });