@@ -409,6 +409,27 @@ private static final class ExitReceiverArrayGetFrame implements ObjectLoweringFr
409409 }
410410 }
411411
412+ private static final class ExitReceiverArrayFirstNFrame implements ObjectLoweringFrame {
413+ final LoweredExpressionBox box ;
414+ final List <PendingReceiverOperation > pendingOperations ;
415+ final int nextIndex ;
416+ final Expression currentExpression ;
417+ final LoweredExpressionBox countBox ;
418+
419+ ExitReceiverArrayFirstNFrame (
420+ LoweredExpressionBox box ,
421+ List <PendingReceiverOperation > pendingOperations ,
422+ int nextIndex ,
423+ Expression currentExpression ,
424+ LoweredExpressionBox countBox ) {
425+ this .box = box ;
426+ this .pendingOperations = pendingOperations ;
427+ this .nextIndex = nextIndex ;
428+ this .currentExpression = currentExpression ;
429+ this .countBox = countBox ;
430+ }
431+ }
432+
412433 private static final class ExitReceiverArrayConcatFrame implements ObjectLoweringFrame {
413434 final LoweredExpressionBox box ;
414435 final List <PendingReceiverOperation > pendingOperations ;
@@ -1556,6 +1577,36 @@ private void processObjectLoweringStack(ArrayDeque<ObjectLoweringFrame> stack)
15561577 indexArg , operationFieldName + ".args[1]" , indexBox ));
15571578 continue ;
15581579 }
1580+ case "arrayfirstn" :
1581+ {
1582+ Object countArg = args .get (1 );
1583+ if (!containsLowerableExpression (countArg )) {
1584+ Object countValue = resolveConstantValue (countArg , operationFieldName + ".args[1]" );
1585+ if (countValue instanceof Number ) {
1586+ stack .push (
1587+ new ContinueReceiverExpressionChainFrame (
1588+ continueFrame .box ,
1589+ null ,
1590+ continueFrame .pendingOperations ,
1591+ nextIndex ,
1592+ currentExpression .arrayFirstN (((Number ) countValue ).intValue ())));
1593+ continue ;
1594+ }
1595+ }
1596+
1597+ LoweredExpressionBox countBox = new LoweredExpressionBox ();
1598+ stack .push (
1599+ new ExitReceiverArrayFirstNFrame (
1600+ continueFrame .box ,
1601+ continueFrame .pendingOperations ,
1602+ nextIndex ,
1603+ currentExpression ,
1604+ countBox ));
1605+ stack .push (
1606+ new EnterObjectExpressionValueFrame (
1607+ countArg , operationFieldName + ".args[1]" , countBox ));
1608+ continue ;
1609+ }
15591610 case "arrayconcat" :
15601611 {
15611612 if (args .size () < 2 ) {
@@ -1787,6 +1838,18 @@ private void processObjectLoweringStack(ArrayDeque<ObjectLoweringFrame> stack)
17871838 continue ;
17881839 }
17891840
1841+ if (frame instanceof ExitReceiverArrayFirstNFrame ) {
1842+ ExitReceiverArrayFirstNFrame exitFrame = (ExitReceiverArrayFirstNFrame ) frame ;
1843+ stack .push (
1844+ new ContinueReceiverExpressionChainFrame (
1845+ exitFrame .box ,
1846+ null ,
1847+ exitFrame .pendingOperations ,
1848+ exitFrame .nextIndex ,
1849+ exitFrame .currentExpression .arrayFirstN (exitFrame .countBox .value )));
1850+ continue ;
1851+ }
1852+
17901853 if (frame instanceof ExitReceiverArrayConcatFrame ) {
17911854 ExitReceiverArrayConcatFrame exitFrame = (ExitReceiverArrayConcatFrame ) frame ;
17921855 Object secondValue = exitFrame .childBoxes .get (0 ).value ;
@@ -2571,6 +2634,7 @@ private boolean isDeferredUnaryExpressionFunction(String normalizedFunctionName)
25712634 return "type" .equals (normalizedFunctionName )
25722635 || "collectionid" .equals (normalizedFunctionName )
25732636 || "documentid" .equals (normalizedFunctionName )
2637+ || "arrayfirst" .equals (normalizedFunctionName )
25742638 || "arraylength" .equals (normalizedFunctionName )
25752639 || "arraysum" .equals (normalizedFunctionName )
25762640 || "vectorlength" .equals (normalizedFunctionName )
@@ -2588,6 +2652,7 @@ private boolean isDeferredReceiverExpressionFunction(String normalizedFunctionNa
25882652 || "mapget" .equals (normalizedFunctionName )
25892653 || "mapmerge" .equals (normalizedFunctionName )
25902654 || "arrayget" .equals (normalizedFunctionName )
2655+ || "arrayfirstn" .equals (normalizedFunctionName )
25912656 || "arrayconcat" .equals (normalizedFunctionName )
25922657 || "cosinedistance" .equals (normalizedFunctionName )
25932658 || "dotproduct" .equals (normalizedFunctionName )
@@ -2612,6 +2677,9 @@ private Expression applyPendingUnaryExpressionFunctions(
26122677 case "documentid" :
26132678 currentExpression = currentExpression .documentId ();
26142679 break ;
2680+ case "arrayfirst" :
2681+ currentExpression = currentExpression .arrayFirst ();
2682+ break ;
26152683 case "arraylength" :
26162684 currentExpression = currentExpression .arrayLength ();
26172685 break ;
@@ -2758,6 +2826,12 @@ private Expression buildSpecialParsedExpressionFunction(
27582826 case "arrayget" :
27592827 requireParsedArgumentCount (args , 2 , functionName , fieldName );
27602828 return buildParsedArrayGetExpression (args , fieldName );
2829+ case "arrayfirst" :
2830+ requireParsedArgumentCount (args , 1 , functionName , fieldName );
2831+ return coerceExpressionValueNode (args .get (0 ), fieldName + ".args[0]" ).arrayFirst ();
2832+ case "arrayfirstn" :
2833+ requireParsedArgumentCount (args , 2 , functionName , fieldName );
2834+ return buildParsedArrayFirstNExpression (args , fieldName );
27612835 case "arrayconcat" :
27622836 return buildParsedArrayConcatExpression (args , functionName , fieldName );
27632837 case "arraysum" :
@@ -2975,6 +3049,19 @@ private Expression buildParsedArrayGetExpression(
29753049 return arrayExpr .arrayGet (coerceExpressionValueNode (args .get (1 ), fieldName + ".args[1]" ));
29763050 }
29773051
3052+ private Expression buildParsedArrayFirstNExpression (
3053+ List <ReactNativeFirebaseFirestorePipelineParser .ParsedValueNode > args , String fieldName )
3054+ throws ReactNativeFirebaseFirestorePipelineExecutor .PipelineValidationException {
3055+ Expression arrayExpr = coerceExpressionValueNode (args .get (0 ), fieldName + ".args[0]" );
3056+ if (!containsParsedExpression (args .get (1 ))) {
3057+ Object countValue = resolveValueNode (args .get (1 ), fieldName + ".args[1]" );
3058+ if (countValue instanceof Number ) {
3059+ return arrayExpr .arrayFirstN (((Number ) countValue ).intValue ());
3060+ }
3061+ }
3062+ return arrayExpr .arrayFirstN (coerceExpressionValueNode (args .get (1 ), fieldName + ".args[1]" ));
3063+ }
3064+
29783065 private Expression buildParsedArrayConcatExpression (
29793066 List <ReactNativeFirebaseFirestorePipelineParser .ParsedValueNode > args ,
29803067 String functionName ,
0 commit comments