Skip to content

Commit 86ea2cc

Browse files
committed
Merge branch 'tomandersen/pipelines' into tomandersen/pipelines-evaluate
# Conflicts: # firebase-firestore/api.txt # firebase-firestore/src/main/java/com/google/firebase/firestore/Pipeline.kt # firebase-firestore/src/main/java/com/google/firebase/firestore/model/Values.kt # firebase-firestore/src/main/java/com/google/firebase/firestore/pipeline/expressions.kt # firebase-firestore/src/main/java/com/google/firebase/firestore/pipeline/stage.kt
2 parents 38b6f39 + 45280be commit 86ea2cc

File tree

10 files changed

+151
-125
lines changed

10 files changed

+151
-125
lines changed

firebase-firestore/api.txt

Lines changed: 42 additions & 53 deletions
Large diffs are not rendered by default.

firebase-firestore/src/androidTest/java/com/google/firebase/firestore/PipelineTest.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
import com.google.firebase.firestore.pipeline.AggregateFunction;
4848
import com.google.firebase.firestore.pipeline.AggregateStage;
4949
import com.google.firebase.firestore.pipeline.Expr;
50-
import com.google.firebase.firestore.pipeline.Stage;
50+
import com.google.firebase.firestore.pipeline.RawStage;
5151
import com.google.firebase.firestore.testutil.IntegrationTestUtil;
5252
import java.util.Collections;
5353
import java.util.LinkedHashMap;
@@ -279,15 +279,14 @@ public void groupAndAccumulateResultsGeneric() {
279279
firestore
280280
.pipeline()
281281
.collection(randomCol)
282-
.addStage(Stage.ofName("where").withArguments(lt(field("published"), 1984)))
283-
.addStage(
284-
Stage.ofName("aggregate")
282+
.rawStage(RawStage.ofName("where").withArguments(lt(field("published"), 1984)))
283+
.rawStage(
284+
RawStage.ofName("aggregate")
285285
.withArguments(
286286
ImmutableMap.of("avgRating", AggregateFunction.avg("rating")),
287287
ImmutableMap.of("genre", field("genre"))))
288-
.addStage(Stage.ofName("where").withArguments(gt("avgRating", 4.3)))
289-
.addStage(
290-
Stage.ofName("sort").withArguments(field("avgRating").descending()))
288+
.rawStage(RawStage.ofName("where").withArguments(gt("avgRating", 4.3)))
289+
.rawStage(RawStage.ofName("sort").withArguments(field("avgRating").descending()))
291290
.execute();
292291
assertThat(waitFor(execute).getResults())
293292
.comparingElementsUsing(DATA_CORRESPONDENCE)

firebase-firestore/src/androidTest/java/com/google/firebase/firestore/QueryToPipelineTest.java

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -613,40 +613,39 @@ public void testQueriesCanUseArrayContainsAnyFilters() {
613613
FirebaseFirestore db = collection.firestore;
614614

615615
// Search for "array" to contain [42, 43].
616-
Pipeline pipeline = db.pipeline()
617-
.convertFrom(collection.whereArrayContainsAny("array", asList(42L, 43L)));
616+
Pipeline pipeline =
617+
db.pipeline().convertFrom(collection.whereArrayContainsAny("array", asList(42L, 43L)));
618618
PipelineSnapshot snapshot = waitFor(pipeline.execute());
619619
assertEquals(asList(docA, docB, docD, docE), pipelineSnapshotToValues(snapshot));
620620

621621
// With objects.
622-
pipeline = db.pipeline()
623-
.convertFrom(collection.whereArrayContainsAny("array", asList(map("a", 42L))));
622+
pipeline =
623+
db.pipeline().convertFrom(collection.whereArrayContainsAny("array", asList(map("a", 42L))));
624624
snapshot = waitFor(pipeline.execute());
625625
assertEquals(asList(docF), pipelineSnapshotToValues(snapshot));
626626

627627
// With null.
628-
pipeline = db.pipeline()
629-
.convertFrom(collection.whereArrayContainsAny("array", nullList()));
628+
pipeline = db.pipeline().convertFrom(collection.whereArrayContainsAny("array", nullList()));
630629
snapshot = waitFor(pipeline.execute());
631630
assertEquals(new ArrayList<>(), pipelineSnapshotToValues(snapshot));
632631

633632
// With null and a value.
634633
List<Object> inputList = nullList();
635634
inputList.add(43L);
636-
pipeline = db.pipeline()
637-
.convertFrom(collection.whereArrayContainsAny("array", inputList));
635+
pipeline = db.pipeline().convertFrom(collection.whereArrayContainsAny("array", inputList));
638636
snapshot = waitFor(pipeline.execute());
639637
assertEquals(asList(docE), pipelineSnapshotToValues(snapshot));
640638

641639
// With NaN.
642-
pipeline = db.pipeline()
643-
.convertFrom(collection.whereArrayContainsAny("array", asList(Double.NaN)));
640+
pipeline =
641+
db.pipeline().convertFrom(collection.whereArrayContainsAny("array", asList(Double.NaN)));
644642
snapshot = waitFor(pipeline.execute());
645643
assertEquals(new ArrayList<>(), pipelineSnapshotToValues(snapshot));
646644

647645
// With NaN and a value.
648-
pipeline = db.pipeline()
649-
.convertFrom(collection.whereArrayContainsAny("array", asList(Double.NaN, 43L)));
646+
pipeline =
647+
db.pipeline()
648+
.convertFrom(collection.whereArrayContainsAny("array", asList(Double.NaN, 43L)));
650649
snapshot = waitFor(pipeline.execute());
651650
assertEquals(asList(docE), pipelineSnapshotToValues(snapshot));
652651
}

firebase-firestore/src/androidTest/java/com/google/firebase/firestore/testutil/IntegrationTestUtil.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,7 @@ public static void checkQueryAndPipelineResultsMatch(Query query, String... expe
575575
}
576576
PipelineSnapshot docsFromPipeline;
577577
try {
578-
docsFromPipeline = waitFor(query.getFirestore().pipeline().convertFrom(query).execute());
578+
docsFromPipeline = waitFor(query.getFirestore().pipeline().convertFrom(query).execute());
579579
} catch (Exception e) {
580580
throw new RuntimeException("Pipeline FAILED", e);
581581
}

firebase-firestore/src/main/java/com/google/firebase/firestore/Pipeline.kt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import com.google.firebase.firestore.pipeline.SampleStage
5151
import com.google.firebase.firestore.pipeline.SelectStage
5252
import com.google.firebase.firestore.pipeline.Selectable
5353
import com.google.firebase.firestore.pipeline.SortStage
54+
import com.google.firebase.firestore.pipeline.Stage
5455
import com.google.firebase.firestore.pipeline.UnionStage
5556
import com.google.firebase.firestore.pipeline.UnnestStage
5657
import com.google.firebase.firestore.pipeline.WhereStage
@@ -62,7 +63,7 @@ open class AbstractPipeline
6263
internal constructor(
6364
internal val firestore: FirebaseFirestore,
6465
internal val userDataReader: UserDataReader,
65-
internal val stages: FluentIterable<BaseStage<*>>
66+
internal val stages: FluentIterable<Stage<*>>
6667
) {
6768
private fun toStructuredPipelineProto(options: InternalOptions?): StructuredPipeline {
6869
val builder = StructuredPipeline.newBuilder()
@@ -136,10 +137,10 @@ private constructor(
136137
internal constructor(
137138
firestore: FirebaseFirestore,
138139
userDataReader: UserDataReader,
139-
stage: BaseStage<*>
140+
stage: Stage<*>
140141
) : this(firestore, userDataReader, FluentIterable.of(stage))
141142

142-
private fun append(stage: BaseStage<*>): Pipeline {
143+
private fun append(stage: Stage<*>): Pipeline {
143144
return Pipeline(firestore, userDataReader, stages.append(stage))
144145
}
145146

@@ -152,17 +153,17 @@ private constructor(
152153
}
153154

154155
/**
155-
* Adds a stage to the pipeline by specifying the stage name as an argument. This does not offer
156-
* any type safety on the stage params and requires the caller to know the order (and optionally
157-
* names) of parameters accepted by the stage.
156+
* Adds a raw stage to the pipeline by specifying the stage name as an argument. This does not
157+
* offer any type safety on the stage params and requires the caller to know the order (and
158+
* optionally names) of parameters accepted by the stage.
158159
*
159160
* This method provides a way to call stages that are supported by the Firestore backend but that
160161
* are not implemented in the SDK version being used.
161162
*
162163
* @param rawStage An [RawStage] object that specifies stage name and parameters.
163164
* @return A new [Pipeline] object with this stage appended to the stage list.
164165
*/
165-
fun addStage(rawStage: RawStage): Pipeline = append(rawStage)
166+
fun rawStage(rawStage: RawStage): Pipeline = append(rawStage)
166167

167168
/**
168169
* Adds new fields to outputs from previous stages.

firebase-firestore/src/main/java/com/google/firebase/firestore/core/FieldFilter.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import static com.google.firebase.firestore.util.Assert.hardAssert;
2121

2222
import androidx.annotation.NonNull;
23-
2423
import com.google.firebase.firestore.model.Document;
2524
import com.google.firebase.firestore.model.FieldPath;
2625
import com.google.firebase.firestore.model.Values;
@@ -29,7 +28,6 @@
2928
import com.google.firebase.firestore.pipeline.Field;
3029
import com.google.firebase.firestore.util.Assert;
3130
import com.google.firestore.v1.Value;
32-
3331
import java.util.ArrayList;
3432
import java.util.Arrays;
3533
import java.util.Collections;
@@ -219,14 +217,16 @@ BooleanExpr toPipelineExpr() {
219217
return and(exists, x.arrayContainsAny(value.getArrayValue().getValuesList()));
220218
case IN:
221219
return and(exists, x.eqAny(value.getArrayValue().getValuesList()));
222-
case NOT_IN: {
223-
List<Value> list = value.getArrayValue().getValuesList();
224-
if (hasNaN(list)) {
225-
return and(exists, x.notEqAny(filterNaN(list)), ifError(x.isNotNan(), Expr.constant(true)));
226-
} else {
227-
return and(exists, x.notEqAny(list));
220+
case NOT_IN:
221+
{
222+
List<Value> list = value.getArrayValue().getValuesList();
223+
if (hasNaN(list)) {
224+
return and(
225+
exists, x.notEqAny(filterNaN(list)), ifError(x.isNotNan(), Expr.constant(true)));
226+
} else {
227+
return and(exists, x.notEqAny(list));
228+
}
228229
}
229-
}
230230
default:
231231
// Handle OPERATOR_UNSPECIFIED and UNRECOGNIZED cases as needed
232232
throw new IllegalArgumentException("Unsupported operator: " + operator);

firebase-firestore/src/main/java/com/google/firebase/firestore/core/Query.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
import com.google.firebase.firestore.pipeline.FunctionExpr;
3838
import com.google.firebase.firestore.pipeline.InternalOptions;
3939
import com.google.firebase.firestore.pipeline.Ordering;
40-
import com.google.firebase.firestore.pipeline.BaseStage;
40+
import com.google.firebase.firestore.pipeline.Stage;
4141
import com.google.firebase.firestore.util.BiFunction;
4242
import com.google.firebase.firestore.util.Function;
4343
import com.google.firebase.firestore.util.IntFunction;
@@ -549,7 +549,8 @@ public Pipeline toPipeline(FirebaseFirestore firestore, UserDataReader userDataR
549549
if (fields.size() == 1) {
550550
p = p.where(fields.get(0).exists());
551551
} else {
552-
BooleanExpr[] conditions = skipFirstToArray(fields, BooleanExpr[]::new, Expr.Companion::exists);
552+
BooleanExpr[] conditions =
553+
skipFirstToArray(fields, BooleanExpr[]::new, Expr.Companion::exists);
553554
p = p.where(and(fields.get(0).exists(), conditions));
554555
}
555556

@@ -587,17 +588,18 @@ private static <T> T[] skipFirstToArray(List<T> list, IntFunction<T[]> generator
587588
int size = list.size();
588589
T[] result = generator.apply(size - 1);
589590
for (int i = 1; i < size; i++) {
590-
result[i-1] = list.get(i);
591+
result[i - 1] = list.get(i);
591592
}
592593
return result;
593594
}
594595

595596
// Many Pipelines require first parameter to be separated out from rest.
596-
private static <T, R> R[] skipFirstToArray(List<T> list, IntFunction<R[]> generator, Function<T, R> map) {
597+
private static <T, R> R[] skipFirstToArray(
598+
List<T> list, IntFunction<R[]> generator, Function<T, R> map) {
597599
int size = list.size();
598600
R[] result = generator.apply(size - 1);
599601
for (int i = 1; i < size; i++) {
600-
result[i-1] = map.apply(list.get(i));
602+
result[i - 1] = map.apply(list.get(i));
601603
}
602604
return result;
603605
}
@@ -621,7 +623,7 @@ private static BooleanExpr whereConditionsFromCursor(
621623
}
622624

623625
@NonNull
624-
private BaseStage<?> pipelineSource(FirebaseFirestore firestore) {
626+
private Stage<?> pipelineSource(FirebaseFirestore firestore) {
625627
if (isDocumentQuery()) {
626628
return new DocumentsSource(path.canonicalString());
627629
} else if (isCollectionGroupQuery()) {

firebase-firestore/src/main/java/com/google/firebase/firestore/model/Values.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -679,8 +679,7 @@ internal object Values {
679679
fun encodeValue(timestamp: com.google.firebase.Timestamp): Value =
680680
encodeValue(timestamp(timestamp.seconds, timestamp.nanoseconds))
681681

682-
@JvmStatic
683-
fun encodeValue(value: Timestamp): Value = Value.newBuilder().setTimestampValue(value).build()
682+
@JvmStatic fun encodeValue(value: Timestamp): Value = Value.newBuilder().setTimestampValue(value).build()
684683

685684
@JvmField val TRUE_VALUE: Value = Value.newBuilder().setBooleanValue(true).build()
686685

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

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -798,8 +798,7 @@ abstract class Expr internal constructor() {
798798
* @param second Numeric expression to add.
799799
* @return A new [Expr] representing the addition operation.
800800
*/
801-
@JvmStatic
802-
fun add(first: Expr, second: Expr): Expr = FunctionExpr("add", evaluateAdd, first, second)
801+
@JvmStatic fun add(first: Expr, second: Expr): Expr = FunctionExpr("add", evaluateAdd, first, second)
803802

804803
/**
805804
* Creates an expression that adds numeric expressions with a constant.
@@ -808,8 +807,7 @@ abstract class Expr internal constructor() {
808807
* @param second Constant to add.
809808
* @return A new [Expr] representing the addition operation.
810809
*/
811-
@JvmStatic
812-
fun add(first: Expr, second: Number): Expr = FunctionExpr("add", evaluateAdd, first, second)
810+
@JvmStatic fun add(first: Expr, second: Number): Expr = FunctionExpr("add", evaluateAdd, first, second)
813811

814812
/**
815813
* Creates an expression that adds a numeric field with a numeric expression.
@@ -885,8 +883,7 @@ abstract class Expr internal constructor() {
885883
* @return A new [Expr] representing the multiplication operation.
886884
*/
887885
@JvmStatic
888-
fun multiply(first: Expr, second: Expr): Expr =
889-
FunctionExpr("multiply", evaluateMultiply, first, second)
886+
fun multiply(first: Expr, second: Expr): Expr = FunctionExpr("multiply", evaluateMultiply, first, second)
890887

891888
/**
892889
* Creates an expression that multiplies numeric expressions with a constant.
@@ -896,8 +893,7 @@ abstract class Expr internal constructor() {
896893
* @return A new [Expr] representing the multiplication operation.
897894
*/
898895
@JvmStatic
899-
fun multiply(first: Expr, second: Number): Expr =
900-
FunctionExpr("multiply", evaluateMultiply, first, second)
896+
fun multiply(first: Expr, second: Number): Expr = FunctionExpr("multiply", evaluateMultiply, first, second)
901897

902898
/**
903899
* Creates an expression that multiplies a numeric field with a numeric expression.
@@ -1858,6 +1854,28 @@ abstract class Expr internal constructor() {
18581854
fun mapGet(fieldName: String, keyExpression: Expr): Expr =
18591855
FunctionExpr("map_get", evaluateMapGet, fieldName, keyExpression)
18601856

1857+
/**
1858+
* Accesses a value from a map (object) field using the provided [keyExpression].
1859+
*
1860+
* @param mapExpression The expression representing the map.
1861+
* @param keyExpression The key to access in the map.
1862+
* @return A new [Expr] representing the value associated with the given key in the map.
1863+
*/
1864+
@JvmStatic
1865+
fun mapGet(mapExpression: Expr, keyExpression: Expr): Expr =
1866+
FunctionExpr("map_get", mapExpression, keyExpression)
1867+
1868+
/**
1869+
* Accesses a value from a map (object) field using the provided [keyExpression].
1870+
*
1871+
* @param fieldName The field name of the map field.
1872+
* @param keyExpression The key to access in the map.
1873+
* @return A new [Expr] representing the value associated with the given key in the map.
1874+
*/
1875+
@JvmStatic
1876+
fun mapGet(fieldName: String, keyExpression: Expr): Expr =
1877+
FunctionExpr("map_get", fieldName, keyExpression)
1878+
18611879
/**
18621880
* Creates an expression that merges multiple maps into a single map. If multiple maps have the
18631881
* same key, the later value is used.
@@ -2659,6 +2677,25 @@ abstract class Expr internal constructor() {
26592677
fun array(elements: List<Any?>): Expr =
26602678
FunctionExpr("array", evaluateArray, elements.map(::toExprOrConstant).toTypedArray())
26612679

2680+
/**
2681+
* Creates an expression that creates a Firestore array value from an input array.
2682+
*
2683+
* @param elements The input array to evaluate in the expression.
2684+
* @return A new [Expr] representing the array function.
2685+
*/
2686+
@JvmStatic
2687+
fun array(vararg elements: Any?): Expr =
2688+
FunctionExpr("array", elements.map(::toExprOrConstant).toTypedArray<Expr>())
2689+
2690+
/**
2691+
* Creates an expression that creates a Firestore array value from an input array.
2692+
*
2693+
* @param elements The input array to evaluate in the expression.
2694+
* @return A new [Expr] representing the array function.
2695+
*/
2696+
@JvmStatic
2697+
fun array(elements: List<Any?>): Expr =
2698+
FunctionExpr("array", elements.map(::toExprOrConstant).toTypedArray())
26622699
/**
26632700
* Creates an expression that concatenates an array with other arrays.
26642701
*

0 commit comments

Comments
 (0)