@@ -3473,6 +3473,240 @@ describe('FirestorePipeline', function () {
34733473 } ) ;
34743474 } ) ;
34753475
3476+ describe ( 'iOS NodeBuilder stage coercion and operand tail coverage' , function ( ) {
3477+ it ( 'exercises stage option coercion for findNearest dot product and transform stages' , async function ( ) {
3478+ if ( Platform . other ) {
3479+ return ;
3480+ }
3481+
3482+ const { execute, field, constant, add, countAll } = firestorePipelinesModular ;
3483+ const { getFirestore, collection, doc, setDoc, vector } = firestoreModular ;
3484+ const db = getFirestore ( DATABASE_ID ) ;
3485+ const coll = collection (
3486+ db ,
3487+ `${ COLLECTION } /${ Utils . randString ( 12 , '#aA' ) } /stage-coercion-options` ,
3488+ ) ;
3489+
3490+ await Promise . all ( [
3491+ setDoc ( doc ( coll , 'v1' ) , {
3492+ name : 'alpha' ,
3493+ region : 'EU' ,
3494+ score : 40 ,
3495+ embedding : vector ( [ 1.0 , 0.0 , 0.0 ] ) ,
3496+ } ) ,
3497+ setDoc ( doc ( coll , 'v2' ) , {
3498+ name : 'beta' ,
3499+ region : 'EU' ,
3500+ score : 70 ,
3501+ embedding : vector ( [ 0.0 , 1.0 , 0.0 ] ) ,
3502+ } ) ,
3503+ setDoc ( doc ( coll , 'v3' ) , {
3504+ name : 'gamma' ,
3505+ region : 'US' ,
3506+ score : 55 ,
3507+ embedding : vector ( [ 0.0 , 0.0 , 1.0 ] ) ,
3508+ } ) ,
3509+ ] ) ;
3510+
3511+ const dotSnapshot = await execute (
3512+ db
3513+ . pipeline ( )
3514+ . collection ( coll )
3515+ . findNearest ( {
3516+ field : 'embedding' ,
3517+ vectorValue : [ 1.0 , 0.0 , 0.0 ] ,
3518+ distanceMeasure : 'DOT_PRODUCT' ,
3519+ limit : 2 ,
3520+ } )
3521+ . select ( 'name' ) ,
3522+ ) ;
3523+
3524+ dotSnapshot . results . should . have . length ( 2 ) ;
3525+ dotSnapshot . results [ 0 ] . data ( ) . name . should . equal ( 'alpha' ) ;
3526+
3527+ const transformSnapshot = await execute (
3528+ db
3529+ . pipeline ( )
3530+ . collection ( coll )
3531+ . where ( field ( 'region' ) . equal ( 'EU' ) )
3532+ . addFields ( add ( field ( 'score' ) , constant ( 5 ) ) . as ( 'scorePlusFive' ) )
3533+ . sort ( field ( 'scorePlusFive' ) . descending ( ) , field ( 'name' ) . ascending ( ) )
3534+ . offset ( 1 )
3535+ . limit ( 1 )
3536+ . select ( 'name' , 'scorePlusFive' ) ,
3537+ ) ;
3538+
3539+ transformSnapshot . results . should . have . length ( 1 ) ;
3540+ transformSnapshot . results [ 0 ] . data ( ) . name . should . equal ( 'alpha' ) ;
3541+ transformSnapshot . results [ 0 ] . data ( ) . scorePlusFive . should . equal ( 45 ) ;
3542+
3543+ const groupedSnapshot = await execute (
3544+ db
3545+ . pipeline ( )
3546+ . collection ( coll )
3547+ . aggregate ( {
3548+ accumulators : [ countAll ( ) . as ( 'rows' ) ] ,
3549+ groups : [ field ( 'region' ) . as ( 'regionKey' ) ] ,
3550+ } )
3551+ . sort ( field ( 'regionKey' ) . ascending ( ) ) ,
3552+ ) ;
3553+
3554+ groupedSnapshot . results . should . have . length ( 2 ) ;
3555+ groupedSnapshot . results [ 0 ] . data ( ) . regionKey . should . equal ( 'EU' ) ;
3556+ groupedSnapshot . results [ 0 ] . data ( ) . rows . should . equal ( 2 ) ;
3557+ } ) ;
3558+
3559+ it ( 'covers remaining comparison and numeric operand rhs coercion tails' , async function ( ) {
3560+ const {
3561+ execute,
3562+ field,
3563+ subtract,
3564+ divide,
3565+ pow,
3566+ equal,
3567+ notEqual,
3568+ lessThanOrEqual,
3569+ greaterThanOrEqual,
3570+ greaterThan,
3571+ and,
3572+ } = firestorePipelinesModular ;
3573+ const { getFirestore, collection, doc, setDoc } = firestoreModular ;
3574+ const db = getFirestore ( DATABASE_ID ) ;
3575+ const docPath = `${ COLLECTION } /${ Utils . randString ( 12 , '#aA' ) } ` ;
3576+
3577+ await setDoc ( doc ( db , docPath ) , {
3578+ base : 20 ,
3579+ divisor : 4 ,
3580+ exp : 3 ,
3581+ tag : 'alpha' ,
3582+ mirrorTag : 'alpha' ,
3583+ score : 42 ,
3584+ cap : '50' ,
3585+ status : 'active' ,
3586+ } ) ;
3587+
3588+ if ( Platform . other ) {
3589+ const macSnapshot = await execute (
3590+ db
3591+ . pipeline ( )
3592+ . documents ( [ docPath ] )
3593+ . select (
3594+ subtract ( field ( 'base' ) , true ) . as ( 'subTrue' ) ,
3595+ divide ( field ( 'base' ) , false ) . as ( 'divFalse' ) ,
3596+ pow ( field ( 'exp' ) , true ) . as ( 'powTrue' ) ,
3597+ equal ( field ( 'tag' ) , field ( 'mirrorTag' ) ) . as ( 'tagsMatch' ) ,
3598+ equal ( field ( 'tag' ) , 'alpha' ) . as ( 'tagStringRhs' ) ,
3599+ lessThanOrEqual ( field ( 'score' ) , '50' ) . as ( 'scoreLteString' ) ,
3600+ ) ,
3601+ ) ;
3602+
3603+ macSnapshot . results . should . have . length ( 1 ) ;
3604+ const macData = macSnapshot . results [ 0 ] . data ( ) ;
3605+ macData . subTrue . should . equal ( 19 ) ;
3606+ macData . divFalse . should . equal ( 0 ) ;
3607+ macData . powTrue . should . equal ( 3 ) ;
3608+ macData . tagsMatch . should . equal ( true ) ;
3609+ macData . tagStringRhs . should . equal ( true ) ;
3610+ macData . scoreLteString . should . equal ( true ) ;
3611+ return ;
3612+ }
3613+
3614+ const snapshot = await execute (
3615+ db
3616+ . pipeline ( )
3617+ . documents ( [ docPath ] )
3618+ . select (
3619+ subtract ( field ( 'base' ) , true ) . as ( 'subTrue' ) ,
3620+ subtract ( field ( 'base' ) , '5' ) . as ( 'subString' ) ,
3621+ divide ( field ( 'base' ) , field ( 'divisor' ) ) . as ( 'divField' ) ,
3622+ pow ( field ( 'exp' ) , true ) . as ( 'powTrue' ) ,
3623+ equal ( field ( 'tag' ) , field ( 'mirrorTag' ) ) . as ( 'tagsMatch' ) ,
3624+ equal ( field ( 'tag' ) , 'alpha' ) . as ( 'tagStringRhs' ) ,
3625+ notEqual ( field ( 'status' ) , 'inactive' ) . as ( 'statusActive' ) ,
3626+ lessThanOrEqual ( field ( 'score' ) , '50' ) . as ( 'scoreLteString' ) ,
3627+ greaterThanOrEqual ( field ( 'base' ) , false ) . as ( 'baseGteFalse' ) ,
3628+ ) ,
3629+ ) ;
3630+
3631+ snapshot . results . should . have . length ( 1 ) ;
3632+ const data = snapshot . results [ 0 ] . data ( ) ;
3633+ data . subTrue . should . equal ( 19 ) ;
3634+ data . subString . should . equal ( 15 ) ;
3635+ data . divField . should . equal ( 5 ) ;
3636+ data . powTrue . should . equal ( 3 ) ;
3637+ data . tagsMatch . should . equal ( true ) ;
3638+ data . tagStringRhs . should . equal ( true ) ;
3639+ data . statusActive . should . equal ( true ) ;
3640+ data . scoreLteString . should . equal ( true ) ;
3641+ data . baseGteFalse . should . equal ( true ) ;
3642+
3643+ const coll = collection (
3644+ db ,
3645+ `${ COLLECTION } /${ Utils . randString ( 12 , '#aA' ) } /operand-tail-raw-where` ,
3646+ ) ;
3647+
3648+ await Promise . all ( [
3649+ setDoc ( doc ( coll , 'match' ) , {
3650+ tag : 'alpha' ,
3651+ score : 42 ,
3652+ bump : true ,
3653+ status : 'active' ,
3654+ } ) ,
3655+ setDoc ( doc ( coll , 'skip-tag' ) , {
3656+ tag : 'beta' ,
3657+ score : 42 ,
3658+ bump : true ,
3659+ status : 'active' ,
3660+ } ) ,
3661+ setDoc ( doc ( coll , 'skip-score' ) , {
3662+ tag : 'alpha' ,
3663+ score : 10 ,
3664+ bump : true ,
3665+ status : 'active' ,
3666+ } ) ,
3667+ ] ) ;
3668+
3669+ const filtered = await execute (
3670+ db
3671+ . pipeline ( )
3672+ . collection ( coll )
3673+ . where ( {
3674+ condition : {
3675+ operator : 'AND' ,
3676+ queries : [
3677+ { operator : '==' , fieldPath : 'tag' , value : 'alpha' } ,
3678+ { operator : '==' , fieldPath : 'bump' , value : true } ,
3679+ { operator : '==' , fieldPath : 'status' , value : 'active' } ,
3680+ ] ,
3681+ } ,
3682+ } )
3683+ . select ( 'tag' , 'score' , 'status' ) ,
3684+ ) ;
3685+
3686+ filtered . results . should . have . length ( 1 ) ;
3687+ filtered . results [ 0 ] . data ( ) . tag . should . equal ( 'alpha' ) ;
3688+ filtered . results [ 0 ] . data ( ) . score . should . equal ( 42 ) ;
3689+
3690+ const whereSnapshot = await execute (
3691+ db
3692+ . pipeline ( )
3693+ . collection ( coll )
3694+ . where (
3695+ and (
3696+ equal ( field ( 'tag' ) , 'alpha' ) ,
3697+ equal ( field ( 'status' ) , 'active' ) ,
3698+ greaterThanOrEqual ( field ( 'score' ) , false ) ,
3699+ greaterThan ( field ( 'score' ) , 20 ) ,
3700+ ) ,
3701+ )
3702+ . select ( 'tag' , 'score' ) ,
3703+ ) ;
3704+
3705+ whereSnapshot . results . should . have . length ( 1 ) ;
3706+ whereSnapshot . results [ 0 ] . data ( ) . score . should . equal ( 42 ) ;
3707+ } ) ;
3708+ } ) ;
3709+
34763710 describe ( 'aggregate expression argument lowering' , function ( ) {
34773711 it ( 'evaluates aggregates with computed expression arguments' , async function ( ) {
34783712 const {
0 commit comments