UNPKG

13.5 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.Condition = void 0;
4const Document_1 = require("./Document");
5const CustomError = require("./Error");
6const utils = require("./utils");
7const OR = Symbol("OR");
8const isRawConditionObject = (object) => Object.keys(object).length === 3 && ["ExpressionAttributeValues", "ExpressionAttributeNames"].every((item) => Boolean(object[item]) && typeof object[item] === "object");
9var ConditionComparisonComparatorName;
10(function (ConditionComparisonComparatorName) {
11 ConditionComparisonComparatorName["equals"] = "eq";
12 ConditionComparisonComparatorName["lessThan"] = "lt";
13 ConditionComparisonComparatorName["lessThanEquals"] = "le";
14 ConditionComparisonComparatorName["greaterThan"] = "gt";
15 ConditionComparisonComparatorName["greaterThanEquals"] = "ge";
16 ConditionComparisonComparatorName["beginsWith"] = "beginsWith";
17 ConditionComparisonComparatorName["contains"] = "contains";
18 ConditionComparisonComparatorName["exists"] = "exists";
19 ConditionComparisonComparatorName["in"] = "in";
20 ConditionComparisonComparatorName["between"] = "between";
21})(ConditionComparisonComparatorName || (ConditionComparisonComparatorName = {}));
22var ConditionComparisonComparatorDynamoName;
23(function (ConditionComparisonComparatorDynamoName) {
24 ConditionComparisonComparatorDynamoName["equals"] = "EQ";
25 ConditionComparisonComparatorDynamoName["notEquals"] = "NE";
26 ConditionComparisonComparatorDynamoName["lessThan"] = "LT";
27 ConditionComparisonComparatorDynamoName["lessThanEquals"] = "LE";
28 ConditionComparisonComparatorDynamoName["greaterThan"] = "GT";
29 ConditionComparisonComparatorDynamoName["greaterThanEquals"] = "GE";
30 ConditionComparisonComparatorDynamoName["beginsWith"] = "BEGINS_WITH";
31 ConditionComparisonComparatorDynamoName["contains"] = "CONTAINS";
32 ConditionComparisonComparatorDynamoName["notContains"] = "NOT_CONTAINS";
33 ConditionComparisonComparatorDynamoName["exists"] = "EXISTS";
34 ConditionComparisonComparatorDynamoName["notExists"] = "NOT_EXISTS";
35 ConditionComparisonComparatorDynamoName["in"] = "IN";
36 ConditionComparisonComparatorDynamoName["between"] = "BETWEEN";
37})(ConditionComparisonComparatorDynamoName || (ConditionComparisonComparatorDynamoName = {}));
38const types = [
39 { "name": ConditionComparisonComparatorName.equals, "typeName": ConditionComparisonComparatorDynamoName.equals, "not": ConditionComparisonComparatorDynamoName.notEquals },
40 { "name": ConditionComparisonComparatorName.lessThan, "typeName": ConditionComparisonComparatorDynamoName.lessThan, "not": ConditionComparisonComparatorDynamoName.greaterThanEquals },
41 { "name": ConditionComparisonComparatorName.lessThanEquals, "typeName": ConditionComparisonComparatorDynamoName.lessThanEquals, "not": ConditionComparisonComparatorDynamoName.greaterThan },
42 { "name": ConditionComparisonComparatorName.greaterThan, "typeName": ConditionComparisonComparatorDynamoName.greaterThan, "not": ConditionComparisonComparatorDynamoName.lessThanEquals },
43 { "name": ConditionComparisonComparatorName.greaterThanEquals, "typeName": ConditionComparisonComparatorDynamoName.greaterThanEquals, "not": ConditionComparisonComparatorDynamoName.lessThan },
44 { "name": ConditionComparisonComparatorName.beginsWith, "typeName": ConditionComparisonComparatorDynamoName.beginsWith },
45 { "name": ConditionComparisonComparatorName.contains, "typeName": ConditionComparisonComparatorDynamoName.contains, "not": ConditionComparisonComparatorDynamoName.notContains },
46 { "name": ConditionComparisonComparatorName.exists, "typeName": ConditionComparisonComparatorDynamoName.exists, "not": ConditionComparisonComparatorDynamoName.notExists },
47 { "name": ConditionComparisonComparatorName.in, "typeName": ConditionComparisonComparatorDynamoName.in },
48 { "name": ConditionComparisonComparatorName.between, "typeName": ConditionComparisonComparatorDynamoName.between, "multipleArguments": true }
49];
50class Condition {
51 constructor(object) {
52 if (object instanceof Condition) {
53 Object.entries(object).forEach((entry) => {
54 const [key, value] = entry;
55 this[key] = value;
56 });
57 }
58 else {
59 this.settings = {
60 "conditions": [],
61 "pending": {} // represents the pending chain of filter data waiting to be attached to the `conditions` parameter. For example, storing the key before we know what the comparison operator is.
62 };
63 if (typeof object === "object") {
64 if (!isRawConditionObject(object)) {
65 Object.keys(object).forEach((key) => {
66 const value = object[key];
67 const valueType = typeof value === "object" && Object.keys(value).length > 0 ? Object.keys(value)[0] : "eq";
68 const comparisonType = types.find((item) => item.name === valueType);
69 if (!comparisonType) {
70 throw new CustomError.InvalidFilterComparison(`The type: ${valueType} is invalid.`);
71 }
72 this.settings.conditions.push({
73 [key]: {
74 "type": comparisonType.typeName,
75 "value": typeof value[valueType] !== "undefined" && value[valueType] !== null ? value[valueType] : value
76 }
77 });
78 });
79 }
80 }
81 else if (object) {
82 this.settings.pending.key = object;
83 }
84 }
85 this.settings.raw = object;
86 return this;
87 }
88}
89exports.Condition = Condition;
90function finalizePending(instance) {
91 const pending = instance.settings.pending;
92 let dynamoNameType;
93 if (pending.not === true) {
94 if (!pending.type.not) {
95 throw new CustomError.InvalidFilterComparison(`${pending.type.typeName} can not follow not()`);
96 }
97 dynamoNameType = pending.type.not;
98 }
99 else {
100 dynamoNameType = pending.type.typeName;
101 }
102 instance.settings.conditions.push({
103 [pending.key]: {
104 "type": dynamoNameType,
105 "value": pending.value
106 }
107 });
108 instance.settings.pending = {};
109}
110Condition.prototype.parenthesis = Condition.prototype.group = function (value) {
111 value = typeof value === "function" ? value(new Condition()) : value;
112 this.settings.conditions.push(value.settings.conditions);
113 return this;
114};
115Condition.prototype.or = function () {
116 this.settings.conditions.push(OR);
117 return this;
118};
119Condition.prototype.and = function () {
120 return this;
121};
122Condition.prototype.not = function () {
123 this.settings.pending.not = !this.settings.pending.not;
124 return this;
125};
126Condition.prototype.where = Condition.prototype.filter = Condition.prototype.attribute = function (key) {
127 this.settings.pending = { key };
128 return this;
129};
130// TODO: I don't think this prototypes are being exposed which is gonna cause a lot of problems with our type definition file. Need to figure out a better way to do this since they aren't defined and are dynamic.
131types.forEach((type) => {
132 Condition.prototype[type.name] = function (...args) {
133 this.settings.pending.value = type.multipleArguments ? args : args[0];
134 this.settings.pending.type = type;
135 finalizePending(this);
136 return this;
137 };
138});
139Condition.prototype.requestObject = function (settings = { "conditionString": "ConditionExpression", "conditionStringType": "string" }) {
140 if (this.settings.raw && utils.object.equals(Object.keys(this.settings.raw).sort(), [settings.conditionString, "ExpressionAttributeValues", "ExpressionAttributeNames"].sort())) {
141 return Object.entries(this.settings.raw.ExpressionAttributeValues).reduce((obj, entry) => {
142 const [key, value] = entry;
143 // TODO: we should fix this so that we can do `isDynamoItem(value)`
144 if (!Document_1.Document.isDynamoObject({ "key": value })) {
145 obj.ExpressionAttributeValues[key] = Document_1.Document.objectToDynamo(value, { "type": "value" });
146 }
147 return obj;
148 }, this.settings.raw);
149 }
150 else if (this.settings.conditions.length === 0) {
151 return {};
152 }
153 let index = (settings.index || {}).start || 0;
154 const setIndex = (i) => {
155 index = i;
156 (settings.index || { "set": utils.empty_function }).set(i);
157 };
158 function main(input) {
159 return input.reduce((object, entry, i, arr) => {
160 let expression = "";
161 if (Array.isArray(entry)) {
162 const result = main(entry);
163 const newData = utils.merge_objects.main({ "combineMethod": "object_combine" })(Object.assign({}, result), Object.assign({}, object));
164 const returnObject = utils.object.pick(newData, ["ExpressionAttributeNames", "ExpressionAttributeValues"]);
165 expression = settings.conditionStringType === "array" ? result[settings.conditionString] : `(${result[settings.conditionString]})`;
166 object = Object.assign(Object.assign({}, object), returnObject);
167 }
168 else if (entry !== OR) {
169 const [key, condition] = Object.entries(entry)[0];
170 const { value } = condition;
171 const keys = { "name": `#a${index}`, "value": `:v${index}` };
172 setIndex(++index);
173 const keyParts = key.split(".");
174 if (keyParts.length === 1) {
175 object.ExpressionAttributeNames[keys.name] = key;
176 }
177 else {
178 keys.name = keyParts.reduce((finalName, part, index) => {
179 const name = `${keys.name}_${index}`;
180 object.ExpressionAttributeNames[name] = part;
181 finalName.push(name);
182 return finalName;
183 }, []).join(".");
184 }
185 const toDynamo = (value) => {
186 return Document_1.Document.objectToDynamo(value, { "type": "value" });
187 };
188 object.ExpressionAttributeValues[keys.value] = toDynamo(value);
189 switch (condition.type) {
190 case "EQ":
191 case "NE":
192 expression = `${keys.name} ${condition.type === "EQ" ? "=" : "<>"} ${keys.value}`;
193 break;
194 case "IN":
195 delete object.ExpressionAttributeValues[keys.value];
196 expression = `${keys.name} IN (${value.map((_v, i) => `${keys.value}_${i + 1}`).join(", ")})`;
197 value.forEach((valueItem, i) => {
198 object.ExpressionAttributeValues[`${keys.value}_${i + 1}`] = toDynamo(valueItem);
199 });
200 break;
201 case "GT":
202 case "GE":
203 case "LT":
204 case "LE":
205 expression = `${keys.name} ${condition.type.startsWith("G") ? ">" : "<"}${condition.type.endsWith("E") ? "=" : ""} ${keys.value}`;
206 break;
207 case "BETWEEN":
208 expression = `${keys.name} BETWEEN ${keys.value}_1 AND ${keys.value}_2`;
209 object.ExpressionAttributeValues[`${keys.value}_1`] = toDynamo(value[0]);
210 object.ExpressionAttributeValues[`${keys.value}_2`] = toDynamo(value[1]);
211 delete object.ExpressionAttributeValues[keys.value];
212 break;
213 case "CONTAINS":
214 case "NOT_CONTAINS":
215 expression = `${condition.type === "NOT_CONTAINS" ? "NOT " : ""}contains (${keys.name}, ${keys.value})`;
216 break;
217 case "EXISTS":
218 case "NOT_EXISTS":
219 expression = `attribute_${condition.type === "NOT_EXISTS" ? "not_" : ""}exists (${keys.name})`;
220 delete object.ExpressionAttributeValues[keys.value];
221 break;
222 case "BEGINS_WITH":
223 expression = `begins_with (${keys.name}, ${keys.value})`;
224 break;
225 }
226 }
227 else {
228 return object;
229 }
230 const conditionStringNewItems = [expression];
231 if (object[settings.conditionString].length > 0) {
232 conditionStringNewItems.unshift(` ${arr[i - 1] === OR ? "OR" : "AND"} `);
233 }
234 conditionStringNewItems.forEach((item) => {
235 if (typeof object[settings.conditionString] === "string") {
236 object[settings.conditionString] = `${object[settings.conditionString]}${item}`;
237 }
238 else {
239 object[settings.conditionString].push(Array.isArray(item) ? item : item.trim());
240 }
241 });
242 return object;
243 }, { [settings.conditionString]: settings.conditionStringType === "array" ? [] : "", "ExpressionAttributeNames": {}, "ExpressionAttributeValues": {} });
244 }
245 return main(this.settings.conditions);
246};
247//# sourceMappingURL=Condition.js.map
\No newline at end of file