@@ -648,5 +648,124 @@ describe(`Query`, () => {
648648 expect ( result . price ) . toBeLessThan ( 1000 )
649649 } )
650650 } )
651+
652+ test ( `select callback function` , ( ) => {
653+ const query : Query < Context > = {
654+ select : [
655+ ( { products } ) => ( {
656+ displayName : `${ products . name } (${ products . category } )` ,
657+ priceLevel : products . price > 500 ? `expensive` : `affordable` ,
658+ availability : products . inStock ? `in-stock` : `out-of-stock` ,
659+ } ) ,
660+ ] ,
661+ from : `products` ,
662+ where : [ [ `@id` , `<=` , 3 ] ] , // First three products
663+ }
664+
665+ const graph = new D2 ( { initialFrontier : v ( [ 0 , 0 ] ) } )
666+ const input = graph . newInput < [ number , Product ] > ( )
667+ const pipeline = compileQueryPipeline ( query , { [ query . from ] : input } )
668+
669+ const messages : Array < Message < any > > = [ ]
670+ pipeline . pipe (
671+ output ( ( message ) => {
672+ messages . push ( message )
673+ } )
674+ )
675+
676+ graph . finalize ( )
677+
678+ input . sendData (
679+ v ( [ 1 , 0 ] ) ,
680+ new MultiSet (
681+ sampleProducts . map ( ( product ) => [ [ product . id , product ] , 1 ] )
682+ )
683+ )
684+ input . sendFrontier ( new Antichain ( [ v ( [ 1 , 0 ] ) ] ) )
685+
686+ graph . run ( )
687+
688+ // Check the transformed results
689+ const dataMessages = messages . filter ( ( m ) => m . type === MessageType . DATA )
690+ const results = dataMessages [ 0 ] ! . data . collection
691+ . getInner ( )
692+ . map ( ( [ data ] ) => data )
693+
694+ expect ( results ) . toHaveLength ( 3 ) // First three products
695+
696+ // Verify the callback transformation
697+ results . forEach ( ( [ _key , result ] ) => {
698+ expect ( result ) . toHaveProperty ( `displayName` )
699+ expect ( result ) . toHaveProperty ( `priceLevel` )
700+ expect ( result ) . toHaveProperty ( `availability` )
701+ expect ( typeof result . displayName ) . toBe ( `string` )
702+ expect ( [ `expensive` , `affordable` ] ) . toContain ( result . priceLevel )
703+ expect ( [ `in-stock` , `out-of-stock` ] ) . toContain ( result . availability )
704+ } )
705+
706+ // Check specific transformations for known products
707+ const laptop = results . find ( ( [ _key , r ] ) =>
708+ r . displayName . includes ( `Laptop` )
709+ )
710+ expect ( laptop ) . toBeDefined ( )
711+ expect ( laptop ! [ 1 ] . priceLevel ) . toBe ( `expensive` )
712+ expect ( laptop ! [ 1 ] . availability ) . toBe ( `in-stock` )
713+ } )
714+
715+ test ( `mixed select: traditional columns and callback` , ( ) => {
716+ const query : Query < Context > = {
717+ select : [
718+ `@id` ,
719+ `@name` ,
720+ ( { products } ) => ( {
721+ computedField : `${ products . name } _computed` ,
722+ doublePrice : products . price * 2 ,
723+ } ) ,
724+ ] ,
725+ from : `products` ,
726+ where : [ [ `@id` , `=` , 1 ] ] , // Just the laptop
727+ }
728+
729+ const graph = new D2 ( { initialFrontier : v ( [ 0 , 0 ] ) } )
730+ const input = graph . newInput < [ number , Product ] > ( )
731+ const pipeline = compileQueryPipeline ( query , { [ query . from ] : input } )
732+
733+ const messages : Array < Message < any > > = [ ]
734+ pipeline . pipe (
735+ output ( ( message ) => {
736+ messages . push ( message )
737+ } )
738+ )
739+
740+ graph . finalize ( )
741+
742+ input . sendData (
743+ v ( [ 1 , 0 ] ) ,
744+ new MultiSet (
745+ sampleProducts . map ( ( product ) => [ [ product . id , product ] , 1 ] )
746+ )
747+ )
748+ input . sendFrontier ( new Antichain ( [ v ( [ 1 , 0 ] ) ] ) )
749+
750+ graph . run ( )
751+
752+ // Check the mixed results
753+ const dataMessages = messages . filter ( ( m ) => m . type === MessageType . DATA )
754+ const results = dataMessages [ 0 ] ! . data . collection
755+ . getInner ( )
756+ . map ( ( [ data ] ) => data )
757+
758+ expect ( results ) . toHaveLength ( 1 )
759+
760+ const [ _key , result ] = results [ 0 ] !
761+
762+ // Check traditional columns
763+ expect ( result . id ) . toBe ( 1 )
764+ expect ( result . name ) . toBe ( `Laptop` )
765+
766+ // Check callback-generated fields
767+ expect ( result . computedField ) . toBe ( `Laptop_computed` )
768+ expect ( result . doublePrice ) . toBe ( 2400 ) // 1200 * 2
769+ } )
651770 } )
652771} )
0 commit comments