jsm-utilities
Version:
A utilities library.
368 lines (367 loc) • 14.6 kB
JavaScript
"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;