@@ -4,7 +4,10 @@ import {
44 resolveNodeType ,
55 resolveLogicalOperator
66} from "./predicateEnumHelpers.js" ;
7-
7+ import { QUERY_OPERATIONS } from "./queryConstants.js" ;
8+ import { areConditionsCompatible } from "./areConditionsCompatible.js" ;
9+ import { advancedOptimizeNestedOrFilter } from "./optimizeConditions.js" ;
10+ import { optimizeOrGroupStructure } from "./optimizeOrGroupStructure.js" ;
811/**
912 * Flattens a Universal Predicate Tree into a nestedOrFilter structure.
1013 * This creates `orGroups -> andGroups -> conditions[]`
@@ -34,9 +37,10 @@ export function flattenUniversalPredicate(rootNode) {
3437 ]
3538 } ) ) ;
3639
40+ let orGroupsOptimized = optimizeOrGroupStructure ( advancedOptimizeNestedOrFilter ( orGroups ) ) ;
3741 const result = {
3842 nestedOrFilterUnclean : {
39- orGroups
43+ orGroups : orGroupsOptimized
4044 } ,
4145 isUniversalTrue : detection . isUniversalTrue ,
4246 isUniversalFalse : detection . isUniversalFalse
@@ -48,60 +52,54 @@ export function flattenUniversalPredicate(rootNode) {
4852
4953function flattenNodeToAndGroups ( node ) {
5054 const nodeType = resolveNodeType ( node . nodeType ) ;
51-
52- if ( ! nodeType ) {
53- debugLog ( "Skipping node with null/invalid nodeType" , node ) ;
54- return [ ] ;
55- }
55+ if ( ! nodeType ) return [ ] ;
5656
5757 if ( nodeType === "Condition" ) {
58- if ( ! node . condition ) {
59- debugLog ( "Skipping condition node with null condition" , node ) ;
60- return [ ] ;
61- }
62- return [ [ node . condition ] ] ;
58+ if ( ! node . condition ) return [ ] ;
59+ const normalized = normalizeCondition ( node . condition ) ;
60+ return [ [ normalized ] ] ;
6361 }
6462
6563 if ( nodeType === "Logical" ) {
6664 const operator = resolveLogicalOperator ( node . operator ) ;
6765 const { children } = node ;
6866
69- if ( ! operator || ! Array . isArray ( children ) ) {
70- debugLog ( "Skipping logical node with null operator or children" , node ) ;
71- return [ ] ;
72- }
67+ if ( ! operator || ! Array . isArray ( children ) ) return [ ] ;
7368
7469 if ( operator === "And" ) {
7570 let result = [ [ ] ] ;
71+
7672 for ( const child of children ) {
77- const childFlattened = flattenNodeToAndGroups ( child ) ;
73+ const childGroups = flattenNodeToAndGroups ( child ) ;
7874 const newResult = [ ] ;
7975
80- for ( const resGroup of result ) {
81- for ( const childGroup of childFlattened ) {
82- newResult . push ( [ ...resGroup , ...childGroup ] ) ;
76+ for ( const groupA of result ) {
77+ for ( const groupB of childGroups ) {
78+ if ( areConditionsCompatible ( groupA , groupB ) ) {
79+ newResult . push ( [ ...groupA , ...groupB ] ) ;
80+ } else {
81+ debugLog ( "Skipping incompatible group merge" , { groupA, groupB } ) ;
82+ }
8383 }
8484 }
8585
8686 result = newResult ;
8787 }
88+
8889 return result ;
8990 }
9091
9192 if ( operator === "Or" ) {
9293 let result = [ ] ;
9394 for ( const child of children ) {
94- const childFlattened = flattenNodeToAndGroups ( child ) ;
95- result = result . concat ( childFlattened ) ;
95+ result = result . concat ( flattenNodeToAndGroups ( child ) ) ;
9696 }
9797 return result ;
9898 }
9999
100- debugLog ( "Unknown operator encountered during flattening" , operator ) ;
101100 return [ ] ;
102101 }
103102
104- debugLog ( "Unknown node type encountered during flattening" , nodeType ) ;
105103 return [ ] ;
106104}
107105
@@ -126,3 +124,27 @@ function detectUniversalTruth(node) {
126124 isUniversalFalse : value === false
127125 } ;
128126}
127+
128+
129+ /**
130+ * Normalizes a condition before execution.
131+ * Handles special logic like null-equality conversion, case handling, etc.
132+ */
133+ function normalizeCondition ( condition ) {
134+ const normalized = { ...condition } ;
135+
136+ if (
137+ ( condition . operation === QUERY_OPERATIONS . EQUAL || condition . operation === QUERY_OPERATIONS . NOT_EQUAL ) &&
138+ ( condition . value === null || condition . value === undefined )
139+ ) {
140+ normalized . operation = condition . operation === QUERY_OPERATIONS . EQUAL
141+ ? QUERY_OPERATIONS . IS_NULL
142+ : QUERY_OPERATIONS . IS_NOT_NULL ;
143+
144+ // Leave value in place to pass validation
145+ normalized . value = null ;
146+ }
147+
148+ return normalized ;
149+ }
150+
0 commit comments