@@ -27,8 +27,10 @@ export async function runCursorQuery(db, table, conditions, queryAdditions, yiel
27
27
// **Metadata Path: Extract primary keys and sorting properties**
28
28
let primaryKeyList = await runMetaDataCursorQuery ( db , table , structuredPredicateTree , queryAdditions , yieldedPrimaryKeys , compoundKeys ) ;
29
29
30
+ const indexOrderProps = detectIndexOrderProperties ( structuredPredicateTree , table ) ;
31
+
30
32
// **Apply sorting, take, and skip operations**
31
- let finalPrimaryKeys = applyCursorQueryAdditions ( primaryKeyList , queryAdditions , compoundKeys ) ;
33
+ let finalPrimaryKeys = applyCursorQueryAdditions ( primaryKeyList , queryAdditions , compoundKeys , true , indexOrderProps ) ;
32
34
33
35
// **Fetch only the required records from IndexedDB**
34
36
let finalRecords = await fetchRecordsByPrimaryKeys ( db , table , finalPrimaryKeys , compoundKeys ) ;
@@ -41,6 +43,55 @@ export async function runCursorQuery(db, table, conditions, queryAdditions, yiel
41
43
}
42
44
}
43
45
46
+ function detectIndexOrderProperties ( predicateTree , table ) {
47
+ const indexedProps = new Set ( ) ;
48
+
49
+ // Step 1: Get actual indexed fields from Dexie table schema
50
+ const dexieIndexKeys = Object . keys ( table . schema . idxByName || { } ) ;
51
+
52
+ // This gives you:
53
+ // - For single indexes: ["Email", "Age"]
54
+ // - For compound indexes: ["[FirstName+LastName]", "[LastName+Age]"], etc.
55
+
56
+ // Step 2: Expand compound indexes
57
+ const normalizedIndexProps = new Set ( ) ;
58
+
59
+ for ( const idx of dexieIndexKeys ) {
60
+ if ( idx . startsWith ( '[' ) ) {
61
+ const parts = idx . replace ( / [ \[ \] ] / g, "" ) . split ( "+" ) . map ( x => x . trim ( ) ) ;
62
+ for ( const p of parts ) normalizedIndexProps . add ( p ) ;
63
+ } else {
64
+ normalizedIndexProps . add ( idx ) ;
65
+ }
66
+ }
67
+
68
+ // Step 3: Walk the predicate tree
69
+ walkPredicateTree ( predicateTree , node => {
70
+ if ( node . nodeType === "condition" ) {
71
+ const prop = node . condition ?. property ;
72
+ if ( normalizedIndexProps . has ( prop ) ) {
73
+ indexedProps . add ( prop ) ;
74
+ }
75
+ }
76
+ } ) ;
77
+
78
+ return [ ...indexedProps ] ;
79
+ }
80
+
81
+ function walkPredicateTree ( node , visitFn ) {
82
+ if ( ! node )
83
+ return ;
84
+
85
+ if ( node . nodeType === "condition" ) {
86
+ visitFn ( node ) ; // Visit condition nodes directly
87
+ } else if ( node . nodeType === "logical" && Array . isArray ( node . children ) ) {
88
+ for ( const child of node . children ) {
89
+ walkPredicateTree ( child , visitFn ) ;
90
+ }
91
+ }
92
+ }
93
+
94
+
44
95
let lastCursorWarningTime = null ;
45
96
46
97
/**
@@ -679,32 +730,43 @@ async function fetchRecordsByPrimaryKeys(db, table, primaryKeys, compoundKeys, b
679
730
}
680
731
681
732
682
- function applyCursorQueryAdditions ( primaryKeyList , queryAdditions , compoundKeys , flipSkipTakeOrder = true ) {
733
+ function applyCursorQueryAdditions (
734
+ primaryKeyList ,
735
+ queryAdditions ,
736
+ compoundKeys ,
737
+ flipSkipTakeOrder = true ,
738
+ detectedIndexOrderProperties = [ ]
739
+ ) {
683
740
if ( ! queryAdditions || queryAdditions . length === 0 ) {
684
741
return primaryKeyList . map ( item =>
685
742
compoundKeys . map ( key => item . sortingProperties [ key ] )
686
743
) ;
687
744
}
745
+ // waca
746
+ debugLog ( "Applying cursor query additions in strict given order" , {
747
+ queryAdditions,
748
+ detectedIndexOrderProperties
749
+ } ) ;
688
750
689
- debugLog ( "Applying cursor query additions in strict given order" , { queryAdditions } ) ;
690
-
691
- let additions = [ ...queryAdditions ] ; // Copy to avoid modifying original
751
+ let additions = [ ...queryAdditions ] ; // Avoid modifying original
692
752
let needsReverse = false ;
693
753
694
- // **Avoid unnecessary sort if `_MagicOrderId` is already ordered**
695
- let isAlreadyOrdered = additions . every ( a =>
696
- a . additionFunction !== QUERY_ADDITIONS . ORDER_BY &&
697
- a . additionFunction !== QUERY_ADDITIONS . ORDER_BY_DESCENDING
698
- ) ;
699
-
700
- if ( ! isAlreadyOrdered ) {
701
- primaryKeyList . sort ( ( a , b ) => a . sortingProperties [ "_MagicOrderId" ] - b . sortingProperties [ "_MagicOrderId" ] ) ;
754
+ // Step 0: Always apply detectedIndexOrderProperties first
755
+ if ( detectedIndexOrderProperties ?. length > 0 ) {
756
+ primaryKeyList . sort ( ( a , b ) => {
757
+ for ( let prop of detectedIndexOrderProperties ) {
758
+ const aVal = a . sortingProperties [ prop ] ;
759
+ const bVal = b . sortingProperties [ prop ] ;
760
+ if ( aVal !== bVal ) return aVal > bVal ? 1 : - 1 ;
761
+ }
762
+ // Always fallback to internal row ordering
763
+ return a . sortingProperties [ "_MagicOrderId" ] - b . sortingProperties [ "_MagicOrderId" ] ;
764
+ } ) ;
702
765
}
703
766
704
- // **Optimized order-flipping logic**
767
+ // Flip TAKE + SKIP if needed (for consistent cursor behavior)
705
768
if ( flipSkipTakeOrder ) {
706
769
let takeIndex = - 1 , skipIndex = - 1 ;
707
-
708
770
for ( let i = 0 ; i < additions . length ; i ++ ) {
709
771
if ( additions [ i ] . additionFunction === QUERY_ADDITIONS . TAKE ) takeIndex = i ;
710
772
if ( additions [ i ] . additionFunction === QUERY_ADDITIONS . SKIP ) skipIndex = i ;
@@ -716,7 +778,7 @@ function applyCursorQueryAdditions(primaryKeyList, queryAdditions, compoundKeys,
716
778
}
717
779
}
718
780
719
- // ** Step 2 : Apply Query Additions in Exact Order**
781
+ // Step 1 : Apply all query additions in declared order
720
782
for ( const addition of additions ) {
721
783
switch ( addition . additionFunction ) {
722
784
case QUERY_ADDITIONS . ORDER_BY :
@@ -731,6 +793,8 @@ function applyCursorQueryAdditions(primaryKeyList, queryAdditions, compoundKeys,
731
793
? ( valueB > valueA ? 1 : - 1 )
732
794
: ( valueA > valueB ? 1 : - 1 ) ;
733
795
}
796
+
797
+ // Fallback to row order
734
798
return a . sortingProperties [ "_MagicOrderId" ] - b . sortingProperties [ "_MagicOrderId" ] ;
735
799
} ) ;
736
800
break ;
@@ -740,7 +804,7 @@ function applyCursorQueryAdditions(primaryKeyList, queryAdditions, compoundKeys,
740
804
break ;
741
805
742
806
case QUERY_ADDITIONS . TAKE :
743
- primaryKeyList . length = Math . min ( primaryKeyList . length , addition . intValue ) ; // **Avoid new array allocation**
807
+ primaryKeyList . length = Math . min ( primaryKeyList . length , addition . intValue ) ;
744
808
break ;
745
809
746
810
case QUERY_ADDITIONS . TAKE_LAST :
@@ -749,7 +813,7 @@ function applyCursorQueryAdditions(primaryKeyList, queryAdditions, compoundKeys,
749
813
break ;
750
814
751
815
case QUERY_ADDITIONS . FIRST :
752
- primaryKeyList . length = primaryKeyList . length > 0 ? 1 : 0 ; // **No new array allocation**
816
+ primaryKeyList . length = primaryKeyList . length > 0 ? 1 : 0 ;
753
817
break ;
754
818
755
819
case QUERY_ADDITIONS . LAST :
@@ -761,14 +825,12 @@ function applyCursorQueryAdditions(primaryKeyList, queryAdditions, compoundKeys,
761
825
}
762
826
}
763
827
764
- // **Step 3: Reverse if TAKE_LAST was used**
765
828
if ( needsReverse ) {
766
829
primaryKeyList . reverse ( ) ;
767
830
}
768
831
769
832
debugLog ( "Final Ordered Primary Key List" , primaryKeyList ) ;
770
833
771
- // **Final Map: Extract only needed keys in the correct order**
772
834
return primaryKeyList . map ( item =>
773
835
compoundKeys . map ( key => item . sortingProperties [ key ] )
774
836
) ;
0 commit comments