如何编译并评估含嵌套逻辑运算符的类MongoDB风格对象?
Absolutely! You can build a lightweight custom evaluator that handles nested $and/$or operators, maps identifiers like foo/bar/baz to your evaluation functions, and even supports inline functions. Let's put this together step by step.
Step 1: Keep Your Evaluation Functions
First, retain your existing evaluation logic as-is:
export const evalFoo = items => items.indexOf("foo") >= 0; export const evalBar = items => items.indexOf("bar") >= 0; export const evalBaz = items => items.indexOf("baz") >= 0;
Step 2: Build the Core Evaluator
This recursive function will traverse your expression structure, resolve each component, and apply the logical operations:
// Map identifiers to their corresponding evaluation functions const evalMap = { foo: evalFoo, bar: evalBar, baz: evalBaz }; export const evaluateExpression = (expr) => { // Case 1: Execute inline functions directly (like () => m < n) if (typeof expr === 'function') { return expr(); } // Case 2: Handle logical operators ($and/$or) if (typeof expr === 'object' && expr !== null) { // $and requires all sub-expressions to be true (short-circuits on first false) if ('$and' in expr) { return expr.$and.every(subExpr => evaluateExpression(subExpr)); } // $or requires at least one sub-expression to be true (short-circuits on first true) if ('$or' in expr) { return expr.$or.some(subExpr => evaluateExpression(subExpr)); } // Case 3: Match identifiers (foo/bar/baz) to their eval functions for (const [key, value] of Object.entries(expr)) { if (evalMap[key]) { return evalMap[key](value); } } } // Fallback for unrecognized structures return false; };
Step 3: Validate with Your Test Cases
Let's test this evaluator against all your scenarios. Each test wraps variables like m/n to scope them properly:
Test Set 1
const testSet1 = () => { const m = 4; const n = 1; const expr = { $or: [ { $and: [ { foo: ['foo', 'x'] }, { bar: ['bar', 'y'] } ] }, { baz: ['baz', 'z'] }, () => m < n ] }; return evaluateExpression(expr); // Expected: true }; console.log(testSet1()); // Output: true
Why? The $and clause passes (both evalFoo and evalBar return true), so the $or short-circuits to true immediately.
Test Set 2
const testSet2 = () => { const m = 4; const n = 1; const expr = { $or: [ { $and: [ { foo: ['x'] }, { bar: ['y'] } ] }, { baz: ['x'] }, () => m < n ] }; return evaluateExpression(expr); // Expected: false }; console.log(testSet2()); // Output: false
Why? All options fail: the $and clause returns false, evalBaz returns false, and m < n is false.
Test Set 3
const testSet3 = () => { const m = 1; const n = 3; const expr = { $or: [ { $and: [ { foo: ['x'] }, { bar: ['y'] } ] }, { baz: ['x'] }, () => m < n ] }; return evaluateExpression(expr); // Expected: true }; console.log(testSet3()); // Output: true
Why? The inline function () => m < n returns true, so the $or evaluates to true.
Test Set 4
const testSet4 = () => { const m = 3; const n = 1; const expr = { $or: [ { $and: [ { foo: ['x'] }, { bar: ['bar'] } ] }, { baz: ['z'] }, () => m > n // Corrected to match test description's "m > n" logic ] }; return evaluateExpression(expr); // Expected: true }; console.log(testSet4()); // Output: true
Why? The inline function () => m > n returns true, so the $or evaluates to true.
Key Features Breakdown
- Recursive Nested Support: The evaluator drills down into any depth of
$and/$orarrays, so you can build complex expressions without limits. - Extensible Mapping: Add new identifier-to-function mappings to
evalMapanytime to support more evaluation logic. - Efficient Short-Circuiting: Uses
every()for$andandsome()for$orto stop evaluating as soon as the result is determined, saving unnecessary computations. - Flexible Inline Functions: Pass custom logic directly into the expression for one-off conditions that don't fit the identifier pattern.
内容的提问来源于stack exchange,提问作者Mutai Mwiti

