UNPKG

14.1 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 if (args.includes(undefined)) {
134 console.warn(`Dynamoose Warning: Passing \`undefined\` into a condition ${type.name} is not supported and can lead to behavior where DynamoDB returns an error related to your conditional. In a future version of Dynamoose this behavior will throw an error. If you believe your conditional is valid and you received this message in error, please submit an issue at https://github.com/dynamoose/dynamoose/issues/new/choose.`);
135 }
136 this.settings.pending.value = type.multipleArguments ? args : args[0];
137 this.settings.pending.type = type;
138 finalizePending(this);
139 return this;
140 };
141});
142Condition.prototype.requestObject = function (settings = { "conditionString": "ConditionExpression", "conditionStringType": "string" }) {
143 if (this.settings.raw && utils.object.equals(Object.keys(this.settings.raw).sort(), [settings.conditionString, "ExpressionAttributeValues", "ExpressionAttributeNames"].sort())) {
144 return Object.entries(this.settings.raw.ExpressionAttributeValues).reduce((obj, entry) => {
145 const [key, value] = entry;
146 // TODO: we should fix this so that we can do `isDynamoItem(value)`
147 if (!Document_1.Document.isDynamoObject({ "key": value })) {
148 obj.ExpressionAttributeValues[key] = Document_1.Document.objectToDynamo(value, { "type": "value" });
149 }
150 return obj;
151 }, this.settings.raw);
152 }
153 else if (this.settings.conditions.length === 0) {
154 return {};
155 }
156 let index = (settings.index || {}).start || 0;
157 const setIndex = (i) => {
158 index = i;
159 (settings.index || { "set": utils.empty_function }).set(i);
160 };
161 function main(input) {
162 return input.reduce((object, entry, i, arr) => {
163 let expression = "";
164 if (Array.isArray(entry)) {
165 const result = main(entry);
166 const newData = utils.merge_objects.main({ "combineMethod": "object_combine" })(Object.assign({}, result), Object.assign({}, object));
167 const returnObject = utils.object.pick(newData, ["ExpressionAttributeNames", "ExpressionAttributeValues"]);
168 expression = settings.conditionStringType === "array" ? result[settings.conditionString] : `(${result[settings.conditionString]})`;
169 object = Object.assign(Object.assign({}, object), returnObject);
170 }
171 else if (entry !== OR) {
172 const [key, condition] = Object.entries(entry)[0];
173 const { value } = condition;
174 const keys = { "name": `#a${index}`, "value": `:v${index}` };
175 setIndex(++index);
176 const keyParts = key.split(".");
177 if (keyParts.length === 1) {
178 object.ExpressionAttributeNames[keys.name] = key;
179 }
180 else {
181 keys.name = keyParts.reduce((finalName, part, index) => {
182 const name = `${keys.name}_${index}`;
183 object.ExpressionAttributeNames[name] = part;
184 finalName.push(name);
185 return finalName;
186 }, []).join(".");
187 }
188 const toDynamo = (value) => {
189 return Document_1.Document.objectToDynamo(value, { "type": "value" });
190 };
191 object.ExpressionAttributeValues[keys.value] = toDynamo(value);
192 switch (condition.type) {
193 case "EQ":
194 case "NE":
195 expression = `${keys.name} ${condition.type === "EQ" ? "=" : "<>"} ${keys.value}`;
196 break;
197 case "IN":
198 delete object.ExpressionAttributeValues[keys.value];
199 expression = `${keys.name} IN (${value.map((_v, i) => `${keys.value}_${i + 1}`).join(", ")})`;
200 value.forEach((valueItem, i) => {
201 object.ExpressionAttributeValues[`${keys.value}_${i + 1}`] = toDynamo(valueItem);
202 });
203 break;
204 case "GT":
205 case "GE":
206 case "LT":
207 case "LE":
208 expression = `${keys.name} ${condition.type.startsWith("G") ? ">" : "<"}${condition.type.endsWith("E") ? "=" : ""} ${keys.value}`;
209 break;
210 case "BETWEEN":
211 expression = `${keys.name} BETWEEN ${keys.value}_1 AND ${keys.value}_2`;
212 object.ExpressionAttributeValues[`${keys.value}_1`] = toDynamo(value[0]);
213 object.ExpressionAttributeValues[`${keys.value}_2`] = toDynamo(value[1]);
214 delete object.ExpressionAttributeValues[keys.value];
215 break;
216 case "CONTAINS":
217 case "NOT_CONTAINS":
218 expression = `${condition.type === "NOT_CONTAINS" ? "NOT " : ""}contains (${keys.name}, ${keys.value})`;
219 break;
220 case "EXISTS":
221 case "NOT_EXISTS":
222 expression = `attribute_${condition.type === "NOT_EXISTS" ? "not_" : ""}exists (${keys.name})`;
223 delete object.ExpressionAttributeValues[keys.value];
224 break;
225 case "BEGINS_WITH":
226 expression = `begins_with (${keys.name}, ${keys.value})`;
227 break;
228 }
229 }
230 else {
231 return object;
232 }
233 const conditionStringNewItems = [expression];
234 if (object[settings.conditionString].length > 0) {
235 conditionStringNewItems.unshift(` ${arr[i - 1] === OR ? "OR" : "AND"} `);
236 }
237 conditionStringNewItems.forEach((item) => {
238 if (typeof object[settings.conditionString] === "string") {
239 object[settings.conditionString] = `${object[settings.conditionString]}${item}`;
240 }
241 else {
242 object[settings.conditionString].push(Array.isArray(item) ? item : item.trim());
243 }
244 });
245 return object;
246 }, { [settings.conditionString]: settings.conditionStringType === "array" ? [] : "", "ExpressionAttributeNames": {}, "ExpressionAttributeValues": {} });
247 }
248 return utils.object.clearEmpties(main(this.settings.conditions));
249};
250//# sourceMappingURL=Condition.js.map
\No newline at end of file