fix(classes.doc (convertFilterForMongoDb)): Improve filter conversion: handle logical operators, merge operator objects, add nested filter tests and docs, and fix test script
This commit is contained in:
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartdata',
|
||||
version: '5.16.3',
|
||||
version: '5.16.4',
|
||||
description: 'An advanced library for NoSQL data organization and manipulation using TypeScript with support for MongoDB, data validation, collections, and custom data types.'
|
||||
}
|
||||
|
@@ -199,21 +199,51 @@ export const convertFilterForMongoDb = (filterArg: { [key: string]: any }) => {
|
||||
throw new Error('$where operator is not allowed for security reasons');
|
||||
}
|
||||
|
||||
// Special case: detect MongoDB operators and pass them through directly
|
||||
const topLevelOperators = ['$and', '$or', '$nor', '$not', '$text', '$regex'];
|
||||
// Handle logical operators recursively
|
||||
const logicalOperators = ['$and', '$or', '$nor', '$not'];
|
||||
const processedFilter: { [key: string]: any } = {};
|
||||
|
||||
for (const key of Object.keys(filterArg)) {
|
||||
if (topLevelOperators.includes(key)) {
|
||||
return filterArg; // Return the filter as-is for MongoDB operators
|
||||
if (logicalOperators.includes(key)) {
|
||||
if (key === '$not') {
|
||||
processedFilter[key] = convertFilterForMongoDb(filterArg[key]);
|
||||
} else if (Array.isArray(filterArg[key])) {
|
||||
processedFilter[key] = filterArg[key].map((subFilter: any) => convertFilterForMongoDb(subFilter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If only logical operators, return them
|
||||
const hasOnlyLogicalOperators = Object.keys(filterArg).every(key => logicalOperators.includes(key));
|
||||
if (hasOnlyLogicalOperators) {
|
||||
return processedFilter;
|
||||
}
|
||||
|
||||
// Original conversion logic for non-MongoDB query objects
|
||||
const convertedFilter: { [key: string]: any } = {};
|
||||
|
||||
// Helper to merge operator objects
|
||||
const mergeIntoConverted = (path: string, value: any) => {
|
||||
const existing = convertedFilter[path];
|
||||
if (!existing) {
|
||||
convertedFilter[path] = value;
|
||||
} else if (
|
||||
typeof existing === 'object' && !Array.isArray(existing) &&
|
||||
typeof value === 'object' && !Array.isArray(value) &&
|
||||
(Object.keys(existing).some(k => k.startsWith('$')) || Object.keys(value).some(k => k.startsWith('$')))
|
||||
) {
|
||||
// Both have operators, merge them
|
||||
convertedFilter[path] = { ...existing, ...value };
|
||||
} else {
|
||||
// Otherwise later wins
|
||||
convertedFilter[path] = value;
|
||||
}
|
||||
};
|
||||
|
||||
const convertFilterArgument = (keyPathArg2: string, filterArg2: any) => {
|
||||
if (Array.isArray(filterArg2)) {
|
||||
// Arrays are typically used as values for operators like $in or as direct equality matches
|
||||
convertedFilter[keyPathArg2] = filterArg2;
|
||||
mergeIntoConverted(keyPathArg2, filterArg2);
|
||||
return;
|
||||
} else if (typeof filterArg2 === 'object' && filterArg2 !== null) {
|
||||
// Check if this is an object with MongoDB operators
|
||||
@@ -264,8 +294,8 @@ export const convertFilterForMongoDb = (filterArg: { [key: string]: any }) => {
|
||||
throw new Error('$size operator requires a numeric value');
|
||||
}
|
||||
|
||||
// Pass the operator object through
|
||||
convertedFilter[keyPathArg2] = filterArg2;
|
||||
// Use merge helper to handle duplicate paths
|
||||
mergeIntoConverted(keyPathArg2, filterArg2);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -282,13 +312,20 @@ export const convertFilterForMongoDb = (filterArg: { [key: string]: any }) => {
|
||||
}
|
||||
} else {
|
||||
// Primitive values
|
||||
convertedFilter[keyPathArg2] = filterArg2;
|
||||
mergeIntoConverted(keyPathArg2, filterArg2);
|
||||
}
|
||||
};
|
||||
|
||||
for (const key of Object.keys(filterArg)) {
|
||||
convertFilterArgument(key, filterArg[key]);
|
||||
// Skip logical operators, they were already processed
|
||||
if (!logicalOperators.includes(key)) {
|
||||
convertFilterArgument(key, filterArg[key]);
|
||||
}
|
||||
}
|
||||
|
||||
// Add back processed logical operators
|
||||
Object.assign(convertedFilter, processedFilter);
|
||||
|
||||
return convertedFilter;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user