UNPKG

jsm-utilities

Version:
368 lines (367 loc) 14.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createNumberFilter_deprecated = exports.createDateRangeFilter_deprecated = exports.createBooleanFilter_deprecated = exports.createStringFilter_deprecated = exports.createConfigurableFilter = exports.createTextSearchFilter = exports.createGeoFilter = exports.createArrayFilter = exports.createNumberFilter = exports.createDateRangeFilter = exports.createBooleanFilter = exports.createStringFilter = void 0; const mongodb_1 = require("mongodb"); const numbers_1 = require("../../../common/numbers"); /** * Enhanced string filter with support for multiple fields and various matching modes */ const createStringFilter = (q, totalQ, value, fields, options = {}) => { const { omit = false, operator = "or", mode = "exact", caseSensitive = false } = options; // Handle ObjectId conversion if (mongodb_1.ObjectId.isValid(value)) { value = value.toString(); } // Handle object with numeric keys if (value && typeof value === "object" && "0" in value && "1" in value) { value = Object.values(value); } // Skip empty values if (!value && value !== null && value !== 0 && value !== false) { return { q, totalQ }; } // Normalize fields to array const fieldArray = Array.isArray(fields) ? fields : [fields]; // Create filter value based on mode const createFilterValue = (val) => { if (val instanceof Array) { return { [omit ? "$nin" : "$in"]: val }; } if (val === null) { return omit ? { $ne: null } : null; } if (typeof val !== "string") { return omit ? { $ne: val } : val; } switch (mode) { case "contains": return omit ? { $not: { $regex: val, $options: caseSensitive ? "" : "i" } } : { $regex: val, $options: caseSensitive ? "" : "i" }; case "regex": return omit ? { $not: { $regex: val, $options: caseSensitive ? "" : "i" } } : { $regex: val, $options: caseSensitive ? "" : "i" }; case "startsWith": return omit ? { $not: { $regex: `^${val}`, $options: caseSensitive ? "" : "i" } } : { $regex: `^${val}`, $options: caseSensitive ? "" : "i" }; case "endsWith": return omit ? { $not: { $regex: `${val}$`, $options: caseSensitive ? "" : "i" } } : { $regex: `${val}$`, $options: caseSensitive ? "" : "i" }; case "exact": default: if (!caseSensitive && typeof val === "string") { return omit ? { $not: { $regex: `^${val}$`, $options: "i" } } : { $regex: `^${val}$`, $options: "i" }; } return omit ? { $ne: val } : val; } }; const filterValue = createFilterValue(value); // Build query conditions if (fieldArray.length > 1) { const conditions = fieldArray.map(field => ({ [field]: filterValue })); const queryOperator = omit ? "$and" : (operator === "and" ? "$and" : "$or"); const totalQueryOperator = (omit || operator === "and") ? "$and" : "$or"; q = q === null || q === void 0 ? void 0 : q.where({ [queryOperator]: conditions }); totalQ = totalQ ? totalQ.where({ [totalQueryOperator]: conditions }) : totalQ; } else { const field = fieldArray[0]; const condition = { [field]: filterValue }; q = q === null || q === void 0 ? void 0 : q.where(condition); totalQ = totalQ ? totalQ.where(condition) : totalQ; } return { q, totalQ }; }; exports.createStringFilter = createStringFilter; /** * Enhanced boolean filter */ const createBooleanFilter = (q, totalQ, value, field, omit = false) => { if (value === null || value === undefined) { return { q, totalQ }; } const boolValue = Boolean(value); const condition = { [field]: omit ? { $ne: boolValue } : boolValue, }; q = q === null || q === void 0 ? void 0 : q.where(condition); totalQ = totalQ ? totalQ.where(condition) : totalQ; return { q, totalQ }; }; exports.createBooleanFilter = createBooleanFilter; /** * Enhanced date range filter */ const createDateRangeFilter = (q, totalQ, value, field, options = {}) => { const { omit = false, timezone, includeTime = true } = options; if (!value) { return { q, totalQ }; } let dateCondition = {}; if (value instanceof Array && value.length === 2) { const [start, end] = value; if (start) { dateCondition.$gte = includeTime ? new Date(start) : new Date(new Date(start).setHours(0, 0, 0, 0)); } if (end) { dateCondition.$lte = includeTime ? new Date(end) : new Date(new Date(end).setHours(23, 59, 59, 999)); } } else { // Single date const date = new Date(value); if (includeTime) { dateCondition = date; } else { dateCondition = { $gte: new Date(date.setHours(0, 0, 0, 0)), $lte: new Date(date.setHours(23, 59, 59, 999)) }; } } const condition = { [field]: omit ? { $not: dateCondition } : dateCondition, }; q = q === null || q === void 0 ? void 0 : q.where(condition); totalQ = totalQ ? totalQ.where(condition) : totalQ; return { q, totalQ }; }; exports.createDateRangeFilter = createDateRangeFilter; /** * Enhanced number filter with range support */ const createNumberFilter = (q, totalQ, value, field, options = {}) => { const { omit = false, allowZero = true, precision, operator = "or" } = options; if (!value && value !== 0) { return { q, totalQ }; } if (!allowZero && value === 0) { return { q, totalQ }; } // Handle precision rounding const processValue = (val) => { return precision !== undefined ? Number(val.toFixed(precision)) : val; }; // Handle range values if ((0, numbers_1.isNumberRange)(value)) { let rangeCondition = {}; if (value.exact !== undefined) { rangeCondition = processValue(value.exact); } else { if (value.min !== undefined) { rangeCondition.$gte = processValue(value.min); } if (value.max !== undefined) { rangeCondition.$lte = processValue(value.max); } } const fieldArray = Array.isArray(field) ? field : [field]; if (fieldArray.length > 1) { const conditions = fieldArray.map(f => ({ [f]: omit ? { $not: rangeCondition } : rangeCondition })); const queryOperator = omit ? "$and" : (operator === "and" ? "$and" : "$or"); q = q === null || q === void 0 ? void 0 : q.where({ [queryOperator]: conditions }); totalQ = totalQ ? totalQ.where({ [queryOperator]: conditions }) : totalQ; } else { const condition = { [fieldArray[0]]: omit ? { $not: rangeCondition } : rangeCondition, }; q = q === null || q === void 0 ? void 0 : q.where(condition); totalQ = totalQ ? totalQ.where(condition) : totalQ; } return { q, totalQ }; } // Handle array values if (value instanceof Array) { const processedValues = value.map(processValue); const condition = { [field]: { [omit ? "$nin" : "$in"]: processedValues, }, }; q = q === null || q === void 0 ? void 0 : q.where(condition); totalQ = totalQ ? totalQ.where(condition) : totalQ; return { q, totalQ }; } // Handle single values const processedValue = processValue(value); const condition = { [field]: omit ? { $ne: processedValue } : processedValue, }; q = q === null || q === void 0 ? void 0 : q.where(condition); totalQ = totalQ ? totalQ.where(condition) : totalQ; return { q, totalQ }; }; exports.createNumberFilter = createNumberFilter; /** * Array filter for matching array fields */ const createArrayFilter = (q, totalQ, value, field, options = {}) => { const { omit = false, matchAll = false, size } = options; if (!value) { return { q, totalQ }; } let condition = {}; if (size !== undefined) { condition[field] = { $size: size }; } else if (value instanceof Array) { const operator = matchAll ? "$all" : "$in"; condition[field] = omit ? { $not: { [operator]: value } } : { [operator]: value }; } else { condition[field] = omit ? { $ne: value } : value; } q = q === null || q === void 0 ? void 0 : q.where(condition); totalQ = totalQ ? totalQ.where(condition) : totalQ; return { q, totalQ }; }; exports.createArrayFilter = createArrayFilter; /** * Geospatial filter */ const createGeoFilter = (q, totalQ, geoFilter, field) => { if (!geoFilter) { return { q, totalQ }; } let condition = {}; switch (geoFilter.type) { case 'near': if (geoFilter.longitude !== undefined && geoFilter.latitude !== undefined) { condition[field] = { $near: Object.assign(Object.assign({ $geometry: { type: "Point", coordinates: [geoFilter.longitude, geoFilter.latitude] } }, (geoFilter.maxDistance && { $maxDistance: geoFilter.maxDistance })), (geoFilter.minDistance && { $minDistance: geoFilter.minDistance })) }; } break; case 'within': if (geoFilter.geometry) { condition[field] = { $geoWithin: { $geometry: geoFilter.geometry } }; } break; case 'intersects': if (geoFilter.geometry) { condition[field] = { $geoIntersects: { $geometry: geoFilter.geometry } }; } break; } if (Object.keys(condition).length > 0) { q = q === null || q === void 0 ? void 0 : q.where(condition); totalQ = totalQ ? totalQ.where(condition) : totalQ; } return { q, totalQ }; }; exports.createGeoFilter = createGeoFilter; /** * Text search filter */ const createTextSearchFilter = (q, totalQ, searchText, options = {}) => { if (!searchText) { return { q, totalQ }; } const condition = { $text: Object.assign(Object.assign(Object.assign({ $search: searchText }, (options.language && { $language: options.language })), (options.caseSensitive && { $caseSensitive: options.caseSensitive })), (options.diacriticSensitive && { $diacriticSensitive: options.diacriticSensitive })) }; q = q === null || q === void 0 ? void 0 : q.where(condition); totalQ = totalQ ? totalQ.where(condition) : totalQ; return { q, totalQ }; }; exports.createTextSearchFilter = createTextSearchFilter; /** * Configuration-driven filter function */ const createConfigurableFilter = (q, totalQ, filters, config) => { var _a; let result = { q, totalQ }; for (const [fieldName, filterValue] of Object.entries(filters)) { const filterConfig = config[fieldName]; if (!filterConfig || filterValue === undefined) { continue; } // Apply transform if provided const transformedValue = filterConfig.transform ? filterConfig.transform(filterValue) : filterValue; // Apply validator if provided if (filterConfig.validator && !filterConfig.validator(transformedValue)) { continue; } // Apply appropriate filter based on type switch (filterConfig.type) { case 'string': result = (0, exports.createStringFilter)(result.q, result.totalQ, transformedValue, fieldName, filterConfig.options); break; case 'number': result = (0, exports.createNumberFilter)(result.q, result.totalQ, transformedValue, fieldName, filterConfig.options); break; case 'date': result = (0, exports.createDateRangeFilter)(result.q, result.totalQ, transformedValue, fieldName, filterConfig.options); break; case 'boolean': result = (0, exports.createBooleanFilter)(result.q, result.totalQ, transformedValue, fieldName, (_a = filterConfig.options) === null || _a === void 0 ? void 0 : _a.omit); break; case 'array': result = (0, exports.createArrayFilter)(result.q, result.totalQ, transformedValue, fieldName, filterConfig.options); break; case 'geo': result = (0, exports.createGeoFilter)(result.q, result.totalQ, transformedValue, fieldName); break; } } return result; }; exports.createConfigurableFilter = createConfigurableFilter; // Legacy compatibility functions (deprecated) const createStringFilter_deprecated = (q, totalQ, value, fields, omit, operator) => { return (0, exports.createStringFilter)(q, totalQ, value, fields, { omit, operator }); }; exports.createStringFilter_deprecated = createStringFilter_deprecated; const createBooleanFilter_deprecated = (q, totalQ, value, field, omit) => { return (0, exports.createBooleanFilter)(q, totalQ, value, field, omit); }; exports.createBooleanFilter_deprecated = createBooleanFilter_deprecated; const createDateRangeFilter_deprecated = (q, totalQ, value, field, omit) => { return (0, exports.createDateRangeFilter)(q, totalQ, value, field, { omit }); }; exports.createDateRangeFilter_deprecated = createDateRangeFilter_deprecated; const createNumberFilter_deprecated = (q, totalQ, value, field, omit) => { return (0, exports.createNumberFilter)(q, totalQ, value, field, { omit }); }; exports.createNumberFilter_deprecated = createNumberFilter_deprecated;