@@ -53,21 +53,33 @@ async function processCursorRecords(db, table, predicateTree, yieldedPrimaryKeys
5353 const now = Date . now ( ) ;
5454 let shouldLogWarning = ! lastCursorWarningTime || now - lastCursorWarningTime > 10 * 60 * 1000 ;
5555
56- // Dynamically extract all required properties from the predicate tree
5756 const requiredPropertiesFiltered = new Set ( ) ;
58- collectPropertiesFromTree ( predicateTree , requiredPropertiesFiltered ) ;
57+
58+ // Only collect properties if we actually have a filter tree with children
59+ const hasConditions =
60+ predicateTree &&
61+ (
62+ predicateTree . nodeType === "condition" ||
63+ ( predicateTree . children && predicateTree . children . length > 0 )
64+ ) ;
65+
66+ if ( hasConditions ) {
67+ collectPropertiesFromTree ( predicateTree , requiredPropertiesFiltered ) ;
68+ }
5969
6070 await db . transaction ( 'r' , table , async ( ) => {
6171 await table . orderBy ( compoundKeys [ 0 ] ) . each ( ( record ) => {
62- // Skip if required property is missing
63- for ( const prop of requiredPropertiesFiltered ) {
64- if ( record [ prop ] === undefined ) {
65- if ( shouldLogWarning ) {
66- console . warn ( `[IndexedDB Cursor Warning] Skipping record due to missing property: ${ prop } ` ) ;
67- lastCursorWarningTime = now ;
68- shouldLogWarning = false ;
72+ // Still apply property checks *only* if we have any to check
73+ if ( requiredPropertiesFiltered . size > 0 ) {
74+ for ( const prop of requiredPropertiesFiltered ) {
75+ if ( record [ prop ] === undefined ) {
76+ if ( shouldLogWarning ) {
77+ console . warn ( `[IndexedDB Cursor Warning] Skipping record due to missing property: ${ prop } ` ) ;
78+ lastCursorWarningTime = now ;
79+ shouldLogWarning = false ;
80+ }
81+ return ;
6982 }
70- return ;
7183 }
7284 }
7385
@@ -76,48 +88,77 @@ async function processCursorRecords(db, table, predicateTree, yieldedPrimaryKeys
7688 return ;
7789 }
7890
79- // Evaluate the structured predicate tree
80- if ( ! evaluatePredicateTree ( predicateTree , record ) ) return ;
91+ // Only evaluate if we actually have predicate logic
92+ if ( hasConditions && ! evaluatePredicateTree ( predicateTree , record ) )
93+ return ;
8194
8295 recordHandler ( record , recordKey ) ;
8396 } ) ;
8497 } ) ;
8598}
8699
100+
87101function collectPropertiesFromTree ( node , propertySet ) {
88102 if ( node . nodeType === "condition" ) {
89103 propertySet . add ( node . condition . property ) ;
90104 return ;
91105 }
92- for ( const child of node . children ) {
106+ for ( const child of node . children ?? [ ] ) {
93107 collectPropertiesFromTree ( child , propertySet ) ;
94108 }
95109}
96110
97111function evaluatePredicateTree ( node , record ) {
98112 if ( node . nodeType === "condition" ) {
99- const condition = optimizeSingleCondition ( node . condition ) ;
100- return applyCondition ( record , condition ) ;
113+ if ( ! node . optimized ) {
114+ node . optimized = optimizeSingleCondition ( node . condition ) ;
115+ }
116+ return applyCondition ( record , node . optimized ) ;
101117 }
102118
103- const results = node . children . map ( child => evaluatePredicateTree ( child , record ) ) ;
119+ const results = ( node . children ?? [ ] ) . map ( child => evaluatePredicateTree ( child , record ) ) ;
104120 return node . operator === "And"
105121 ? results . every ( r => r )
106122 : results . some ( r => r ) ;
107123}
108124
109125function optimizeSingleCondition ( condition ) {
110- let optimized = { ...condition } ;
126+ if ( condition . value === - Infinity || condition . value === Infinity ) {
127+ return condition ; // Already a no-op filter, skip transformation
128+ }
111129
130+ const optimized = { ...condition } ;
131+
132+ // Lowercase normalization for string values if not case-sensitive
112133 if ( ! condition . caseSensitive && typeof condition . value === "string" ) {
113134 optimized . value = condition . value . toLowerCase ( ) ;
114135 }
115136
137+ // Edge case: GETDAY sanity check
138+ if (
139+ condition . operation === QUERY_OPERATIONS . GETDAY &&
140+ typeof condition . value !== "number"
141+ ) {
142+ console . warn ( `[IndexedDB Warning] GETDAY expects a numeric day (1–31). Got:` , condition . value ) ;
143+ }
144+
145+ // Generic numeric validation for date-based derived operations
146+ if (
147+ [ QUERY_OPERATIONS . GET_DAY_OF_WEEK , QUERY_OPERATIONS . GET_DAY_OF_YEAR , QUERY_OPERATIONS . GETDAY ]
148+ . includes ( condition . operation )
149+ ) {
150+ if ( typeof condition . value !== "number" ) {
151+ console . warn ( `[IndexedDB Warning] ${ condition . operation } expects a numeric value. Got:` , condition . value ) ;
152+ }
153+ }
154+
116155 optimized . comparisonFunction = getComparisonFunction ( condition . operation ) ;
117156 return optimized ;
118157}
119158
120159
160+
161+
121162/**
122163 * Directly retrieves records that match the conditions without metadata processing.
123164 */
@@ -158,12 +199,14 @@ async function runMetaDataCursorQuery(db, table, conditions, queryAdditions, yie
158199 let requiredProperties = new Set ( ) ;
159200 let magicOrder = 0 ;
160201
161- for ( const andGroup of conditions ) {
162- for ( const condition of andGroup ) {
163- if ( condition . property ) requiredProperties . add ( condition . property ) ;
164- }
202+ if ( conditions ?. nodeType === "logical" && ! conditions . children ) {
203+ // No conditions — grab everything
204+ debugLog ( "Detected no-op predicate. All records will be evaluated." ) ;
205+ } else {
206+ collectPropertiesFromTree ( conditions , requiredProperties ) ;
165207 }
166208
209+
167210 for ( const addition of queryAdditions ) {
168211 if ( ( addition . additionFunction === QUERY_ADDITIONS . ORDER_BY
169212 || addition . additionFunction === QUERY_ADDITIONS . ORDER_BY_DESCENDING ) &&
@@ -209,33 +252,6 @@ async function runMetaDataCursorQuery(db, table, conditions, queryAdditions, yie
209252 return primaryKeyList . slice ( 0 , resultIndex ) ;
210253}
211254
212-
213- function optimizeConditions ( conditions ) {
214- return conditions . map ( condition => {
215- let optimizedCondition = { ...condition } ;
216-
217- if ( ! condition . caseSensitive && typeof condition . value === "string" ) {
218- optimizedCondition . value = condition . value . toLowerCase ( ) ;
219- }
220-
221- // Edge case: GETDAY sanity check
222- if ( condition . operation === QUERY_OPERATIONS . GETDAY &&
223- typeof condition . value !== "number" ) {
224- console . warn ( `[IndexedDB Warning] GETDAY expects a numeric day (131). Got:` , condition . value ) ;
225- }
226-
227- if ( [ QUERY_OPERATIONS . GET_DAY_OF_WEEK , QUERY_OPERATIONS . GET_DAY_OF_YEAR , QUERY_OPERATIONS . GETDAY ] . includes ( condition . operation ) ) {
228- if ( typeof condition . value !== "number" ) {
229- console . warn ( `[IndexedDB Warning] ${ condition . operation } expects a numeric value. Got:` , condition . value ) ;
230- }
231- }
232-
233- optimizedCondition . comparisonFunction = getComparisonFunction ( condition . operation ) ;
234- return optimizedCondition ;
235- } ) ;
236- }
237-
238-
239255function getComparisonFunction ( operation ) {
240256 const operations = {
241257 [ QUERY_OPERATIONS . EQUAL ] : ( recordValue , queryValue ) => recordValue === queryValue ,
0 commit comments