UNPKG

react-querybuilder

Version:

React Query Builder component for constructing queries and filters, with utilities for executing them in various database and evaluation contexts

1,191 lines (1,168 loc) 127 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/utils/formatQuery/index.ts var formatQuery_exports = {}; __export(formatQuery_exports, { bigIntJsonParseReviver: () => bigIntJsonParseReviver, bigIntJsonStringifyReplacer: () => bigIntJsonStringifyReplacer, celCombinatorMap: () => celCombinatorMap, defaultCELValueProcessor: () => defaultCELValueProcessor, defaultExportOperatorMap: () => defaultExportOperatorMap, defaultMongoDBValueProcessor: () => defaultMongoDBValueProcessor, defaultNLTranslations: () => defaultNLTranslations, defaultOperatorProcessorNL: () => defaultOperatorProcessorNL, defaultOperatorProcessorSQL: () => defaultOperatorProcessorSQL, defaultRuleGroupProcessorCEL: () => defaultRuleGroupProcessorCEL, defaultRuleGroupProcessorDrizzle: () => defaultRuleGroupProcessorDrizzle, defaultRuleGroupProcessorElasticSearch: () => defaultRuleGroupProcessorElasticSearch, defaultRuleGroupProcessorJSONata: () => defaultRuleGroupProcessorJSONata, defaultRuleGroupProcessorJsonLogic: () => defaultRuleGroupProcessorJsonLogic, defaultRuleGroupProcessorLDAP: () => defaultRuleGroupProcessorLDAP, defaultRuleGroupProcessorMongoDB: () => defaultRuleGroupProcessorMongoDB, defaultRuleGroupProcessorMongoDBQuery: () => defaultRuleGroupProcessorMongoDBQuery, defaultRuleGroupProcessorNL: () => defaultRuleGroupProcessorNL, defaultRuleGroupProcessorParameterized: () => defaultRuleGroupProcessorParameterized, defaultRuleGroupProcessorPrisma: () => defaultRuleGroupProcessorPrisma, defaultRuleGroupProcessorSQL: () => defaultRuleGroupProcessorSQL, defaultRuleGroupProcessorSequelize: () => defaultRuleGroupProcessorSequelize, defaultRuleGroupProcessorSpEL: () => defaultRuleGroupProcessorSpEL, defaultRuleProcessorCEL: () => defaultRuleProcessorCEL, defaultRuleProcessorDrizzle: () => defaultRuleProcessorDrizzle, defaultRuleProcessorElasticSearch: () => defaultRuleProcessorElasticSearch, defaultRuleProcessorJSONata: () => defaultRuleProcessorJSONata, defaultRuleProcessorJsonLogic: () => defaultRuleProcessorJsonLogic, defaultRuleProcessorLDAP: () => defaultRuleProcessorLDAP, defaultRuleProcessorMongoDB: () => defaultRuleProcessorMongoDB, defaultRuleProcessorMongoDBQuery: () => defaultRuleProcessorMongoDBQuery, defaultRuleProcessorNL: () => defaultRuleProcessorNL, defaultRuleProcessorParameterized: () => defaultRuleProcessorParameterized, defaultRuleProcessorPrisma: () => defaultRuleProcessorPrisma, defaultRuleProcessorSQL: () => defaultRuleProcessorSQL, defaultRuleProcessorSequelize: () => defaultRuleProcessorSequelize, defaultRuleProcessorSpEL: () => defaultRuleProcessorSpEL, defaultSpELValueProcessor: () => defaultSpELValueProcessor, defaultValueProcessor: () => defaultValueProcessor, defaultValueProcessorByRule: () => defaultValueProcessorByRule, defaultValueProcessorCELByRule: () => defaultValueProcessorCELByRule, defaultValueProcessorMongoDBByRule: () => defaultValueProcessorMongoDBByRule, defaultValueProcessorNL: () => defaultValueProcessorNL, defaultValueProcessorSpELByRule: () => defaultValueProcessorSpELByRule, formatQuery: () => formatQuery, formatQueryOptionPresets: () => formatQueryOptionPresets, getNLTranslataion: () => getNLTranslataion, getQuoteFieldNamesWithArray: () => getQuoteFieldNamesWithArray, getQuotedFieldName: () => getQuotedFieldName, isValidValue: () => isValidValue, isValueProcessorLegacy: () => isValueProcessorLegacy, jsonLogicAdditionalOperators: () => jsonLogicAdditionalOperators, mapSQLOperator: () => mapSQLOperator, mongoDbFallback: () => mongoDbFallback, mongoOperators: () => mongoOperators, normalizeConstituentWordOrder: () => normalizeConstituentWordOrder, numerifyValues: () => numerifyValues, prismaFallback: () => prismaFallback, prismaOperators: () => prismaOperators, processMatchMode: () => processMatchMode, shouldRenderAsNumber: () => shouldRenderAsNumber, sqlDialectPresets: () => sqlDialectPresets }); module.exports = __toCommonJS(formatQuery_exports); // src/defaults.ts var defaultPlaceholderName = "~"; var defaultPlaceholderFieldName = defaultPlaceholderName; var defaultPlaceholderOperatorName = defaultPlaceholderName; var defaultJoinChar = ","; var defaultCombinators = [ { name: "and", value: "and", label: "AND" }, { name: "or", value: "or", label: "OR" } ]; var defaultCombinatorsExtended = [ ...defaultCombinators, { name: "xor", value: "xor", label: "XOR" } ]; // src/utils/arrayUtils.ts var splitBy = (str, splitChar = defaultJoinChar) => typeof str === "string" ? str.split(`\\${splitChar}`).map((c) => c.split(splitChar)).reduce((prev, curr, idx) => { if (idx === 0) { return curr; } return [...prev.slice(0, -1), `${prev.at(-1)}${splitChar}${curr[0]}`, ...curr.slice(1)]; }, []) : []; var joinWith = (strArr, joinChar = defaultJoinChar) => strArr.map((str) => `${str ?? ""}`.replaceAll(joinChar[0], `\\${joinChar[0]}`)).join(joinChar); var trimIfString = (val) => typeof val === "string" ? val.trim() : val; var toArray = (v, { retainEmptyStrings } = {}) => Array.isArray(v) ? v.map((v2) => trimIfString(v2)) : typeof v === "string" ? splitBy(v, defaultJoinChar).filter(retainEmptyStrings ? () => true : (s) => !/^\s*$/.test(s)).map((s) => s.trim()) : typeof v === "number" ? [v] : []; // src/utils/misc.ts var import_numeric_quantity = require("numeric-quantity"); var lc = (v) => typeof v === "string" ? v.toLowerCase() : v; var numericRegex = new RegExp( import_numeric_quantity.numericRegex.source.replace(/^\^/, String.raw`^\s*`).replace(/\$$/, String.raw`\s*$`) ); var isPojo = (obj) => obj === null || typeof obj !== "object" ? false : Object.getPrototypeOf(obj) === Object.prototype; var nullOrUndefinedOrEmpty = (value) => value === null || value === void 0 || value === ""; // src/utils/parseNumber.ts var import_numeric_quantity2 = require("numeric-quantity"); var parseNumber = (val, { parseNumbers, bigIntOnOverflow } = {}) => { if (!parseNumbers || typeof val === "bigint" || typeof val === "number") { return val; } if (parseNumbers === "native") { return Number.parseFloat(val); } const valAsNum = ( // TODO: Should these options be configurable? (0, import_numeric_quantity2.numericQuantity)(val, { allowTrailingInvalid: parseNumbers === "enhanced", bigIntOnOverflow, romanNumerals: false, round: false }) ); return typeof valAsNum === "bigint" || !Number.isNaN(valAsNum) ? valAsNum : val; }; // src/utils/transformQuery.ts var import_immer = require("immer"); // src/utils/isRuleGroup.ts var isRuleGroup = (rg) => isPojo(rg) && Array.isArray(rg.rules); var isRuleGroupType = (rg) => isRuleGroup(rg) && typeof rg.combinator === "string"; var isRuleGroupTypeIC = (rg) => isRuleGroup(rg) && rg.combinator === void 0; // src/utils/transformQuery.ts var remapProperties = (obj, propertyMap, deleteRemappedProperties) => (0, import_immer.produce)(obj, (draft) => { for (const [k, v] of Object.entries(propertyMap)) { if (v === false) { delete draft[k]; } else if (!!v && k !== v && k in draft) { draft[v] = draft[k]; if (deleteRemappedProperties) { delete draft[k]; } } } }); function transformQuery(query, options = {}) { const { ruleProcessor = (r) => r, ruleGroupProcessor = (rg) => rg, propertyMap = {}, combinatorMap = {}, operatorMap = {}, omitPath = false, deleteRemappedProperties = true } = options; const processGroup = (rg) => ({ ...ruleGroupProcessor( remapProperties( { ...rg, ...isRuleGroupType(rg) ? { combinator: combinatorMap[rg.combinator] ?? rg.combinator } : {} }, propertyMap, deleteRemappedProperties ) ), ...propertyMap["rules"] === false ? null : { // oxlint-disable-next-line typescript/no-explicit-any [propertyMap["rules"] ?? "rules"]: rg.rules.map((r, idx) => { const pathObject = omitPath ? null : { path: [...rg.path, idx] }; if (typeof r === "string") { return combinatorMap[r] ?? r; } else if (isRuleGroup(r)) { return processGroup({ ...r, ...pathObject }); } return ruleProcessor( remapProperties( { ...r, ...pathObject, ..."operator" in r ? { operator: operatorMap[r.operator] ?? r.operator } : {} }, propertyMap, deleteRemappedProperties ) ); }) } }); return processGroup({ ...query, ...omitPath ? null : { path: [] } }); } // src/utils/isRuleOrGroupValid.ts var isValidationResult = (vr) => isPojo(vr) && typeof vr.valid === "boolean"; var isRuleOrGroupValid = (rg, validationResult, validator) => { if (typeof validationResult === "boolean") { return validationResult; } if (isValidationResult(validationResult)) { return validationResult.valid; } if (typeof validator === "function" && !isRuleGroup(rg)) { const vr = validator(rg); if (typeof vr === "boolean") { return vr; } if (isValidationResult(vr)) { return vr.valid; } } return true; }; // src/utils/optGroupUtils.ts var import_immer2 = require("immer"); var isOptionWithName = (opt) => isPojo(opt) && "name" in opt && typeof opt.name === "string"; var isOptionWithValue = (opt) => isPojo(opt) && "value" in opt && typeof opt.value === "string"; function toFullOption(opt, baseProperties, labelMap) { const recipe = (0, import_immer2.produce)((draft) => { const idObj = {}; let needsUpdating = !!baseProperties; if (typeof draft === "string") { return { ...baseProperties, name: draft, value: draft, label: labelMap?.[draft] ?? draft }; } if (isOptionWithName(draft) && !isOptionWithValue(draft)) { idObj.value = draft.name; needsUpdating = true; } else if (!isOptionWithName(draft) && isOptionWithValue(draft)) { idObj.name = draft.value; needsUpdating = true; } if (needsUpdating) { return Object.assign({}, baseProperties, draft, idObj); } }); return recipe(opt); } function toFullOptionList(optList, baseProperties, labelMap) { if (!Array.isArray(optList)) { return []; } const recipe = (0, import_immer2.produce)((draft) => { if (isFlexibleOptionGroupArray(draft)) { for (const optGroup of draft) { for (const [idx, opt] of optGroup.options.entries()) optGroup.options[idx] = toFullOption(opt, baseProperties, labelMap); } } else { for (const [idx, opt] of draft.entries()) draft[idx] = toFullOption(opt, baseProperties, labelMap); } }); return recipe(optList); } var uniqByIdentifier = (originalArray) => { const names = /* @__PURE__ */ new Set(); const newArray = []; for (const el of originalArray) { if (!names.has(el.value ?? el.name)) { names.add(el.value ?? el.name); newArray.push(el); } } return originalArray.length === newArray.length ? originalArray : newArray; }; var isOptionGroupArray = (arr) => Array.isArray(arr) && arr.length > 0 && isPojo(arr[0]) && "options" in arr[0] && Array.isArray(arr[0].options); var isFlexibleOptionArray = (arr) => { let isFOA = false; if (Array.isArray(arr)) { for (const o of arr) { if (isOptionWithName(o) || isOptionWithValue(o)) { isFOA = true; } else { return false; } } } return isFOA; }; var isFlexibleOptionGroupArray = (arr, { allowEmpty = false } = {}) => { let isFOGA = false; if (Array.isArray(arr)) { for (const og of arr) { if (isPojo(og) && "options" in og && (isFlexibleOptionArray(og.options) || allowEmpty && Array.isArray(og.options) && og.options.length === 0)) { isFOGA = true; } else { return false; } } } return isFOGA; }; var getOption = (arr, name) => (isFlexibleOptionGroupArray(arr, { allowEmpty: true }) ? arr.flatMap((og) => og.options) : arr).find((op) => op.value === name || op.name === name); var toFlatOptionArray = (arr) => uniqByIdentifier(isOptionGroupArray(arr) ? arr.flatMap((og) => og.options) : arr); // src/utils/getParseNumberMethod.ts var getParseNumberMethod = ({ parseNumbers, inputType }) => { if (typeof parseNumbers === "string") { const [method, level] = parseNumbers.split("-"); if (level === "limited") { return inputType === "number" ? method : false; } return method; } return parseNumbers ? "strict" : false; }; // src/utils/formatQuery/utils.ts var mapSQLOperator = (rqbOperator) => { switch (lc(rqbOperator)) { case "null": return "is null"; case "notnull": return "is not null"; case "notin": return "not in"; case "notbetween": return "not between"; case "contains": case "beginswith": case "endswith": return "like"; case "doesnotcontain": case "doesnotbeginwith": case "doesnotendwith": return "not like"; default: return rqbOperator; } }; var mongoOperators = { "=": "$eq", "!=": "$ne", "<": "$lt", "<=": "$lte", ">": "$gt", ">=": "$gte", in: "$in", notin: "$nin", notIn: "$nin" // only here for backwards compatibility }; var prismaOperators = { "=": "equals", "!=": "not", "<": "lt", "<=": "lte", ">": "gt", ">=": "gte", in: "in", notin: "notIn" }; var celCombinatorMap = { and: "&&", or: "||" }; var jsonLogicAdditionalOperators = { startsWith: (a, b) => typeof a === "string" && a.startsWith(b), endsWith: (a, b) => typeof a === "string" && a.endsWith(b) }; var numerifyValues = (rg, options) => ({ ...rg, // @ts-expect-error TS doesn't keep track of odd/even indexes here rules: rg.rules.map((r) => { if (typeof r === "string") { return r; } if (isRuleGroup(r)) { return numerifyValues(r, options); } const fieldData = getOption(options.fields, r.field); const parseNumbers = getParseNumberMethod({ parseNumbers: options.parseNumbers, inputType: fieldData?.inputType }); if (Array.isArray(r.value)) { return { ...r, value: r.value.map((v) => parseNumber(v, { parseNumbers })) }; } const valAsArray = toArray(r.value, { retainEmptyStrings: true }).map( (v) => parseNumber(v, { parseNumbers }) ); if (valAsArray.every((v) => typeof v === "number")) { if (valAsArray.length > 1) { return { ...r, value: valAsArray }; } else if (valAsArray.length === 1) { return { ...r, value: valAsArray[0] }; } } return r; }) }); var isValidValue = (value) => typeof value === "string" && value.length > 0 || typeof value === "number" && !Number.isNaN(value) || typeof value !== "string" && typeof value !== "number"; var shouldRenderAsNumber = (value, parseNumbers) => !!parseNumbers && (typeof value === "number" || typeof value === "bigint" || typeof value === "string" && numericRegex.test(value)); var isValueProcessorLegacy = (valueProcessor) => valueProcessor.length >= 3; var getQuoteFieldNamesWithArray = (quoteFieldNamesWith = ["", ""]) => Array.isArray(quoteFieldNamesWith) ? quoteFieldNamesWith : typeof quoteFieldNamesWith === "string" ? [quoteFieldNamesWith, quoteFieldNamesWith] : quoteFieldNamesWith ?? ["", ""]; var getQuotedFieldName = (fieldName, { quoteFieldNamesWith, fieldIdentifierSeparator }) => { const [qPre, qPost] = getQuoteFieldNamesWithArray(quoteFieldNamesWith); return typeof fieldIdentifierSeparator === "string" && fieldIdentifierSeparator.length > 0 ? joinWith( splitBy(fieldName, fieldIdentifierSeparator).map((part) => `${qPre}${part}${qPost}`), fieldIdentifierSeparator ) : `${qPre}${fieldName}${qPost}`; }; var defaultWordOrder = ["S", "V", "O"]; var normalizeConstituentWordOrder = (input) => { const result = []; const letterSet = new Set(defaultWordOrder); for (const char of input.toUpperCase()) { if (letterSet.has(char)) { result.push(char); letterSet.delete(char); if (letterSet.size === 0) break; } } for (const letter of defaultWordOrder) { if (letterSet.has(letter)) { result.push(letter); } } return result; }; var defaultNLTranslations = { // and: 'and', // or: 'or', // true: 'true', // false: 'false', groupPrefix: "", // groupPrefix_not: '', groupPrefix_not_xor: "either zero or more than one of", groupPrefix_xor: "exactly one of", groupSuffix: "is true", groupSuffix_not: "is not true" // groupSuffix_not_xor: 'is true', // groupSuffix_xor: 'is true', }; var translationMatchFilter = (key, keyToTest, conditions) => ( // The translation matches the base key keyToTest.startsWith(key) && // The translation specifies all conditions conditions.every( (c) => ( // This translation specifies _this_ condition keyToTest.includes(`_${c}`) && // This translation specifies the same _total number_ of conditions keyToTest.match(/_/g)?.length === conditions.length ) ) ); var getNLTranslataion = (key, translations, conditions = []) => conditions.length === 0 ? translations[key] ?? defaultNLTranslations[key] ?? /* istanbul ignore next */ "" : Object.entries(translations).find( ([keyToTest]) => translationMatchFilter(key, keyToTest, conditions) )?.[1] ?? Object.entries(defaultNLTranslations).find( ([keyToTest]) => translationMatchFilter(key, keyToTest, conditions) )?.[1] ?? defaultNLTranslations[key] ?? /* istanbul ignore next */ ""; var processMatchMode = (rule) => { const { mode, threshold } = rule.match ?? {}; if (mode) { if (!isRuleGroup(rule.value)) return false; const matchModeLC = lc(mode); const matchModeCoerced = matchModeLC === "atleast" && threshold === 1 ? "some" : matchModeLC === "atmost" && threshold === 0 ? "none" : matchModeLC; if ((matchModeCoerced === "atleast" || matchModeCoerced === "atmost" || matchModeCoerced === "exactly") && (typeof threshold !== "number" || threshold < 0)) { return false; } return { mode: matchModeCoerced, threshold }; } }; var bigIntJsonStringifyReplacer = (_key, value) => typeof value === "bigint" ? { $bigint: value.toString() } : value; var bigIntJsonParseReviver = (_key, value) => isPojo(value) && Object.keys(value).length === 1 && typeof value.$bigint === "string" ? BigInt(value.$bigint) : value; // src/utils/formatQuery/defaultRuleGroupProcessorCEL.ts var defaultRuleGroupProcessorCEL = (ruleGroup, options) => { const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options; const processRuleGroup = (rg, outermost) => { if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* istanbul ignore next */ ""])) { return outermost ? fallbackExpression : ""; } const expression = rg.rules.map((rule) => { if (typeof rule === "string") { return celCombinatorMap[rule]; } if (isRuleGroup(rule)) { return processRuleGroup(rule); } const [validationResult, fieldValidator] = validateRule(rule); if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || /* istanbul ignore next */ placeholderValueName !== void 0 && rule.value === placeholderValueName) { return ""; } const fieldData = getOption(fields, rule.field); return ruleProcessor(rule, { ...options, parseNumbers: getParseNumberBoolean(fieldData?.inputType), escapeQuotes: (rule.valueSource ?? "value") === "value", fieldData }); }).filter(Boolean).join( isRuleGroupType(rg) ? ` ${celCombinatorMap[rg.combinator]} ` : " " ); const [prefix, suffix] = rg.not || !outermost ? [`${rg.not ? "!" : ""}(`, ")"] : ["", ""]; return expression ? `${prefix}${expression}${suffix}` : fallbackExpression; }; return processRuleGroup(ruleGroup, true); }; // src/utils/formatQuery/defaultRuleProcessorCEL.ts var shouldNegate = (op) => op.startsWith("not") || op.startsWith("doesnot"); var escapeDoubleQuotes = (v, escapeQuotes) => typeof v !== "string" || !escapeQuotes ? v : v.replaceAll(`"`, `\\"`); var defaultRuleProcessorCEL = (rule, opts = {}) => { const { escapeQuotes, parseNumbers, preserveValueOrder } = opts; const { field, operator, value, valueSource } = rule; const valueIsField = valueSource === "field"; const operatorTL = lc(operator === "=" ? "==" : operator); const useBareValue = typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || shouldRenderAsNumber(value, parseNumbers); const matchEval = processMatchMode(rule); if (matchEval === false) { return ""; } else if (matchEval) { const { mode, threshold } = matchEval; const arrayElementAlias = "elem_alias"; const celQuery = transformQuery(rule.value, { ruleProcessor: (r) => ({ ...r, field: `${arrayElementAlias}${r.field ? `.${r.field}` : ""}` }) }); const nestedArrayFilter = defaultRuleGroupProcessorCEL( celQuery, opts ); switch (mode) { case "all": return `${field}.all(${arrayElementAlias}, ${nestedArrayFilter})`; case "none": case "some": return `${mode === "none" ? "!" : ""}${field}.exists(${arrayElementAlias}, ${nestedArrayFilter})`; case "atleast": case "atmost": case "exactly": { const totalCount = `double(${field}.size())`; const filteredCount = `${field}.filter(${arrayElementAlias}, ${nestedArrayFilter}).size()`; const op = mode === "atleast" ? ">=" : mode === "atmost" ? "<=" : "=="; if (threshold > 0 && threshold < 1) { return `${filteredCount} ${op} (${totalCount} * ${threshold})`; } return `${filteredCount} ${op} ${threshold}`; } } } switch (operatorTL) { case "<": case "<=": case "==": case "!=": case ">": case ">=": return `${field} ${operatorTL} ${valueIsField || useBareValue ? trimIfString(value) : `"${escapeDoubleQuotes(value, escapeQuotes)}"`}`; case "contains": case "doesnotcontain": { const negate2 = shouldNegate(operatorTL) ? "!" : ""; return `${negate2}${field}.contains(${valueIsField ? trimIfString(value) : `"${escapeDoubleQuotes(value, escapeQuotes)}"`})`; } case "beginswith": case "doesnotbeginwith": { const negate2 = shouldNegate(operatorTL) ? "!" : ""; return `${negate2}${field}.startsWith(${valueIsField ? trimIfString(value) : `"${escapeDoubleQuotes(value, escapeQuotes)}"`})`; } case "endswith": case "doesnotendwith": { const negate2 = shouldNegate(operatorTL) ? "!" : ""; return `${negate2}${field}.endsWith(${valueIsField ? trimIfString(value) : `"${escapeDoubleQuotes(value, escapeQuotes)}"`})`; } case "null": return `${field} == null`; case "notnull": return `${field} != null`; case "in": case "notin": { const [prefix, suffix] = shouldNegate(operatorTL) ? ["!(", ")"] : ["", ""]; const valueAsArray = toArray(value); return `${prefix}${field} in [${valueAsArray.map( (val) => valueIsField || shouldRenderAsNumber(val, parseNumbers) ? `${trimIfString(val)}` : `"${escapeDoubleQuotes(val, escapeQuotes)}"` ).join(", ")}]${suffix}`; } case "between": case "notbetween": { const valueAsArray = toArray(value); if (valueAsArray.length >= 2 && !nullOrUndefinedOrEmpty(valueAsArray[0]) && !nullOrUndefinedOrEmpty(valueAsArray[1])) { const [first, second] = valueAsArray; const shouldParseNumbers = !(parseNumbers === false); const firstNum = shouldRenderAsNumber(first, shouldParseNumbers) ? parseNumber(first, { parseNumbers: shouldParseNumbers }) : Number.NaN; const secondNum = shouldRenderAsNumber(second, shouldParseNumbers) ? parseNumber(second, { parseNumbers: shouldParseNumbers }) : Number.NaN; let firstValue = Number.isNaN(firstNum) ? valueIsField ? `${first}` : `"${escapeDoubleQuotes(first, escapeQuotes)}"` : firstNum; let secondValue = Number.isNaN(secondNum) ? valueIsField ? `${second}` : `"${escapeDoubleQuotes(second, escapeQuotes)}"` : secondNum; if (!preserveValueOrder && firstValue === firstNum && secondValue === secondNum && secondNum < firstNum) { const tempNum = secondNum; secondValue = firstNum; firstValue = tempNum; } return operatorTL === "between" ? `(${field} >= ${firstValue} && ${field} <= ${secondValue})` : `(${field} < ${firstValue} || ${field} > ${secondValue})`; } else { return ""; } } } return ""; }; // src/utils/convertQuery.ts var import_immer3 = require("immer"); var combinatorLevels = ["or", "xor", "and"]; var isSameString = (a, b) => lc(a) === b; var generateRuleGroupICWithConsistentCombinators = (rg, baseCombinatorLevel = 0) => { const baseCombinator = combinatorLevels[baseCombinatorLevel]; if (!rg.rules.includes(baseCombinator)) { return baseCombinatorLevel < combinatorLevels.length - 2 ? generateRuleGroupICWithConsistentCombinators(rg, baseCombinatorLevel + 1) : rg; } return (0, import_immer3.produce)(rg, (draft) => { let cursor = 0; while (cursor < draft.rules.length - 2) { if (isSameString(draft.rules[cursor + 1], baseCombinator)) { cursor += 2; continue; } const nextBaseCombinatorIndex = draft.rules.findIndex( (r, i) => i > cursor && typeof r === "string" && lc(r) === baseCombinator ); if (nextBaseCombinatorIndex === -1) { draft.rules.splice( cursor, draft.rules.length, generateRuleGroupICWithConsistentCombinators( // oxlint-disable-next-line typescript/no-explicit-any { rules: draft.rules.slice(cursor) }, baseCombinatorLevel + 1 ) ); break; } else { draft.rules.splice( cursor, nextBaseCombinatorIndex - cursor, generateRuleGroupICWithConsistentCombinators( // oxlint-disable-next-line typescript/no-explicit-any { rules: draft.rules.slice(cursor, nextBaseCombinatorIndex) }, baseCombinatorLevel + 1 ) ); } } }); }; var convertFromIC = (rg) => { if (isRuleGroupType(rg)) { return rg; } const processedRG = generateRuleGroupICWithConsistentCombinators(rg); const rulesAsMixedList = processedRG.rules.map( (r) => typeof r === "string" || !isRuleGroup(r) ? r : convertFromIC(r) ); const combinator = rulesAsMixedList.length < 2 ? "and" : rulesAsMixedList[1]; const rules = rulesAsMixedList.filter((r) => typeof r !== "string"); return { ...processedRG, combinator, rules }; }; // src/utils/formatQuery/defaultRuleGroupProcessorMongoDBQuery.ts var mongoDbFallback = { $and: [{ $expr: true }] }; var defaultRuleGroupProcessorMongoDBQuery = (ruleGroup, options, meta) => { const { fields, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options; const processRuleGroup = (rg, outermost) => { if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* istanbul ignore next */ ""])) { return outermost ? mongoDbFallback : false; } const combinator = `$${lc(rg.combinator)}`; let hasChildRules = false; const expressions = rg.rules.map((rule) => { if (isRuleGroup(rule)) { const processedRuleGroup = processRuleGroup(rule); if (processedRuleGroup) { hasChildRules = true; return processedRuleGroup; } return false; } const [validationResult, fieldValidator] = validateRule(rule); if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || /* istanbul ignore next */ placeholderValueName !== void 0 && rule.value === placeholderValueName) { return false; } const fieldData = getOption(fields, rule.field); return ruleProcessor( rule, { ...options, parseNumbers: getParseNumberBoolean(fieldData?.inputType), fieldData }, meta ); }).filter(Boolean); return expressions.length > 0 ? expressions.length === 1 && !hasChildRules ? expressions[0] : { [combinator]: expressions } : mongoDbFallback; }; return processRuleGroup(convertFromIC(ruleGroup), true); }; // src/utils/formatQuery/defaultRuleProcessorMongoDBQuery.ts var processNumber = (value, fallback, parseNumbers = false) => shouldRenderAsNumber(value, parseNumbers || typeof value === "bigint") ? Number(parseNumber(value, { parseNumbers: "strict" })) : fallback; var defaultRuleProcessorMongoDBQuery = (rule, options = {}) => { const { field, operator, value, valueSource } = rule; const { parseNumbers, preserveValueOrder, context } = options; const valueIsField = valueSource === "field"; const { avoidFieldsAsKeys } = context ?? {}; const matchEval = processMatchMode(rule); if (matchEval === false) { return; } else if (matchEval) { const { mode, threshold } = matchEval; const totalCount = { $size: { $ifNull: [`$${field}`, []] } }; const subQueryNoAggCtx = defaultRuleGroupProcessorMongoDBQuery( transformQuery(value, { ruleProcessor: (r) => ({ ...r, field: r.field ? `${field}.${r.field}` : field }) }), { ...options, // We have to override `ruleProcessor` in case original `format` is "mongodb" ruleProcessor: defaultRuleProcessorMongoDBQuery, context: { ...options.context, avoidFieldsAsKeys: false } } ); const subQueryWithAggCtx = defaultRuleGroupProcessorMongoDBQuery( transformQuery(value, { ruleProcessor: (r) => ({ ...r, field: r.field ? `$item.${r.field}` : "$item" }) }), { ...options, // We have to override `ruleProcessor` in case original `format` is "mongodb" ruleProcessor: defaultRuleProcessorMongoDBQuery, context: { ...options.context, avoidFieldsAsKeys: true } } ); const filteredCount = { $size: { $ifNull: [ { $filter: { input: `$${field}`, as: "item", cond: { $and: [subQueryWithAggCtx] } } }, [] ] } }; switch (mode) { case "all": return { $expr: { $eq: [filteredCount, totalCount] } }; case "none": return { $nor: [subQueryNoAggCtx] }; case "some": return subQueryNoAggCtx; case "atleast": case "atmost": case "exactly": { const op = mode === "atleast" ? mongoOperators[">="] : mode === "atmost" ? mongoOperators["<="] : mongoOperators["="]; if (threshold > 0 && threshold < 1) { return { $expr: { [op]: [filteredCount, { $multiply: [totalCount, threshold] }] } }; } return { $expr: { [op]: [filteredCount, threshold] } }; } } } if (operator === "=" && !valueIsField) { return avoidFieldsAsKeys ? { $eq: [`$${field}`, processNumber(value, value, parseNumbers)] } : { [field]: processNumber(value, value, parseNumbers) }; } const operatorLC = lc(operator); switch (operatorLC) { case "<": case "<=": case "=": case "!=": case ">": case ">=": { const mongoOperator = mongoOperators[operatorLC]; return valueIsField ? { [mongoOperator]: [`$${field}`, `$${value}`] } : avoidFieldsAsKeys ? { $and: [ { $ne: [`$${field}`, null] }, { [mongoOperator]: [`$${field}`, processNumber(value, value, parseNumbers)] } ] } : { [field]: { [mongoOperator]: processNumber(value, value, parseNumbers) } }; } case "contains": return valueIsField ? { $where: `this.${field}.includes(this.${value})` } : avoidFieldsAsKeys ? { $regexMatch: { input: `$${field}`, regex: value } } : { [field]: { $regex: value } }; case "beginswith": return valueIsField ? { $where: `this.${field}.startsWith(this.${value})` } : avoidFieldsAsKeys ? { $regexMatch: { input: `$${field}`, regex: `^${value}` } } : { [field]: { $regex: `^${value}` } }; case "endswith": return valueIsField ? { $where: `this.${field}.endsWith(this.${value})` } : avoidFieldsAsKeys ? { $regexMatch: { input: `$${field}`, regex: `${value}$` } } : { [field]: { $regex: `${value}$` } }; case "doesnotcontain": return valueIsField ? { $where: `!this.${field}.includes(this.${value})` } : avoidFieldsAsKeys ? { $not: { $regexMatch: { input: `$${field}`, regex: value } } } : { [field]: { $not: { $regex: value } } }; case "doesnotbeginwith": return valueIsField ? { $where: `!this.${field}.startsWith(this.${value})` } : avoidFieldsAsKeys ? { $not: { $regexMatch: { input: `$${field}`, regex: `^${value}` } } } : { [field]: { $not: { $regex: `^${value}` } } }; case "doesnotendwith": return valueIsField ? { $where: `!this.${field}.endsWith(this.${value})` } : avoidFieldsAsKeys ? { $not: { $regexMatch: { input: `$${field}`, regex: `${value}$` } } } : { [field]: { $not: { $regex: `${value}$` } } }; case "null": return avoidFieldsAsKeys ? { $eq: [`$${field}`, null] } : { [field]: null }; case "notnull": return avoidFieldsAsKeys ? { $ne: [`$${field}`, null] } : { [field]: { $ne: null } }; case "in": case "notin": { const valueAsArray = toArray(value); 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(val, val, parseNumbers)) ] } } : { [mongoOperators[operatorLC]]: [ `$${field}`, valueAsArray.map((val) => processNumber(val, val, parseNumbers)) ] } : { [field]: { [mongoOperators[operatorLC]]: valueAsArray.map( (val) => processNumber(val, val, parseNumbers) ) } }; } case "between": case "notbetween": { const valueAsArray = toArray(value); if (valueAsArray.length >= 2 && isValidValue(valueAsArray[0]) && isValidValue(valueAsArray[1])) { const [first, second] = valueAsArray; const firstNum = processNumber(first, Number.NaN, true); const secondNum = processNumber(second, Number.NaN, true); let firstValue = valueIsField ? first : Number.isNaN(firstNum) ? first : firstNum; let secondValue = valueIsField ? second : Number.isNaN(secondNum) ? second : secondNum; if (!preserveValueOrder && firstValue === firstNum && secondValue === secondNum && secondNum < firstNum) { const tempNum = secondNum; secondValue = firstNum; firstValue = tempNum; } if (operatorLC === "between") { return valueIsField ? { $gte: [`$${field}`, `$${firstValue}`], $lte: [`$${field}`, `$${secondValue}`] } : avoidFieldsAsKeys ? { $and: [{ $gte: [`$${field}`, firstValue] }, { $lte: [`$${field}`, secondValue] }] } : { [field]: { $gte: firstValue, $lte: secondValue } }; } 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 } }] }; } } else { return ""; } } } return ""; }; // src/utils/formatQuery/defaultRuleProcessorMongoDB.ts var defaultRuleProcessorMongoDB = (rule, options) => { const queryObj = defaultRuleProcessorMongoDBQuery(rule, options); return queryObj ? JSON.stringify(queryObj) : ""; }; // src/utils/formatQuery/defaultRuleGroupProcessorSpEL.ts var defaultRuleGroupProcessorSpEL = (ruleGroup, options) => { const { fields, fallbackExpression, getParseNumberBoolean, placeholderFieldName, placeholderOperatorName, placeholderValueName, ruleProcessor, validateRule, validationMap } = options; const processRuleGroup = (rg, outermost) => { if (!isRuleOrGroupValid(rg, validationMap[rg.id ?? /* istanbul ignore next */ ""])) { return outermost ? fallbackExpression : ""; } const expression = rg.rules.map((rule) => { if (typeof rule === "string") { return rule; } if (isRuleGroup(rule)) { return processRuleGroup(rule); } const [validationResult, fieldValidator] = validateRule(rule); if (!isRuleOrGroupValid(rule, validationResult, fieldValidator) || rule.field === placeholderFieldName || rule.operator === placeholderOperatorName || /* istanbul ignore next */ placeholderValueName !== void 0 && rule.value === placeholderValueName) { return ""; } const fieldData = getOption(fields, rule.field); return ruleProcessor(rule, { ...options, parseNumbers: getParseNumberBoolean(fieldData?.inputType), escapeQuotes: (rule.valueSource ?? "value") === "value", fieldData }); }).filter(Boolean).join(isRuleGroupType(rg) ? ` ${rg.combinator} ` : " "); const [prefix, suffix] = rg.not || !outermost ? [`${rg.not ? "!" : ""}(`, ")"] : ["", ""]; return expression ? `${prefix}${expression}${suffix}` : fallbackExpression; }; return processRuleGroup(ruleGroup, true); }; // src/utils/formatQuery/defaultRuleProcessorSpEL.ts var shouldNegate2 = (op) => op.startsWith("not") || op.startsWith("doesnot"); var wrapInNegation = (clause, negate2) => negate2 ? `!(${clause})` : `${clause}`; var escapeSingleQuotes = (v, escapeQuotes) => typeof v !== "string" || !escapeQuotes ? v : v.replaceAll(`'`, `\\'`); var defaultRuleProcessorSpEL = (rule, opts = {}) => { const { field, operator, value, valueSource } = rule; const { escapeQuotes, parseNumbers, preserveValueOrder } = opts; const valueIsField = valueSource === "field"; const operatorTL = lc(operator === "=" ? "==" : operator); const useBareValue = typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || shouldRenderAsNumber(value, parseNumbers); const matchEval = processMatchMode(rule); if (matchEval === false) { return ""; } else if (matchEval) { const { mode, threshold } = matchEval; const nestedArrayFilter = defaultRuleGroupProcessorSpEL( transformQuery(rule.value, { ruleProcessor: (r) => ({ ...r, field: `${r.field || "#this"}` }) }), opts ); const totalCount = `${field}.size()`; const filteredCount = `${field}.?[${nestedArrayFilter}].size()`; switch (mode) { case "all": return `${filteredCount} == ${totalCount}`; case "none": return `${filteredCount} == 0`; case "some": return `${filteredCount} >= 1`; case "atleast": case "atmost": case "exactly": { const op = mode === "atleast" ? ">=" : mode === "atmost" ? "<=" : "=="; if (threshold > 0 && threshold < 1) { return `${filteredCount} ${op} (${totalCount} * ${threshold})`; } return `${filteredCount} ${op} ${threshold}`; } } } switch (operatorTL) { case "<": case "<=": case "==": case "!=": case ">": case ">=": return `${field} ${operatorTL} ${valueIsField || useBareValue ? trimIfString(value) : `'${escapeSingleQuotes(value, escapeQuotes)}'`}`; case "contains": case "doesnotcontain": return wrapInNegation( `${field} matches ${valueIsField || useBareValue ? trimIfString(value) : `'${escapeSingleQuotes(value, escapeQuotes)}'`}`, shouldNegate2(operatorTL) ); case "beginswith": case "doesnotbeginwith": { const valueTL = valueIsField ? `'^'.concat(${trimIfString(value)})` : `'${typeof value === "string" && !value.startsWith("^") || useBareValue ? "^" : ""}${escapeSingleQuotes(value, escapeQuotes)}'`; return wrapInNegation(`${field} matches ${valueTL}`, shouldNegate2(operatorTL)); } case "endswith": case "doesnotendwith": { const valueTL = valueIsField ? `${trimIfString(value)}.concat('$')` : `'${escapeSingleQuotes(value, escapeQuotes)}${typeof value === "string" && !value.endsWith("$") || useBareValue ? "$" : ""}'`; return wrapInNegation(`${field} matches ${valueTL}`, shouldNegate2(operatorTL)); } case "null": return `${field} == null`; case "notnull": return `${field} != null`; case "in": case "notin": { const negate2 = shouldNegate2(operatorTL) ? "!" : ""; const valueAsArray = toArray(value); return valueAsArray.length > 0 ? `${negate2}(${valueAsArray.map( (val) => `${field} == ${valueIsField || shouldRenderAsNumber(val, parseNumbers) ? `${trimIfString(val)}` : `'${escapeSingleQuotes(val, escapeQuotes)}'`}` ).join(" or ")})` : ""; } case "between": case "notbetween": { const valueAsArray = toArray(value); if (valueAsArray.length >= 2 && !nullOrUndefinedOrEmpty(valueAsArray[0]) && !nullOrUndefinedOrEmpty(valueAsArray[1])) { const [first, second] = valueAsArray; const shouldParseNumbers = !(parseNumbers === false); const firstNum = shouldRenderAsNumber(first, shouldParseNumbers) ? parseNumber(first, { parseNumbers: shouldParseNumbers }) : Number.NaN; const secondNum = shouldRenderAsNumber(second, shouldParseNumbers) ? parseNumber(second, { parseNumbers: shouldParseNumbers }) : Number.NaN; let firstValue = Number.isNaN(firstNum) ? valueIsField ? `${first}` : `'${escapeSingleQuotes(first, escapeQuotes)}'` : firstNum; let secondValue = Number.isNaN(secondNum) ? valueIsField ? `${second}` : `'${escapeSingleQuotes(second, escapeQuotes)}'` : secondNum; if (!preserveValueOrder && firstValue === firstNum && secondValue === secondNum && secondNum < firstNum) { const tempNum = secondNum; secondValue = firstNum; firstValue = tempNum; } return operatorTL === "between" ? `(${field} >= ${firstValue} and ${field} <= ${secondValue})` : `(${field} < ${firstValue} or ${field} > ${secondValue})`; } else { return ""; } } } return ""; }; // src/utils/formatQuery/defaultValueProcessorByRule.ts var escapeStringValueQuotes = (v, quoteChar, escapeQuotes) => escapeQuotes && typeof v === "string" ? v.replaceAll(`${quoteChar}`, `${quoteChar}${quoteChar}`) : v; var defaultValueProcessorByRule = ({ operator, value, valueSource }, { escapeQuotes, parseNumbers, preserveValueOrder, quoteFieldNamesWith, quoteValuesWith, concatOperator = "||", fieldIdentifierSeparator, wrapValueWith = ["", ""], translations } = {}) => { const valueIsField = valueSource === "field"; const operatorLowerCase = lc(operator); const quoteChar = quoteValuesWith || "'"; const quoteValue = (v) => `${wrapValueWith[0]}${quoteChar}${v}${quoteChar}${wrapValueWith[1]}`; const escapeValue = (v) => escapeStringValueQuotes(v, quoteChar, escapeQuotes); const wrapAndEscape = (v) => quoteValue(escapeValue(v)); const wrapFieldName = (v) => getQuotedFieldName(v, { quoteFieldNamesWith, fieldIdentifierSeparator }); const concat = (...values) => concatOperator.toUpperCase() === "CONCAT" ? `CONCAT(${values.join(", ")})` : values.join(` ${concatOperator} `); switch (operatorLowerCase) { case "null": case "notnull": { return ""; } case "in": case "notin": { const valueAsArray = toArray(value); if (valueAsArray.length > 0) { return `(${valueAsArray.map( (v) => valueIsField ? wrapFieldName(v) : shouldRenderAsNumber(v, parseNumbers) ? `${trimIfString(v)}` : `${wrapAndEscape(v)}` ).join(", ")})`; } return ""; } case "between": case "notbetween": { const valueAsArray = toArray(value, { retainEmptyStrings: true }); if (valueAsArray.length < 2 || !isValidValue(valueAsArray[0]) || !isValidValue(valueAsArray[1])) { return ""; } const [first, second] = valueAsArray; const firstNum = shouldRenderAsNumber(first, parseNumbers) ? parseNumber(first, { parseNumbers: "strict" }) : Number.NaN; const secondNum = shouldRenderAsNumber(second, parseNumbers) ? parseNumber(second, { parseNumbers: "strict" }) : Number.NaN; const firstValue = Number.isNaN(firstNum) ? valueIsField ? `${first}` : first : firstNum; const secondValue = Number.isNaN(secondNum) ? valueIsField ? `${second}` : second : secondNum; const valsOneAndTwoOnly = [firstValue, secondValue]; if (!preserveValueOrder && firstValue === firstNum && secondValue === secondNum && secondNum < firstNum) { valsOneAndTwoOnly[0] = secondNum; valsOneAndTwoOnly[1] = firstNum; } 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"} `); } case "contains": case "doesnotcontain": return valueIsField ? concat(quoteValue("%"), wrapFieldName(value), quoteValue("%")) : quoteValue(`%${escapeValue(value)}%`); case "beginswith": case "doesnotbeginwith": return valueIsField ? concat(wrapFieldName(value), quoteValue("%")) : quoteValue(`${escapeValue(value)}%`); case "endswith": case "doesnotendwith": return valueIsField ? concat(quoteValue("%"), wrapFieldName(value)) : quoteValue(`%${escapeValue(value)}`); } if (typeof value === "boolean") { return value ? "TRUE" : "FALSE"; } return valueIsField ? wrapFieldName(value) : shouldRenderAsNumber(value, parseNumbers) ? `${trimIfString(value)}` : `${wrapAndEscape(value)}`; }; // src/utils/formatQuery/defaultRuleProcessorDrizzle.ts var defaultRuleProcessorDrizzle = (rule, _options) => { const opts = _options ?? /* istanbul ignore next */ {}; const { parseNumbers, preserveValueOrder, context = {} } = opts; const { columns, drizzleOperators, useRawFields } = context; if (!columns || !drizzleOperators) return; const { between, eq, gt, gte, inArray, isNotNull, isNull, like, lt, lte, ne, notBetween, notInArray, notLike, sql } = drizzleOperators; const { field, operator, value, valueSource } = rule; const column = useRawFields && /[a-z][a-z0-9]*/i.test(field) ? sql.raw(field) : columns[field]; const operatorLC = lc(operator); const valueIsField = valueSource === "field"; const asFieldOrValue = (v) => valueIsField ? columns[v] : v; if (!column) return; const matchEval = processMatchMode(rule); if (matchEval === false) { return; } else if (matchEval) { if (opts.preset !== "postgresql") return; const { mode, threshold } = matchEval; const arrayElementAlias = "elem_alias"; const sqlQuery = transformQuery(rule.value, { ruleProcessor: (r) => ({ ...r, field: arrayElementAlias }) }); const nestedArrayFilter = defaultRuleGroupProcessorDrizzle(sqlQuery, { ...opts, context: { ...opts.context, useRawFields: true } }); switch (mode) { case "all": return sql`(select count(*) from unnest(${column}) as ${sql.raw(arrayElementAlias)} where ${nestedArrayFilter({}, drizzleOperators)}) = array_length(${column}, 1)`; case "none": return sql`not exists (select 1 from unnest(${column}) as ${sql.raw(arrayElementAlias)} where ${nestedArrayFilter({}, drizzleOperators)})`; case "some": return sql`exists (select 1 from unnest(${column}) as ${sql.raw(arrayElementAlias)} where ${nestedArrayFilter({}, drizzleOperators)})`; case "atleast": case "atmost": case "exactly": { const op = mode === "atleast" ? ">=" : mode === "atmost" ? "<=" : "="; 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}`)}`; } } } switch (operatorLC) { case "=": return eq(column, asFieldOrValue(value)); case "!=": return ne(column, asFieldOrValue(value)); case ">": return gt(column, asFieldOrValue(value));