@@ -3,6 +3,7 @@ import { concat, createLiveQueryCollection } from "../../src/query/index.js"
33import { createCollection } from "../../src/collection.js"
44import { mockSyncCollectionOptions } from "../utils.js"
55import { DistinctRequiresSelectError } from "../../src/errors"
6+ import { count , eq , gte , not } from "../../src/query/builder/functions.js"
67
78// Sample data types for comprehensive DISTINCT testing
89type User = {
@@ -546,6 +547,177 @@ function createDistinctTests(autoIndex: `off` | `eager`): void {
546547 expect ( locations ) . toContain ( `Junior` )
547548 } )
548549 } )
550+
551+ describe ( `Distinct with Other Operators` , ( ) => {
552+ let usersCollection : ReturnType < typeof createUsersCollection >
553+
554+ beforeEach ( ( ) => {
555+ usersCollection = createUsersCollection ( autoIndex )
556+ } )
557+
558+ test ( `distinct with groupBy - should work with aggregates` , ( ) => {
559+ const distinctGroupedData = createLiveQueryCollection ( {
560+ startSync : true ,
561+ query : ( q ) =>
562+ q
563+ . from ( { users : usersCollection } )
564+ . groupBy ( ( { users } ) => users . department )
565+ . select ( ( { users } ) => ( {
566+ department : users . department ,
567+ user_count : count ( users . id ) ,
568+ } ) )
569+ . distinct ( ) ,
570+ } )
571+
572+ // Should have 3 distinct department groups: Engineering, Marketing, Sales
573+ expect ( distinctGroupedData . size ) . toBe ( 3 )
574+
575+ const departments = Array . from ( distinctGroupedData . values ( ) )
576+ const departmentNames = departments . map ( ( d ) => d . department ) . sort ( )
577+ const userCounts = departments . map ( ( d ) => d . user_count ) . sort ( )
578+ expect ( departmentNames ) . toEqual ( [ `Engineering` , `Marketing` , `Sales` ] )
579+ expect ( userCounts ) . toEqual ( [ 1 , 2 , 5 ] )
580+
581+ // Check that counts are correct
582+ const engineeringGroup = departments . find (
583+ ( d ) => d . department === `Engineering`
584+ )
585+ expect ( engineeringGroup ?. user_count ) . toBe ( 5 ) // John, Jane, Alice, Diana, Frank
586+ } )
587+
588+ test ( `distinct with filter - should apply distinct after filtering` , ( ) => {
589+ const distinctFilteredUsers = createLiveQueryCollection ( {
590+ startSync : true ,
591+ query : ( q ) =>
592+ q
593+ . from ( { users : usersCollection } )
594+ . where ( ( { users } ) => not ( eq ( users . country , `USA` ) ) )
595+ . select ( ( { users } ) => ( { country : users . country } ) )
596+ . distinct ( ) ,
597+ } )
598+
599+ expect ( distinctFilteredUsers . size ) . toBe ( 2 )
600+
601+ const countries = Array . from ( distinctFilteredUsers . values ( ) ) . map (
602+ ( u ) => u . country
603+ )
604+ expect ( countries ) . toContain ( `Canada` )
605+ expect ( countries ) . toContain ( `UK` )
606+ } )
607+
608+ test ( `distinct with orderBy - should maintain distinct results in order` , ( ) => {
609+ const distinctOrderedCountries = createLiveQueryCollection ( {
610+ startSync : true ,
611+ query : ( q ) =>
612+ q
613+ . from ( { users : usersCollection } )
614+ . orderBy ( ( { users } ) => users . country , `asc` )
615+ . select ( ( { users } ) => ( { country : users . country } ) )
616+ . distinct ( ) ,
617+ } )
618+
619+ expect ( distinctOrderedCountries . size ) . toBe ( 3 )
620+
621+ const orderedCountries = distinctOrderedCountries . toArray . map (
622+ ( u ) => u . country
623+ )
624+ expect ( orderedCountries ) . toEqual ( [ `Canada` , `UK` , `USA` ] )
625+ } )
626+
627+ test ( `distinct with multiple chained operators` , ( ) => {
628+ const complexQuery = createLiveQueryCollection ( {
629+ startSync : true ,
630+ query : ( q ) =>
631+ q
632+ . from ( { users : usersCollection } )
633+ . where ( ( { users } ) => gte ( users . salary , 75000 ) )
634+ . orderBy ( ( { users } ) => users . department , `asc` )
635+ . select ( ( { users } ) => ( {
636+ department : users . department ,
637+ role : users . role ,
638+ } ) )
639+ . distinct ( ) ,
640+ } )
641+
642+ // Should have distinct department-role combinations for users with salary >= 75000
643+ const results = complexQuery . toArray
644+ expect ( results . length ) . toBeGreaterThan ( 0 )
645+
646+ // Check that we have distinct combinations
647+ const combinations = results . map ( ( r ) => `${ r . department } -${ r . role } ` )
648+ const uniqueCombinations = [ ...new Set ( combinations ) ]
649+ expect ( combinations . length ) . toBe ( uniqueCombinations . length )
650+ } )
651+
652+ test ( `groupBy with distinct on aggregated results` , ( ) => {
653+ const groupedDistinctSalaries = createLiveQueryCollection ( {
654+ startSync : true ,
655+ query : ( q ) =>
656+ q
657+ . from ( { users : usersCollection } )
658+ . groupBy ( ( { users } ) => users . city )
659+ . select ( ( { users } ) => ( {
660+ count : count ( users . id ) ,
661+ } ) )
662+ . distinct ( ) ,
663+ } )
664+
665+ // There are 3 distinct counts of users per city
666+ expect ( groupedDistinctSalaries . size ) . toBe ( 3 )
667+
668+ const counts = Array . from ( groupedDistinctSalaries . values ( ) ) . map (
669+ ( result ) => result . count
670+ )
671+
672+ // All average salaries should be unique (distinct)
673+ const uniqueCounts = [ ...new Set ( counts ) ]
674+ expect ( counts . length ) . toBe ( uniqueCounts . length )
675+ } )
676+
677+ test ( `distinct with join operations` , ( ) => {
678+ // Create a simple departments collection to join with
679+ const departmentsData = [
680+ { id : `Engineering` , budget : 1000000 } ,
681+ { id : `Marketing` , budget : 500000 } ,
682+ { id : `Sales` , budget : 750000 } ,
683+ ]
684+
685+ const departmentsCollection = createCollection (
686+ mockSyncCollectionOptions ( {
687+ id : `test-departments` ,
688+ getKey : ( dept ) => dept . id ,
689+ initialData : departmentsData ,
690+ autoIndex,
691+ } )
692+ )
693+
694+ const distinctJoinedData = createLiveQueryCollection ( {
695+ startSync : true ,
696+ query : ( q ) =>
697+ q
698+ . from ( { users : usersCollection } )
699+ . join (
700+ { departments : departmentsCollection } ,
701+ ( { users, departments } ) => eq ( users . department , departments . id )
702+ )
703+ . where ( ( { users } ) => eq ( users . active , true ) )
704+ . select ( ( { departments } ) => ( {
705+ department : departments . id ,
706+ } ) )
707+ . distinct ( ) ,
708+ } )
709+
710+ // There are 3 distinct departments that have active users
711+ expect ( distinctJoinedData . size ) . toBe ( 3 )
712+
713+ const results = Array . from ( distinctJoinedData . values ( ) )
714+
715+ // Should have distinct combinations of department
716+ const combinations = results . map ( ( r ) => `${ r . department } ` )
717+ const uniqueCombinations = [ ...new Set ( combinations ) ]
718+ expect ( combinations . length ) . toBe ( uniqueCombinations . length )
719+ } )
720+ } )
549721 } )
550722}
551723
0 commit comments