* Optimizes AND expressions
( expression: BasicExpression, collection: CollectionLike<T, TKey>, )
| 608 | * Optimizes AND expressions |
| 609 | */ |
| 610 | function optimizeAndExpression<T extends object, TKey extends string | number>( |
| 611 | expression: BasicExpression, |
| 612 | collection: CollectionLike<T, TKey>, |
| 613 | ): OptimizationResult<TKey> { |
| 614 | if (expression.type !== `func` || expression.args.length < 2) { |
| 615 | return { canOptimize: false, matchingKeys: new Set(), isExact: false } |
| 616 | } |
| 617 | |
| 618 | // First, try to optimize compound range queries on the same field |
| 619 | // (e.g. age > 5 AND age < 10 becomes a single range query) |
| 620 | const compoundRangeResult = optimizeCompoundRangeQuery(expression, collection) |
| 621 | const coveredArgIndices = compoundRangeResult.canOptimize |
| 622 | ? compoundRangeResult.coveredArgIndices |
| 623 | : new Set<number>() |
| 624 | |
| 625 | const results: Array<OptimizationResult<TKey>> = [] |
| 626 | if (compoundRangeResult.canOptimize) { |
| 627 | results.push(compoundRangeResult) |
| 628 | } |
| 629 | |
| 630 | // Try to optimize the remaining conjuncts, keep the optimizable ones. |
| 631 | // Conjuncts that cannot use an index make the result inexact: the |
| 632 | // intersection is then a superset of the true result and must be |
| 633 | // re-filtered against the full expression by the caller. The compound |
| 634 | // range result may itself be inexact (e.g. a null/undefined bound). |
| 635 | let allConjunctsExact = !compoundRangeResult.canOptimize |
| 636 | ? true |
| 637 | : compoundRangeResult.isExact |
| 638 | for (const [argIndex, arg] of expression.args.entries()) { |
| 639 | if (coveredArgIndices.has(argIndex)) { |
| 640 | continue |
| 641 | } |
| 642 | const result = optimizeQueryRecursive(arg, collection) |
| 643 | if (result.canOptimize) { |
| 644 | results.push(result) |
| 645 | if (!result.isExact) { |
| 646 | allConjunctsExact = false |
| 647 | } |
| 648 | } else { |
| 649 | allConjunctsExact = false |
| 650 | } |
| 651 | } |
| 652 | |
| 653 | if (results.length > 0) { |
| 654 | // Use intersectSets utility for AND logic |
| 655 | const allMatchingSets = results.map((r) => r.matchingKeys) |
| 656 | const intersectedKeys = intersectSets(allMatchingSets) |
| 657 | return { |
| 658 | canOptimize: true, |
| 659 | matchingKeys: intersectedKeys, |
| 660 | isExact: allConjunctsExact, |
| 661 | } |
| 662 | } |
| 663 | |
| 664 | return { canOptimize: false, matchingKeys: new Set(), isExact: false } |
| 665 | } |
| 666 | |
| 667 | /** |
no test coverage detected