Skip to content

Commit 8c516fd

Browse files
committed
Implement and test realtime array functions
1 parent 00211cc commit 8c516fd

File tree

3 files changed

+384
-10
lines changed

3 files changed

+384
-10
lines changed

firebase-firestore/src/main/java/com/google/firebase/firestore/pipeline/evaluation.kt

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,15 +243,63 @@ internal val evaluateSubtract = arithmeticPrimitive(Math::subtractExact, Double:
243243

244244
internal val evaluateArray = variadicNullableValueFunction(EvaluateResult.Companion::list)
245245

246-
internal val evaluateEqAny = notImplemented
246+
internal val evaluateEqAny = binaryFunction { list: List<Value>, value: Value ->
247+
eqAny(value, list)
248+
}
247249

248250
internal val evaluateNotEqAny = notImplemented
249251

250-
internal val evaluateArrayContains = notImplemented
252+
internal val evaluateArrayContains = binaryFunction { array: Value, value: Value ->
253+
if (array.hasArrayValue()) eqAny(value, array.arrayValue.valuesList) else EvaluateResultError
254+
}
251255

252-
internal val evaluateArrayContainsAny = notImplemented
256+
internal val evaluateArrayContainsAny =
257+
binaryFunction { array: List<Value>, searchValues: List<Value> ->
258+
var foundNull = false
259+
for (value in array) for (search in searchValues) when (strictEquals(value, search)) {
260+
true -> return@binaryFunction EvaluateResult.TRUE
261+
false -> {}
262+
null -> foundNull = true
263+
}
264+
return@binaryFunction if (foundNull) EvaluateResult.NULL else EvaluateResult.FALSE
265+
}
253266

254-
internal val evaluateArrayLength = notImplemented
267+
internal val evaluateArrayContainsAll =
268+
binaryFunction { array: List<Value>, searchValues: List<Value> ->
269+
var foundNullAtLeastOnce = false
270+
for (search in searchValues) {
271+
var found = false
272+
var foundNull = false
273+
for (value in array) when (strictEquals(value, search)) {
274+
true -> {
275+
found = true
276+
break
277+
}
278+
false -> {}
279+
null -> foundNull = true
280+
}
281+
if (foundNull) {
282+
foundNullAtLeastOnce = true
283+
} else if (!found) {
284+
return@binaryFunction EvaluateResult.FALSE
285+
}
286+
}
287+
return@binaryFunction if (foundNullAtLeastOnce) EvaluateResult.NULL else EvaluateResult.TRUE
288+
}
289+
290+
internal val evaluateArrayLength = unaryFunction { array: List<Value> ->
291+
EvaluateResult.long(array.size)
292+
}
293+
294+
private fun eqAny(value: Value, list: List<Value>): EvaluateResult {
295+
var foundNull = false
296+
for (element in list) when (strictEquals(value, element)) {
297+
true -> return EvaluateResult.TRUE
298+
false -> {}
299+
null -> foundNull = true
300+
}
301+
return if (foundNull) EvaluateResult.NULL else EvaluateResult.FALSE
302+
}
255303

256304
// === String Functions ===
257305

@@ -490,6 +538,14 @@ private inline fun unaryFunction(crossinline timestampOp: (Timestamp) -> Evaluat
490538
timestampOp,
491539
)
492540

541+
@JvmName("unaryArrayFunction")
542+
private inline fun unaryFunction(crossinline longOp: (List<Value>) -> EvaluateResult) =
543+
unaryFunctionType(
544+
Value.ValueTypeCase.ARRAY_VALUE,
545+
{ it.arrayValue.valuesList },
546+
longOp,
547+
)
548+
493549
private inline fun unaryFunction(
494550
crossinline byteOp: (ByteString) -> EvaluateResult,
495551
crossinline stringOp: (String) -> EvaluateResult
@@ -559,6 +615,20 @@ private inline fun binaryFunction(
559615
}
560616
}
561617

618+
@JvmName("binaryValueArrayFunction")
619+
private inline fun binaryFunction(
620+
crossinline function: (Value, List<Value>) -> EvaluateResult
621+
): EvaluateFunction = binaryFunction { v1: Value, v2: Value ->
622+
if (v2.hasArrayValue()) function(v1, v2.arrayValue.valuesList) else EvaluateResultError
623+
}
624+
625+
@JvmName("binaryArrayValueFunction")
626+
private inline fun binaryFunction(
627+
crossinline function: (List<Value>, Value) -> EvaluateResult
628+
): EvaluateFunction = binaryFunction { v1: Value, v2: Value ->
629+
if (v1.hasArrayValue()) function(v1.arrayValue.valuesList, v2) else EvaluateResultError
630+
}
631+
562632
@JvmName("binaryStringStringFunction")
563633
private inline fun binaryFunction(crossinline function: (String, String) -> EvaluateResult) =
564634
binaryFunctionType(
@@ -569,6 +639,18 @@ private inline fun binaryFunction(crossinline function: (String, String) -> Eval
569639
function
570640
)
571641

642+
@JvmName("binaryArrayArrayFunction")
643+
private inline fun binaryFunction(
644+
crossinline function: (List<Value>, List<Value>) -> EvaluateResult
645+
) =
646+
binaryFunctionType(
647+
Value.ValueTypeCase.ARRAY_VALUE,
648+
{ it.arrayValue.valuesList },
649+
Value.ValueTypeCase.ARRAY_VALUE,
650+
{ it.arrayValue.valuesList },
651+
function
652+
)
653+
572654
private inline fun ternaryTimestampFunction(
573655
crossinline function: (Timestamp, String, Long) -> EvaluateResult
574656
): EvaluateFunction = ternaryNullableValueFunction { timestamp: Value, unit: Value, number: Value ->

firebase-firestore/src/main/java/com/google/firebase/firestore/pipeline/expressions.kt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2610,7 +2610,8 @@ abstract class Expr internal constructor() {
26102610
* @return A new [Expr] representing the array function.
26112611
*/
26122612
@JvmStatic
2613-
fun array(vararg elements: Expr): Expr = FunctionExpr("array", evaluateArray, elements)
2613+
fun array(vararg elements: Any?): Expr =
2614+
FunctionExpr("array", evaluateArray, elements.map(::toExprOrConstant).toTypedArray<Expr>())
26142615

26152616
/**
26162617
* Creates an expression that creates a Firestore array value from an input array.
@@ -2619,8 +2620,8 @@ abstract class Expr internal constructor() {
26192620
* @return A new [Expr] representing the array function.
26202621
*/
26212622
@JvmStatic
2622-
fun array(elements: List<Expr>): Expr =
2623-
FunctionExpr("array", evaluateArray, elements.toTypedArray())
2623+
fun array(elements: List<Any?>): Expr =
2624+
FunctionExpr("array", evaluateArray, elements.map(::toExprOrConstant).toTypedArray())
26242625

26252626
/**
26262627
* Creates an expression that concatenates an array with other arrays.
@@ -2753,7 +2754,7 @@ abstract class Expr internal constructor() {
27532754
*/
27542755
@JvmStatic
27552756
fun arrayContainsAll(array: Expr, arrayExpression: Expr) =
2756-
BooleanExpr("array_contains_all", notImplemented, array, arrayExpression)
2757+
BooleanExpr("array_contains_all", evaluateArrayContainsAll, array, arrayExpression)
27572758

27582759
/**
27592760
* Creates an expression that checks if array field contains all the specified [values].
@@ -2766,7 +2767,7 @@ abstract class Expr internal constructor() {
27662767
fun arrayContainsAll(arrayFieldName: String, values: List<Any>) =
27672768
BooleanExpr(
27682769
"array_contains_all",
2769-
notImplemented,
2770+
evaluateArrayContainsAll,
27702771
arrayFieldName,
27712772
ListOfExprs(toArrayOfExprOrConstant(values))
27722773
)
@@ -2780,7 +2781,7 @@ abstract class Expr internal constructor() {
27802781
*/
27812782
@JvmStatic
27822783
fun arrayContainsAll(arrayFieldName: String, arrayExpression: Expr) =
2783-
BooleanExpr("array_contains_all", notImplemented, arrayFieldName, arrayExpression)
2784+
BooleanExpr("array_contains_all", evaluateArrayContainsAll, arrayFieldName, arrayExpression)
27842785

27852786
/**
27862787
* Creates an expression that checks if [array] contains any of the specified [values].

0 commit comments

Comments
 (0)