@react-querybuilder/core 8.9.0 → 8.9.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/LICENSE.md +21 -0
- package/dist/arrayUtils-BF1P8iHS.mjs +122 -0
- package/dist/arrayUtils-BF1P8iHS.mjs.map +1 -0
- package/dist/basic-BfD-7CN3.d.mts +1235 -0
- package/dist/cjs/react-querybuilder_core.cjs.development.d.ts +21 -10
- package/dist/cjs/react-querybuilder_core.cjs.development.js +22 -19
- package/dist/cjs/react-querybuilder_core.cjs.development.js.map +1 -1
- package/dist/cjs/react-querybuilder_core.cjs.production.d.ts +21 -10
- package/dist/cjs/react-querybuilder_core.cjs.production.js +1 -1
- package/dist/cjs/react-querybuilder_core.cjs.production.js.map +1 -1
- package/dist/convertQuery-H7RhQiIc.mjs +75 -0
- package/dist/convertQuery-H7RhQiIc.mjs.map +1 -0
- package/dist/export-r-V7bU31.d.mts +452 -0
- package/dist/formatQuery.d.mts +667 -0
- package/dist/formatQuery.mjs +2366 -0
- package/dist/formatQuery.mjs.map +1 -0
- package/dist/import-BwbbP4oU.d.mts +28 -0
- package/dist/isRuleGroup-CnhYpLOM.mjs +40 -0
- package/dist/isRuleGroup-CnhYpLOM.mjs.map +1 -0
- package/dist/isRuleGroup-DqAs2x4E.js.map +1 -1
- package/dist/objectUtils-BtWdcZVG.mjs +11 -0
- package/dist/objectUtils-BtWdcZVG.mjs.map +1 -0
- package/dist/optGroupUtils-Duv-M8rf.mjs +102 -0
- package/dist/optGroupUtils-Duv-M8rf.mjs.map +1 -0
- package/dist/parseCEL.d.mts +34 -0
- package/dist/parseCEL.mjs +2593 -0
- package/dist/parseCEL.mjs.map +1 -0
- package/dist/parseJSONata.d.mts +36 -0
- package/dist/parseJSONata.mjs +268 -0
- package/dist/parseJSONata.mjs.map +1 -0
- package/dist/parseJsonLogic.d.mts +36 -0
- package/dist/parseJsonLogic.mjs +191 -0
- package/dist/parseJsonLogic.mjs.map +1 -0
- package/dist/parseMongoDB.d.mts +79 -0
- package/dist/parseMongoDB.mjs +267 -0
- package/dist/parseMongoDB.mjs.map +1 -0
- package/dist/parseNumber-BtGKa58z.mjs +24 -0
- package/dist/parseNumber-BtGKa58z.mjs.map +1 -0
- package/dist/parseSQL.d.mts +37 -0
- package/dist/parseSQL.mjs +6626 -0
- package/dist/parseSQL.mjs.map +1 -0
- package/dist/parseSpEL.d.mts +34 -0
- package/dist/parseSpEL.mjs +273 -0
- package/dist/parseSpEL.mjs.map +1 -0
- package/dist/prepareQueryObjects-CS6Wmhmf.mjs +154 -0
- package/dist/prepareQueryObjects-CS6Wmhmf.mjs.map +1 -0
- package/dist/react-querybuilder_core.d.mts +21 -10
- package/dist/react-querybuilder_core.legacy-esm.d.ts +21 -10
- package/dist/react-querybuilder_core.legacy-esm.js +19 -18
- package/dist/react-querybuilder_core.legacy-esm.js.map +1 -1
- package/dist/react-querybuilder_core.mjs +22 -20
- package/dist/react-querybuilder_core.mjs.map +1 -1
- package/dist/react-querybuilder_core.production.d.mts +21 -10
- package/dist/react-querybuilder_core.production.mjs +1 -1
- package/dist/react-querybuilder_core.production.mjs.map +1 -1
- package/dist/transformQuery-DdMvmrCh.mjs +41 -0
- package/dist/transformQuery-DdMvmrCh.mjs.map +1 -0
- package/dist/transformQuery.d.mts +118 -0
- package/dist/transformQuery.mjs +4 -0
- package/package.json +68 -19
|
@@ -0,0 +1,2366 @@
|
|
|
1
|
+
import { defaultPlaceholderFieldName, defaultPlaceholderOperatorName, joinWith, splitBy, toArray, trimIfString } from "./arrayUtils-BF1P8iHS.mjs";
|
|
2
|
+
import { isPojo, isRuleGroup, isRuleGroupType, isRuleGroupTypeIC, lc, nullOrUndefinedOrEmpty, numericRegex } from "./isRuleGroup-CnhYpLOM.mjs";
|
|
3
|
+
import { parseNumber } from "./parseNumber-BtGKa58z.mjs";
|
|
4
|
+
import { transformQuery } from "./transformQuery-DdMvmrCh.mjs";
|
|
5
|
+
import { getOption, toFlatOptionArray, toFullOptionList } from "./optGroupUtils-Duv-M8rf.mjs";
|
|
6
|
+
import { convertFromIC } from "./convertQuery-H7RhQiIc.mjs";
|
|
7
|
+
import { produce } from "immer";
|
|
8
|
+
|
|
9
|
+
//#region src/utils/isRuleOrGroupValid.ts
|
|
10
|
+
/**
|
|
11
|
+
* Determines if an object is useful as a validation result.
|
|
12
|
+
*/
|
|
13
|
+
const isValidationResult = (vr) => isPojo(vr) && typeof vr.valid === "boolean";
|
|
14
|
+
/**
|
|
15
|
+
* Determines if a rule or group is valid based on a validation result (if defined)
|
|
16
|
+
* or a validator function. Returns `true` if neither are defined.
|
|
17
|
+
*/
|
|
18
|
+
const isRuleOrGroupValid = (rg, validationResult, validator) => {
|
|
19
|
+
if (typeof validationResult === "boolean") return validationResult;
|
|
20
|
+
if (isValidationResult(validationResult)) return validationResult.valid;
|
|
21
|
+
if (typeof validator === "function" && !isRuleGroup(rg)) {
|
|
22
|
+
const vr = validator(rg);
|
|
23
|
+
if (typeof vr === "boolean") return vr;
|
|
24
|
+
// istanbul ignore else
|
|
25
|
+
if (isValidationResult(vr)) return vr.valid;
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/utils/getParseNumberMethod.ts
|
|
32
|
+
const getParseNumberMethod = ({ parseNumbers, inputType }) => {
|
|
33
|
+
if (typeof parseNumbers === "string") {
|
|
34
|
+
const [method, level] = parseNumbers.split("-");
|
|
35
|
+
if (level === "limited") return inputType === "number" ? method : false;
|
|
36
|
+
return method;
|
|
37
|
+
}
|
|
38
|
+
return parseNumbers ? "strict" : false;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/utils/formatQuery/utils.ts
|
|
43
|
+
/**
|
|
44
|
+
* Maps a {@link DefaultOperatorName} to a SQL operator.
|
|
45
|
+
*
|
|
46
|
+
* @group Export
|
|
47
|
+
*/
|
|
48
|
+
const mapSQLOperator = (rqbOperator) => {
|
|
49
|
+
switch (lc(rqbOperator)) {
|
|
50
|
+
case "null": return "is null";
|
|
51
|
+
case "notnull": return "is not null";
|
|
52
|
+
case "notin": return "not in";
|
|
53
|
+
case "notbetween": return "not between";
|
|
54
|
+
case "contains":
|
|
55
|
+
case "beginswith":
|
|
56
|
+
case "endswith": return "like";
|
|
57
|
+
case "doesnotcontain":
|
|
58
|
+
case "doesnotbeginwith":
|
|
59
|
+
case "doesnotendwith": return "not like";
|
|
60
|
+
default: return rqbOperator;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Maps a (lowercase) {@link DefaultOperatorName} to a MongoDB operator.
|
|
65
|
+
*
|
|
66
|
+
* @group Export
|
|
67
|
+
*/
|
|
68
|
+
const mongoOperators = {
|
|
69
|
+
"=": "$eq",
|
|
70
|
+
"!=": "$ne",
|
|
71
|
+
"<": "$lt",
|
|
72
|
+
"<=": "$lte",
|
|
73
|
+
">": "$gt",
|
|
74
|
+
">=": "$gte",
|
|
75
|
+
in: "$in",
|
|
76
|
+
notin: "$nin",
|
|
77
|
+
notIn: "$nin"
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Maps a (lowercase) {@link DefaultOperatorName} to a Prisma ORM operator.
|
|
81
|
+
*
|
|
82
|
+
* @group Export
|
|
83
|
+
*/
|
|
84
|
+
const prismaOperators = {
|
|
85
|
+
"=": "equals",
|
|
86
|
+
"!=": "not",
|
|
87
|
+
"<": "lt",
|
|
88
|
+
"<=": "lte",
|
|
89
|
+
">": "gt",
|
|
90
|
+
">=": "gte",
|
|
91
|
+
in: "in",
|
|
92
|
+
notin: "notIn"
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Maps a {@link DefaultCombinatorName} to a CEL combinator.
|
|
96
|
+
*
|
|
97
|
+
* @group Export
|
|
98
|
+
*/
|
|
99
|
+
const celCombinatorMap = {
|
|
100
|
+
and: "&&",
|
|
101
|
+
or: "||"
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Register these operators with `jsonLogic` before applying the result
|
|
105
|
+
* of `formatQuery(query, 'jsonlogic')`.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```
|
|
109
|
+
* for (const [op, func] of Object.entries(jsonLogicAdditionalOperators)) {
|
|
110
|
+
* jsonLogic.add_operation(op, func);
|
|
111
|
+
* }
|
|
112
|
+
* jsonLogic.apply({ "startsWith": [{ "var": "firstName" }, "Stev"] }, data);
|
|
113
|
+
* ```
|
|
114
|
+
*
|
|
115
|
+
* @group Export
|
|
116
|
+
*/
|
|
117
|
+
const jsonLogicAdditionalOperators = {
|
|
118
|
+
startsWith: (a, b) => typeof a === "string" && a.startsWith(b),
|
|
119
|
+
endsWith: (a, b) => typeof a === "string" && a.endsWith(b)
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Converts all `string`-type `value` properties of a query object into `number` where appropriate.
|
|
123
|
+
*
|
|
124
|
+
* Used by {@link formatQuery} for the `json*` formats when `parseNumbers` is `true`.
|
|
125
|
+
*
|
|
126
|
+
* @group Export
|
|
127
|
+
*/
|
|
128
|
+
const numerifyValues = (rg, options) => ({
|
|
129
|
+
...rg,
|
|
130
|
+
rules: rg.rules.map((r) => {
|
|
131
|
+
if (typeof r === "string") return r;
|
|
132
|
+
if (isRuleGroup(r)) return numerifyValues(r, options);
|
|
133
|
+
const fieldData = getOption(options.fields, r.field);
|
|
134
|
+
const parseNumbers = getParseNumberMethod({
|
|
135
|
+
parseNumbers: options.parseNumbers,
|
|
136
|
+
inputType: fieldData?.inputType
|
|
137
|
+
});
|
|
138
|
+
if (Array.isArray(r.value)) return {
|
|
139
|
+
...r,
|
|
140
|
+
value: r.value.map((v) => parseNumber(v, { parseNumbers }))
|
|
141
|
+
};
|
|
142
|
+
const valAsArray = toArray(r.value, { retainEmptyStrings: true }).map((v) => parseNumber(v, { parseNumbers }));
|
|
143
|
+
if (valAsArray.every((v) => typeof v === "number")) {
|
|
144
|
+
// istanbul ignore else
|
|
145
|
+
if (valAsArray.length > 1) return {
|
|
146
|
+
...r,
|
|
147
|
+
value: valAsArray
|
|
148
|
+
};
|
|
149
|
+
else if (valAsArray.length === 1) return {
|
|
150
|
+
...r,
|
|
151
|
+
value: valAsArray[0]
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return r;
|
|
155
|
+
})
|
|
156
|
+
});
|
|
157
|
+
/**
|
|
158
|
+
* Determines whether a value is _anything_ except an empty `string` or `NaN`.
|
|
159
|
+
*
|
|
160
|
+
* @group Export
|
|
161
|
+
*/
|
|
162
|
+
const isValidValue = (value) => typeof value === "string" && value.length > 0 || typeof value === "number" && !Number.isNaN(value) || typeof value !== "string" && typeof value !== "number";
|
|
163
|
+
/**
|
|
164
|
+
* Determines whether {@link formatQuery} should render the given value as a number.
|
|
165
|
+
* As long as `parseNumbers` is `true`, `number` and `bigint` values will return `true` and
|
|
166
|
+
* `string` values will return `true` if they test positive against {@link numericRegex}.
|
|
167
|
+
*
|
|
168
|
+
* @group Export
|
|
169
|
+
*/
|
|
170
|
+
const shouldRenderAsNumber = (value, parseNumbers) => !!parseNumbers && (typeof value === "number" || typeof value === "bigint" || typeof value === "string" && numericRegex.test(value));
|
|
171
|
+
/**
|
|
172
|
+
* Used by {@link formatQuery} to determine whether the given value processor is a
|
|
173
|
+
* "legacy" value processor by counting the number of arguments. Legacy value
|
|
174
|
+
* processors take 3 arguments (not counting any arguments with default values), while
|
|
175
|
+
* rule-based value processors take no more than 2 arguments.
|
|
176
|
+
*
|
|
177
|
+
* @group Export
|
|
178
|
+
*/
|
|
179
|
+
const isValueProcessorLegacy = (valueProcessor) => valueProcessor.length >= 3;
|
|
180
|
+
/**
|
|
181
|
+
* Converts the `quoteFieldNamesWith` option into an array of two strings.
|
|
182
|
+
* If the option is a string, the array elements are both that string.
|
|
183
|
+
*
|
|
184
|
+
* @default
|
|
185
|
+
* ['', '']
|
|
186
|
+
*
|
|
187
|
+
* @group Export
|
|
188
|
+
*/
|
|
189
|
+
const getQuoteFieldNamesWithArray = (quoteFieldNamesWith = ["", ""]) => Array.isArray(quoteFieldNamesWith) ? quoteFieldNamesWith : typeof quoteFieldNamesWith === "string" ? [quoteFieldNamesWith, quoteFieldNamesWith] : quoteFieldNamesWith ?? ["", ""];
|
|
190
|
+
/**
|
|
191
|
+
* Given a field name and relevant {@link ValueProcessorOptions}, returns the field name
|
|
192
|
+
* wrapped in the configured quote character(s).
|
|
193
|
+
*
|
|
194
|
+
* @group Export
|
|
195
|
+
*/
|
|
196
|
+
const getQuotedFieldName = (fieldName, { quoteFieldNamesWith, fieldIdentifierSeparator }) => {
|
|
197
|
+
const [qPre, qPost] = getQuoteFieldNamesWithArray(quoteFieldNamesWith);
|
|
198
|
+
return typeof fieldIdentifierSeparator === "string" && fieldIdentifierSeparator.length > 0 ? joinWith(splitBy(fieldName, fieldIdentifierSeparator).map((part) => `${qPre}${part}${qPost}`), fieldIdentifierSeparator) : `${qPre}${fieldName}${qPost}`;
|
|
199
|
+
};
|
|
200
|
+
const defaultWordOrder = [
|
|
201
|
+
"S",
|
|
202
|
+
"V",
|
|
203
|
+
"O"
|
|
204
|
+
];
|
|
205
|
+
/**
|
|
206
|
+
* Given a [Constituent word order](https://en.wikipedia.org/wiki/Word_order#Constituent_word_orders)
|
|
207
|
+
* like "svo" or "sov", returns a permutation of `["S", "V", "O"]` based on the first occurrence of
|
|
208
|
+
* each letter in the input string (case insensitive). This widens the valid input from abbreviations
|
|
209
|
+
* like "svo" to more expressive strings like "subject-verb-object" or "sub ver obj". Any missing
|
|
210
|
+
* letters are appended in the default order "SVO" (e.g., "object" would yield `["O", "S", "V"]`).
|
|
211
|
+
*
|
|
212
|
+
* @group Export
|
|
213
|
+
*/
|
|
214
|
+
const normalizeConstituentWordOrder = (input) => {
|
|
215
|
+
const result = [];
|
|
216
|
+
const letterSet = new Set(defaultWordOrder);
|
|
217
|
+
for (const char of input.toUpperCase()) if (letterSet.has(char)) {
|
|
218
|
+
result.push(char);
|
|
219
|
+
letterSet.delete(char);
|
|
220
|
+
if (letterSet.size === 0) break;
|
|
221
|
+
}
|
|
222
|
+
for (const letter of defaultWordOrder) if (letterSet.has(letter)) result.push(letter);
|
|
223
|
+
return result;
|
|
224
|
+
};
|
|
225
|
+
/**
|
|
226
|
+
* Default translations used by {@link formatQuery} for "natural_language" format.
|
|
227
|
+
*
|
|
228
|
+
* @group Export
|
|
229
|
+
*/
|
|
230
|
+
const defaultNLTranslations = {
|
|
231
|
+
groupPrefix: "",
|
|
232
|
+
groupPrefix_not_xor: "either zero or more than one of",
|
|
233
|
+
groupPrefix_xor: "exactly one of",
|
|
234
|
+
groupSuffix: "is true",
|
|
235
|
+
groupSuffix_not: "is not true"
|
|
236
|
+
};
|
|
237
|
+
/**
|
|
238
|
+
* Note: This function assumes `conditions.length > 0`
|
|
239
|
+
*/
|
|
240
|
+
const translationMatchFilter = (key, keyToTest, conditions) => keyToTest.startsWith(key) && conditions.every((c) => keyToTest.includes(`_${c}`) && keyToTest.match(/_/g)?.length === conditions.length);
|
|
241
|
+
/**
|
|
242
|
+
* Used by {@link formatQuery} to get a translation based on certain conditions
|
|
243
|
+
* for the "natural_language" format.
|
|
244
|
+
*
|
|
245
|
+
* @group Export
|
|
246
|
+
*/
|
|
247
|
+
const getNLTranslataion = (key, translations, conditions = []) => conditions.length === 0 ? translations[key] ?? defaultNLTranslations[key] ?? "" : Object.entries(translations).find(([keyToTest]) => translationMatchFilter(key, keyToTest, conditions))?.[1] ?? Object.entries(defaultNLTranslations).find(([keyToTest]) => translationMatchFilter(key, keyToTest, conditions))?.[1] ?? defaultNLTranslations[key] ?? "";
|
|
248
|
+
const processMatchMode = (rule) => {
|
|
249
|
+
const { mode, threshold } = rule.match ?? {};
|
|
250
|
+
if (mode) {
|
|
251
|
+
if (!isRuleGroup(rule.value)) return false;
|
|
252
|
+
const matchModeLC = lc(mode);
|
|
253
|
+
const matchModeCoerced = matchModeLC === "atleast" && threshold === 1 ? "some" : matchModeLC === "atmost" && threshold === 0 ? "none" : matchModeLC;
|
|
254
|
+
if ((matchModeCoerced === "atleast" || matchModeCoerced === "atmost" || matchModeCoerced === "exactly") && (typeof threshold !== "number" || threshold < 0)) return false;
|
|
255
|
+
return {
|
|
256
|
+
mode: matchModeCoerced,
|
|
257
|
+
threshold
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
/**
|
|
262
|
+
* "Replacer" method for JSON.stringify's second argument. Converts `bigint` values to
|
|
263
|
+
* objects with a `$bigint` property having a value of a string representation of
|
|
264
|
+
* the actual `bigint`-type value.
|
|
265
|
+
*
|
|
266
|
+
* Inverse of {@link bigIntJsonParseReviver}.
|
|
267
|
+
*
|
|
268
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#use_within_json
|
|
269
|
+
*/
|
|
270
|
+
const bigIntJsonStringifyReplacer = (_key, value) => typeof value === "bigint" ? { $bigint: value.toString() } : value;
|
|
271
|
+
/**
|
|
272
|
+
* "Reviver" method for JSON.parse's second argument. Converts objects having a single
|
|
273
|
+
* `$bigint: string` property to an actual `bigint` value.
|
|
274
|
+
*
|
|
275
|
+
* Inverse of {@link bigIntJsonStringifyReplacer}.
|
|
276
|
+
*
|
|
277
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#use_within_json
|
|
278
|
+
*/
|
|
279
|
+
const bigIntJsonParseReviver = (_key, value) => isPojo(value) && Object.keys(value).length === 1 && typeof value.$bigint === "string" ? BigInt(value.$bigint) : value;
|
|
280
|
+
|
|
281
|
+
//#endregion
|
|
282
|
+
//#region src/utils/formatQuery/defaultRuleGroupProcessorCEL.ts
|
|
283
|
+
/**
|
|
284
|
+
* Rule group processor used by {@link formatQuery} for "cel" format.
|
|
285
|
+
*
|
|
286
|
+
* @group Export
|
|
287
|
+
*/
|
|
288
|
+
const defaultRuleGroupProcessorCEL = (ruleGroup, options) => {
|
|
289
|
+
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
290
|
+
const processRuleGroup = (rg, outermost) => {
|
|
291
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermost ? fallbackExpression : "";
|
|
292
|
+
const expression = rg.rules.map((rule) => {
|
|
293
|
+
if (typeof rule === "string") return celCombinatorMap[rule];
|
|
294
|
+
if (isRuleGroup(rule)) return processRuleGroup(rule);
|
|
295
|
+
const [validationResult, fieldValidator] = validateRule(rule);
|
|
296
|
+
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || placeholderValueName !== void 0 && rule.value === placeholderValueName) return "";
|
|
297
|
+
const fieldData = getOption(fields, rule.field);
|
|
298
|
+
return ruleProcessor(rule, {
|
|
299
|
+
...options,
|
|
300
|
+
parseNumbers: getParseNumberBoolean(fieldData?.inputType),
|
|
301
|
+
escapeQuotes: (rule.valueSource ?? "value") === "value",
|
|
302
|
+
fieldData
|
|
303
|
+
});
|
|
304
|
+
}).filter(Boolean).join(isRuleGroupType(rg) ? ` ${celCombinatorMap[rg.combinator]} ` : " ");
|
|
305
|
+
const [prefix, suffix] = rg.not || !outermost ? [`${rg.not ? "!" : ""}(`, ")"] : ["", ""];
|
|
306
|
+
return expression ? `${prefix}${expression}${suffix}` : fallbackExpression;
|
|
307
|
+
};
|
|
308
|
+
return processRuleGroup(ruleGroup, true);
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
//#endregion
|
|
312
|
+
//#region src/utils/formatQuery/defaultRuleProcessorCEL.ts
|
|
313
|
+
const shouldNegate$2 = (op) => op.startsWith("not") || op.startsWith("doesnot");
|
|
314
|
+
const escapeDoubleQuotes = (v, escapeQuotes) => typeof v !== "string" || !escapeQuotes ? `${v}` : v.replaceAll(`"`, `\\"`);
|
|
315
|
+
/**
|
|
316
|
+
* Default rule processor used by {@link formatQuery} for "cel" format.
|
|
317
|
+
*
|
|
318
|
+
* @group Export
|
|
319
|
+
*/
|
|
320
|
+
const defaultRuleProcessorCEL = (rule, opts = {}) => {
|
|
321
|
+
const { escapeQuotes, parseNumbers, preserveValueOrder } = opts;
|
|
322
|
+
const { field, operator, value, valueSource } = rule;
|
|
323
|
+
const valueIsField = valueSource === "field";
|
|
324
|
+
const operatorTL = lc(operator === "=" ? "==" : operator);
|
|
325
|
+
const useBareValue = typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || shouldRenderAsNumber(value, parseNumbers);
|
|
326
|
+
const matchEval = processMatchMode(rule);
|
|
327
|
+
if (matchEval === false) return "";
|
|
328
|
+
else if (matchEval) {
|
|
329
|
+
const { mode, threshold } = matchEval;
|
|
330
|
+
const arrayElementAlias = "elem_alias";
|
|
331
|
+
const celQuery = transformQuery(rule.value, { ruleProcessor: (r) => ({
|
|
332
|
+
...r,
|
|
333
|
+
field: `${arrayElementAlias}${r.field ? `.${r.field}` : ""}`
|
|
334
|
+
}) });
|
|
335
|
+
const nestedArrayFilter = defaultRuleGroupProcessorCEL(celQuery, opts);
|
|
336
|
+
switch (mode) {
|
|
337
|
+
case "all": return `${field}.all(${arrayElementAlias}, ${nestedArrayFilter})`;
|
|
338
|
+
case "none":
|
|
339
|
+
case "some": return `${mode === "none" ? "!" : ""}${field}.exists(${arrayElementAlias}, ${nestedArrayFilter})`;
|
|
340
|
+
case "atleast":
|
|
341
|
+
case "atmost":
|
|
342
|
+
case "exactly": {
|
|
343
|
+
const totalCount = `double(${field}.size())`;
|
|
344
|
+
const filteredCount = `${field}.filter(${arrayElementAlias}, ${nestedArrayFilter}).size()`;
|
|
345
|
+
const op = mode === "atleast" ? ">=" : mode === "atmost" ? "<=" : "==";
|
|
346
|
+
if (threshold > 0 && threshold < 1) return `${filteredCount} ${op} (${totalCount} * ${threshold})`;
|
|
347
|
+
return `${filteredCount} ${op} ${threshold}`;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
switch (operatorTL) {
|
|
352
|
+
case "<":
|
|
353
|
+
case "<=":
|
|
354
|
+
case "==":
|
|
355
|
+
case "!=":
|
|
356
|
+
case ">":
|
|
357
|
+
case ">=": return `${field} ${operatorTL} ${valueIsField || useBareValue ? trimIfString(value) : `"${escapeDoubleQuotes(value, escapeQuotes)}"`}`;
|
|
358
|
+
case "contains":
|
|
359
|
+
case "doesnotcontain": return `${shouldNegate$2(operatorTL) ? "!" : ""}${field}.contains(${valueIsField ? trimIfString(value) : `"${escapeDoubleQuotes(value, escapeQuotes)}"`})`;
|
|
360
|
+
case "beginswith":
|
|
361
|
+
case "doesnotbeginwith": return `${shouldNegate$2(operatorTL) ? "!" : ""}${field}.startsWith(${valueIsField ? trimIfString(value) : `"${escapeDoubleQuotes(value, escapeQuotes)}"`})`;
|
|
362
|
+
case "endswith":
|
|
363
|
+
case "doesnotendwith": return `${shouldNegate$2(operatorTL) ? "!" : ""}${field}.endsWith(${valueIsField ? trimIfString(value) : `"${escapeDoubleQuotes(value, escapeQuotes)}"`})`;
|
|
364
|
+
case "null": return `${field} == null`;
|
|
365
|
+
case "notnull": return `${field} != null`;
|
|
366
|
+
case "in":
|
|
367
|
+
case "notin": {
|
|
368
|
+
const [prefix, suffix] = shouldNegate$2(operatorTL) ? ["!(", ")"] : ["", ""];
|
|
369
|
+
const valueAsArray = toArray(value);
|
|
370
|
+
return `${prefix}${field} in [${valueAsArray.map((val) => valueIsField || shouldRenderAsNumber(val, parseNumbers) ? `${trimIfString(val)}` : `"${escapeDoubleQuotes(val, escapeQuotes)}"`).join(", ")}]${suffix}`;
|
|
371
|
+
}
|
|
372
|
+
case "between":
|
|
373
|
+
case "notbetween": {
|
|
374
|
+
const valueAsArray = toArray(value);
|
|
375
|
+
if (valueAsArray.length >= 2 && !nullOrUndefinedOrEmpty(valueAsArray[0]) && !nullOrUndefinedOrEmpty(valueAsArray[1])) {
|
|
376
|
+
const [first, second] = valueAsArray;
|
|
377
|
+
const shouldParseNumbers = !(parseNumbers === false);
|
|
378
|
+
const firstNum = shouldRenderAsNumber(first, shouldParseNumbers) ? parseNumber(first, { parseNumbers: shouldParseNumbers }) : NaN;
|
|
379
|
+
const secondNum = shouldRenderAsNumber(second, shouldParseNumbers) ? parseNumber(second, { parseNumbers: shouldParseNumbers }) : NaN;
|
|
380
|
+
let firstValue = Number.isNaN(firstNum) ? valueIsField ? `${first}` : `"${escapeDoubleQuotes(first, escapeQuotes)}"` : firstNum;
|
|
381
|
+
let secondValue = Number.isNaN(secondNum) ? valueIsField ? `${second}` : `"${escapeDoubleQuotes(second, escapeQuotes)}"` : secondNum;
|
|
382
|
+
if (!preserveValueOrder && firstValue === firstNum && secondValue === secondNum && secondNum < firstNum) {
|
|
383
|
+
const tempNum = secondNum;
|
|
384
|
+
secondValue = firstNum;
|
|
385
|
+
firstValue = tempNum;
|
|
386
|
+
}
|
|
387
|
+
return operatorTL === "between" ? `(${field} >= ${firstValue} && ${field} <= ${secondValue})` : `(${field} < ${firstValue} || ${field} > ${secondValue})`;
|
|
388
|
+
} else return "";
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return "";
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
//#endregion
|
|
395
|
+
//#region src/utils/formatQuery/defaultRuleGroupProcessorMongoDBQuery.ts
|
|
396
|
+
/**
|
|
397
|
+
* Default fallback object used by {@link formatQuery} for "mongodb_query" format.
|
|
398
|
+
*
|
|
399
|
+
* @group Export
|
|
400
|
+
*/
|
|
401
|
+
const mongoDbFallback = { $and: [{ $expr: true }] };
|
|
402
|
+
/**
|
|
403
|
+
* Rule group processor used by {@link formatQuery} for "mongodb_query" format.
|
|
404
|
+
*
|
|
405
|
+
* @group Export
|
|
406
|
+
*/
|
|
407
|
+
const defaultRuleGroupProcessorMongoDBQuery = (ruleGroup, options, meta) => {
|
|
408
|
+
const { fields, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
409
|
+
const processRuleGroup = (rg, outermost) => {
|
|
410
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermost ? mongoDbFallback : false;
|
|
411
|
+
const combinator = `$${lc(rg.combinator)}`;
|
|
412
|
+
let hasChildRules = false;
|
|
413
|
+
const expressions = rg.rules.map((rule) => {
|
|
414
|
+
if (isRuleGroup(rule)) {
|
|
415
|
+
const processedRuleGroup = processRuleGroup(rule);
|
|
416
|
+
if (processedRuleGroup) {
|
|
417
|
+
hasChildRules = true;
|
|
418
|
+
return processedRuleGroup;
|
|
419
|
+
}
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
const [validationResult, fieldValidator] = validateRule(rule);
|
|
423
|
+
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || placeholderValueName !== void 0 && rule.value === placeholderValueName) return false;
|
|
424
|
+
const fieldData = getOption(fields, rule.field);
|
|
425
|
+
return ruleProcessor(rule, {
|
|
426
|
+
...options,
|
|
427
|
+
parseNumbers: getParseNumberBoolean(fieldData?.inputType),
|
|
428
|
+
fieldData
|
|
429
|
+
}, meta);
|
|
430
|
+
}).filter(Boolean);
|
|
431
|
+
return expressions.length > 0 ? expressions.length === 1 && !hasChildRules ? expressions[0] : { [combinator]: expressions } : mongoDbFallback;
|
|
432
|
+
};
|
|
433
|
+
return processRuleGroup(convertFromIC(ruleGroup), true);
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
//#endregion
|
|
437
|
+
//#region src/utils/formatQuery/defaultRuleProcessorMongoDBQuery.ts
|
|
438
|
+
const processNumber$1 = (value, fallback, parseNumbers = false) => shouldRenderAsNumber(value, parseNumbers || typeof value === "bigint") ? Number(parseNumber(value, { parseNumbers: "strict" })) : fallback;
|
|
439
|
+
/**
|
|
440
|
+
* Default rule processor used by {@link formatQuery} for "mongodb_query" format.
|
|
441
|
+
*
|
|
442
|
+
* @group Export
|
|
443
|
+
*/
|
|
444
|
+
const defaultRuleProcessorMongoDBQuery = (rule, options = {}) => {
|
|
445
|
+
const { field, operator, value, valueSource } = rule;
|
|
446
|
+
const { parseNumbers, preserveValueOrder, context } = options;
|
|
447
|
+
const valueIsField = valueSource === "field";
|
|
448
|
+
const { avoidFieldsAsKeys } = context ?? {};
|
|
449
|
+
const matchEval = processMatchMode(rule);
|
|
450
|
+
if (matchEval === false) return;
|
|
451
|
+
else if (matchEval) {
|
|
452
|
+
const { mode, threshold } = matchEval;
|
|
453
|
+
const totalCount = { $size: { $ifNull: [`$${field}`, []] } };
|
|
454
|
+
const subQueryNoAggCtx = defaultRuleGroupProcessorMongoDBQuery(transformQuery(value, { ruleProcessor: (r) => ({
|
|
455
|
+
...r,
|
|
456
|
+
field: r.field ? `${field}.${r.field}` : field
|
|
457
|
+
}) }), {
|
|
458
|
+
...options,
|
|
459
|
+
ruleProcessor: defaultRuleProcessorMongoDBQuery,
|
|
460
|
+
context: {
|
|
461
|
+
...options.context,
|
|
462
|
+
avoidFieldsAsKeys: false
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
const subQueryWithAggCtx = defaultRuleGroupProcessorMongoDBQuery(transformQuery(value, { ruleProcessor: (r) => ({
|
|
466
|
+
...r,
|
|
467
|
+
field: r.field ? `$item.${r.field}` : "$item"
|
|
468
|
+
}) }), {
|
|
469
|
+
...options,
|
|
470
|
+
ruleProcessor: defaultRuleProcessorMongoDBQuery,
|
|
471
|
+
context: {
|
|
472
|
+
...options.context,
|
|
473
|
+
avoidFieldsAsKeys: true
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
const filteredCount = { $size: { $ifNull: [{ $filter: {
|
|
477
|
+
input: `$${field}`,
|
|
478
|
+
as: "item",
|
|
479
|
+
cond: { $and: [subQueryWithAggCtx] }
|
|
480
|
+
} }, []] } };
|
|
481
|
+
switch (mode) {
|
|
482
|
+
case "all": return { $expr: { $eq: [filteredCount, totalCount] } };
|
|
483
|
+
case "none": return { $nor: [subQueryNoAggCtx] };
|
|
484
|
+
case "some": return subQueryNoAggCtx;
|
|
485
|
+
case "atleast":
|
|
486
|
+
case "atmost":
|
|
487
|
+
case "exactly": {
|
|
488
|
+
const op = mode === "atleast" ? mongoOperators[">="] : mode === "atmost" ? mongoOperators["<="] : mongoOperators["="];
|
|
489
|
+
if (threshold > 0 && threshold < 1) return { $expr: { [op]: [filteredCount, { $multiply: [totalCount, threshold] }] } };
|
|
490
|
+
return { $expr: { [op]: [filteredCount, threshold] } };
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
if (operator === "=" && !valueIsField) return avoidFieldsAsKeys ? { $eq: [`$${field}`, processNumber$1(value, value, parseNumbers)] } : { [field]: processNumber$1(value, value, parseNumbers) };
|
|
495
|
+
const operatorLC = lc(operator);
|
|
496
|
+
switch (operatorLC) {
|
|
497
|
+
case "<":
|
|
498
|
+
case "<=":
|
|
499
|
+
case "=":
|
|
500
|
+
case "!=":
|
|
501
|
+
case ">":
|
|
502
|
+
case ">=": {
|
|
503
|
+
const mongoOperator = mongoOperators[operatorLC];
|
|
504
|
+
return valueIsField ? { [mongoOperator]: [`$${field}`, `$${value}`] } : avoidFieldsAsKeys ? { $and: [{ $ne: [`$${field}`, null] }, { [mongoOperator]: [`$${field}`, processNumber$1(value, value, parseNumbers)] }] } : { [field]: { [mongoOperator]: processNumber$1(value, value, parseNumbers) } };
|
|
505
|
+
}
|
|
506
|
+
case "contains": return valueIsField ? { $where: `this.${field}.includes(this.${value})` } : avoidFieldsAsKeys ? { $regexMatch: {
|
|
507
|
+
input: `$${field}`,
|
|
508
|
+
regex: value
|
|
509
|
+
} } : { [field]: { $regex: value } };
|
|
510
|
+
case "beginswith": return valueIsField ? { $where: `this.${field}.startsWith(this.${value})` } : avoidFieldsAsKeys ? { $regexMatch: {
|
|
511
|
+
input: `$${field}`,
|
|
512
|
+
regex: `^${value}`
|
|
513
|
+
} } : { [field]: { $regex: `^${value}` } };
|
|
514
|
+
case "endswith": return valueIsField ? { $where: `this.${field}.endsWith(this.${value})` } : avoidFieldsAsKeys ? { $regexMatch: {
|
|
515
|
+
input: `$${field}`,
|
|
516
|
+
regex: `${value}$`
|
|
517
|
+
} } : { [field]: { $regex: `${value}$` } };
|
|
518
|
+
case "doesnotcontain": return valueIsField ? { $where: `!this.${field}.includes(this.${value})` } : avoidFieldsAsKeys ? { $not: { $regexMatch: {
|
|
519
|
+
input: `$${field}`,
|
|
520
|
+
regex: value
|
|
521
|
+
} } } : { [field]: { $not: { $regex: value } } };
|
|
522
|
+
case "doesnotbeginwith": return valueIsField ? { $where: `!this.${field}.startsWith(this.${value})` } : avoidFieldsAsKeys ? { $not: { $regexMatch: {
|
|
523
|
+
input: `$${field}`,
|
|
524
|
+
regex: `^${value}`
|
|
525
|
+
} } } : { [field]: { $not: { $regex: `^${value}` } } };
|
|
526
|
+
case "doesnotendwith": return valueIsField ? { $where: `!this.${field}.endsWith(this.${value})` } : avoidFieldsAsKeys ? { $not: { $regexMatch: {
|
|
527
|
+
input: `$${field}`,
|
|
528
|
+
regex: `${value}$`
|
|
529
|
+
} } } : { [field]: { $not: { $regex: `${value}$` } } };
|
|
530
|
+
case "null": return avoidFieldsAsKeys ? { $eq: [`$${field}`, null] } : { [field]: null };
|
|
531
|
+
case "notnull": return avoidFieldsAsKeys ? { $ne: [`$${field}`, null] } : { [field]: { $ne: null } };
|
|
532
|
+
case "in":
|
|
533
|
+
case "notin": {
|
|
534
|
+
const valueAsArray = toArray(value);
|
|
535
|
+
return valueIsField ? { $where: `${operatorLC === "notin" ? "!" : ""}[${valueAsArray.map((val) => `this.${val}`).join(",")}].includes(this.${field})` } : avoidFieldsAsKeys ? operatorLC === "notin" ? { $not: { [mongoOperators.in]: [`$${field}`, valueAsArray.map((val) => processNumber$1(val, val, parseNumbers))] } } : { [mongoOperators[operatorLC]]: [`$${field}`, valueAsArray.map((val) => processNumber$1(val, val, parseNumbers))] } : { [field]: { [mongoOperators[operatorLC]]: valueAsArray.map((val) => processNumber$1(val, val, parseNumbers)) } };
|
|
536
|
+
}
|
|
537
|
+
case "between":
|
|
538
|
+
case "notbetween": {
|
|
539
|
+
const valueAsArray = toArray(value);
|
|
540
|
+
if (valueAsArray.length >= 2 && isValidValue(valueAsArray[0]) && isValidValue(valueAsArray[1])) {
|
|
541
|
+
const [first, second] = valueAsArray;
|
|
542
|
+
const firstNum = processNumber$1(first, NaN, true);
|
|
543
|
+
const secondNum = processNumber$1(second, NaN, true);
|
|
544
|
+
let firstValue = valueIsField ? first : Number.isNaN(firstNum) ? first : firstNum;
|
|
545
|
+
let secondValue = valueIsField ? second : Number.isNaN(secondNum) ? second : secondNum;
|
|
546
|
+
if (!preserveValueOrder && firstValue === firstNum && secondValue === secondNum && secondNum < firstNum) {
|
|
547
|
+
const tempNum = secondNum;
|
|
548
|
+
secondValue = firstNum;
|
|
549
|
+
firstValue = tempNum;
|
|
550
|
+
}
|
|
551
|
+
if (operatorLC === "between") return valueIsField ? {
|
|
552
|
+
$gte: [`$${field}`, `$${firstValue}`],
|
|
553
|
+
$lte: [`$${field}`, `$${secondValue}`]
|
|
554
|
+
} : avoidFieldsAsKeys ? { $and: [{ $gte: [`$${field}`, firstValue] }, { $lte: [`$${field}`, secondValue] }] } : { [field]: {
|
|
555
|
+
$gte: firstValue,
|
|
556
|
+
$lte: secondValue
|
|
557
|
+
} };
|
|
558
|
+
else return valueIsField ? { $or: [{ $lt: [`$${field}`, `$${firstValue}`] }, { $gt: [`$${field}`, `$${secondValue}`] }] } : avoidFieldsAsKeys ? { $or: [{ $lt: [`$${field}`, firstValue] }, { $gt: [`$${field}`, secondValue] }] } : { $or: [{ [field]: { $lt: firstValue } }, { [field]: { $gt: secondValue } }] };
|
|
559
|
+
} else return "";
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
return "";
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
//#endregion
|
|
566
|
+
//#region src/utils/formatQuery/defaultRuleProcessorMongoDB.ts
|
|
567
|
+
/**
|
|
568
|
+
* Default rule processor used by {@link formatQuery} for "mongodb" format.
|
|
569
|
+
*
|
|
570
|
+
* Note that the "mongodb" format is deprecated in favor of the "mongodb_query" format.
|
|
571
|
+
*
|
|
572
|
+
* @group Export
|
|
573
|
+
*/
|
|
574
|
+
const defaultRuleProcessorMongoDB = (rule, options) => {
|
|
575
|
+
const queryObj = defaultRuleProcessorMongoDBQuery(rule, options);
|
|
576
|
+
return queryObj ? JSON.stringify(queryObj) : "";
|
|
577
|
+
};
|
|
578
|
+
|
|
579
|
+
//#endregion
|
|
580
|
+
//#region src/utils/formatQuery/defaultRuleGroupProcessorSpEL.ts
|
|
581
|
+
/**
|
|
582
|
+
* Default rule processor used by {@link formatQuery} for "spel" format.
|
|
583
|
+
*
|
|
584
|
+
* @group Export
|
|
585
|
+
*/
|
|
586
|
+
const defaultRuleGroupProcessorSpEL = (ruleGroup, options) => {
|
|
587
|
+
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
588
|
+
const processRuleGroup = (rg, outermost) => {
|
|
589
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermost ? fallbackExpression : "";
|
|
590
|
+
const expression = rg.rules.map((rule) => {
|
|
591
|
+
if (typeof rule === "string") return rule;
|
|
592
|
+
if (isRuleGroup(rule)) return processRuleGroup(rule);
|
|
593
|
+
const [validationResult, fieldValidator] = validateRule(rule);
|
|
594
|
+
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || placeholderValueName !== void 0 && rule.value === placeholderValueName) return "";
|
|
595
|
+
const fieldData = getOption(fields, rule.field);
|
|
596
|
+
return ruleProcessor(rule, {
|
|
597
|
+
...options,
|
|
598
|
+
parseNumbers: getParseNumberBoolean(fieldData?.inputType),
|
|
599
|
+
escapeQuotes: (rule.valueSource ?? "value") === "value",
|
|
600
|
+
fieldData
|
|
601
|
+
});
|
|
602
|
+
}).filter(Boolean).join(isRuleGroupType(rg) ? ` ${rg.combinator} ` : " ");
|
|
603
|
+
const [prefix, suffix] = rg.not || !outermost ? [`${rg.not ? "!" : ""}(`, ")"] : ["", ""];
|
|
604
|
+
return expression ? `${prefix}${expression}${suffix}` : fallbackExpression;
|
|
605
|
+
};
|
|
606
|
+
return processRuleGroup(ruleGroup, true);
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
//#endregion
|
|
610
|
+
//#region src/utils/formatQuery/defaultRuleProcessorSpEL.ts
|
|
611
|
+
const shouldNegate$1 = (op) => op.startsWith("not") || op.startsWith("doesnot");
|
|
612
|
+
const wrapInNegation = (clause, negate$1) => negate$1 ? `!(${clause})` : clause;
|
|
613
|
+
const escapeSingleQuotes = (v, escapeQuotes) => typeof v !== "string" || !escapeQuotes ? `${v}` : v.replaceAll(`'`, `\\'`);
|
|
614
|
+
/**
|
|
615
|
+
* Default rule processor used by {@link formatQuery} for "spel" format.
|
|
616
|
+
*
|
|
617
|
+
* @group Export
|
|
618
|
+
*/
|
|
619
|
+
const defaultRuleProcessorSpEL = (rule, opts = {}) => {
|
|
620
|
+
const { field, operator, value, valueSource } = rule;
|
|
621
|
+
const { escapeQuotes, parseNumbers, preserveValueOrder } = opts;
|
|
622
|
+
const valueIsField = valueSource === "field";
|
|
623
|
+
const operatorTL = lc(operator === "=" ? "==" : operator);
|
|
624
|
+
const useBareValue = typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || shouldRenderAsNumber(value, parseNumbers);
|
|
625
|
+
const matchEval = processMatchMode(rule);
|
|
626
|
+
if (matchEval === false) return "";
|
|
627
|
+
else if (matchEval) {
|
|
628
|
+
const { mode, threshold } = matchEval;
|
|
629
|
+
const nestedArrayFilter = defaultRuleGroupProcessorSpEL(transformQuery(rule.value, { ruleProcessor: (r) => ({
|
|
630
|
+
...r,
|
|
631
|
+
field: r.field || "#this"
|
|
632
|
+
}) }), opts);
|
|
633
|
+
const totalCount = `${field}.size()`;
|
|
634
|
+
const filteredCount = `${field}.?[${nestedArrayFilter}].size()`;
|
|
635
|
+
switch (mode) {
|
|
636
|
+
case "all": return `${filteredCount} == ${totalCount}`;
|
|
637
|
+
case "none": return `${filteredCount} == 0`;
|
|
638
|
+
case "some": return `${filteredCount} >= 1`;
|
|
639
|
+
case "atleast":
|
|
640
|
+
case "atmost":
|
|
641
|
+
case "exactly": {
|
|
642
|
+
const op = mode === "atleast" ? ">=" : mode === "atmost" ? "<=" : "==";
|
|
643
|
+
if (threshold > 0 && threshold < 1) return `${filteredCount} ${op} (${totalCount} * ${threshold})`;
|
|
644
|
+
return `${filteredCount} ${op} ${threshold}`;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
switch (operatorTL) {
|
|
649
|
+
case "<":
|
|
650
|
+
case "<=":
|
|
651
|
+
case "==":
|
|
652
|
+
case "!=":
|
|
653
|
+
case ">":
|
|
654
|
+
case ">=": return `${field} ${operatorTL} ${valueIsField || useBareValue ? trimIfString(value) : `'${escapeSingleQuotes(value, escapeQuotes)}'`}`;
|
|
655
|
+
case "contains":
|
|
656
|
+
case "doesnotcontain": return wrapInNegation(`${field} matches ${valueIsField || useBareValue ? trimIfString(value) : `'${escapeSingleQuotes(value, escapeQuotes)}'`}`, shouldNegate$1(operatorTL));
|
|
657
|
+
case "beginswith":
|
|
658
|
+
case "doesnotbeginwith": {
|
|
659
|
+
const valueTL = valueIsField ? `'^'.concat(${trimIfString(value)})` : `'${typeof value === "string" && !value.startsWith("^") || useBareValue ? "^" : ""}${escapeSingleQuotes(value, escapeQuotes)}'`;
|
|
660
|
+
return wrapInNegation(`${field} matches ${valueTL}`, shouldNegate$1(operatorTL));
|
|
661
|
+
}
|
|
662
|
+
case "endswith":
|
|
663
|
+
case "doesnotendwith": {
|
|
664
|
+
const valueTL = valueIsField ? `${trimIfString(value)}.concat('$')` : `'${escapeSingleQuotes(value, escapeQuotes)}${typeof value === "string" && !value.endsWith("$") || useBareValue ? "$" : ""}'`;
|
|
665
|
+
return wrapInNegation(`${field} matches ${valueTL}`, shouldNegate$1(operatorTL));
|
|
666
|
+
}
|
|
667
|
+
case "null": return `${field} == null`;
|
|
668
|
+
case "notnull": return `${field} != null`;
|
|
669
|
+
case "in":
|
|
670
|
+
case "notin": {
|
|
671
|
+
const negate$1 = shouldNegate$1(operatorTL) ? "!" : "";
|
|
672
|
+
const valueAsArray = toArray(value);
|
|
673
|
+
return valueAsArray.length > 0 ? `${negate$1}(${valueAsArray.map((val) => `${field} == ${valueIsField || shouldRenderAsNumber(val, parseNumbers) ? `${trimIfString(val)}` : `'${escapeSingleQuotes(val, escapeQuotes)}'`}`).join(" or ")})` : "";
|
|
674
|
+
}
|
|
675
|
+
case "between":
|
|
676
|
+
case "notbetween": {
|
|
677
|
+
const valueAsArray = toArray(value);
|
|
678
|
+
if (valueAsArray.length >= 2 && !nullOrUndefinedOrEmpty(valueAsArray[0]) && !nullOrUndefinedOrEmpty(valueAsArray[1])) {
|
|
679
|
+
const [first, second] = valueAsArray;
|
|
680
|
+
const shouldParseNumbers = !(parseNumbers === false);
|
|
681
|
+
const firstNum = shouldRenderAsNumber(first, shouldParseNumbers) ? parseNumber(first, { parseNumbers: shouldParseNumbers }) : NaN;
|
|
682
|
+
const secondNum = shouldRenderAsNumber(second, shouldParseNumbers) ? parseNumber(second, { parseNumbers: shouldParseNumbers }) : NaN;
|
|
683
|
+
let firstValue = Number.isNaN(firstNum) ? valueIsField ? `${first}` : `'${escapeSingleQuotes(first, escapeQuotes)}'` : firstNum;
|
|
684
|
+
let secondValue = Number.isNaN(secondNum) ? valueIsField ? `${second}` : `'${escapeSingleQuotes(second, escapeQuotes)}'` : secondNum;
|
|
685
|
+
if (!preserveValueOrder && firstValue === firstNum && secondValue === secondNum && secondNum < firstNum) {
|
|
686
|
+
const tempNum = secondNum;
|
|
687
|
+
secondValue = firstNum;
|
|
688
|
+
firstValue = tempNum;
|
|
689
|
+
}
|
|
690
|
+
return operatorTL === "between" ? `(${field} >= ${firstValue} and ${field} <= ${secondValue})` : `(${field} < ${firstValue} or ${field} > ${secondValue})`;
|
|
691
|
+
} else return "";
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
return "";
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
//#endregion
|
|
698
|
+
//#region src/utils/formatQuery/defaultValueProcessorByRule.ts
|
|
699
|
+
const escapeStringValueQuotes$1 = (v, quoteChar, escapeQuotes) => escapeQuotes && typeof v === "string" ? v.replaceAll(`${quoteChar}`, `${quoteChar}${quoteChar}`) : v;
|
|
700
|
+
/**
|
|
701
|
+
* Default value processor used by {@link formatQuery} for "sql" format.
|
|
702
|
+
*
|
|
703
|
+
* @group Export
|
|
704
|
+
*/
|
|
705
|
+
const defaultValueProcessorByRule = ({ operator, value, valueSource }, { escapeQuotes, parseNumbers, preserveValueOrder, quoteFieldNamesWith, quoteValuesWith, concatOperator = "||", fieldIdentifierSeparator, wrapValueWith = ["", ""], translations } = {}) => {
|
|
706
|
+
const valueIsField = valueSource === "field";
|
|
707
|
+
const operatorLowerCase = lc(operator);
|
|
708
|
+
const quoteChar = quoteValuesWith || "'";
|
|
709
|
+
const quoteValue = (v) => `${wrapValueWith[0]}${quoteChar}${v}${quoteChar}${wrapValueWith[1]}`;
|
|
710
|
+
const escapeValue = (v) => escapeStringValueQuotes$1(v, quoteChar, escapeQuotes);
|
|
711
|
+
const wrapAndEscape = (v) => quoteValue(escapeValue(v));
|
|
712
|
+
const wrapFieldName = (v) => getQuotedFieldName(v, {
|
|
713
|
+
quoteFieldNamesWith,
|
|
714
|
+
fieldIdentifierSeparator
|
|
715
|
+
});
|
|
716
|
+
const concat = (...values) => concatOperator.toUpperCase() === "CONCAT" ? `CONCAT(${values.join(", ")})` : values.join(` ${concatOperator} `);
|
|
717
|
+
switch (operatorLowerCase) {
|
|
718
|
+
case "null":
|
|
719
|
+
case "notnull": return "";
|
|
720
|
+
case "in":
|
|
721
|
+
case "notin": {
|
|
722
|
+
const valueAsArray = toArray(value);
|
|
723
|
+
if (valueAsArray.length > 0) return `(${valueAsArray.map((v) => valueIsField ? wrapFieldName(v) : shouldRenderAsNumber(v, parseNumbers) ? `${trimIfString(v)}` : `${wrapAndEscape(v)}`).join(", ")})`;
|
|
724
|
+
return "";
|
|
725
|
+
}
|
|
726
|
+
case "between":
|
|
727
|
+
case "notbetween": {
|
|
728
|
+
const valueAsArray = toArray(value, { retainEmptyStrings: true });
|
|
729
|
+
if (valueAsArray.length < 2 || !isValidValue(valueAsArray[0]) || !isValidValue(valueAsArray[1])) return "";
|
|
730
|
+
const [first, second] = valueAsArray;
|
|
731
|
+
const firstNum = shouldRenderAsNumber(first, parseNumbers) ? parseNumber(first, { parseNumbers: "strict" }) : NaN;
|
|
732
|
+
const secondNum = shouldRenderAsNumber(second, parseNumbers) ? parseNumber(second, { parseNumbers: "strict" }) : NaN;
|
|
733
|
+
const firstValue = Number.isNaN(firstNum) ? valueIsField ? `${first}` : first : firstNum;
|
|
734
|
+
const secondValue = Number.isNaN(secondNum) ? valueIsField ? `${second}` : second : secondNum;
|
|
735
|
+
const valsOneAndTwoOnly = [firstValue, secondValue];
|
|
736
|
+
if (!preserveValueOrder && firstValue === firstNum && secondValue === secondNum && secondNum < firstNum) {
|
|
737
|
+
valsOneAndTwoOnly[0] = secondNum;
|
|
738
|
+
valsOneAndTwoOnly[1] = firstNum;
|
|
739
|
+
}
|
|
740
|
+
return (valueIsField ? valsOneAndTwoOnly.map((v) => wrapFieldName(v)) : valsOneAndTwoOnly.every((v) => shouldRenderAsNumber(v, parseNumbers)) ? valsOneAndTwoOnly.map((v) => parseNumber(v, { parseNumbers: "strict" })) : valsOneAndTwoOnly.map((v) => wrapAndEscape(v))).join(` ${translations?.and ?? "and"} `);
|
|
741
|
+
}
|
|
742
|
+
case "contains":
|
|
743
|
+
case "doesnotcontain": return valueIsField ? concat(quoteValue("%"), wrapFieldName(value), quoteValue("%")) : quoteValue(`%${escapeValue(value)}%`);
|
|
744
|
+
case "beginswith":
|
|
745
|
+
case "doesnotbeginwith": return valueIsField ? concat(wrapFieldName(value), quoteValue("%")) : quoteValue(`${escapeValue(value)}%`);
|
|
746
|
+
case "endswith":
|
|
747
|
+
case "doesnotendwith": return valueIsField ? concat(quoteValue("%"), wrapFieldName(value)) : quoteValue(`%${escapeValue(value)}`);
|
|
748
|
+
}
|
|
749
|
+
if (typeof value === "boolean") return value ? "TRUE" : "FALSE";
|
|
750
|
+
return valueIsField ? wrapFieldName(value) : shouldRenderAsNumber(value, parseNumbers) ? `${trimIfString(value)}` : `${wrapAndEscape(value)}`;
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
//#endregion
|
|
754
|
+
//#region src/utils/formatQuery/defaultRuleProcessorDrizzle.ts
|
|
755
|
+
/**
|
|
756
|
+
* Default rule processor used by {@link formatQuery} for the "drizzle" format.
|
|
757
|
+
*
|
|
758
|
+
* @group Export
|
|
759
|
+
*/
|
|
760
|
+
const defaultRuleProcessorDrizzle = (rule, _options) => {
|
|
761
|
+
const opts = _options ?? ( /* istanbul ignore next */ {});
|
|
762
|
+
// istanbul ignore next
|
|
763
|
+
const { parseNumbers, preserveValueOrder, context = {} } = opts;
|
|
764
|
+
const { columns, drizzleOperators, useRawFields } = context;
|
|
765
|
+
if (!columns || !drizzleOperators) return;
|
|
766
|
+
const { between, eq, gt, gte, inArray, isNotNull, isNull, like, lt, lte, ne, notBetween, notInArray, notLike, sql } = drizzleOperators;
|
|
767
|
+
const { field, operator, value, valueSource } = rule;
|
|
768
|
+
const column = useRawFields && /[a-z][a-z0-9]*/i.test(field) ? sql.raw(field) : columns[field];
|
|
769
|
+
const operatorLC = lc(operator);
|
|
770
|
+
const valueIsField = valueSource === "field";
|
|
771
|
+
const asFieldOrValue = (v) => valueIsField ? columns[v] : v;
|
|
772
|
+
if (!column) return;
|
|
773
|
+
const matchEval = processMatchMode(rule);
|
|
774
|
+
if (matchEval === false) return;
|
|
775
|
+
else if (matchEval) {
|
|
776
|
+
if (opts.preset !== "postgresql") return;
|
|
777
|
+
const { mode, threshold } = matchEval;
|
|
778
|
+
const arrayElementAlias = "elem_alias";
|
|
779
|
+
const sqlQuery = transformQuery(rule.value, { ruleProcessor: (r) => ({
|
|
780
|
+
...r,
|
|
781
|
+
field: arrayElementAlias
|
|
782
|
+
}) });
|
|
783
|
+
const nestedArrayFilter = defaultRuleGroupProcessorDrizzle(sqlQuery, {
|
|
784
|
+
...opts,
|
|
785
|
+
context: {
|
|
786
|
+
...opts.context,
|
|
787
|
+
useRawFields: true
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
switch (mode) {
|
|
791
|
+
case "all": return sql`(select count(*) from unnest(${column}) as ${sql.raw(arrayElementAlias)} where ${nestedArrayFilter({}, drizzleOperators)}) = array_length(${column}, 1)`;
|
|
792
|
+
case "none": return sql`not exists (select 1 from unnest(${column}) as ${sql.raw(arrayElementAlias)} where ${nestedArrayFilter({}, drizzleOperators)})`;
|
|
793
|
+
case "some": return sql`exists (select 1 from unnest(${column}) as ${sql.raw(arrayElementAlias)} where ${nestedArrayFilter({}, drizzleOperators)})`;
|
|
794
|
+
case "atleast":
|
|
795
|
+
case "atmost":
|
|
796
|
+
case "exactly": {
|
|
797
|
+
const op = mode === "atleast" ? ">=" : mode === "atmost" ? "<=" : "=";
|
|
798
|
+
return threshold > 0 && threshold < 1 ? sql`(select count(*) / array_length(${column}, 1) from unnest(${column}) as ${sql.raw(arrayElementAlias)} where ${nestedArrayFilter({}, drizzleOperators)}) ${sql.raw(`${op} ${threshold}`)}` : sql`(select count(*) from unnest(${column}) as ${sql.raw(arrayElementAlias)} where ${nestedArrayFilter({}, drizzleOperators)}) ${sql.raw(`${op} ${threshold}`)}`;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
switch (operatorLC) {
|
|
803
|
+
case "=": return eq(column, asFieldOrValue(value));
|
|
804
|
+
case "!=": return ne(column, asFieldOrValue(value));
|
|
805
|
+
case ">": return gt(column, asFieldOrValue(value));
|
|
806
|
+
case "<": return lt(column, asFieldOrValue(value));
|
|
807
|
+
case ">=": return gte(column, asFieldOrValue(value));
|
|
808
|
+
case "<=": return lte(column, asFieldOrValue(value));
|
|
809
|
+
case "beginswith":
|
|
810
|
+
case "doesnotbeginwith": return (operatorLC === "doesnotbeginwith" ? notLike : like)(column, valueIsField ? sql`${asFieldOrValue(value)} || '%'` : `${value}%`);
|
|
811
|
+
case "contains":
|
|
812
|
+
case "doesnotcontain": return (operatorLC === "doesnotcontain" ? notLike : like)(column, valueIsField ? sql`'%' || ${asFieldOrValue(value)} || '%'` : `%${value}%`);
|
|
813
|
+
case "endswith":
|
|
814
|
+
case "doesnotendwith": return (operatorLC === "doesnotendwith" ? notLike : like)(column, valueIsField ? sql`'%' || ${asFieldOrValue(value)}` : `%${value}`);
|
|
815
|
+
case "null": return isNull(column);
|
|
816
|
+
case "notnull": return isNotNull(column);
|
|
817
|
+
case "in":
|
|
818
|
+
case "notin": {
|
|
819
|
+
const valueAsArray = toArray(value).map((v) => asFieldOrValue(v));
|
|
820
|
+
return operatorLC === "notin" ? notInArray(column, valueAsArray) : inArray(column, valueAsArray);
|
|
821
|
+
}
|
|
822
|
+
case "between":
|
|
823
|
+
case "notbetween": {
|
|
824
|
+
const valueAsArray = toArray(value);
|
|
825
|
+
if (valueAsArray.length >= 2 && isValidValue(valueAsArray[0]) && isValidValue(valueAsArray[1])) {
|
|
826
|
+
let [first, second] = valueAsArray;
|
|
827
|
+
const shouldParseNumbers = !(parseNumbers === false);
|
|
828
|
+
if (!valueIsField && shouldRenderAsNumber(first, shouldParseNumbers) && shouldRenderAsNumber(second, shouldParseNumbers)) {
|
|
829
|
+
const firstNum = parseNumber(first, { parseNumbers: shouldParseNumbers });
|
|
830
|
+
const secondNum = parseNumber(second, { parseNumbers: shouldParseNumbers });
|
|
831
|
+
if (!preserveValueOrder && secondNum < firstNum) {
|
|
832
|
+
const tempNum = secondNum;
|
|
833
|
+
second = firstNum;
|
|
834
|
+
first = tempNum;
|
|
835
|
+
} else {
|
|
836
|
+
first = firstNum;
|
|
837
|
+
second = secondNum;
|
|
838
|
+
}
|
|
839
|
+
} else if (valueIsField) {
|
|
840
|
+
first = asFieldOrValue(first);
|
|
841
|
+
second = asFieldOrValue(second);
|
|
842
|
+
}
|
|
843
|
+
return operatorLC === "notbetween" ? notBetween(column, first, second) : between(column, first, second);
|
|
844
|
+
}
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
default: return;
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
//#endregion
|
|
852
|
+
//#region src/utils/formatQuery/defaultRuleGroupProcessorDrizzle.ts
|
|
853
|
+
/**
|
|
854
|
+
* Default rule group processor used by {@link formatQuery} for the "drizzle" format. The returned
|
|
855
|
+
* function can be assigned to the `where` property in the Drizzle relational queries API.
|
|
856
|
+
*
|
|
857
|
+
* @example
|
|
858
|
+
* const where = formatQuery(query, 'drizzle');
|
|
859
|
+
* const results = db.query.users.findMany({ where });
|
|
860
|
+
*
|
|
861
|
+
* @returns Function that takes a Drizzle table config and an object of Drizzle operators.
|
|
862
|
+
*
|
|
863
|
+
* @group Export
|
|
864
|
+
*/
|
|
865
|
+
const defaultRuleGroupProcessorDrizzle = (ruleGroup, options, _meta) => (columns, drizzleOperators) => {
|
|
866
|
+
const { fields, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, validateRule, validationMap } = options;
|
|
867
|
+
if (!columns || !drizzleOperators) return;
|
|
868
|
+
const { and, not, or } = drizzleOperators;
|
|
869
|
+
const query = isRuleGroupType(ruleGroup) ? ruleGroup : convertFromIC(ruleGroup);
|
|
870
|
+
const ruleProcessor = defaultRuleProcessorDrizzle;
|
|
871
|
+
const processRuleGroup = (rg, _outermost) => {
|
|
872
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return;
|
|
873
|
+
const processedRules = rg.rules.map((rule) => {
|
|
874
|
+
if (isRuleGroup(rule)) return processRuleGroup(rule);
|
|
875
|
+
const [validationResult, fieldValidator] = validateRule(rule);
|
|
876
|
+
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || placeholderValueName !== void 0 && rule.value === placeholderValueName) return;
|
|
877
|
+
const fieldData = getOption(fields, rule.field);
|
|
878
|
+
return ruleProcessor(rule, {
|
|
879
|
+
...options,
|
|
880
|
+
parseNumbers: getParseNumberBoolean(fieldData?.inputType),
|
|
881
|
+
fieldData,
|
|
882
|
+
context: {
|
|
883
|
+
...options.context,
|
|
884
|
+
columns,
|
|
885
|
+
drizzleOperators
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
}).filter(Boolean);
|
|
889
|
+
if (processedRules.length === 0) return;
|
|
890
|
+
const ruleGroupSQL = rg.combinator === "or" ? or(...processedRules) : and(...processedRules);
|
|
891
|
+
return rg.not ? not(ruleGroupSQL) : ruleGroupSQL;
|
|
892
|
+
};
|
|
893
|
+
return processRuleGroup(query, true);
|
|
894
|
+
};
|
|
895
|
+
|
|
896
|
+
//#endregion
|
|
897
|
+
//#region src/utils/formatQuery/defaultRuleGroupProcessorElasticSearch.ts
|
|
898
|
+
/**
|
|
899
|
+
* Rule group processor used by {@link formatQuery} for "elasticsearch" format.
|
|
900
|
+
*
|
|
901
|
+
* @group Export
|
|
902
|
+
*/
|
|
903
|
+
const defaultRuleGroupProcessorElasticSearch = (ruleGroup, options) => {
|
|
904
|
+
const { fields, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
905
|
+
const query = convertFromIC(ruleGroup);
|
|
906
|
+
const processRuleGroup = (rg) => {
|
|
907
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return false;
|
|
908
|
+
const processedRules = rg.rules.map((rule) => {
|
|
909
|
+
if (isRuleGroup(rule)) return processRuleGroup(rule);
|
|
910
|
+
const [validationResult, fieldValidator] = validateRule(rule);
|
|
911
|
+
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || placeholderValueName !== void 0 && rule.value === placeholderValueName) return false;
|
|
912
|
+
const fieldData = getOption(fields, rule.field);
|
|
913
|
+
return ruleProcessor(rule, {
|
|
914
|
+
...options,
|
|
915
|
+
parseNumbers: getParseNumberBoolean(fieldData?.inputType),
|
|
916
|
+
fieldData
|
|
917
|
+
});
|
|
918
|
+
}).filter(Boolean);
|
|
919
|
+
if (processedRules.length === 0) return false;
|
|
920
|
+
return { bool: rg.not ? { must_not: /^or$/i.test(rg.combinator) ? { bool: { should: processedRules } } : processedRules } : { [/^or$/i.test(rg.combinator) ? "should" : "must"]: processedRules } };
|
|
921
|
+
};
|
|
922
|
+
const processedRuleGroup = processRuleGroup(query);
|
|
923
|
+
return processedRuleGroup === false ? {} : processedRuleGroup;
|
|
924
|
+
};
|
|
925
|
+
|
|
926
|
+
//#endregion
|
|
927
|
+
//#region src/utils/formatQuery/defaultRuleGroupProcessorJSONata.ts
|
|
928
|
+
/**
|
|
929
|
+
* Rule group processor used by {@link formatQuery} for "jsonata" format.
|
|
930
|
+
*
|
|
931
|
+
* @group Export
|
|
932
|
+
*/
|
|
933
|
+
const defaultRuleGroupProcessorJSONata = (ruleGroup, options) => {
|
|
934
|
+
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
935
|
+
const processRuleGroup = (rg, outermost) => {
|
|
936
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermost ? fallbackExpression : "";
|
|
937
|
+
const expression = rg.rules.map((rule) => {
|
|
938
|
+
if (typeof rule === "string") return rule;
|
|
939
|
+
if (isRuleGroup(rule)) return processRuleGroup(rule);
|
|
940
|
+
const [validationResult, fieldValidator] = validateRule(rule);
|
|
941
|
+
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || placeholderValueName !== void 0 && rule.value === placeholderValueName) return "";
|
|
942
|
+
const fieldData = getOption(fields, rule.field);
|
|
943
|
+
return ruleProcessor(rule, {
|
|
944
|
+
...options,
|
|
945
|
+
parseNumbers: getParseNumberBoolean(fieldData?.inputType),
|
|
946
|
+
escapeQuotes: (rule.valueSource ?? "value") === "value",
|
|
947
|
+
fieldData
|
|
948
|
+
});
|
|
949
|
+
}).filter(Boolean).join(isRuleGroupType(rg) ? ` ${rg.combinator} ` : " ");
|
|
950
|
+
const [prefix, suffix] = rg.not || !outermost ? [`${rg.not ? "$not" : ""}(`, ")"] : ["", ""];
|
|
951
|
+
return expression ? `${prefix}${expression}${suffix}` : fallbackExpression;
|
|
952
|
+
};
|
|
953
|
+
return processRuleGroup(ruleGroup, true);
|
|
954
|
+
};
|
|
955
|
+
|
|
956
|
+
//#endregion
|
|
957
|
+
//#region src/utils/formatQuery/defaultRuleGroupProcessorJsonLogic.ts
|
|
958
|
+
/**
|
|
959
|
+
* Rule group processor used by {@link formatQuery} for "jsonlogic" format.
|
|
960
|
+
*
|
|
961
|
+
* @group Export
|
|
962
|
+
*/
|
|
963
|
+
const defaultRuleGroupProcessorJsonLogic = (ruleGroup, options) => {
|
|
964
|
+
const { fields, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
965
|
+
const query = convertFromIC(ruleGroup);
|
|
966
|
+
const processRuleGroup = (rg, _outermost) => {
|
|
967
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return false;
|
|
968
|
+
const processedRules = rg.rules.map((rule) => {
|
|
969
|
+
if (isRuleGroup(rule)) return processRuleGroup(rule);
|
|
970
|
+
const [validationResult, fieldValidator] = validateRule(rule);
|
|
971
|
+
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || placeholderValueName !== void 0 && rule.value === placeholderValueName) return false;
|
|
972
|
+
const fieldData = getOption(fields, rule.field);
|
|
973
|
+
return ruleProcessor(rule, {
|
|
974
|
+
...options,
|
|
975
|
+
parseNumbers: getParseNumberBoolean(fieldData?.inputType),
|
|
976
|
+
fieldData
|
|
977
|
+
});
|
|
978
|
+
}).filter(Boolean);
|
|
979
|
+
if (processedRules.length === 0) return false;
|
|
980
|
+
const jsonRuleGroup = { [rg.combinator]: processedRules };
|
|
981
|
+
return rg.not ? { "!": jsonRuleGroup } : jsonRuleGroup;
|
|
982
|
+
};
|
|
983
|
+
return processRuleGroup(query, true);
|
|
984
|
+
};
|
|
985
|
+
|
|
986
|
+
//#endregion
|
|
987
|
+
//#region src/utils/formatQuery/defaultRuleGroupProcessorLDAP.ts
|
|
988
|
+
/**
|
|
989
|
+
* Rule group processor used by {@link formatQuery} for "ldap" format.
|
|
990
|
+
*
|
|
991
|
+
* @group Export
|
|
992
|
+
*/
|
|
993
|
+
const defaultRuleGroupProcessorLDAP = (ruleGroup, options) => {
|
|
994
|
+
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
995
|
+
const query = convertFromIC(ruleGroup);
|
|
996
|
+
const processRuleGroup = (rg, outermost) => {
|
|
997
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermost ? fallbackExpression : "";
|
|
998
|
+
const rules = rg.rules.map((rule) => {
|
|
999
|
+
if (isRuleGroup(rule)) return processRuleGroup(rule);
|
|
1000
|
+
const [validationResult, fieldValidator] = validateRule(rule);
|
|
1001
|
+
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || placeholderValueName !== void 0 && rule.value === placeholderValueName) return "";
|
|
1002
|
+
const fieldData = getOption(fields, rule.field);
|
|
1003
|
+
return ruleProcessor(rule, {
|
|
1004
|
+
...options,
|
|
1005
|
+
parseNumbers: getParseNumberBoolean(fieldData?.inputType),
|
|
1006
|
+
escapeQuotes: (rule.valueSource ?? "value") === "value",
|
|
1007
|
+
fieldData
|
|
1008
|
+
});
|
|
1009
|
+
}).filter(Boolean);
|
|
1010
|
+
const expression = rules.join("");
|
|
1011
|
+
const [notPrefix, notSuffix] = rg.not ? ["(!", ")"] : ["", ""];
|
|
1012
|
+
const [prefix, suffix] = rules.length > 1 ? [`${notPrefix}(${rg.combinator === "or" ? "|" : "&"}`, `)${notSuffix}`] : [notPrefix, notSuffix];
|
|
1013
|
+
return expression ? `${prefix}${expression}${suffix}` : fallbackExpression;
|
|
1014
|
+
};
|
|
1015
|
+
return processRuleGroup(query, true);
|
|
1016
|
+
};
|
|
1017
|
+
|
|
1018
|
+
//#endregion
|
|
1019
|
+
//#region src/utils/formatQuery/defaultRuleGroupProcessorMongoDB.ts
|
|
1020
|
+
const isBracketed = (str) => str.startsWith("{") && str.endsWith("}");
|
|
1021
|
+
/**
|
|
1022
|
+
* Rule group processor used by {@link formatQuery} for "mongodb" format.
|
|
1023
|
+
*
|
|
1024
|
+
* Note that the "mongodb" format is deprecated in favor of the "mongodb_query" format.
|
|
1025
|
+
*
|
|
1026
|
+
* @group Export
|
|
1027
|
+
*/
|
|
1028
|
+
const defaultRuleGroupProcessorMongoDB = (ruleGroup, options, meta) => {
|
|
1029
|
+
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
1030
|
+
const processRuleGroup = (rg, outermost) => {
|
|
1031
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermost ? fallbackExpression : "";
|
|
1032
|
+
const combinator = `"$${lc(rg.combinator)}"`;
|
|
1033
|
+
let hasChildRules = false;
|
|
1034
|
+
const expressions = rg.rules.map((rule) => {
|
|
1035
|
+
if (isRuleGroup(rule)) {
|
|
1036
|
+
const processedRuleGroup = processRuleGroup(rule);
|
|
1037
|
+
if (processedRuleGroup) {
|
|
1038
|
+
hasChildRules = true;
|
|
1039
|
+
return isBracketed(processedRuleGroup) ? processedRuleGroup : `{${processedRuleGroup}}`;
|
|
1040
|
+
}
|
|
1041
|
+
return "";
|
|
1042
|
+
}
|
|
1043
|
+
const [validationResult, fieldValidator] = validateRule(rule);
|
|
1044
|
+
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || placeholderValueName !== void 0 && rule.value === placeholderValueName) return "";
|
|
1045
|
+
const fieldData = getOption(fields, rule.field);
|
|
1046
|
+
return ruleProcessor(rule, {
|
|
1047
|
+
...options,
|
|
1048
|
+
parseNumbers: getParseNumberBoolean(fieldData?.inputType),
|
|
1049
|
+
fieldData
|
|
1050
|
+
}, meta);
|
|
1051
|
+
}).filter(Boolean);
|
|
1052
|
+
return expressions.length > 0 ? expressions.length === 1 && !hasChildRules ? expressions[0] : `${combinator}:[${expressions.join(",")}]` : fallbackExpression;
|
|
1053
|
+
};
|
|
1054
|
+
const processedQuery = processRuleGroup(convertFromIC(ruleGroup), true);
|
|
1055
|
+
return isBracketed(processedQuery) ? processedQuery : `{${processedQuery}}`;
|
|
1056
|
+
};
|
|
1057
|
+
|
|
1058
|
+
//#endregion
|
|
1059
|
+
//#region src/utils/formatQuery/defaultRuleGroupProcessorNL.ts
|
|
1060
|
+
/**
|
|
1061
|
+
* Rule group processor used by {@link formatQuery} for "natural_language" format.
|
|
1062
|
+
*
|
|
1063
|
+
* @group Export
|
|
1064
|
+
*/
|
|
1065
|
+
const defaultRuleGroupProcessorNL = (ruleGroup, options) => {
|
|
1066
|
+
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, translations, validateRule, validationMap } = options;
|
|
1067
|
+
const processRuleGroup = (rg, outermostOrLonelyInGroup) => {
|
|
1068
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermostOrLonelyInGroup ? fallbackExpression : "";
|
|
1069
|
+
const rg2 = isRuleGroupTypeIC(rg) && rg.rules.some((r) => typeof r === "string" && lc(r) === "xor") ? convertFromIC(rg) : rg;
|
|
1070
|
+
const processedRules = rg2.rules.map((rule) => {
|
|
1071
|
+
if (typeof rule === "string") return `, ${translations[rule] ?? rule} `;
|
|
1072
|
+
if (isRuleGroup(rule)) return processRuleGroup(rule, rg2.rules.length === 1 && !(rg2.not || /^xor$/i.test(rg2.combinator ?? "")));
|
|
1073
|
+
const [validationResult, fieldValidator] = validateRule(rule);
|
|
1074
|
+
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || placeholderValueName !== void 0 && rule.value === placeholderValueName) return "";
|
|
1075
|
+
const escapeQuotes = (rule.valueSource ?? "value") === "value";
|
|
1076
|
+
const fieldData = getOption(fields, rule.field);
|
|
1077
|
+
return ruleProcessor(rule, {
|
|
1078
|
+
...options,
|
|
1079
|
+
parseNumbers: getParseNumberBoolean(fieldData?.inputType),
|
|
1080
|
+
escapeQuotes,
|
|
1081
|
+
fieldData
|
|
1082
|
+
});
|
|
1083
|
+
});
|
|
1084
|
+
if (processedRules.length === 0) return fallbackExpression;
|
|
1085
|
+
const isXOR = lc(rg2.combinator ?? "") === "xor";
|
|
1086
|
+
const combinator = isXOR ? rg2.combinator.slice(1) : rg2.combinator;
|
|
1087
|
+
const mustWrap = rg2.not || !outermostOrLonelyInGroup || isXOR && processedRules.length > 1;
|
|
1088
|
+
const [prefixTL, suffixTL] = ["groupPrefix", "groupSuffix"].map((key) => rg2.not ? isXOR ? getNLTranslataion(key, translations, ["not", "xor"]) : getNLTranslataion(key, translations, ["not"]) : isXOR ? getNLTranslataion(key, translations, ["xor"]) : getNLTranslataion(key, translations));
|
|
1089
|
+
const prefix = mustWrap ? `${prefixTL} (`.trim() : "";
|
|
1090
|
+
const suffix = mustWrap ? `) ${suffixTL}`.trim() : "";
|
|
1091
|
+
return `${prefix}${processedRules.filter(Boolean).join(isRuleGroupType(rg2) ? `, ${translations[combinator] ?? combinator} ` : "")}${suffix}`;
|
|
1092
|
+
};
|
|
1093
|
+
return processRuleGroup(ruleGroup, true);
|
|
1094
|
+
};
|
|
1095
|
+
|
|
1096
|
+
//#endregion
|
|
1097
|
+
//#region src/utils/formatQuery/defaultRuleGroupProcessorParameterized.ts
|
|
1098
|
+
/**
|
|
1099
|
+
* Rule group processor used by {@link formatQuery} for "parameterized" and
|
|
1100
|
+
* "parameterized_named" formats.
|
|
1101
|
+
*
|
|
1102
|
+
* @group Export
|
|
1103
|
+
*/
|
|
1104
|
+
const defaultRuleGroupProcessorParameterized = (ruleGroup, options) => {
|
|
1105
|
+
const { format, fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
1106
|
+
const parameterized = format === "parameterized";
|
|
1107
|
+
const params = [];
|
|
1108
|
+
const paramsNamed = {};
|
|
1109
|
+
const fieldParams = /* @__PURE__ */ new Map();
|
|
1110
|
+
const getNextNamedParam = (field) => {
|
|
1111
|
+
if (!fieldParams.has(field)) fieldParams.set(field, /* @__PURE__ */ new Set());
|
|
1112
|
+
const nextNamedParam = `${field}_${fieldParams.get(field).size + 1}`;
|
|
1113
|
+
fieldParams.get(field).add(nextNamedParam);
|
|
1114
|
+
return nextNamedParam;
|
|
1115
|
+
};
|
|
1116
|
+
const processRule = (rule) => {
|
|
1117
|
+
const [validationResult, fieldValidator] = validateRule(rule);
|
|
1118
|
+
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || placeholderValueName !== void 0 && rule.value === placeholderValueName) return "";
|
|
1119
|
+
const fieldData = getOption(fields, rule.field);
|
|
1120
|
+
const fieldParamNames = Object.fromEntries([...fieldParams.entries()].map(([f, s]) => [f, [...s]]));
|
|
1121
|
+
const processedRule = ruleProcessor(rule, {
|
|
1122
|
+
...options,
|
|
1123
|
+
parseNumbers: getParseNumberBoolean(fieldData?.inputType),
|
|
1124
|
+
getNextNamedParam,
|
|
1125
|
+
fieldParamNames,
|
|
1126
|
+
fieldData
|
|
1127
|
+
}, { processedParams: params });
|
|
1128
|
+
if (!isPojo(processedRule)) return "";
|
|
1129
|
+
const { sql, params: customParams } = processedRule;
|
|
1130
|
+
if (typeof sql !== "string" || !sql) return "";
|
|
1131
|
+
// istanbul ignore else
|
|
1132
|
+
if (format === "parameterized" && Array.isArray(customParams)) params.push(...customParams);
|
|
1133
|
+
else if (format === "parameterized_named" && isPojo(customParams)) {
|
|
1134
|
+
Object.assign(paramsNamed, customParams);
|
|
1135
|
+
for (const p of Object.keys(customParams)) fieldParams.get(rule.field)?.add(p);
|
|
1136
|
+
}
|
|
1137
|
+
return sql;
|
|
1138
|
+
};
|
|
1139
|
+
const processRuleGroup = (rg, outermostOrLonelyInGroup) => {
|
|
1140
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermostOrLonelyInGroup ? fallbackExpression : "";
|
|
1141
|
+
const processedRules = rg.rules.map((rule) => {
|
|
1142
|
+
if (typeof rule === "string") return rule;
|
|
1143
|
+
if (isRuleGroup(rule)) return processRuleGroup(rule, rg.rules.length === 1);
|
|
1144
|
+
return processRule(rule);
|
|
1145
|
+
}).filter(Boolean);
|
|
1146
|
+
if (processedRules.length === 0) return fallbackExpression;
|
|
1147
|
+
return `${rg.not ? "NOT " : ""}(${processedRules.join(isRuleGroupType(rg) ? ` ${rg.combinator} ` : " ")})`;
|
|
1148
|
+
};
|
|
1149
|
+
if (parameterized) return {
|
|
1150
|
+
sql: processRuleGroup(ruleGroup, true),
|
|
1151
|
+
params
|
|
1152
|
+
};
|
|
1153
|
+
return {
|
|
1154
|
+
sql: processRuleGroup(ruleGroup, true),
|
|
1155
|
+
params: paramsNamed
|
|
1156
|
+
};
|
|
1157
|
+
};
|
|
1158
|
+
|
|
1159
|
+
//#endregion
|
|
1160
|
+
//#region src/utils/formatQuery/defaultRuleGroupProcessorPrisma.ts
|
|
1161
|
+
/**
|
|
1162
|
+
* Default fallback object used by {@link formatQuery} for "prisma" format.
|
|
1163
|
+
*
|
|
1164
|
+
* @group Export
|
|
1165
|
+
*/
|
|
1166
|
+
const prismaFallback = {};
|
|
1167
|
+
/**
|
|
1168
|
+
* Rule group processor used by {@link formatQuery} for "prisma" format.
|
|
1169
|
+
*
|
|
1170
|
+
* @group Export
|
|
1171
|
+
*/
|
|
1172
|
+
const defaultRuleGroupProcessorPrisma = (ruleGroup, options) => {
|
|
1173
|
+
const { fields, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
1174
|
+
const processRuleGroup = (rg, outermost) => {
|
|
1175
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermost ? prismaFallback : void 0;
|
|
1176
|
+
const combinator = rg.combinator.toUpperCase();
|
|
1177
|
+
let hasChildRules = false;
|
|
1178
|
+
const expressions = rg.rules.map((rule) => {
|
|
1179
|
+
if (isRuleGroup(rule)) {
|
|
1180
|
+
const processedRuleGroup = processRuleGroup(rule);
|
|
1181
|
+
if (processedRuleGroup) {
|
|
1182
|
+
hasChildRules = true;
|
|
1183
|
+
return processedRuleGroup;
|
|
1184
|
+
}
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
const [validationResult, fieldValidator] = validateRule(rule);
|
|
1188
|
+
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || placeholderValueName !== void 0 && rule.value === placeholderValueName) return;
|
|
1189
|
+
const fieldData = getOption(fields, rule.field);
|
|
1190
|
+
return ruleProcessor(rule, {
|
|
1191
|
+
...options,
|
|
1192
|
+
parseNumbers: getParseNumberBoolean(fieldData?.inputType),
|
|
1193
|
+
fieldData
|
|
1194
|
+
});
|
|
1195
|
+
}).filter(Boolean);
|
|
1196
|
+
return expressions.length > 0 ? expressions.length === 1 && !hasChildRules ? expressions[0] : { [combinator]: expressions } : prismaFallback;
|
|
1197
|
+
};
|
|
1198
|
+
const result = processRuleGroup(convertFromIC(ruleGroup), true);
|
|
1199
|
+
return ruleGroup.not ? { NOT: result } : result;
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1202
|
+
//#endregion
|
|
1203
|
+
//#region src/utils/formatQuery/defaultRuleGroupProcessorSequelize.ts
|
|
1204
|
+
/**
|
|
1205
|
+
* Rule group processor used by {@link formatQuery} for "sequelize" format.
|
|
1206
|
+
*
|
|
1207
|
+
* @group Export
|
|
1208
|
+
*/
|
|
1209
|
+
const defaultRuleGroupProcessorSequelize = (ruleGroup, options) => {
|
|
1210
|
+
// istanbul ignore next
|
|
1211
|
+
const { fields, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap, context = {} } = options;
|
|
1212
|
+
const { sequelizeOperators: Op } = context;
|
|
1213
|
+
if (!Op) return;
|
|
1214
|
+
const processRuleGroup = (rg, _outermost) => {
|
|
1215
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return;
|
|
1216
|
+
const combinator = rg.combinator.toUpperCase();
|
|
1217
|
+
let hasChildRules = false;
|
|
1218
|
+
const expressions = rg.rules.map((rule) => {
|
|
1219
|
+
if (isRuleGroup(rule)) {
|
|
1220
|
+
const processedRuleGroup = processRuleGroup(rule);
|
|
1221
|
+
if (processedRuleGroup) {
|
|
1222
|
+
hasChildRules = true;
|
|
1223
|
+
return processedRuleGroup;
|
|
1224
|
+
}
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
const [validationResult, fieldValidator] = validateRule(rule);
|
|
1228
|
+
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || placeholderValueName !== void 0 && rule.value === placeholderValueName) return;
|
|
1229
|
+
const fieldData = getOption(fields, rule.field);
|
|
1230
|
+
return ruleProcessor(rule, {
|
|
1231
|
+
...options,
|
|
1232
|
+
parseNumbers: getParseNumberBoolean(fieldData?.inputType),
|
|
1233
|
+
fieldData
|
|
1234
|
+
});
|
|
1235
|
+
}).filter(Boolean);
|
|
1236
|
+
if (expressions.length === 0) return;
|
|
1237
|
+
const result = expressions.length === 1 && !hasChildRules ? expressions[0] : { [lc(combinator) === "or" ? Op.or : Op.and]: expressions };
|
|
1238
|
+
return rg.not ? { [Op.not]: result } : result;
|
|
1239
|
+
};
|
|
1240
|
+
return processRuleGroup(convertFromIC(ruleGroup), true);
|
|
1241
|
+
};
|
|
1242
|
+
|
|
1243
|
+
//#endregion
|
|
1244
|
+
//#region src/utils/formatQuery/defaultRuleGroupProcessorSQL.ts
|
|
1245
|
+
/**
|
|
1246
|
+
* Default rule processor used by {@link formatQuery} for "sql" format.
|
|
1247
|
+
*
|
|
1248
|
+
* @group Export
|
|
1249
|
+
*/
|
|
1250
|
+
const defaultRuleGroupProcessorSQL = (ruleGroup, options) => {
|
|
1251
|
+
const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options;
|
|
1252
|
+
const processRuleGroup = (rg, outermostOrLonelyInGroup) => {
|
|
1253
|
+
if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? ""])) return outermostOrLonelyInGroup ? fallbackExpression : "";
|
|
1254
|
+
const processedRules = rg.rules.map((rule) => {
|
|
1255
|
+
if (typeof rule === "string") return rule;
|
|
1256
|
+
if (isRuleGroup(rule)) return processRuleGroup(rule, rg.rules.length === 1);
|
|
1257
|
+
const [validationResult, fieldValidator] = validateRule(rule);
|
|
1258
|
+
if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || placeholderValueName !== void 0 && rule.value === placeholderValueName) return "";
|
|
1259
|
+
const escapeQuotes = (rule.valueSource ?? "value") === "value";
|
|
1260
|
+
const fieldData = getOption(fields, rule.field);
|
|
1261
|
+
return ruleProcessor(rule, {
|
|
1262
|
+
...options,
|
|
1263
|
+
parseNumbers: getParseNumberBoolean(fieldData?.inputType),
|
|
1264
|
+
escapeQuotes,
|
|
1265
|
+
fieldData
|
|
1266
|
+
});
|
|
1267
|
+
}).filter(Boolean);
|
|
1268
|
+
if (processedRules.length === 0) return fallbackExpression;
|
|
1269
|
+
return `${rg.not ? "NOT " : ""}(${processedRules.join(isRuleGroupType(rg) ? ` ${rg.combinator} ` : " ")})`;
|
|
1270
|
+
};
|
|
1271
|
+
return processRuleGroup(ruleGroup, true);
|
|
1272
|
+
};
|
|
1273
|
+
|
|
1274
|
+
//#endregion
|
|
1275
|
+
//#region src/utils/formatQuery/defaultRuleProcessorElasticSearch.ts
|
|
1276
|
+
const rangeOperatorMap = {
|
|
1277
|
+
"<": "lt",
|
|
1278
|
+
"<=": "lte",
|
|
1279
|
+
">": "gt",
|
|
1280
|
+
">=": "gte"
|
|
1281
|
+
};
|
|
1282
|
+
const negateIfNotOp$1 = (op, elasticSearchRule) => op.startsWith("not") || op.startsWith("doesnot") ? { bool: { must_not: elasticSearchRule } } : elasticSearchRule;
|
|
1283
|
+
const escapeSQ = (s) => s?.replace(/('|\\)/g, `\\$1`);
|
|
1284
|
+
const textFunctionMap = {
|
|
1285
|
+
beginswith: "startsWith",
|
|
1286
|
+
doesnotbeginwith: "startsWith",
|
|
1287
|
+
doesnotcontain: "contains",
|
|
1288
|
+
doesnotendwith: "endsWith",
|
|
1289
|
+
endswith: "endsWith"
|
|
1290
|
+
};
|
|
1291
|
+
const getTextScript = (f, o, v) => {
|
|
1292
|
+
const script = `doc['${f}'].value.${textFunctionMap[o] ?? o}(doc['${v}'].value)`;
|
|
1293
|
+
return o.startsWith("d") ? `!${script}` : script;
|
|
1294
|
+
};
|
|
1295
|
+
const valueRenderer = (v, parseNumbers) => typeof v === "boolean" ? v : shouldRenderAsNumber(v, parseNumbers) ? parseNumber(v, { parseNumbers }) : v;
|
|
1296
|
+
/**
|
|
1297
|
+
* Default rule processor used by {@link formatQuery} for "elasticsearch" format.
|
|
1298
|
+
*
|
|
1299
|
+
* @group Export
|
|
1300
|
+
*/
|
|
1301
|
+
const defaultRuleProcessorElasticSearch = (rule, options = {}) => {
|
|
1302
|
+
const { field, operator, value, valueSource } = rule;
|
|
1303
|
+
const { parseNumbers, preserveValueOrder } = options;
|
|
1304
|
+
const operatorLC = lc(operator);
|
|
1305
|
+
if (processMatchMode(rule)) return false;
|
|
1306
|
+
if (valueSource === "field") {
|
|
1307
|
+
if (toArray(value).some((v) => typeof v !== "string")) return false;
|
|
1308
|
+
const fieldForScript = escapeSQ(field);
|
|
1309
|
+
switch (operatorLC) {
|
|
1310
|
+
case "=":
|
|
1311
|
+
case "!=":
|
|
1312
|
+
case ">":
|
|
1313
|
+
case ">=":
|
|
1314
|
+
case "<":
|
|
1315
|
+
case "<=": {
|
|
1316
|
+
const operatorForScript = operatorLC === "=" ? "==" : operatorLC;
|
|
1317
|
+
const valueForScript = escapeSQ(value);
|
|
1318
|
+
return valueForScript ? { bool: { filter: { script: { script: `doc['${fieldForScript}'].value ${operatorForScript} doc['${valueForScript}'].value` } } } } : false;
|
|
1319
|
+
}
|
|
1320
|
+
case "in":
|
|
1321
|
+
case "notin": {
|
|
1322
|
+
const valueAsArray = toArray(value);
|
|
1323
|
+
if (valueAsArray.length > 0) {
|
|
1324
|
+
const arr = valueAsArray.map((v) => ({ bool: { filter: { script: { script: `doc['${fieldForScript}'].value == doc['${v}'].value` } } } }));
|
|
1325
|
+
return { bool: operatorLC === "in" ? { should: arr } : { must_not: arr } };
|
|
1326
|
+
}
|
|
1327
|
+
return false;
|
|
1328
|
+
}
|
|
1329
|
+
case "between":
|
|
1330
|
+
case "notbetween": {
|
|
1331
|
+
const valueAsArray = toArray(value);
|
|
1332
|
+
if (valueAsArray.length >= 2 && valueAsArray[0] && valueAsArray[1]) {
|
|
1333
|
+
const script = `doc['${fieldForScript}'].value >= doc['${valueAsArray[0]}'].value && doc['${fieldForScript}'].value <= doc['${valueAsArray[1]}'].value`;
|
|
1334
|
+
return { bool: { filter: { script: { script: operatorLC === "notbetween" ? `!(${script})` : script } } } };
|
|
1335
|
+
}
|
|
1336
|
+
return false;
|
|
1337
|
+
}
|
|
1338
|
+
case "contains":
|
|
1339
|
+
case "doesnotcontain":
|
|
1340
|
+
case "beginswith":
|
|
1341
|
+
case "doesnotbeginwith":
|
|
1342
|
+
case "endswith":
|
|
1343
|
+
case "doesnotendwith": {
|
|
1344
|
+
const valueForScript = escapeSQ(value);
|
|
1345
|
+
if (!valueForScript) return false;
|
|
1346
|
+
return { bool: { filter: { script: { script: getTextScript(fieldForScript, operatorLC, valueForScript) } } } };
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
switch (operatorLC) {
|
|
1351
|
+
case "<":
|
|
1352
|
+
case "<=":
|
|
1353
|
+
case ">":
|
|
1354
|
+
case ">=": return { range: { [field]: { [rangeOperatorMap[operatorLC]]: valueRenderer(value, parseNumbers) } } };
|
|
1355
|
+
case "=": return { term: { [field]: valueRenderer(value, parseNumbers) } };
|
|
1356
|
+
case "!=": return { bool: { must_not: { term: { [field]: valueRenderer(value, parseNumbers) } } } };
|
|
1357
|
+
case "null": return { bool: { must_not: { exists: { field } } } };
|
|
1358
|
+
case "notnull": return { exists: { field } };
|
|
1359
|
+
case "in":
|
|
1360
|
+
case "notin": {
|
|
1361
|
+
const valueAsArray = toArray(value).map((v) => valueRenderer(v, parseNumbers));
|
|
1362
|
+
if (valueAsArray.length > 0) {
|
|
1363
|
+
const arr = valueAsArray.map((v) => ({ term: { [field]: valueRenderer(v, parseNumbers) } }));
|
|
1364
|
+
return { bool: operatorLC === "in" ? { should: arr } : { must_not: arr } };
|
|
1365
|
+
}
|
|
1366
|
+
return false;
|
|
1367
|
+
}
|
|
1368
|
+
case "between":
|
|
1369
|
+
case "notbetween": {
|
|
1370
|
+
const valueAsArray = toArray(value);
|
|
1371
|
+
if (valueAsArray.length >= 2 && isValidValue(valueAsArray[0]) && isValidValue(valueAsArray[1])) {
|
|
1372
|
+
let [first, second] = valueAsArray;
|
|
1373
|
+
const shouldParseNumbers = !(parseNumbers === false);
|
|
1374
|
+
if (shouldRenderAsNumber(first, shouldParseNumbers) && shouldRenderAsNumber(second, shouldParseNumbers)) {
|
|
1375
|
+
const firstNum = parseNumber(first, { parseNumbers: shouldParseNumbers });
|
|
1376
|
+
const secondNum = parseNumber(second, { parseNumbers: shouldParseNumbers });
|
|
1377
|
+
if (!preserveValueOrder && secondNum < firstNum) {
|
|
1378
|
+
const tempNum = secondNum;
|
|
1379
|
+
second = firstNum;
|
|
1380
|
+
first = tempNum;
|
|
1381
|
+
} else {
|
|
1382
|
+
first = firstNum;
|
|
1383
|
+
second = secondNum;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
return negateIfNotOp$1(operatorLC, { range: { [field]: {
|
|
1387
|
+
gte: first,
|
|
1388
|
+
lte: second
|
|
1389
|
+
} } });
|
|
1390
|
+
}
|
|
1391
|
+
return false;
|
|
1392
|
+
}
|
|
1393
|
+
case "contains":
|
|
1394
|
+
case "doesnotcontain": return negateIfNotOp$1(operatorLC, { regexp: { [field]: { value: `.*${value}.*` } } });
|
|
1395
|
+
case "beginswith":
|
|
1396
|
+
case "doesnotbeginwith": return negateIfNotOp$1(operatorLC, { regexp: { [field]: { value: `${value}.*` } } });
|
|
1397
|
+
case "endswith":
|
|
1398
|
+
case "doesnotendwith": return negateIfNotOp$1(operatorLC, { regexp: { [field]: { value: `.*${value}` } } });
|
|
1399
|
+
}
|
|
1400
|
+
return false;
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
//#endregion
|
|
1404
|
+
//#region src/utils/formatQuery/defaultRuleProcessorJSONata.ts
|
|
1405
|
+
const shouldNegate = (op) => op.startsWith("not") || op.startsWith("doesnot");
|
|
1406
|
+
const quote = (v, escapeQuotes) => `"${typeof v !== "string" || !escapeQuotes ? `${v}` : v.replaceAll(`"`, `\\"`)}"`;
|
|
1407
|
+
const negate = (clause, negate$1) => negate$1 ? `$not(${clause})` : clause;
|
|
1408
|
+
const escapeStringRegex = (s) => `${s}`.replaceAll(/[/$()*+.?[\\\]^{|}]/g, String.raw`\$&`).replaceAll("-", String.raw`\x2d`);
|
|
1409
|
+
/**
|
|
1410
|
+
* Default rule processor used by {@link formatQuery} for "jsonata" format.
|
|
1411
|
+
*
|
|
1412
|
+
* @group Export
|
|
1413
|
+
*/
|
|
1414
|
+
const defaultRuleProcessorJSONata = (rule, options = {}) => {
|
|
1415
|
+
const { field, operator, value, valueSource } = rule;
|
|
1416
|
+
const { escapeQuotes, parseNumbers, preserveValueOrder, quoteFieldNamesWith = ["", ""], fieldIdentifierSeparator = "" } = options;
|
|
1417
|
+
const valueIsField = valueSource === "field";
|
|
1418
|
+
const useBareValue = typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || shouldRenderAsNumber(value, parseNumbers);
|
|
1419
|
+
const qfn = (f) => getQuotedFieldName(f, {
|
|
1420
|
+
quoteFieldNamesWith,
|
|
1421
|
+
fieldIdentifierSeparator
|
|
1422
|
+
});
|
|
1423
|
+
const matchEval = processMatchMode(rule);
|
|
1424
|
+
if (matchEval === false) return;
|
|
1425
|
+
else if (matchEval) {
|
|
1426
|
+
const { mode, threshold } = matchEval;
|
|
1427
|
+
const totalCount = `$count(${qfn(field)})`;
|
|
1428
|
+
const filteredCount = `$count($filter(${qfn(field)}, function($v) {${defaultRuleGroupProcessorJSONata(transformQuery(value, { ruleProcessor: (r) => ({
|
|
1429
|
+
...r,
|
|
1430
|
+
field: r.field ? `$v.${r.field}` : "$v"
|
|
1431
|
+
}) }), options)}}))`;
|
|
1432
|
+
switch (mode) {
|
|
1433
|
+
case "all": return `${filteredCount} = ${totalCount}`;
|
|
1434
|
+
case "none": return `${filteredCount} = 0`;
|
|
1435
|
+
case "some": return `${filteredCount} > 0`;
|
|
1436
|
+
case "atleast":
|
|
1437
|
+
case "atmost":
|
|
1438
|
+
case "exactly": {
|
|
1439
|
+
const op = mode === "atleast" ? ">=" : mode === "atmost" ? "<=" : "=";
|
|
1440
|
+
if (threshold > 0 && threshold < 1) return `${filteredCount} ${op} (${totalCount} * ${threshold})`;
|
|
1441
|
+
return `${filteredCount} ${op} ${threshold}`;
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
const operatorLC = lc(operator);
|
|
1446
|
+
switch (operatorLC) {
|
|
1447
|
+
case "<":
|
|
1448
|
+
case "<=":
|
|
1449
|
+
case "=":
|
|
1450
|
+
case "!=":
|
|
1451
|
+
case ">":
|
|
1452
|
+
case ">=": return `${qfn(field)} ${operatorLC} ${valueIsField ? qfn(trimIfString(value)) : useBareValue ? trimIfString(value) : quote(value, escapeQuotes)}`;
|
|
1453
|
+
case "contains":
|
|
1454
|
+
case "doesnotcontain": return negate(`$contains(${qfn(field)}, ${valueIsField ? qfn(trimIfString(value)) : quote(value, escapeQuotes)})`, shouldNegate(operatorLC));
|
|
1455
|
+
case "beginswith":
|
|
1456
|
+
case "doesnotbeginwith": return negate(valueIsField ? `$substring(${qfn(field)}, 0, $length(${qfn(trimIfString(value))})) = ${qfn(trimIfString(value))}` : `$contains(${qfn(field)}, /^${escapeStringRegex(value)}/)`, shouldNegate(operatorLC));
|
|
1457
|
+
case "endswith":
|
|
1458
|
+
case "doesnotendwith": return negate(valueIsField ? `$substring(${qfn(field)}, $length(${qfn(field)}) - $length(${qfn(trimIfString(value))})) = ${qfn(trimIfString(value))}` : `$contains(${qfn(field)}, /${escapeStringRegex(value)}$/)`, shouldNegate(operatorLC));
|
|
1459
|
+
case "null": return `${qfn(field)} = null`;
|
|
1460
|
+
case "notnull": return `${qfn(field)} != null`;
|
|
1461
|
+
case "in":
|
|
1462
|
+
case "notin": {
|
|
1463
|
+
const valueAsArray = toArray(value);
|
|
1464
|
+
return negate(`${qfn(field)} in [${valueAsArray.map((val) => valueIsField ? `${qfn(trimIfString(val))}` : shouldRenderAsNumber(val, parseNumbers) ? `${trimIfString(val)}` : quote(val, escapeQuotes)).join(", ")}]`, shouldNegate(operatorLC));
|
|
1465
|
+
}
|
|
1466
|
+
case "between":
|
|
1467
|
+
case "notbetween": {
|
|
1468
|
+
const valueAsArray = toArray(value);
|
|
1469
|
+
if (valueAsArray.length < 2 || nullOrUndefinedOrEmpty(valueAsArray[0]) || nullOrUndefinedOrEmpty(valueAsArray[1])) return "";
|
|
1470
|
+
const [first, second] = valueAsArray;
|
|
1471
|
+
const shouldParseNumbers = !(parseNumbers === false);
|
|
1472
|
+
const firstNum = shouldRenderAsNumber(first, shouldParseNumbers) ? parseNumber(first, { parseNumbers: shouldParseNumbers }) : NaN;
|
|
1473
|
+
const secondNum = shouldRenderAsNumber(second, shouldParseNumbers) ? parseNumber(second, { parseNumbers: shouldParseNumbers }) : NaN;
|
|
1474
|
+
let firstValue = Number.isNaN(firstNum) ? valueIsField ? `${first}` : first : firstNum;
|
|
1475
|
+
let secondValue = Number.isNaN(secondNum) ? valueIsField ? `${second}` : second : secondNum;
|
|
1476
|
+
if (!preserveValueOrder && firstValue === firstNum && secondValue === secondNum && secondNum < firstNum) {
|
|
1477
|
+
const tempNum = secondNum;
|
|
1478
|
+
secondValue = firstNum;
|
|
1479
|
+
firstValue = tempNum;
|
|
1480
|
+
}
|
|
1481
|
+
const renderAsNumbers = shouldRenderAsNumber(first, parseNumbers) && shouldRenderAsNumber(second, parseNumbers);
|
|
1482
|
+
const getValueString = (raw, val) => valueIsField ? qfn(raw) : renderAsNumbers ? val : quote(val, escapeQuotes);
|
|
1483
|
+
const expression = `${qfn(field)} >= ${getValueString(first, firstValue)} and ${qfn(field)} <= ${getValueString(second, secondValue)}`;
|
|
1484
|
+
return operatorLC === "between" ? `(${expression})` : negate(expression, true);
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
return "";
|
|
1488
|
+
};
|
|
1489
|
+
|
|
1490
|
+
//#endregion
|
|
1491
|
+
//#region src/utils/formatQuery/defaultRuleProcessorJsonLogic.ts
|
|
1492
|
+
const convertOperator = (op) => op.replace(/^(=)$/, "$1=").replace(/^notnull$/i, "!=").replace(/^null$/i, "==");
|
|
1493
|
+
const negateIfNotOp = (op, jsonRule) => op.startsWith("not") || op.startsWith("doesnot") ? { "!": jsonRule } : jsonRule;
|
|
1494
|
+
/**
|
|
1495
|
+
* Default rule processor used by {@link formatQuery} for "jsonlogic" format.
|
|
1496
|
+
*
|
|
1497
|
+
* @group Export
|
|
1498
|
+
*/
|
|
1499
|
+
const defaultRuleProcessorJsonLogic = (rule, options = {}) => {
|
|
1500
|
+
const { field, operator, value, valueSource } = rule;
|
|
1501
|
+
const { parseNumbers, preserveValueOrder } = options;
|
|
1502
|
+
const valueIsField = valueSource === "field";
|
|
1503
|
+
const fieldObject = { var: field };
|
|
1504
|
+
const fieldOrNumberRenderer = (v) => valueIsField ? { var: `${v}` } : shouldRenderAsNumber(v, parseNumbers) ? parseNumber(v, { parseNumbers }) : v;
|
|
1505
|
+
const matchEval = processMatchMode(rule);
|
|
1506
|
+
if (matchEval === false) return false;
|
|
1507
|
+
else if (matchEval) {
|
|
1508
|
+
const { mode, threshold } = matchEval;
|
|
1509
|
+
switch (mode) {
|
|
1510
|
+
case "all":
|
|
1511
|
+
case "none":
|
|
1512
|
+
case "some": return { [mode]: [{ var: field }, value.rules.length === 1 && !isRuleGroup(value.rules[0]) ? defaultRuleProcessorJsonLogic(value.rules[0], options) : defaultRuleGroupProcessorJsonLogic(value, options)] };
|
|
1513
|
+
case "atleast":
|
|
1514
|
+
case "atmost":
|
|
1515
|
+
case "exactly": {
|
|
1516
|
+
const op = mode === "atleast" ? ">=" : mode === "atmost" ? "<=" : "==";
|
|
1517
|
+
const filteredCount = { reduce: [
|
|
1518
|
+
{ filter: [{ var: field }, value.rules.length === 1 && !isRuleGroup(value.rules[0]) ? defaultRuleProcessorJsonLogic(value.rules[0], options) : defaultRuleGroupProcessorJsonLogic(value, options)] },
|
|
1519
|
+
{ "+": [1, { var: "accumulator" }] },
|
|
1520
|
+
0
|
|
1521
|
+
] };
|
|
1522
|
+
if (threshold > 0 && threshold < 1) {
|
|
1523
|
+
const totalCount = { reduce: [
|
|
1524
|
+
{ var: field },
|
|
1525
|
+
{ "+": [1, { var: "accumulator" }] },
|
|
1526
|
+
0
|
|
1527
|
+
] };
|
|
1528
|
+
return { [op]: [filteredCount, { "*": [totalCount, threshold] }] };
|
|
1529
|
+
}
|
|
1530
|
+
return { [op]: [filteredCount, threshold] };
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
const operatorLC = lc(operator);
|
|
1535
|
+
switch (operatorLC) {
|
|
1536
|
+
case "<":
|
|
1537
|
+
case "<=":
|
|
1538
|
+
case "=":
|
|
1539
|
+
case "!=":
|
|
1540
|
+
case ">":
|
|
1541
|
+
case ">=": return { [convertOperator(operatorLC)]: [fieldObject, fieldOrNumberRenderer(value)] };
|
|
1542
|
+
case "null":
|
|
1543
|
+
case "notnull": return { [`${operatorLC === "notnull" ? "!" : "="}=`]: [fieldObject, null] };
|
|
1544
|
+
case "in":
|
|
1545
|
+
case "notin": {
|
|
1546
|
+
const valueAsArray = toArray(value).map((v) => fieldOrNumberRenderer(v));
|
|
1547
|
+
return negateIfNotOp(operatorLC, { in: [fieldObject, valueAsArray] });
|
|
1548
|
+
}
|
|
1549
|
+
case "between":
|
|
1550
|
+
case "notbetween": {
|
|
1551
|
+
const valueAsArray = toArray(value);
|
|
1552
|
+
if (valueAsArray.length >= 2 && isValidValue(valueAsArray[0]) && isValidValue(valueAsArray[1])) {
|
|
1553
|
+
let [first, second] = valueAsArray;
|
|
1554
|
+
const shouldParseNumbers = !(parseNumbers === false);
|
|
1555
|
+
if (!valueIsField && shouldRenderAsNumber(first, shouldParseNumbers) && shouldRenderAsNumber(second, shouldParseNumbers)) {
|
|
1556
|
+
const firstNum = parseNumber(first, { parseNumbers: shouldParseNumbers });
|
|
1557
|
+
const secondNum = parseNumber(second, { parseNumbers: shouldParseNumbers });
|
|
1558
|
+
if (!preserveValueOrder && secondNum < firstNum) {
|
|
1559
|
+
const tempNum = secondNum;
|
|
1560
|
+
second = firstNum;
|
|
1561
|
+
first = tempNum;
|
|
1562
|
+
} else {
|
|
1563
|
+
first = firstNum;
|
|
1564
|
+
second = secondNum;
|
|
1565
|
+
}
|
|
1566
|
+
} else if (valueIsField) {
|
|
1567
|
+
first = { var: first };
|
|
1568
|
+
second = { var: second };
|
|
1569
|
+
}
|
|
1570
|
+
return negateIfNotOp(operatorLC, { "<=": [
|
|
1571
|
+
first,
|
|
1572
|
+
fieldObject,
|
|
1573
|
+
second
|
|
1574
|
+
] });
|
|
1575
|
+
}
|
|
1576
|
+
return false;
|
|
1577
|
+
}
|
|
1578
|
+
case "contains":
|
|
1579
|
+
case "doesnotcontain": {
|
|
1580
|
+
const jsonRule = { in: [fieldOrNumberRenderer(value), fieldObject] };
|
|
1581
|
+
return negateIfNotOp(operatorLC, jsonRule);
|
|
1582
|
+
}
|
|
1583
|
+
case "beginswith":
|
|
1584
|
+
case "doesnotbeginwith": {
|
|
1585
|
+
const jsonRule = { startsWith: [fieldObject, fieldOrNumberRenderer(value)] };
|
|
1586
|
+
return negateIfNotOp(operatorLC, jsonRule);
|
|
1587
|
+
}
|
|
1588
|
+
case "endswith":
|
|
1589
|
+
case "doesnotendwith": {
|
|
1590
|
+
const jsonRule = { endsWith: [fieldObject, fieldOrNumberRenderer(value)] };
|
|
1591
|
+
return negateIfNotOp(operatorLC, jsonRule);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
return false;
|
|
1595
|
+
};
|
|
1596
|
+
|
|
1597
|
+
//#endregion
|
|
1598
|
+
//#region src/utils/formatQuery/defaultRuleProcessorLDAP.ts
|
|
1599
|
+
const negateIf = (clause, negate$1) => negate$1 ? `(!${clause})` : `${clause}`;
|
|
1600
|
+
const ldapEscape = (s) => `${trimIfString(s)}`.replaceAll(/[()&|=<>~*\\/]/g, (m) => `\\${m.codePointAt(0).toString(16)}`);
|
|
1601
|
+
/**
|
|
1602
|
+
* Default rule processor used by {@link formatQuery} for "ldap" format.
|
|
1603
|
+
*
|
|
1604
|
+
* @group Export
|
|
1605
|
+
*/
|
|
1606
|
+
const defaultRuleProcessorLDAP = (rule, options = {}) => {
|
|
1607
|
+
const { field, operator, value, valueSource } = rule;
|
|
1608
|
+
const { preserveValueOrder } = options;
|
|
1609
|
+
const operatorLC = lc(operator);
|
|
1610
|
+
if (valueSource === "field" || nullOrUndefinedOrEmpty(value) && operatorLC !== "null" && operatorLC !== "notnull" || processMatchMode(rule)) return "";
|
|
1611
|
+
switch (operatorLC) {
|
|
1612
|
+
case "=":
|
|
1613
|
+
case "!=": return negateIf(`(${field}=${ldapEscape(value)})`, operatorLC === "!=");
|
|
1614
|
+
case ">":
|
|
1615
|
+
case ">=": return `(${field}>=${ldapEscape(value)})`;
|
|
1616
|
+
case "<":
|
|
1617
|
+
case "<=": return `(${field}<=${ldapEscape(value)})`;
|
|
1618
|
+
case "contains":
|
|
1619
|
+
case "doesnotcontain": return negateIf(`(${field}=*${ldapEscape(value)}*)`, operatorLC === "doesnotcontain");
|
|
1620
|
+
case "beginswith":
|
|
1621
|
+
case "doesnotbeginwith": return negateIf(`(${field}=${ldapEscape(value)}*)`, operatorLC === "doesnotbeginwith");
|
|
1622
|
+
case "endswith":
|
|
1623
|
+
case "doesnotendwith": return negateIf(`(${field}=*${ldapEscape(value)})`, operatorLC === "doesnotendwith");
|
|
1624
|
+
case "null":
|
|
1625
|
+
case "notnull": return negateIf(`(${field}=*)`, operatorLC === "notnull");
|
|
1626
|
+
case "in":
|
|
1627
|
+
case "notin": {
|
|
1628
|
+
const valueAsArray = toArray(value);
|
|
1629
|
+
return negateIf(`(|${valueAsArray.map((val) => `(${field}=${ldapEscape(val)})`).join("")})`, operatorLC === "notin");
|
|
1630
|
+
}
|
|
1631
|
+
case "between":
|
|
1632
|
+
case "notbetween": {
|
|
1633
|
+
const valueAsArray = toArray(value);
|
|
1634
|
+
if (valueAsArray.length < 2 || nullOrUndefinedOrEmpty(valueAsArray[0]) || nullOrUndefinedOrEmpty(valueAsArray[1])) return "";
|
|
1635
|
+
const [first, second] = valueAsArray;
|
|
1636
|
+
const firstNum = shouldRenderAsNumber(first, true) ? parseNumber(first, { parseNumbers: true }) : NaN;
|
|
1637
|
+
const secondNum = shouldRenderAsNumber(second, true) ? parseNumber(second, { parseNumbers: true }) : NaN;
|
|
1638
|
+
let firstValue = Number.isNaN(firstNum) ? first : firstNum;
|
|
1639
|
+
let secondValue = Number.isNaN(secondNum) ? second : secondNum;
|
|
1640
|
+
if (!preserveValueOrder && firstValue === firstNum && secondValue === secondNum && secondNum < firstNum) {
|
|
1641
|
+
const tempNum = secondNum;
|
|
1642
|
+
secondValue = firstNum;
|
|
1643
|
+
firstValue = tempNum;
|
|
1644
|
+
}
|
|
1645
|
+
return negateIf(`(&(${field}>=${ldapEscape(firstValue)})(${field}<=${ldapEscape(secondValue)}))`, operatorLC === "notbetween");
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
// istanbul ignore next
|
|
1649
|
+
return "";
|
|
1650
|
+
};
|
|
1651
|
+
|
|
1652
|
+
//#endregion
|
|
1653
|
+
//#region src/utils/formatQuery/defaultValueProcessorNL.ts
|
|
1654
|
+
const escapeStringValueQuotes = (v, quoteChar, escapeQuotes) => escapeQuotes && typeof v === "string" ? v.replaceAll(`${quoteChar}`, `${quoteChar}${quoteChar}`) : v;
|
|
1655
|
+
/**
|
|
1656
|
+
* Default value processor used by {@link formatQuery} for "natural_language" format.
|
|
1657
|
+
*
|
|
1658
|
+
* @group Export
|
|
1659
|
+
*/
|
|
1660
|
+
const defaultValueProcessorNL = (rule, opts = {}) => {
|
|
1661
|
+
const { escapeQuotes, fields, parseNumbers, quoteFieldNamesWith, quoteValuesWith, fieldIdentifierSeparator, translations } = opts;
|
|
1662
|
+
const valueIsField = rule.valueSource === "field";
|
|
1663
|
+
const operatorLowerCase = lc(rule.operator);
|
|
1664
|
+
const quoteChar = quoteValuesWith || "'";
|
|
1665
|
+
const quoteValue = (v) => `${quoteChar}${v}${quoteChar}`;
|
|
1666
|
+
const escapeValue = (v) => escapeStringValueQuotes(v, quoteChar, escapeQuotes);
|
|
1667
|
+
const wrapAndEscape = (v) => quoteValue(escapeValue(v));
|
|
1668
|
+
const wrapFieldName = (v) => getQuotedFieldName(v, {
|
|
1669
|
+
quoteFieldNamesWith,
|
|
1670
|
+
fieldIdentifierSeparator
|
|
1671
|
+
});
|
|
1672
|
+
const t = translations ?? ( /* istanbul ignore next */ {});
|
|
1673
|
+
const orTL = t.or ?? "or";
|
|
1674
|
+
const trueTL = t.true ?? "true";
|
|
1675
|
+
const falseTL = t.false ?? "false";
|
|
1676
|
+
switch (operatorLowerCase) {
|
|
1677
|
+
case "null":
|
|
1678
|
+
case "notnull": return "";
|
|
1679
|
+
case "between":
|
|
1680
|
+
case "notbetween": {
|
|
1681
|
+
if (!valueIsField) return defaultValueProcessorByRule(rule, opts);
|
|
1682
|
+
const valueAsArray = toArray(rule.value, { retainEmptyStrings: true }).slice(0, 2).map((v) => wrapFieldName(getOption(fields ?? [], v)?.label ?? v));
|
|
1683
|
+
if (valueAsArray.length < 2 || !isValidValue(valueAsArray[0]) || !isValidValue(valueAsArray[1])) return "";
|
|
1684
|
+
return defaultValueProcessorByRule({
|
|
1685
|
+
...rule,
|
|
1686
|
+
value: valueAsArray
|
|
1687
|
+
}, opts);
|
|
1688
|
+
}
|
|
1689
|
+
case "in":
|
|
1690
|
+
case "notin": {
|
|
1691
|
+
const valueAsArray = toArray(rule.value);
|
|
1692
|
+
if (valueAsArray.length === 0) return "";
|
|
1693
|
+
const valStringArray = valueAsArray.map((v) => valueIsField ? wrapFieldName(getOption(fields ?? [], v)?.label ?? v) : shouldRenderAsNumber(v, parseNumbers) ? `${trimIfString(v)}` : `${wrapAndEscape(v)}`);
|
|
1694
|
+
return `${valStringArray.slice(0, -1).join(", ")}${valStringArray.length > 2 ? "," : ""} ${orTL} ${valStringArray.at(-1)}`;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
if (typeof rule.value === "boolean") return rule.value ? trueTL : falseTL;
|
|
1698
|
+
return valueIsField ? wrapFieldName(getOption(fields ?? [], rule.value)?.label ?? rule.value) : shouldRenderAsNumber(rule.value, parseNumbers) ? `${trimIfString(rule.value)}` : `${wrapAndEscape(rule.value)}`;
|
|
1699
|
+
};
|
|
1700
|
+
|
|
1701
|
+
//#endregion
|
|
1702
|
+
//#region src/utils/formatQuery/defaultRuleProcessorNL.ts
|
|
1703
|
+
/**
|
|
1704
|
+
* Default operator map used by {@link formatQuery} for "natural_language" format.
|
|
1705
|
+
*
|
|
1706
|
+
* @group Export
|
|
1707
|
+
*/
|
|
1708
|
+
const defaultExportOperatorMap = {
|
|
1709
|
+
"=": ["is", "is the same as the value in"],
|
|
1710
|
+
"!=": ["is not", "is not the same as the value in"],
|
|
1711
|
+
"<": ["is less than", "is less than the value in"],
|
|
1712
|
+
">": ["is greater than", "is greater than the value in"],
|
|
1713
|
+
"<=": ["is less than or equal to", "is less than or equal to the value in"],
|
|
1714
|
+
">=": ["is greater than or equal to", "is greater than or equal to the value in"],
|
|
1715
|
+
contains: ["contains", "contains the value in"],
|
|
1716
|
+
beginswith: ["starts with", "starts with the value in"],
|
|
1717
|
+
endswith: ["ends with", "ends with the value in"],
|
|
1718
|
+
doesnotcontain: ["does not contain", "does not contain the value in"],
|
|
1719
|
+
doesnotbeginwith: ["does not start with", "does not start with the value in"],
|
|
1720
|
+
doesnotendwith: ["does not end with", "does not end with the value in"],
|
|
1721
|
+
null: "is null",
|
|
1722
|
+
notnull: "is not null",
|
|
1723
|
+
in: ["is one of the values", "is the same as a value in"],
|
|
1724
|
+
notin: ["is not one of the values", "is not the same as any value in"],
|
|
1725
|
+
between: ["is between", "is between the values in"],
|
|
1726
|
+
notbetween: ["is not between", "is not between the values in"]
|
|
1727
|
+
};
|
|
1728
|
+
/* istanbul ignore next */
|
|
1729
|
+
const defaultGetOperators = () => [];
|
|
1730
|
+
/**
|
|
1731
|
+
* Default operator processor used by {@link formatQuery} for "natural_language" format.
|
|
1732
|
+
*
|
|
1733
|
+
* @group Export
|
|
1734
|
+
*/
|
|
1735
|
+
const defaultOperatorProcessorNL = (rule, opts = {}) => {
|
|
1736
|
+
const { field, operator, valueSource = "value" } = rule;
|
|
1737
|
+
// istanbul ignore next
|
|
1738
|
+
const { getOperators = defaultGetOperators, operatorMap: operatorMapParam = defaultExportOperatorMap } = opts;
|
|
1739
|
+
const mapOperatorMap = new Map(Object.entries(defaultExportOperatorMap));
|
|
1740
|
+
for (const [key, value] of Object.entries(operatorMapParam)) mapOperatorMap.set(lc(key), value);
|
|
1741
|
+
const operatorMap = Object.fromEntries(mapOperatorMap);
|
|
1742
|
+
const { value: operatorNL, label } = getOption(toFullOptionList(getOperators(field, { fieldData: opts.fieldData ?? {
|
|
1743
|
+
name: field,
|
|
1744
|
+
value: field,
|
|
1745
|
+
label: field
|
|
1746
|
+
} }) ?? []), operator) ?? {
|
|
1747
|
+
name: operator,
|
|
1748
|
+
value: operator,
|
|
1749
|
+
label: operator
|
|
1750
|
+
};
|
|
1751
|
+
const operatorTL = operatorMap[operatorNL] ?? operatorMap[lc(operatorNL)] ?? [label, label];
|
|
1752
|
+
return typeof operatorTL === "string" ? operatorTL : operatorTL[valueSource === "field" ? 1 : 0];
|
|
1753
|
+
};
|
|
1754
|
+
/**
|
|
1755
|
+
* Default rule processor used by {@link formatQuery} for "natural_language" format.
|
|
1756
|
+
*
|
|
1757
|
+
* @group Export
|
|
1758
|
+
*/
|
|
1759
|
+
const defaultRuleProcessorNL = (rule, opts) => {
|
|
1760
|
+
const { field, operator } = rule;
|
|
1761
|
+
// istanbul ignore next
|
|
1762
|
+
const { fieldData, quoteFieldNamesWith = ["", ""], fieldIdentifierSeparator = "", quoteValuesWith = `'`, operatorProcessor = defaultOperatorProcessorNL, valueProcessor = defaultValueProcessorNL, concatOperator = "||", wordOrder = "SVO" } = opts ?? ( /* istanbul ignore next */ {});
|
|
1763
|
+
const processedField = getQuotedFieldName(fieldData?.label ?? field, {
|
|
1764
|
+
quoteFieldNamesWith,
|
|
1765
|
+
fieldIdentifierSeparator
|
|
1766
|
+
});
|
|
1767
|
+
const matchEval = processMatchMode(rule);
|
|
1768
|
+
if (matchEval === false) return "";
|
|
1769
|
+
else if (matchEval) {
|
|
1770
|
+
const { mode, threshold } = matchEval;
|
|
1771
|
+
const nestedArrayFilter = defaultRuleGroupProcessorNL(rule.value, {
|
|
1772
|
+
...opts,
|
|
1773
|
+
fields: toFullOptionList(fieldData?.subproperties ?? [])
|
|
1774
|
+
});
|
|
1775
|
+
const hsp = (fieldData?.subproperties?.length ?? 0) > 0;
|
|
1776
|
+
switch (mode) {
|
|
1777
|
+
case "all": return `(${hsp ? "for " : ""}every item in ${processedField}${hsp ? "," : ""} ${nestedArrayFilter})`;
|
|
1778
|
+
case "none": return `(${hsp ? "for " : ""}no item in ${processedField}${hsp ? "," : ""} ${nestedArrayFilter})`;
|
|
1779
|
+
case "some": return `(${hsp ? "for " : ""}at least one item in ${processedField}${hsp ? "," : ""} ${nestedArrayFilter})`;
|
|
1780
|
+
case "atleast":
|
|
1781
|
+
case "atmost":
|
|
1782
|
+
case "exactly": {
|
|
1783
|
+
const mm = mode.replace("at", "at ");
|
|
1784
|
+
if (threshold > 0 && threshold < 1) return `(${hsp ? "for " : ""}${mm} ${threshold * 100}% of the items in ${processedField}${hsp ? "," : ""} ${nestedArrayFilter})`;
|
|
1785
|
+
return `(${hsp ? "for " : ""}${mm} ${threshold} of the items in ${processedField}${hsp ? "," : ""} ${nestedArrayFilter})`;
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
const value = valueProcessor(rule, {
|
|
1790
|
+
...opts,
|
|
1791
|
+
quoteFieldNamesWith,
|
|
1792
|
+
fieldIdentifierSeparator,
|
|
1793
|
+
quoteValuesWith,
|
|
1794
|
+
concatOperator
|
|
1795
|
+
});
|
|
1796
|
+
const operatorLC = lc(operator);
|
|
1797
|
+
if ((operatorLC === "in" || operatorLC === "notin" || operatorLC === "between" || operatorLC === "notbetween") && !value) return "";
|
|
1798
|
+
const processedOperator = operatorProcessor(rule, opts);
|
|
1799
|
+
const wordOrderMap = {
|
|
1800
|
+
S: processedField,
|
|
1801
|
+
V: processedOperator,
|
|
1802
|
+
O: value
|
|
1803
|
+
};
|
|
1804
|
+
return normalizeConstituentWordOrder(wordOrder).map((term) => `${wordOrderMap[term]}`).join(" ").trim();
|
|
1805
|
+
};
|
|
1806
|
+
|
|
1807
|
+
//#endregion
|
|
1808
|
+
//#region src/utils/formatQuery/defaultRuleProcessorSQL.ts
|
|
1809
|
+
/**
|
|
1810
|
+
* Default operator processor used by {@link formatQuery} for "sql" and "parameterized*" formats.
|
|
1811
|
+
*
|
|
1812
|
+
* @group Export
|
|
1813
|
+
*/
|
|
1814
|
+
const defaultOperatorProcessorSQL = (rule) => lc(mapSQLOperator(rule.operator));
|
|
1815
|
+
/**
|
|
1816
|
+
* Default rule processor used by {@link formatQuery} for "sql" format.
|
|
1817
|
+
*
|
|
1818
|
+
* @group Export
|
|
1819
|
+
*/
|
|
1820
|
+
const defaultRuleProcessorSQL = (rule, opts = {}) => {
|
|
1821
|
+
const { quoteFieldNamesWith = ["", ""], fieldIdentifierSeparator = "", quoteValuesWith = `'`, operatorProcessor = defaultOperatorProcessorSQL, valueProcessor = defaultValueProcessorByRule, concatOperator = "||" } = opts;
|
|
1822
|
+
const wrapFieldName = (v) => getQuotedFieldName(v, {
|
|
1823
|
+
quoteFieldNamesWith,
|
|
1824
|
+
fieldIdentifierSeparator
|
|
1825
|
+
});
|
|
1826
|
+
const ruleField = wrapFieldName(rule.field);
|
|
1827
|
+
const matchEval = processMatchMode(rule);
|
|
1828
|
+
if (matchEval === false) return;
|
|
1829
|
+
else if (matchEval) {
|
|
1830
|
+
if (opts?.preset !== "postgresql") return "";
|
|
1831
|
+
const { mode, threshold } = matchEval;
|
|
1832
|
+
const arrayElementAlias = "elem_alias";
|
|
1833
|
+
const nestedArrayFilter = defaultRuleGroupProcessorSQL(transformQuery(rule.value, { ruleProcessor: (r) => ({
|
|
1834
|
+
...r,
|
|
1835
|
+
field: arrayElementAlias
|
|
1836
|
+
}) }), opts);
|
|
1837
|
+
switch (mode) {
|
|
1838
|
+
case "all": return `(select count(*) from unnest(${ruleField}) as ${wrapFieldName(arrayElementAlias)} where ${nestedArrayFilter}) = array_length(${ruleField}, 1)`;
|
|
1839
|
+
case "none": return `not exists (select 1 from unnest(${ruleField}) as ${wrapFieldName(arrayElementAlias)} where ${nestedArrayFilter})`;
|
|
1840
|
+
case "some": return `exists (select 1 from unnest(${ruleField}) as ${wrapFieldName(arrayElementAlias)} where ${nestedArrayFilter})`;
|
|
1841
|
+
case "atleast":
|
|
1842
|
+
case "atmost":
|
|
1843
|
+
case "exactly": {
|
|
1844
|
+
const op = mode === "atleast" ? ">=" : mode === "atmost" ? "<=" : "=";
|
|
1845
|
+
return `(select count(*)${threshold > 0 && threshold < 1 ? ` / array_length(${ruleField}, 1)` : ""} from unnest(${ruleField}) as ${wrapFieldName(arrayElementAlias)} where ${nestedArrayFilter}) ${op} ${threshold}`;
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
const value = valueProcessor(rule, {
|
|
1850
|
+
...opts,
|
|
1851
|
+
quoteFieldNamesWith,
|
|
1852
|
+
fieldIdentifierSeparator,
|
|
1853
|
+
quoteValuesWith,
|
|
1854
|
+
concatOperator
|
|
1855
|
+
});
|
|
1856
|
+
const operator = operatorProcessor(rule, opts);
|
|
1857
|
+
const operatorLowerCase = lc(operator);
|
|
1858
|
+
if ((operatorLowerCase === "in" || operatorLowerCase === "not in" || operatorLowerCase === "between" || operatorLowerCase === "not between") && !value) return "";
|
|
1859
|
+
return `${ruleField} ${operator} ${value}`.trim();
|
|
1860
|
+
};
|
|
1861
|
+
|
|
1862
|
+
//#endregion
|
|
1863
|
+
//#region src/utils/formatQuery/defaultRuleProcessorParameterized.ts
|
|
1864
|
+
/**
|
|
1865
|
+
* Default rule processor used by {@link formatQuery} for "parameterized" and
|
|
1866
|
+
* "parameterized_named" formats.
|
|
1867
|
+
*
|
|
1868
|
+
* @group Export
|
|
1869
|
+
*/
|
|
1870
|
+
const defaultRuleProcessorParameterized = (rule, opts, meta) => {
|
|
1871
|
+
// istanbul ignore next
|
|
1872
|
+
const { fieldData, format, getNextNamedParam, parseNumbers, paramPrefix, paramsKeepPrefix, numberedParams, quoteFieldNamesWith = ["", ""], fieldIdentifierSeparator, concatOperator, operatorProcessor = defaultOperatorProcessorSQL, valueProcessor = defaultValueProcessorByRule } = opts ?? {};
|
|
1873
|
+
const { processedParams = [] } = meta ?? {};
|
|
1874
|
+
const parameterized = format === "parameterized";
|
|
1875
|
+
const params = [];
|
|
1876
|
+
const paramsNamed = {};
|
|
1877
|
+
const finalize = (sql) => parameterized ? {
|
|
1878
|
+
sql,
|
|
1879
|
+
params
|
|
1880
|
+
} : {
|
|
1881
|
+
sql,
|
|
1882
|
+
params: paramsNamed
|
|
1883
|
+
};
|
|
1884
|
+
const wrapFieldName = (v) => getQuotedFieldName(v, {
|
|
1885
|
+
quoteFieldNamesWith,
|
|
1886
|
+
fieldIdentifierSeparator
|
|
1887
|
+
});
|
|
1888
|
+
const ruleField = wrapFieldName(rule.field);
|
|
1889
|
+
const matchEval = processMatchMode(rule);
|
|
1890
|
+
if (matchEval === false) return;
|
|
1891
|
+
else if (matchEval) {
|
|
1892
|
+
if (opts?.preset !== "postgresql") return finalize("");
|
|
1893
|
+
const { mode, threshold } = matchEval;
|
|
1894
|
+
const arrayElementAlias = "elem_alias";
|
|
1895
|
+
const { sql: nestedSQL, params: nestedParams } = defaultRuleGroupProcessorParameterized(transformQuery(rule.value, { ruleProcessor: (r) => ({
|
|
1896
|
+
...r,
|
|
1897
|
+
field: arrayElementAlias
|
|
1898
|
+
}) }), {
|
|
1899
|
+
...opts,
|
|
1900
|
+
fields: []
|
|
1901
|
+
});
|
|
1902
|
+
// istanbul ignore else
|
|
1903
|
+
if (Array.isArray(nestedParams)) params.push(...nestedParams);
|
|
1904
|
+
else Object.assign(paramsNamed, nestedParams);
|
|
1905
|
+
switch (mode) {
|
|
1906
|
+
case "all": return finalize(`(select count(*) from unnest(${ruleField}) as ${wrapFieldName(arrayElementAlias)} where ${nestedSQL}) = array_length(${ruleField}, 1)`);
|
|
1907
|
+
case "none": return finalize(`not exists (select 1 from unnest(${ruleField}) as ${wrapFieldName(arrayElementAlias)} where ${nestedSQL})`);
|
|
1908
|
+
case "some": return finalize(`exists (select 1 from unnest(${ruleField}) as ${wrapFieldName(arrayElementAlias)} where ${nestedSQL})`);
|
|
1909
|
+
case "atleast":
|
|
1910
|
+
case "atmost":
|
|
1911
|
+
case "exactly": {
|
|
1912
|
+
const op = mode === "atleast" ? ">=" : mode === "atmost" ? "<=" : "=";
|
|
1913
|
+
return finalize(`(select count(*)${threshold > 0 && threshold < 1 ? ` / array_length(${ruleField}, 1)` : ""} from unnest(${ruleField}) as ${wrapFieldName(arrayElementAlias)} where ${nestedSQL}) ${op} ${threshold}`);
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
const value = valueProcessor(rule, {
|
|
1918
|
+
parseNumbers,
|
|
1919
|
+
quoteFieldNamesWith,
|
|
1920
|
+
concatOperator,
|
|
1921
|
+
fieldData,
|
|
1922
|
+
format
|
|
1923
|
+
});
|
|
1924
|
+
const sqlOperator = operatorProcessor(rule, opts);
|
|
1925
|
+
const sqlOperatorLowerCase = lc(sqlOperator);
|
|
1926
|
+
const [qPre, qPost] = quoteFieldNamesWith;
|
|
1927
|
+
if ((sqlOperatorLowerCase === "in" || sqlOperatorLowerCase === "not in" || sqlOperatorLowerCase === "between" || sqlOperatorLowerCase === "not between") && !value) return finalize("");
|
|
1928
|
+
else if (sqlOperatorLowerCase === "is null" || sqlOperatorLowerCase === "is not null") return finalize(`${qPre}${rule.field}${qPost} ${sqlOperator}`);
|
|
1929
|
+
else if (rule.valueSource === "field") return finalize(`${qPre}${rule.field}${qPost} ${sqlOperator} ${value}`.trim());
|
|
1930
|
+
else if (sqlOperatorLowerCase === "in" || sqlOperatorLowerCase === "not in") {
|
|
1931
|
+
const splitValue = toArray(rule.value);
|
|
1932
|
+
if (parameterized) {
|
|
1933
|
+
for (const v of splitValue) params.push(shouldRenderAsNumber(v, parseNumbers) ? parseNumber(v, { parseNumbers }) : v);
|
|
1934
|
+
return finalize(`${qPre}${rule.field}${qPost} ${sqlOperator} (${splitValue.map((_v, i) => numberedParams ? `${paramPrefix}${processedParams.length + 1 + splitValue.length - (splitValue.length - i)}` : "?").join(", ")})`);
|
|
1935
|
+
}
|
|
1936
|
+
const inParams = [];
|
|
1937
|
+
for (const v of splitValue) {
|
|
1938
|
+
const thisParamName = getNextNamedParam(rule.field);
|
|
1939
|
+
inParams.push(`${paramPrefix}${thisParamName}`);
|
|
1940
|
+
paramsNamed[`${paramsKeepPrefix ? paramPrefix : ""}${thisParamName}`] = shouldRenderAsNumber(v, parseNumbers) ? parseNumber(v, { parseNumbers }) : v;
|
|
1941
|
+
}
|
|
1942
|
+
return finalize(`${qPre}${rule.field}${qPost} ${sqlOperator} (${inParams.join(", ")})`);
|
|
1943
|
+
} else if (sqlOperatorLowerCase === "between" || sqlOperatorLowerCase === "not between") {
|
|
1944
|
+
const [first, second] = toArray(rule.value, { retainEmptyStrings: true }).slice(0, 2).map((v) => shouldRenderAsNumber(v, parseNumbers) ? parseNumber(v, { parseNumbers }) : v);
|
|
1945
|
+
if (parameterized) {
|
|
1946
|
+
params.push(first, second);
|
|
1947
|
+
return finalize(`${qPre}${rule.field}${qPost} ${sqlOperator} ${numberedParams ? `${paramPrefix}${processedParams.length + 1}` : "?"} and ${numberedParams ? `${paramPrefix}${processedParams.length + 2}` : "?"}`);
|
|
1948
|
+
}
|
|
1949
|
+
const firstParamName = getNextNamedParam(rule.field);
|
|
1950
|
+
const secondParamName = getNextNamedParam(rule.field);
|
|
1951
|
+
paramsNamed[`${paramsKeepPrefix ? paramPrefix : ""}${firstParamName}`] = first;
|
|
1952
|
+
paramsNamed[`${paramsKeepPrefix ? paramPrefix : ""}${secondParamName}`] = second;
|
|
1953
|
+
return finalize(`${qPre}${rule.field}${qPost} ${sqlOperator} ${paramPrefix}${firstParamName} and ${paramPrefix}${secondParamName}`);
|
|
1954
|
+
}
|
|
1955
|
+
let paramValue = rule.value;
|
|
1956
|
+
if (typeof rule.value === "string") if (shouldRenderAsNumber(rule.value, parseNumbers)) paramValue = parseNumber(rule.value, { parseNumbers });
|
|
1957
|
+
else paramValue = /^'.*'$/g.test(value) ? value.replaceAll(/(^'|'$)/g, "") : value;
|
|
1958
|
+
let paramName = "";
|
|
1959
|
+
if (parameterized) params.push(paramValue);
|
|
1960
|
+
else {
|
|
1961
|
+
paramName = getNextNamedParam(rule.field);
|
|
1962
|
+
paramsNamed[`${paramsKeepPrefix ? paramPrefix : ""}${paramName}`] = paramValue;
|
|
1963
|
+
}
|
|
1964
|
+
return finalize(`${qPre}${rule.field}${qPost} ${sqlOperator} ${parameterized ? numberedParams ? `${paramPrefix}${processedParams.length + 1}` : "?" : `${paramPrefix}${paramName}`}`.trim());
|
|
1965
|
+
};
|
|
1966
|
+
|
|
1967
|
+
//#endregion
|
|
1968
|
+
//#region src/utils/formatQuery/defaultRuleProcessorPrisma.ts
|
|
1969
|
+
const processNumber = (value, fallback, parseNumbers) => shouldRenderAsNumber(value, !!parseNumbers || typeof value === "bigint") ? Number(parseNumber(value, { parseNumbers: !!parseNumbers })) : fallback;
|
|
1970
|
+
/**
|
|
1971
|
+
* Default rule processor used by {@link formatQuery} for "prisma" format.
|
|
1972
|
+
*
|
|
1973
|
+
* @group Export
|
|
1974
|
+
*/
|
|
1975
|
+
const defaultRuleProcessorPrisma = (rule, options = {}) => {
|
|
1976
|
+
const { field, operator, value, valueSource } = rule;
|
|
1977
|
+
// istanbul ignore next
|
|
1978
|
+
const { parseNumbers, preserveValueOrder } = options;
|
|
1979
|
+
if (valueSource === "field" || processMatchMode(rule)) return;
|
|
1980
|
+
const operatorLC = lc(operator);
|
|
1981
|
+
switch (operatorLC) {
|
|
1982
|
+
case "=": return { [field]: processNumber(value, value, parseNumbers) };
|
|
1983
|
+
case "!=":
|
|
1984
|
+
case "<":
|
|
1985
|
+
case "<=":
|
|
1986
|
+
case ">":
|
|
1987
|
+
case ">=": {
|
|
1988
|
+
const prismaOperator = prismaOperators[operatorLC];
|
|
1989
|
+
return { [field]: { [prismaOperator]: processNumber(value, value, parseNumbers) } };
|
|
1990
|
+
}
|
|
1991
|
+
case "contains": return { [field]: { contains: value } };
|
|
1992
|
+
case "beginswith": return { [field]: { startsWith: value } };
|
|
1993
|
+
case "endswith": return { [field]: { endsWith: value } };
|
|
1994
|
+
case "doesnotcontain": return { NOT: { [field]: { contains: value } } };
|
|
1995
|
+
case "doesnotbeginwith": return { NOT: { [field]: { startsWith: value } } };
|
|
1996
|
+
case "doesnotendwith": return { NOT: { [field]: { endsWith: value } } };
|
|
1997
|
+
case "null": return { [field]: null };
|
|
1998
|
+
case "notnull": return { [field]: { not: null } };
|
|
1999
|
+
case "in":
|
|
2000
|
+
case "notin": {
|
|
2001
|
+
const valueAsArray = toArray(value);
|
|
2002
|
+
return { [field]: { [prismaOperators[operatorLC]]: valueAsArray.map((val) => processNumber(val, val, parseNumbers)) } };
|
|
2003
|
+
}
|
|
2004
|
+
case "between":
|
|
2005
|
+
case "notbetween": {
|
|
2006
|
+
const valueAsArray = toArray(value);
|
|
2007
|
+
if (valueAsArray.length >= 2 && isValidValue(valueAsArray[0]) && isValidValue(valueAsArray[1])) {
|
|
2008
|
+
const [first, second] = valueAsArray;
|
|
2009
|
+
const shouldParseNumbers = !(parseNumbers === false);
|
|
2010
|
+
const firstNum = shouldRenderAsNumber(first, shouldParseNumbers) ? parseNumber(first, { parseNumbers }) : NaN;
|
|
2011
|
+
const secondNum = shouldRenderAsNumber(second, shouldParseNumbers) ? parseNumber(second, { parseNumbers }) : NaN;
|
|
2012
|
+
let firstValue = Number.isNaN(firstNum) ? first : firstNum;
|
|
2013
|
+
let secondValue = Number.isNaN(secondNum) ? second : secondNum;
|
|
2014
|
+
if (!preserveValueOrder && firstValue === firstNum && secondValue === secondNum && secondNum < firstNum) {
|
|
2015
|
+
const tempNum = secondNum;
|
|
2016
|
+
secondValue = firstNum;
|
|
2017
|
+
firstValue = tempNum;
|
|
2018
|
+
}
|
|
2019
|
+
return operatorLC === "between" ? { [field]: {
|
|
2020
|
+
gte: firstValue,
|
|
2021
|
+
lte: secondValue
|
|
2022
|
+
} } : { OR: [{ [field]: { lt: firstValue } }, { [field]: { gt: secondValue } }] };
|
|
2023
|
+
} else return "";
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
return "";
|
|
2027
|
+
};
|
|
2028
|
+
|
|
2029
|
+
//#endregion
|
|
2030
|
+
//#region src/utils/formatQuery/defaultRuleProcessorSequelize.ts
|
|
2031
|
+
/**
|
|
2032
|
+
* Default rule processor used by {@link formatQuery} for the "sequelize" format.
|
|
2033
|
+
*
|
|
2034
|
+
* @group Export
|
|
2035
|
+
*/
|
|
2036
|
+
const defaultRuleProcessorSequelize = (rule, { parseNumbers, preserveValueOrder, context = {} } = {}) => {
|
|
2037
|
+
const { sequelizeOperators: Op, sequelizeCol: col, sequelizeFn: fn } = context;
|
|
2038
|
+
if (processMatchMode(rule)) return;
|
|
2039
|
+
const { field, operator, value, valueSource } = rule;
|
|
2040
|
+
const valueIsField = valueSource === "field";
|
|
2041
|
+
const operatorLC = lc(operator);
|
|
2042
|
+
if (!Op || valueIsField && (!col || !fn && [
|
|
2043
|
+
"doesnotcontain",
|
|
2044
|
+
"doesnotbeginwith",
|
|
2045
|
+
"doesnotendwith"
|
|
2046
|
+
].includes(operatorLC))) return;
|
|
2047
|
+
switch (operatorLC) {
|
|
2048
|
+
case "=":
|
|
2049
|
+
case "!=":
|
|
2050
|
+
case "<":
|
|
2051
|
+
case "<=":
|
|
2052
|
+
case ">":
|
|
2053
|
+
case ">=": {
|
|
2054
|
+
const sequelizeOperator = {
|
|
2055
|
+
"=": Op.eq,
|
|
2056
|
+
"!=": Op.ne,
|
|
2057
|
+
"<": Op.lt,
|
|
2058
|
+
"<=": Op.lte,
|
|
2059
|
+
">": Op.gt,
|
|
2060
|
+
">=": Op.gte
|
|
2061
|
+
}[operatorLC];
|
|
2062
|
+
return { [field]: valueIsField && operatorLC === "=" ? { [Op.col]: value } : { [sequelizeOperator]: valueIsField ? col(value) : shouldRenderAsNumber(value, parseNumbers) ? parseNumber(value, { parseNumbers: "strict" }) : value } };
|
|
2063
|
+
}
|
|
2064
|
+
case "contains": return { [field]: { [Op.substring]: valueIsField ? col(value) : `${value}` } };
|
|
2065
|
+
case "beginswith": return { [field]: { [Op.startsWith]: valueIsField ? col(value) : `${value}` } };
|
|
2066
|
+
case "endswith": return { [field]: { [Op.endsWith]: valueIsField ? col(value) : `${value}` } };
|
|
2067
|
+
case "doesnotcontain": return { [field]: { [Op.notLike]: valueIsField ? fn("CONCAT", "%", col(value), "%") : `%${value}%` } };
|
|
2068
|
+
case "doesnotbeginwith": return { [field]: { [Op.notLike]: valueIsField ? fn("CONCAT", col(value), "%") : `${value}%` } };
|
|
2069
|
+
case "doesnotendwith": return { [field]: { [Op.notLike]: valueIsField ? fn("CONCAT", "%", col(value)) : `%${value}` } };
|
|
2070
|
+
case "null": return { [field]: { [Op.is]: null } };
|
|
2071
|
+
case "notnull": return { [field]: { [Op.not]: null } };
|
|
2072
|
+
case "in":
|
|
2073
|
+
case "notin": {
|
|
2074
|
+
const valueAsArray = toArray(value);
|
|
2075
|
+
return { [field]: { [operatorLC === "in" ? Op.in : Op.notIn]: valueAsArray.map((val) => valueIsField ? col(val) : shouldRenderAsNumber(val, parseNumbers) ? parseNumber(val, { parseNumbers: "strict" }) : val) } };
|
|
2076
|
+
}
|
|
2077
|
+
case "between":
|
|
2078
|
+
case "notbetween": {
|
|
2079
|
+
const valueAsArray = toArray(value, { retainEmptyStrings: true });
|
|
2080
|
+
if (valueAsArray.length < 2 || !isValidValue(valueAsArray[0]) || !isValidValue(valueAsArray[1])) return;
|
|
2081
|
+
const [first, second] = valueAsArray;
|
|
2082
|
+
const firstNum = shouldRenderAsNumber(first, parseNumbers) ? parseNumber(first, { parseNumbers: "strict" }) : NaN;
|
|
2083
|
+
const secondNum = shouldRenderAsNumber(second, parseNumbers) ? parseNumber(second, { parseNumbers: "strict" }) : NaN;
|
|
2084
|
+
const firstValue = Number.isNaN(firstNum) ? first : firstNum;
|
|
2085
|
+
const secondValue = Number.isNaN(secondNum) ? second : secondNum;
|
|
2086
|
+
const valsOneAndTwoOnly = [firstValue, secondValue];
|
|
2087
|
+
if (!preserveValueOrder && firstValue === firstNum && secondValue === secondNum && secondNum < firstNum) {
|
|
2088
|
+
valsOneAndTwoOnly[0] = secondNum;
|
|
2089
|
+
valsOneAndTwoOnly[1] = firstNum;
|
|
2090
|
+
}
|
|
2091
|
+
return { [field]: { [operatorLC === "between" ? Op.between : Op.notBetween]: valueIsField ? valsOneAndTwoOnly.map((v) => col(v)) : valsOneAndTwoOnly.every((v) => shouldRenderAsNumber(v, parseNumbers)) ? valsOneAndTwoOnly.map((v) => parseNumber(v, { parseNumbers: "strict" })) : valsOneAndTwoOnly } };
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
};
|
|
2095
|
+
|
|
2096
|
+
//#endregion
|
|
2097
|
+
//#region src/utils/formatQuery/formatQuery.ts
|
|
2098
|
+
/**
|
|
2099
|
+
* A collection of option presets for {@link formatQuery}, specifically for SQL-based formats.
|
|
2100
|
+
*
|
|
2101
|
+
* @group Export
|
|
2102
|
+
*/
|
|
2103
|
+
const sqlDialectPresets = {
|
|
2104
|
+
ansi: {},
|
|
2105
|
+
sqlite: { paramsKeepPrefix: true },
|
|
2106
|
+
oracle: {},
|
|
2107
|
+
mssql: {
|
|
2108
|
+
concatOperator: "+",
|
|
2109
|
+
quoteFieldNamesWith: ["[", "]"],
|
|
2110
|
+
fieldIdentifierSeparator: ".",
|
|
2111
|
+
paramPrefix: "@"
|
|
2112
|
+
},
|
|
2113
|
+
mysql: { concatOperator: "CONCAT" },
|
|
2114
|
+
postgresql: {
|
|
2115
|
+
quoteFieldNamesWith: "\"",
|
|
2116
|
+
numberedParams: true,
|
|
2117
|
+
paramPrefix: "$"
|
|
2118
|
+
}
|
|
2119
|
+
};
|
|
2120
|
+
/**
|
|
2121
|
+
* A collection of option presets for {@link formatQuery}.
|
|
2122
|
+
*
|
|
2123
|
+
* @group Export
|
|
2124
|
+
*/
|
|
2125
|
+
const formatQueryOptionPresets = { ...sqlDialectPresets };
|
|
2126
|
+
const defaultRuleProcessors = {
|
|
2127
|
+
cel: defaultRuleProcessorCEL,
|
|
2128
|
+
drizzle: defaultRuleProcessorDrizzle,
|
|
2129
|
+
elasticsearch: defaultRuleProcessorElasticSearch,
|
|
2130
|
+
json_without_ids: defaultRuleProcessorSQL,
|
|
2131
|
+
json: defaultRuleProcessorSQL,
|
|
2132
|
+
jsonata: defaultRuleProcessorJSONata,
|
|
2133
|
+
jsonlogic: defaultRuleProcessorJsonLogic,
|
|
2134
|
+
ldap: defaultRuleProcessorLDAP,
|
|
2135
|
+
mongodb_query: defaultRuleProcessorMongoDBQuery,
|
|
2136
|
+
mongodb: defaultRuleProcessorMongoDB,
|
|
2137
|
+
natural_language: defaultRuleProcessorNL,
|
|
2138
|
+
parameterized_named: defaultRuleProcessorParameterized,
|
|
2139
|
+
parameterized: defaultRuleProcessorParameterized,
|
|
2140
|
+
prisma: defaultRuleProcessorPrisma,
|
|
2141
|
+
sequelize: defaultRuleProcessorSequelize,
|
|
2142
|
+
spel: defaultRuleProcessorSpEL,
|
|
2143
|
+
sql: defaultRuleProcessorSQL
|
|
2144
|
+
};
|
|
2145
|
+
/* istanbul ignore next */
|
|
2146
|
+
const defaultOperatorProcessor = (r) => r.operator;
|
|
2147
|
+
const defaultOperatorProcessors = {
|
|
2148
|
+
cel: defaultOperatorProcessor,
|
|
2149
|
+
drizzle: defaultOperatorProcessor,
|
|
2150
|
+
elasticsearch: defaultOperatorProcessor,
|
|
2151
|
+
json_without_ids: defaultOperatorProcessor,
|
|
2152
|
+
json: defaultOperatorProcessor,
|
|
2153
|
+
jsonata: defaultOperatorProcessor,
|
|
2154
|
+
jsonlogic: defaultOperatorProcessor,
|
|
2155
|
+
ldap: defaultOperatorProcessor,
|
|
2156
|
+
mongodb_query: defaultOperatorProcessor,
|
|
2157
|
+
mongodb: defaultOperatorProcessor,
|
|
2158
|
+
natural_language: defaultOperatorProcessorNL,
|
|
2159
|
+
parameterized_named: defaultOperatorProcessorSQL,
|
|
2160
|
+
parameterized: defaultOperatorProcessorSQL,
|
|
2161
|
+
prisma: defaultOperatorProcessor,
|
|
2162
|
+
sequelize: defaultOperatorProcessor,
|
|
2163
|
+
spel: defaultOperatorProcessor,
|
|
2164
|
+
sql: defaultOperatorProcessorSQL
|
|
2165
|
+
};
|
|
2166
|
+
const defaultFallbackExpressions = {
|
|
2167
|
+
cel: "1 == 1",
|
|
2168
|
+
ldap: "",
|
|
2169
|
+
mongodb: "\"$and\":[{\"$expr\":true}]",
|
|
2170
|
+
natural_language: "1 is 1",
|
|
2171
|
+
spel: "1 == 1",
|
|
2172
|
+
sql: "(1 = 1)"
|
|
2173
|
+
};
|
|
2174
|
+
const defaultFormatQueryOptions = {
|
|
2175
|
+
format: "json",
|
|
2176
|
+
fields: [],
|
|
2177
|
+
quoteFieldNamesWith: ["", ""],
|
|
2178
|
+
fieldIdentifierSeparator: "",
|
|
2179
|
+
getOperators: () => [],
|
|
2180
|
+
paramPrefix: ":",
|
|
2181
|
+
paramsKeepPrefix: false,
|
|
2182
|
+
numberedParams: false,
|
|
2183
|
+
preserveValueOrder: false,
|
|
2184
|
+
placeholderFieldName: defaultPlaceholderFieldName,
|
|
2185
|
+
placeholderOperatorName: defaultPlaceholderOperatorName,
|
|
2186
|
+
quoteValuesWith: "'",
|
|
2187
|
+
concatOperator: "||",
|
|
2188
|
+
preset: "ansi",
|
|
2189
|
+
wordOrder: "SVO",
|
|
2190
|
+
translations: {},
|
|
2191
|
+
operatorMap: {}
|
|
2192
|
+
};
|
|
2193
|
+
const valueProcessorCanActAsRuleProcessor = new Set([
|
|
2194
|
+
"cel",
|
|
2195
|
+
"drizzle",
|
|
2196
|
+
"elasticsearch",
|
|
2197
|
+
"jsonata",
|
|
2198
|
+
"jsonlogic",
|
|
2199
|
+
"ldap",
|
|
2200
|
+
"mongodb_query",
|
|
2201
|
+
"mongodb",
|
|
2202
|
+
"prisma",
|
|
2203
|
+
"sequelize",
|
|
2204
|
+
"spel"
|
|
2205
|
+
]);
|
|
2206
|
+
const sqlFormats = new Set([
|
|
2207
|
+
"sql",
|
|
2208
|
+
"parameterized",
|
|
2209
|
+
"parameterized_named",
|
|
2210
|
+
"drizzle",
|
|
2211
|
+
"prisma",
|
|
2212
|
+
"sequelize"
|
|
2213
|
+
]);
|
|
2214
|
+
function formatQuery(ruleGroup, optionParam = {}) {
|
|
2215
|
+
const options = typeof optionParam === "string" ? { format: lc(optionParam) } : optionParam;
|
|
2216
|
+
const optObj = {
|
|
2217
|
+
...defaultFormatQueryOptions,
|
|
2218
|
+
...!options.format || sqlFormats.has(options.format) ? sqlDialectPresets[options.preset ?? "ansi"] ?? null : null,
|
|
2219
|
+
...options,
|
|
2220
|
+
...!options.format && (Object.keys(sqlDialectPresets).includes(options.preset ?? "") ? { format: "sql" } : null)
|
|
2221
|
+
};
|
|
2222
|
+
const format = lc(optObj.format);
|
|
2223
|
+
const { fallbackExpression: fallbackExpression_option, getOperators: getOperators_option, operatorProcessor: operatorProcessor_option, parseNumbers, quoteFieldNamesWith: quoteFieldNamesWith_option, ruleGroupProcessor: ruleGroupProcessor_option, ruleProcessor: ruleProcessor_option, validator, valueProcessor: valueProcessor_option, context } = optObj;
|
|
2224
|
+
const getParseNumberBoolean = (inputType) => {
|
|
2225
|
+
return typeof getParseNumberMethod({
|
|
2226
|
+
parseNumbers,
|
|
2227
|
+
inputType
|
|
2228
|
+
}) === "string" ? true : typeof parseNumbers === "boolean" ? parseNumbers : void 0;
|
|
2229
|
+
};
|
|
2230
|
+
const operatorProcessor = typeof operatorProcessor_option === "function" ? operatorProcessor_option : defaultOperatorProcessors[format] ?? defaultOperatorProcessor;
|
|
2231
|
+
const valueProcessor = typeof valueProcessor_option === "function" ? isValueProcessorLegacy(valueProcessor_option) ? (r) => valueProcessor_option(r.field, r.operator, r.value, r.valueSource) : valueProcessor_option : format === "natural_language" ? defaultValueProcessorNL : valueProcessorCanActAsRuleProcessor.has(format) ? ruleProcessor_option ?? defaultRuleProcessors[format] : defaultValueProcessorByRule;
|
|
2232
|
+
const ruleProcessor = (typeof ruleProcessor_option === "function" ? ruleProcessor_option : null) ?? (valueProcessorCanActAsRuleProcessor.has(format) && typeof ruleProcessor_option !== "function" && valueProcessor_option ? valueProcessor : null) ?? defaultRuleProcessors[format] ?? defaultRuleProcessorSQL;
|
|
2233
|
+
const quoteFieldNamesWith = getQuoteFieldNamesWithArray(quoteFieldNamesWith_option);
|
|
2234
|
+
const fields = toFullOptionList(optObj.fields);
|
|
2235
|
+
const getOperators = (f, m) => toFullOptionList(getOperators_option(f, m) ?? []);
|
|
2236
|
+
const fallbackExpression = fallbackExpression_option ?? defaultFallbackExpressions[format] ?? defaultFallbackExpressions.sql;
|
|
2237
|
+
let validationMap = {};
|
|
2238
|
+
// istanbul ignore else
|
|
2239
|
+
if (typeof validator === "function") {
|
|
2240
|
+
const validationResult = validator(ruleGroup);
|
|
2241
|
+
if (typeof validationResult === "boolean") {
|
|
2242
|
+
// istanbul ignore else
|
|
2243
|
+
if (!validationResult) return format === "parameterized" ? {
|
|
2244
|
+
sql: fallbackExpression,
|
|
2245
|
+
params: []
|
|
2246
|
+
} : format === "parameterized_named" ? {
|
|
2247
|
+
sql: fallbackExpression,
|
|
2248
|
+
params: {}
|
|
2249
|
+
} : format === "mongodb" ? `{${fallbackExpression}}` : format === "mongodb_query" ? mongoDbFallback : format === "prisma" ? prismaFallback : format === "jsonlogic" ? false : format === "elasticsearch" ? {} : format === "drizzle" || format === "sequelize" ? void 0 : fallbackExpression;
|
|
2250
|
+
} else validationMap = validationResult;
|
|
2251
|
+
}
|
|
2252
|
+
const validatorMap = {};
|
|
2253
|
+
const uniqueFields = toFlatOptionArray(fields);
|
|
2254
|
+
for (const f of uniqueFields)
|
|
2255
|
+
// istanbul ignore else
|
|
2256
|
+
if (typeof f.validator === "function") validatorMap[f.value ?? f.name] = f.validator;
|
|
2257
|
+
const validateRule = (rule) => {
|
|
2258
|
+
let validationResult;
|
|
2259
|
+
let fieldValidator;
|
|
2260
|
+
if (rule.id) validationResult = validationMap[rule.id];
|
|
2261
|
+
if (uniqueFields.length > 0) {
|
|
2262
|
+
const fieldArr = uniqueFields.filter((f) => f.name === rule.field);
|
|
2263
|
+
if (fieldArr.length > 0) {
|
|
2264
|
+
const field = fieldArr[0];
|
|
2265
|
+
// istanbul ignore else
|
|
2266
|
+
if (typeof field.validator === "function") fieldValidator = field.validator;
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
return [validationResult, fieldValidator];
|
|
2270
|
+
};
|
|
2271
|
+
const finalOptions = {
|
|
2272
|
+
...optObj,
|
|
2273
|
+
fallbackExpression,
|
|
2274
|
+
fields,
|
|
2275
|
+
format,
|
|
2276
|
+
getOperators,
|
|
2277
|
+
getParseNumberBoolean,
|
|
2278
|
+
quoteFieldNamesWith,
|
|
2279
|
+
operatorProcessor,
|
|
2280
|
+
ruleProcessor,
|
|
2281
|
+
valueProcessor,
|
|
2282
|
+
validateRule,
|
|
2283
|
+
validationMap,
|
|
2284
|
+
context
|
|
2285
|
+
};
|
|
2286
|
+
if (typeof ruleGroupProcessor_option === "function") return ruleGroupProcessor_option(ruleGroup, finalOptions);
|
|
2287
|
+
switch (format) {
|
|
2288
|
+
case "json":
|
|
2289
|
+
case "json_without_ids": {
|
|
2290
|
+
const rg = parseNumbers ? produce(ruleGroup, (g) => numerifyValues(g, finalOptions)) : ruleGroup;
|
|
2291
|
+
if (format === "json_without_ids") return JSON.stringify(rg, (key, value) => key === "id" || key === "path" ? void 0 : bigIntJsonStringifyReplacer(key, value));
|
|
2292
|
+
return JSON.stringify(rg, bigIntJsonStringifyReplacer, 2);
|
|
2293
|
+
}
|
|
2294
|
+
case "sql": return defaultRuleGroupProcessorSQL(ruleGroup, finalOptions);
|
|
2295
|
+
case "parameterized":
|
|
2296
|
+
case "parameterized_named": return defaultRuleGroupProcessorParameterized(ruleGroup, finalOptions);
|
|
2297
|
+
case "mongodb": return defaultRuleGroupProcessorMongoDB(ruleGroup, finalOptions);
|
|
2298
|
+
case "mongodb_query": return defaultRuleGroupProcessorMongoDBQuery(ruleGroup, finalOptions);
|
|
2299
|
+
case "cel": return defaultRuleGroupProcessorCEL(ruleGroup, finalOptions);
|
|
2300
|
+
case "spel": return defaultRuleGroupProcessorSpEL(ruleGroup, finalOptions);
|
|
2301
|
+
case "jsonata": return defaultRuleGroupProcessorJSONata(ruleGroup, finalOptions);
|
|
2302
|
+
case "jsonlogic": return defaultRuleGroupProcessorJsonLogic(ruleGroup, finalOptions);
|
|
2303
|
+
case "elasticsearch": return defaultRuleGroupProcessorElasticSearch(ruleGroup, finalOptions);
|
|
2304
|
+
case "natural_language": return defaultRuleGroupProcessorNL(ruleGroup, finalOptions);
|
|
2305
|
+
case "ldap": return defaultRuleGroupProcessorLDAP(ruleGroup, finalOptions);
|
|
2306
|
+
case "prisma": return defaultRuleGroupProcessorPrisma(ruleGroup, finalOptions);
|
|
2307
|
+
case "drizzle": return defaultRuleGroupProcessorDrizzle(ruleGroup, finalOptions);
|
|
2308
|
+
case "sequelize": return defaultRuleGroupProcessorSequelize(ruleGroup, finalOptions);
|
|
2309
|
+
default: return "";
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
//#endregion
|
|
2314
|
+
//#region src/utils/formatQuery/index.ts
|
|
2315
|
+
const generateValueProcessor = (vpbr) => (field, operator, value, valueSource) => vpbr({
|
|
2316
|
+
field,
|
|
2317
|
+
operator,
|
|
2318
|
+
value,
|
|
2319
|
+
valueSource
|
|
2320
|
+
}, { parseNumbers: false });
|
|
2321
|
+
/**
|
|
2322
|
+
* Default value processor used by {@link formatQuery} for "sql" format.
|
|
2323
|
+
*
|
|
2324
|
+
* @group Export
|
|
2325
|
+
*/
|
|
2326
|
+
const defaultValueProcessor = generateValueProcessor(defaultValueProcessorByRule);
|
|
2327
|
+
/**
|
|
2328
|
+
* @deprecated Prefer {@link defaultRuleProcessorMongoDB}.
|
|
2329
|
+
*
|
|
2330
|
+
* @group Export
|
|
2331
|
+
*/
|
|
2332
|
+
const defaultMongoDBValueProcessor = generateValueProcessor(defaultRuleProcessorMongoDB);
|
|
2333
|
+
/**
|
|
2334
|
+
* @deprecated Prefer {@link defaultRuleProcessorCEL}.
|
|
2335
|
+
*
|
|
2336
|
+
* @group Export
|
|
2337
|
+
*/
|
|
2338
|
+
const defaultCELValueProcessor = generateValueProcessor(defaultRuleProcessorCEL);
|
|
2339
|
+
/**
|
|
2340
|
+
* @deprecated Prefer {@link defaultRuleProcessorSpEL}.
|
|
2341
|
+
*
|
|
2342
|
+
* @group Export
|
|
2343
|
+
*/
|
|
2344
|
+
const defaultSpELValueProcessor = generateValueProcessor(defaultRuleProcessorSpEL);
|
|
2345
|
+
/**
|
|
2346
|
+
* @deprecated Renamed to {@link defaultRuleProcessorCEL}.
|
|
2347
|
+
*
|
|
2348
|
+
* @group Export
|
|
2349
|
+
*/
|
|
2350
|
+
const defaultValueProcessorCELByRule = defaultRuleProcessorCEL;
|
|
2351
|
+
/**
|
|
2352
|
+
* @deprecated Renamed to {@link defaultRuleProcessorMongoDB}.
|
|
2353
|
+
*
|
|
2354
|
+
* @group Export
|
|
2355
|
+
*/
|
|
2356
|
+
const defaultValueProcessorMongoDBByRule = defaultRuleProcessorMongoDB;
|
|
2357
|
+
/**
|
|
2358
|
+
* @deprecated Renamed to {@link defaultRuleProcessorSpEL}.
|
|
2359
|
+
*
|
|
2360
|
+
* @group Export
|
|
2361
|
+
*/
|
|
2362
|
+
const defaultValueProcessorSpELByRule = defaultRuleProcessorSpEL;
|
|
2363
|
+
|
|
2364
|
+
//#endregion
|
|
2365
|
+
export { bigIntJsonParseReviver, bigIntJsonStringifyReplacer, celCombinatorMap, defaultCELValueProcessor, defaultExportOperatorMap, defaultMongoDBValueProcessor, defaultNLTranslations, defaultOperatorProcessorNL, defaultOperatorProcessorSQL, defaultRuleGroupProcessorCEL, defaultRuleGroupProcessorDrizzle, defaultRuleGroupProcessorElasticSearch, defaultRuleGroupProcessorJSONata, defaultRuleGroupProcessorJsonLogic, defaultRuleGroupProcessorLDAP, defaultRuleGroupProcessorMongoDB, defaultRuleGroupProcessorMongoDBQuery, defaultRuleGroupProcessorNL, defaultRuleGroupProcessorParameterized, defaultRuleGroupProcessorPrisma, defaultRuleGroupProcessorSQL, defaultRuleGroupProcessorSequelize, defaultRuleGroupProcessorSpEL, defaultRuleProcessorCEL, defaultRuleProcessorDrizzle, defaultRuleProcessorElasticSearch, defaultRuleProcessorJSONata, defaultRuleProcessorJsonLogic, defaultRuleProcessorLDAP, defaultRuleProcessorMongoDB, defaultRuleProcessorMongoDBQuery, defaultRuleProcessorNL, defaultRuleProcessorParameterized, defaultRuleProcessorPrisma, defaultRuleProcessorSQL, defaultRuleProcessorSequelize, defaultRuleProcessorSpEL, defaultSpELValueProcessor, defaultValueProcessor, defaultValueProcessorByRule, defaultValueProcessorCELByRule, defaultValueProcessorMongoDBByRule, defaultValueProcessorNL, defaultValueProcessorSpELByRule, formatQuery, formatQueryOptionPresets, getNLTranslataion, getQuoteFieldNamesWithArray, getQuotedFieldName, isValidValue, isValueProcessorLegacy, jsonLogicAdditionalOperators, mapSQLOperator, mongoDbFallback, mongoOperators, normalizeConstituentWordOrder, numerifyValues, prismaFallback, prismaOperators, processMatchMode, shouldRenderAsNumber, sqlDialectPresets };
|
|
2366
|
+
//# sourceMappingURL=formatQuery.mjs.map
|