Skip to content
This repository was archived by the owner on May 8, 2026. It is now read-only.

Commit b65de3a

Browse files
committed
Fix breaking tests
1 parent c02ef30 commit b65de3a

4 files changed

Lines changed: 146 additions & 25 deletions

File tree

google-cloud-firestore/src/main/java/com/google/cloud/firestore/Pipeline.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,11 @@ public static Pipeline subcollection(String path) {
278278
* Adds new fields or redefines existing fields in the output documents by
279279
* evaluating given expressions.
280280
*
281+
* <p>
282+
* This stage is useful for binding a value to a variable for internal reuse
283+
* within the pipeline
284+
* body (accessed via the {@link Expression#variable(String)} function).
285+
*
281286
* @param expressions The expressions to define or redefine fields using
282287
* {@link AliasedExpression}.
283288
* @return A new Pipeline object with this stage appended to the stage list.
@@ -1383,7 +1388,8 @@ public void onComplete() {
13831388
}
13841389
};
13851390

1386-
logger.log(Level.FINEST, "Sending pipeline request: " + request.getStructuredPipeline());
1391+
// logger.log(Level.FINEST, "Sending pipeline request: " +
1392+
// request.getStructuredPipeline());
13871393

13881394
rpcContext.streamRequest(request, observer, rpcContext.getClient().executePipelineCallable());
13891395
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.firestore.pipeline.stages;
18+
19+
import static com.google.cloud.firestore.PipelineUtils.encodeValue;
20+
21+
import com.google.api.core.InternalApi;
22+
import com.google.cloud.firestore.pipeline.expressions.Expression;
23+
import com.google.firestore.v1.Value;
24+
import java.util.Collections;
25+
import java.util.Map;
26+
27+
@InternalApi
28+
public final class DefineStage extends Stage {
29+
30+
private final Map<String, Expression> expressions;
31+
32+
@InternalApi
33+
public DefineStage(Map<String, Expression> expressions) {
34+
super("let", InternalOptions.EMPTY);
35+
this.expressions = expressions;
36+
}
37+
38+
@Override
39+
Iterable<Value> toStageArgs() {
40+
java.util.Map<String, Value> converted = new java.util.HashMap<>();
41+
for (Map.Entry<String, Expression> entry : expressions.entrySet()) {
42+
converted.put(entry.getKey(), com.google.cloud.firestore.pipeline.expressions.FunctionUtils.exprToValue(entry.getValue()));
43+
}
44+
return Collections.singletonList(encodeValue(converted));
45+
}
46+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.firestore.pipeline.stages;
18+
19+
import static com.google.cloud.firestore.PipelineUtils.encodeValue;
20+
21+
import com.google.api.core.InternalApi;
22+
import com.google.firestore.v1.Value;
23+
import java.util.Collections;
24+
25+
@InternalApi
26+
public final class SubcollectionSource extends Stage {
27+
28+
private final String path;
29+
30+
@InternalApi
31+
public SubcollectionSource(String path) {
32+
super("from", InternalOptions.EMPTY);
33+
this.path = path;
34+
}
35+
36+
@Override
37+
Iterable<Value> toStageArgs() {
38+
return Collections.singletonList(encodeValue(path));
39+
}
40+
}

google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITPipelineTest.java

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package com.google.cloud.firestore.it;
1818

19+
import java.util.UUID;
20+
1921
import static com.google.cloud.firestore.FieldValue.vector;
2022
import static com.google.cloud.firestore.it.ITQueryTest.map;
2123
import static com.google.cloud.firestore.it.TestHelper.isRunningAgainstFirestoreEmulator;
@@ -2761,14 +2763,17 @@ public void testSubquery() throws Exception {
27612763
java.util.concurrent.TimeUnit.SECONDS);
27622764
}
27632765

2764-
// We mock a subcollection test query where we retrieve these items
2765-
Pipeline sub = Pipeline.subcollection("some_subcollection")
2766-
.select(com.google.cloud.firestore.pipeline.expressions.Expression.variable("p").as("sub_p"));
2766+
// Use absolute path for subquery test
2767+
Pipeline sub = firestore.pipeline().collection(
2768+
collection.document("doc1").collection("some_subcollection").getPath())
2769+
.select(com.google.cloud.firestore.pipeline.expressions.Expression.field("b").as("b"),
2770+
com.google.cloud.firestore.pipeline.expressions.Expression.field("__name__").as("__name__"))
2771+
.removeFields("__name__");
27672772

27682773
List<PipelineResult> results = firestore
27692774
.pipeline()
27702775
.collection(collection.getPath())
2771-
.define(com.google.cloud.firestore.pipeline.expressions.Expression.variable("parentDoc").as("p"))
2776+
27722777
.select(sub.toArrayExpression().as("sub_docs"))
27732778
.limit(1)
27742779
.execute()
@@ -2792,13 +2797,14 @@ public void testSubqueryToScalar() throws Exception {
27922797
java.util.concurrent.TimeUnit.SECONDS);
27932798
}
27942799

2795-
Pipeline sub = Pipeline.subcollection("some_subcollection")
2800+
Pipeline sub = firestore.pipeline().collection(
2801+
collection.document("doc1").collection("some_subcollection").getPath())
27962802
.select(com.google.cloud.firestore.pipeline.expressions.Expression.variable("p").as("sub_p"));
27972803

27982804
List<PipelineResult> results = firestore
27992805
.pipeline()
28002806
.collection(collection.getPath())
2801-
.define(com.google.cloud.firestore.pipeline.expressions.Expression.variable("parentDoc").as("p"))
2807+
.define(com.google.cloud.firestore.pipeline.expressions.Expression.currentDocument().as("p"))
28022808
.select(sub.toScalarExpression().as("sub_doc_scalar"))
28032809
.execute()
28042810
.get()
@@ -2818,7 +2824,8 @@ public void testSubqueryWithCorrelatedField() throws Exception {
28182824
collection.document(doc.getKey()).set(doc.getValue()).get(5, java.util.concurrent.TimeUnit.SECONDS);
28192825
}
28202826

2821-
Pipeline sub = Pipeline.subcollection("some_subcollection")
2827+
Pipeline sub = firestore.pipeline().collection(
2828+
collection.document("doc1").collection("some_subcollection").getPath())
28222829
// Using field access on a variable simulating a correlated query
28232830
.select(com.google.cloud.firestore.pipeline.expressions.Expression.field(
28242831
com.google.cloud.firestore.pipeline.expressions.Expression.variable("p"), "a").as("parent_a"));
@@ -2840,8 +2847,9 @@ public void testSubqueryWithCorrelatedField() throws Exception {
28402847

28412848
@Test
28422849
public void testMultipleArraySubqueries() throws Exception {
2850+
String bookId = "book_" + UUID.randomUUID().toString();
28432851
Map<String, Map<String, Object>> testDocs = map(
2844-
"book1", map("title", "Book 1"));
2852+
bookId, map("title", "Book 1"));
28452853

28462854
for (Map.Entry<String, Map<String, Object>> doc : testDocs.entrySet()) {
28472855
com.google.cloud.firestore.DocumentReference docRef = collection.document(doc.getKey());
@@ -2852,15 +2860,22 @@ public void testMultipleArraySubqueries() throws Exception {
28522860
java.util.concurrent.TimeUnit.SECONDS);
28532861
}
28542862

2855-
Pipeline reviewsSub = Pipeline.subcollection("reviews")
2856-
.select(com.google.cloud.firestore.pipeline.expressions.Expression.variable("p").as("sub_p"));
2857-
Pipeline authorsSub = Pipeline.subcollection("authors")
2858-
.select(com.google.cloud.firestore.pipeline.expressions.Expression.variable("p").as("sub_auth"));
2863+
Pipeline reviewsSub = firestore.pipeline().collection(
2864+
collection.document(bookId).collection("reviews").getPath())
2865+
.select(com.google.cloud.firestore.pipeline.expressions.Expression.field("rating").as("rating"),
2866+
com.google.cloud.firestore.pipeline.expressions.Expression.field("__name__").as("__name__"))
2867+
.removeFields("__name__");
2868+
Pipeline authorsSub = firestore.pipeline().collection(
2869+
collection.document(bookId).collection("authors").getPath())
2870+
.select(com.google.cloud.firestore.pipeline.expressions.Expression.field("name").as("name"),
2871+
com.google.cloud.firestore.pipeline.expressions.Expression.field("__name__").as("__name__"))
2872+
.removeFields("__name__");
28592873

28602874
List<PipelineResult> results = firestore
28612875
.pipeline()
28622876
.collection(collection.getPath())
2863-
.define(com.google.cloud.firestore.pipeline.expressions.Expression.variable("parentDoc").as("p"))
2877+
.where(com.google.cloud.firestore.pipeline.expressions.Expression.field("title").equal("Book 1"))
2878+
28642879
.addFields(
28652880
reviewsSub.toArrayExpression().as("reviews_data"),
28662881
authorsSub.toArrayExpression().as("authors_data"))
@@ -2896,7 +2911,8 @@ public void testScopeBridgingExplicitFieldBinding() throws Exception {
28962911
java.util.concurrent.TimeUnit.SECONDS);
28972912
}
28982913

2899-
Pipeline sub = Pipeline.subcollection("some_subcollection")
2914+
Pipeline sub = firestore.pipeline().collection(
2915+
collection.document("doc1").collection("some_subcollection").getPath())
29002916
.where(com.google.cloud.firestore.pipeline.expressions.Expression.field("parent_id").equal(
29012917
com.google.cloud.firestore.pipeline.expressions.Expression.variable("rid")))
29022918
.select(com.google.cloud.firestore.pipeline.expressions.Expression.field("parent_id").as("matched_id"));
@@ -2918,6 +2934,7 @@ public void testScopeBridgingExplicitFieldBinding() throws Exception {
29182934

29192935
@Test
29202936
public void testArraySubqueryInWhereStage() throws Exception {
2937+
String subCollName = "subchk_" + UUID.randomUUID().toString();
29212938
Map<String, Map<String, Object>> testDocs = map(
29222939
"doc1", map("id", "1"),
29232940
"doc2", map("id", "2"));
@@ -2927,20 +2944,23 @@ public void testArraySubqueryInWhereStage() throws Exception {
29272944
docRef.set(doc.getValue()).get(5, java.util.concurrent.TimeUnit.SECONDS);
29282945
// Only doc1 has a subcollection with value 'target_val'
29292946
if ("doc1".equals(doc.getKey())) {
2930-
docRef.collection("some_subcollection").document("sub1").set(map("val", "target_val")).get(5,
2947+
docRef.collection(subCollName).document("sub1").set(map("val", "target_val", "parent_id", "1")).get(5,
29312948
java.util.concurrent.TimeUnit.SECONDS);
2949+
2950+
29322951
} else {
2933-
docRef.collection("some_subcollection").document("sub1").set(map("val", "other_val")).get(5,
2952+
docRef.collection(subCollName).document("sub1").set(map("val", "other_val", "parent_id", "2")).get(5,
29342953
java.util.concurrent.TimeUnit.SECONDS);
29352954
}
2955+
29362956
}
29372957

2938-
Pipeline sub = Pipeline.subcollection("some_subcollection")
2958+
Pipeline sub = firestore.pipeline().collectionGroup(subCollName)
2959+
.where(com.google.cloud.firestore.pipeline.expressions.Expression.field("parent_id")
2960+
.equal(com.google.cloud.firestore.pipeline.expressions.Expression.variable("pid")))
29392961
.select(com.google.cloud.firestore.pipeline.expressions.Expression.field("val").as("val"));
29402962

2941-
// Find documents where the subquery array contains a specific map
2942-
// Note: testing a standard equality against an array here based on
2943-
// array_contains expression limits
2963+
// Find documents where the subquery array contains a specific value
29442964
List<PipelineResult> results = firestore
29452965
.pipeline()
29462966
.collection(collection.getPath())
@@ -2966,7 +2986,8 @@ public void testSingleLookupScalarSubquery() throws Exception {
29662986
java.util.concurrent.TimeUnit.SECONDS);
29672987
}
29682988

2969-
Pipeline userProfileSub = Pipeline.subcollection("users")
2989+
Pipeline userProfileSub = firestore.pipeline()
2990+
.collection(collection.document("doc1").collection("users").getPath())
29702991
.where(com.google.cloud.firestore.pipeline.expressions.Expression.field("name")
29712992
.equal(com.google.cloud.firestore.pipeline.expressions.Expression.variable("uname")))
29722993
.select(com.google.cloud.firestore.pipeline.expressions.Expression.currentDocument().as("profile"));
@@ -2995,15 +3016,20 @@ public void testMissingSubcollectionReturnsEmptyArray() throws Exception {
29953016
// Notably NO subcollections are added
29963017
}
29973018

2998-
Pipeline missingSub = Pipeline.subcollection("does_not_exist")
3019+
Pipeline missingSub = firestore.pipeline()
3020+
.collection(collection.document("doc1").collection("does_not_exist").getPath())
29993021
.select(com.google.cloud.firestore.pipeline.expressions.Expression.variable("p").as("sub_p"));
30003022

3023+
3024+
3025+
30013026
List<PipelineResult> results = firestore
30023027
.pipeline()
30033028
.collection(collection.getPath())
3004-
.define(com.google.cloud.firestore.pipeline.expressions.Expression.variable("parentDoc").as("p"))
3029+
.define(com.google.cloud.firestore.pipeline.expressions.Expression.currentDocument().as("p"))
30053030
.select(missingSub.toArrayExpression().as("missing_data"))
30063031
.limit(1)
3032+
30073033
.execute()
30083034
.get()
30093035
.getResults();
@@ -3022,8 +3048,11 @@ public void testZeroResultScalarReturnsNull() throws Exception {
30223048
collection.document(doc.getKey()).set(doc.getValue()).get(5, java.util.concurrent.TimeUnit.SECONDS);
30233049
}
30243050

3025-
Pipeline emptyScalar = Pipeline.subcollection("empty_sub")
3051+
Pipeline emptyScalar = firestore.pipeline()
3052+
.collection(collection.document("doc1").collection("empty_sub").getPath(
3053+
))
30263054
.where(com.google.cloud.firestore.pipeline.expressions.Expression.field("nonexistent").equal(1L))
3055+
30273056
.select(com.google.cloud.firestore.pipeline.expressions.Expression.currentDocument().as("data"));
30283057

30293058
List<PipelineResult> results = firestore

0 commit comments

Comments
 (0)