@@ -30,124 +30,6 @@ export interface CollectionLike<
3030 `get` | `has` | `entries` | `indexes` | `id`
3131 > { }
3232
33- /**
34- * Gets ordered keys from a collection using index optimization when possible
35- * @param collection - The collection to get keys from
36- * @param orderBy - The order by clause
37- * @param limit - Optional limit on number of keys to return
38- * @param whereFilter - Optional filter function to apply while traversing
39- * @returns Array of keys in sorted order
40- */
41- function getOrderedKeys < T extends object , TKey extends string | number > (
42- collection : CollectionLike < T , TKey > ,
43- orderBy : OrderBy ,
44- limit ?: number ,
45- whereFilter ?: ( item : T ) => boolean ,
46- optimizedOnly ?: boolean
47- ) : Array < TKey > | undefined {
48- // For single-column orderBy on a ref expression, try index optimization
49- if ( orderBy . length === 1 ) {
50- const clause = orderBy [ 0 ] !
51- const orderByExpression = clause . expression
52-
53- if ( orderByExpression . type === `ref` ) {
54- const propRef = orderByExpression
55- const fieldPath = propRef . path
56-
57- // Ensure index exists for this field
58- ensureIndexForField (
59- fieldPath [ 0 ] ! ,
60- fieldPath ,
61- collection as CollectionImpl < T , TKey > ,
62- clause . compareOptions
63- )
64-
65- // Find the index
66- const index = findIndexForField (
67- collection . indexes ,
68- fieldPath ,
69- clause . compareOptions
70- )
71-
72- if ( index && index . supports ( `gt` ) ) {
73- // Use index optimization
74- const filterFn = ( key : TKey ) : boolean => {
75- const value = collection . get ( key )
76- if ( value === undefined ) {
77- return false
78- }
79- return whereFilter ?.( value ) ?? true
80- }
81-
82- // Take the keys that match the filter and limit
83- // if no limit is provided `index.keyCount` is used,
84- // i.e. we will take all keys that match the filter
85- return index . take ( limit ?? index . keyCount , undefined , filterFn )
86- }
87- }
88- }
89-
90- if ( optimizedOnly ) {
91- return
92- }
93-
94- // Fallback: collect all items and sort in memory
95- const allItems : Array < { key : TKey ; value : T } > = [ ]
96- for ( const [ key , value ] of collection . entries ( ) ) {
97- if ( whereFilter ?.( value ) ?? true ) {
98- allItems . push ( { key, value } )
99- }
100- }
101-
102- // Sort using makeComparator
103- const compare = ( a : { key : TKey ; value : T } , b : { key : TKey ; value : T } ) => {
104- for ( const clause of orderBy ) {
105- const compareFn = makeComparator ( clause . compareOptions )
106-
107- // Extract values for comparison
108- const aValue = extractValueFromItem ( a . value , clause . expression )
109- const bValue = extractValueFromItem ( b . value , clause . expression )
110-
111- const result = compareFn ( aValue , bValue )
112- if ( result !== 0 ) {
113- return result
114- }
115- }
116- return 0
117- }
118-
119- allItems . sort ( compare )
120- const sortedKeys = allItems . map ( ( item ) => item . key )
121-
122- // Apply limit if provided
123- if ( limit !== undefined ) {
124- return sortedKeys . slice ( 0 , limit )
125- }
126-
127- // if no limit is provided, we will return all keys
128- return sortedKeys
129- }
130-
131- /**
132- * Helper function to extract a value from an item based on an expression
133- */
134- function extractValueFromItem ( item : any , expression : BasicExpression ) : any {
135- if ( expression . type === `ref` ) {
136- const propRef = expression
137- let value = item
138- for ( const pathPart of propRef . path ) {
139- value = value ?. [ pathPart ]
140- }
141- return value
142- } else if ( expression . type === `val` ) {
143- return expression . value
144- } else {
145- // It must be a function
146- const evaluator = compileSingleRowExpression ( expression )
147- return evaluator ( item as Record < string , unknown > )
148- }
149- }
150-
15133/**
15234 * Returns the current state of the collection as an array of changes
15335 * @param collection - The collection to get changes from
@@ -420,3 +302,121 @@ export function createFilteredCallback<T extends object>(
420302 }
421303 }
422304}
305+
306+ /**
307+ * Gets ordered keys from a collection using index optimization when possible
308+ * @param collection - The collection to get keys from
309+ * @param orderBy - The order by clause
310+ * @param limit - Optional limit on number of keys to return
311+ * @param whereFilter - Optional filter function to apply while traversing
312+ * @returns Array of keys in sorted order
313+ */
314+ function getOrderedKeys < T extends object , TKey extends string | number > (
315+ collection : CollectionLike < T , TKey > ,
316+ orderBy : OrderBy ,
317+ limit ?: number ,
318+ whereFilter ?: ( item : T ) => boolean ,
319+ optimizedOnly ?: boolean
320+ ) : Array < TKey > | undefined {
321+ // For single-column orderBy on a ref expression, try index optimization
322+ if ( orderBy . length === 1 ) {
323+ const clause = orderBy [ 0 ] !
324+ const orderByExpression = clause . expression
325+
326+ if ( orderByExpression . type === `ref` ) {
327+ const propRef = orderByExpression
328+ const fieldPath = propRef . path
329+
330+ // Ensure index exists for this field
331+ ensureIndexForField (
332+ fieldPath [ 0 ] ! ,
333+ fieldPath ,
334+ collection as CollectionImpl < T , TKey > ,
335+ clause . compareOptions
336+ )
337+
338+ // Find the index
339+ const index = findIndexForField (
340+ collection . indexes ,
341+ fieldPath ,
342+ clause . compareOptions
343+ )
344+
345+ if ( index && index . supports ( `gt` ) ) {
346+ // Use index optimization
347+ const filterFn = ( key : TKey ) : boolean => {
348+ const value = collection . get ( key )
349+ if ( value === undefined ) {
350+ return false
351+ }
352+ return whereFilter ?.( value ) ?? true
353+ }
354+
355+ // Take the keys that match the filter and limit
356+ // if no limit is provided `index.keyCount` is used,
357+ // i.e. we will take all keys that match the filter
358+ return index . take ( limit ?? index . keyCount , undefined , filterFn )
359+ }
360+ }
361+ }
362+
363+ if ( optimizedOnly ) {
364+ return
365+ }
366+
367+ // Fallback: collect all items and sort in memory
368+ const allItems : Array < { key : TKey ; value : T } > = [ ]
369+ for ( const [ key , value ] of collection . entries ( ) ) {
370+ if ( whereFilter ?.( value ) ?? true ) {
371+ allItems . push ( { key, value } )
372+ }
373+ }
374+
375+ // Sort using makeComparator
376+ const compare = ( a : { key : TKey ; value : T } , b : { key : TKey ; value : T } ) => {
377+ for ( const clause of orderBy ) {
378+ const compareFn = makeComparator ( clause . compareOptions )
379+
380+ // Extract values for comparison
381+ const aValue = extractValueFromItem ( a . value , clause . expression )
382+ const bValue = extractValueFromItem ( b . value , clause . expression )
383+
384+ const result = compareFn ( aValue , bValue )
385+ if ( result !== 0 ) {
386+ return result
387+ }
388+ }
389+ return 0
390+ }
391+
392+ allItems . sort ( compare )
393+ const sortedKeys = allItems . map ( ( item ) => item . key )
394+
395+ // Apply limit if provided
396+ if ( limit !== undefined ) {
397+ return sortedKeys . slice ( 0 , limit )
398+ }
399+
400+ // if no limit is provided, we will return all keys
401+ return sortedKeys
402+ }
403+
404+ /**
405+ * Helper function to extract a value from an item based on an expression
406+ */
407+ function extractValueFromItem ( item : any , expression : BasicExpression ) : any {
408+ if ( expression . type === `ref` ) {
409+ const propRef = expression
410+ let value = item
411+ for ( const pathPart of propRef . path ) {
412+ value = value ?. [ pathPart ]
413+ }
414+ return value
415+ } else if ( expression . type === `val` ) {
416+ return expression . value
417+ } else {
418+ // It must be a function
419+ const evaluator = compileSingleRowExpression ( expression )
420+ return evaluator ( item as Record < string , unknown > )
421+ }
422+ }
0 commit comments