11import { beforeEach , describe , expect , it } from "vitest"
2- import mitt from "mitt"
32import { createCollection } from "../../src/collection"
43
54import { createLiveQueryCollection } from "../../src/query/live-query-collection"
@@ -13,8 +12,7 @@ import {
1312 length ,
1413 or ,
1514} from "../../src/query/builder/functions"
16- import type { Collection } from "../../src/collection"
17- import type { PendingMutation } from "../../src/types"
15+ import { mockSyncCollectionOptions } from "../utls"
1816
1917interface TestItem {
2018 id : string
@@ -186,88 +184,65 @@ function withIndexTracking(
186184 }
187185}
188186
187+ const testData : Array < TestItem > = [
188+ {
189+ id : `1` ,
190+ name : `Alice` ,
191+ age : 25 ,
192+ status : `active` ,
193+ score : 95 ,
194+ createdAt : new Date ( `2023-01-01` ) ,
195+ } ,
196+ {
197+ id : `2` ,
198+ name : `Bob` ,
199+ age : 30 ,
200+ status : `inactive` ,
201+ score : 80 ,
202+ createdAt : new Date ( `2023-01-02` ) ,
203+ } ,
204+ {
205+ id : `3` ,
206+ name : `Charlie` ,
207+ age : 35 ,
208+ status : `active` ,
209+ score : 90 ,
210+ createdAt : new Date ( `2023-01-03` ) ,
211+ } ,
212+ {
213+ id : `4` ,
214+ name : `Diana` ,
215+ age : 28 ,
216+ status : `pending` ,
217+ score : 85 ,
218+ createdAt : new Date ( `2023-01-04` ) ,
219+ } ,
220+ {
221+ id : `5` ,
222+ name : `Eve` ,
223+ age : 22 ,
224+ status : `active` ,
225+ score : undefined ,
226+ createdAt : new Date ( `2023-01-05` ) ,
227+ } ,
228+ ]
229+
230+ function createTestItemCollection ( autoIndex : `off` | `eager` = `off` ) {
231+ return createCollection (
232+ mockSyncCollectionOptions < TestItem > ( {
233+ id : `test-collection` ,
234+ getKey : ( item ) => item . id ,
235+ initialData : testData ,
236+ autoIndex,
237+ } )
238+ )
239+ }
240+
189241describe ( `Query Index Optimization` , ( ) => {
190- let collection : Collection < TestItem , string >
191- let testData : Array < TestItem >
192- let emitter : any
242+ let collection : ReturnType < typeof createTestItemCollection >
193243
194244 beforeEach ( async ( ) => {
195- testData = [
196- {
197- id : `1` ,
198- name : `Alice` ,
199- age : 25 ,
200- status : `active` ,
201- score : 95 ,
202- createdAt : new Date ( `2023-01-01` ) ,
203- } ,
204- {
205- id : `2` ,
206- name : `Bob` ,
207- age : 30 ,
208- status : `inactive` ,
209- score : 80 ,
210- createdAt : new Date ( `2023-01-02` ) ,
211- } ,
212- {
213- id : `3` ,
214- name : `Charlie` ,
215- age : 35 ,
216- status : `active` ,
217- score : 90 ,
218- createdAt : new Date ( `2023-01-03` ) ,
219- } ,
220- {
221- id : `4` ,
222- name : `Diana` ,
223- age : 28 ,
224- status : `pending` ,
225- score : 85 ,
226- createdAt : new Date ( `2023-01-04` ) ,
227- } ,
228- {
229- id : `5` ,
230- name : `Eve` ,
231- age : 22 ,
232- status : `active` ,
233- score : undefined ,
234- createdAt : new Date ( `2023-01-05` ) ,
235- } ,
236- ]
237-
238- emitter = mitt ( )
239-
240- collection = createCollection < TestItem , string > ( {
241- getKey : ( item ) => item . id ,
242- startSync : true ,
243- sync : {
244- sync : ( { begin, write, commit } ) => {
245- // Provide initial data through sync
246- begin ( )
247- for ( const item of testData ) {
248- write ( {
249- type : `insert` ,
250- value : item ,
251- } )
252- }
253- commit ( )
254-
255- // Listen for mutations and sync them back (only register once)
256- if ( ! emitter . all . has ( `sync` ) ) {
257- emitter . on ( `sync` , ( changes : Array < PendingMutation > ) => {
258- begin ( )
259- changes . forEach ( ( change ) => {
260- write ( {
261- type : change . type ,
262- value : change . modified as unknown as TestItem ,
263- } )
264- } )
265- commit ( )
266- } )
267- }
268- } ,
269- } ,
270- } )
245+ collection = createTestItemCollection ( )
271246
272247 // Wait for sync to complete
273248 await collection . stateWhenReady ( )
@@ -1315,36 +1290,101 @@ describe(`Query Index Optimization`, () => {
13151290 } )
13161291
13171292 it ( `should optimize live queries with ORDER BY and LIMIT` , async ( ) => {
1318- await withIndexTracking ( collection , async ( tracker ) => {
1319- const liveQuery = createLiveQueryCollection ( {
1320- query : ( q : any ) =>
1321- q
1322- . from ( { item : collection } )
1323- . where ( ( { item } : any ) => eq ( item . status , `active` ) )
1324- . orderBy ( ( { item } : any ) => [ item . age ] )
1325- . limit ( 2 )
1326- . select ( ( { item } : any ) => ( {
1327- id : item . id ,
1328- name : item . name ,
1329- age : item . age ,
1330- } ) ) ,
1331- startSync : true ,
1332- } )
1293+ collection . createIndex ( ( row ) => row . age )
13331294
1334- await liveQuery . stateWhenReady ( )
1295+ const liveQuery = createLiveQueryCollection ( {
1296+ query : ( q : any ) =>
1297+ q
1298+ . from ( { item : collection } )
1299+ . where ( ( { item } : any ) => eq ( item . status , `active` ) )
1300+ . orderBy ( ( { item } : any ) => item . age )
1301+ . limit ( 2 )
1302+ . select ( ( { item } : any ) => ( {
1303+ id : item . id ,
1304+ name : item . name ,
1305+ age : item . age ,
1306+ } ) ) ,
1307+ startSync : true ,
1308+ } )
13351309
1336- // Should have found limited results
1337- expect ( liveQuery . size ) . toBeLessThanOrEqual ( 2 )
1310+ await liveQuery . stateWhenReady ( )
1311+
1312+ // Should have found limited results
1313+ expect ( liveQuery . size ) . toBe ( 2 )
1314+
1315+ expect ( liveQuery . toArray ) . toEqual ( [
1316+ { id : `5` , name : `Eve` , age : 22 } ,
1317+ { id : `1` , name : `Alice` , age : 25 } ,
1318+ ] )
1319+
1320+ collection . utils . begin ( )
1321+ collection . utils . write ( {
1322+ type : `insert` ,
1323+ value : {
1324+ id : `6` ,
1325+ name : `Dave` ,
1326+ age : 20 ,
1327+ status : `active` ,
1328+ score : undefined ,
1329+ createdAt : new Date ( `2023-01-09` ) ,
1330+ } ,
1331+ } )
1332+ collection . utils . commit ( )
13381333
1339- // Should use index optimization for the WHERE clause even with ORDER BY + LIMIT
1340- // The WHERE clause can be optimized independently of the ORDER BY + LIMIT operations
1341- expectIndexUsage ( tracker . stats , {
1342- shouldUseIndex : true ,
1343- shouldUseFullScan : false ,
1344- indexCallCount : 1 , // The status='active' condition can use index
1345- fullScanCallCount : 0 ,
1346- } )
1334+ expect ( liveQuery . size ) . toBe ( 2 )
1335+
1336+ expect ( liveQuery . toArray ) . toEqual ( [
1337+ { id : `6` , name : `Dave` , age : 20 } ,
1338+ { id : `5` , name : `Eve` , age : 22 } ,
1339+ ] )
1340+ } )
1341+
1342+ it ( `should stop loading data for live queries with ORDER BY and LIMIT if no more data available` , async ( ) => {
1343+ collection . createIndex ( ( row ) => row . age )
1344+
1345+ const liveQuery = createLiveQueryCollection ( {
1346+ query : ( q : any ) =>
1347+ q
1348+ . from ( { item : collection } )
1349+ . orderBy ( ( { item } : any ) => item . age )
1350+ . limit ( 10 ) // limit > than total number of items in the collection (5)
1351+ . select ( ( { item } : any ) => ( {
1352+ id : item . id ,
1353+ name : item . name ,
1354+ age : item . age ,
1355+ } ) ) ,
1356+ startSync : true ,
1357+ } )
1358+
1359+ await liveQuery . stateWhenReady ( )
1360+
1361+ expect ( liveQuery . size ) . toBe ( 5 )
1362+
1363+ // Insert a new item
1364+ collection . utils . begin ( )
1365+ collection . utils . write ( {
1366+ type : `insert` ,
1367+ value : {
1368+ id : `6` ,
1369+ name : `Dave` ,
1370+ age : 29 ,
1371+ status : `active` ,
1372+ score : undefined ,
1373+ createdAt : new Date ( `2023-01-09` ) ,
1374+ } ,
13471375 } )
1376+ collection . utils . commit ( )
1377+
1378+ expect ( liveQuery . size ) . toBe ( 6 )
1379+
1380+ expect ( liveQuery . toArray ) . toEqual ( [
1381+ { id : `5` , name : `Eve` , age : 22 } ,
1382+ { id : `1` , name : `Alice` , age : 25 } ,
1383+ { id : `4` , name : `Diana` , age : 28 } ,
1384+ { id : `6` , name : `Dave` , age : 29 } ,
1385+ { id : `2` , name : `Bob` , age : 30 } ,
1386+ { id : `3` , name : `Charlie` , age : 35 } ,
1387+ ] )
13481388 } )
13491389
13501390 it ( `should handle live queries without WHERE clauses` , async ( ) => {
0 commit comments