Skip to content

Pipelines Implementation #6710

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 56 commits into
base: feat/pipelines
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
fe05087
Proto changes
tom-andersen Oct 10, 2024
159cb37
Copyright
tom-andersen Oct 10, 2024
282409a
Expressions
tom-andersen Jan 24, 2025
7f95bb5
Test
tom-andersen Feb 3, 2025
2ec848d
Stage
tom-andersen Feb 3, 2025
16bb38c
Pipeline
tom-andersen Feb 3, 2025
c2d2ae5
Merge remote-tracking branch 'origin/main' into tomandersen/pipelines
tom-andersen Feb 3, 2025
a8361ca
Fix
tom-andersen Feb 3, 2025
a0da150
Spotless
tom-andersen Feb 3, 2025
0ce7d4f
Spotless
tom-andersen Feb 3, 2025
ab5f217
Rename .java to .kt
tom-andersen Feb 15, 2025
dec61c0
Work
tom-andersen Feb 15, 2025
e5734cd
Copyright
tom-andersen Feb 15, 2025
8976978
Merge remote-tracking branch 'origin/main' into tomandersen/pipelines
tom-andersen Feb 15, 2025
70bdf62
spotless
tom-andersen Feb 15, 2025
fc8b2d1
docStubs fix
tom-andersen Feb 15, 2025
1e7e72e
fix
tom-andersen Feb 17, 2025
35bcff7
fix
tom-andersen Feb 17, 2025
3cb1886
More tests
tom-andersen Feb 18, 2025
cea0c48
Merge remote-tracking branch 'origin/main' into tomandersen/pipelines
tom-andersen Feb 18, 2025
5abf13f
Refactor and more tests.
tom-andersen Feb 19, 2025
ef7cbda
Add tests
tom-andersen Feb 20, 2025
ca31612
Cleanup
tom-andersen Feb 20, 2025
63cd54e
Merge branch 'feat/pipelines' into tomandersen/pipelines
tom-andersen Feb 20, 2025
c7605b6
Generic Stage and Refactor
tom-andersen Feb 28, 2025
6842a83
Fix docStubs task
tom-andersen Feb 28, 2025
08af748
Spotless fix
tom-andersen Feb 28, 2025
fea5b06
Generate api.txt
tom-andersen Feb 28, 2025
711b852
Make more of the API internal
tom-andersen Feb 28, 2025
1ccea4d
Make more of the API internal
tom-andersen Feb 28, 2025
9553abc
Add options
tom-andersen Mar 3, 2025
b2f0b3f
GenericOptions
tom-andersen Mar 3, 2025
5e8f7f0
Spotless
tom-andersen Mar 3, 2025
8d049e0
Bit Operators
tom-andersen Mar 19, 2025
fcae380
Convert query to pipeline
tom-andersen Mar 24, 2025
35a9116
Spotless and Generate API
tom-andersen Mar 24, 2025
4e255dc
Fixups
tom-andersen Apr 2, 2025
5e1d84e
Fixups
tom-andersen Apr 8, 2025
8141aa8
Fixups
tom-andersen Apr 9, 2025
9dbce3f
Fixups
tom-andersen Apr 10, 2025
51883b9
Comments word wrap
tom-andersen Apr 10, 2025
daab5a5
Comments and alignment across SDKs
tom-andersen Apr 10, 2025
df1e719
api.txt
tom-andersen Apr 10, 2025
39c9bd7
fix
tom-andersen Apr 10, 2025
ea780f8
fixups
tom-andersen Apr 15, 2025
dd2e9bd
add named options to CollectionSource and CollectionGroupSource
tom-andersen Apr 15, 2025
0231d72
spotless, apiTxt, use Expr types.
tom-andersen Apr 15, 2025
356e883
Comments
tom-andersen Apr 22, 2025
ed24e71
Comments
tom-andersen Apr 23, 2025
b44db06
Comments
tom-andersen Apr 25, 2025
83d94e4
Comments
tom-andersen Apr 25, 2025
309fa91
WIP
tom-andersen Apr 25, 2025
0606a18
Work on pipeline expressions.
tom-andersen Apr 29, 2025
402a98d
Comments
tom-andersen Apr 29, 2025
ef3a8f2
Comments
tom-andersen Apr 29, 2025
e7ec758
More expression work
tom-andersen Apr 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion firebase-firestore/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Unreleased

* [feature] Pipelines

# 25.1.2
* [fixed] Fixed a server and sdk mismatch in unicode string sorting. [#6615](//github.com/firebase/firebase-android-sdk/pull/6615)
Expand Down
935 changes: 934 additions & 1 deletion firebase-firestore/api.txt

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
import com.google.firebase.firestore.FirebaseFirestoreSettings;
import com.google.firebase.firestore.ListenerRegistration;
import com.google.firebase.firestore.MetadataChanges;
import com.google.firebase.firestore.PipelineResult;
import com.google.firebase.firestore.PipelineSnapshot;
import com.google.firebase.firestore.Query;
import com.google.firebase.firestore.QuerySnapshot;
import com.google.firebase.firestore.Source;
Expand Down Expand Up @@ -98,7 +100,7 @@ public enum TargetBackend {

// Set this to the desired enum value to change the target backend when running tests locally.
// Note: DO NOT change this variable except for local testing.
private static final TargetBackend backendForLocalTesting = null;
private static final TargetBackend backendForLocalTesting = TargetBackend.NIGHTLY;

private static final TargetBackend backend = getTargetBackend();
private static final String EMULATOR_HOST = "10.0.2.2";
Expand Down Expand Up @@ -465,6 +467,15 @@ public static List<Map<String, Object>> querySnapshotToValues(QuerySnapshot quer
return res;
}

public static List<Map<String, Object>> pipelineSnapshotToValues(
PipelineSnapshot pipelineSnapshot) {
List<Map<String, Object>> res = new ArrayList<>();
for (PipelineResult result : pipelineSnapshot) {
res.add(result.getData());
}
return res;
}

public static List<String> querySnapshotToIds(QuerySnapshot querySnapshot) {
List<String> res = new ArrayList<>();
for (DocumentSnapshot doc : querySnapshot) {
Expand All @@ -473,6 +484,15 @@ public static List<String> querySnapshotToIds(QuerySnapshot querySnapshot) {
return res;
}

public static List<String> pipelineSnapshotToIds(PipelineSnapshot pipelineResults) {
List<String> res = new ArrayList<>();
for (PipelineResult result : pipelineResults) {
DocumentReference ref = result.getRef();
res.add(ref == null ? null : ref.getId());
}
return res;
}

public static void disableNetwork(FirebaseFirestore firestore) {
if (firestoreStatus.get(firestore)) {
waitFor(firestore.disableNetwork());
Expand Down Expand Up @@ -537,4 +557,24 @@ public static void checkOnlineAndOfflineResultsMatch(Query query, String... expe
assertEquals(expected, querySnapshotToIds(docsFromCache));
}
}

/**
* Checks that running the query while online (against the backend/emulator) results in the same
* documents as running the query while offline. If `expectedDocs` is provided, it also checks
* that both online and offline query result is equal to the expected documents.
*
* @param query The query to check
* @param expectedDocs Ordered list of document keys that are expected to match the query
*/
public static void checkQueryAndPipelineResultsMatch(Query query, String... expectedDocs) {
QuerySnapshot docsFromQuery = waitFor(query.get(Source.SERVER));
PipelineSnapshot docsFromPipeline =
waitFor(query.getFirestore().pipeline().convertFrom(query).execute());

assertEquals(querySnapshotToIds(docsFromQuery), pipelineSnapshotToIds(docsFromPipeline));
List<String> expected = asList(expectedDocs);
if (!expected.isEmpty()) {
assertEquals(expected, querySnapshotToIds(docsFromQuery));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@

package com.google.firebase.firestore;

import static com.google.firebase.firestore.pipeline.Expr.field;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import com.google.firebase.firestore.pipeline.AggregateFunction;
import com.google.firebase.firestore.pipeline.AggregateWithAlias;
import java.util.Objects;

/** Represents an aggregation that can be performed by Firestore. */
Expand Down Expand Up @@ -61,6 +65,9 @@ public String getOperator() {
return operator;
}

@NonNull
abstract AggregateWithAlias toPipeline();

/**
* Returns true if the given object is equal to this object. Two `AggregateField` objects are
* considered equal if they have the same operator and operate on the same field.
Expand Down Expand Up @@ -195,19 +202,37 @@ public static class CountAggregateField extends AggregateField {
private CountAggregateField() {
super(null, "count");
}

@NonNull
@Override
AggregateWithAlias toPipeline() {
return AggregateFunction.countAll().alias(getAlias());
}
}

/** Represents a "sum" aggregation that can be performed by Firestore. */
public static class SumAggregateField extends AggregateField {
private SumAggregateField(@NonNull FieldPath fieldPath) {
super(fieldPath, "sum");
}

@NonNull
@Override
AggregateWithAlias toPipeline() {
return field(getFieldPath()).sum().alias(getAlias());
}
}

/** Represents an "average" aggregation that can be performed by Firestore. */
public static class AverageAggregateField extends AggregateField {
private AverageAggregateField(@NonNull FieldPath fieldPath) {
super(fieldPath, "average");
}

@NonNull
@Override
AggregateWithAlias toPipeline() {
return field(getFieldPath()).avg().alias(getAlias());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import android.app.Activity;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.TaskCompletionSource;
import com.google.android.gms.tasks.Tasks;
Expand All @@ -33,6 +34,7 @@
import com.google.firebase.firestore.core.UserData.ParsedSetData;
import com.google.firebase.firestore.core.UserData.ParsedUpdateData;
import com.google.firebase.firestore.core.ViewSnapshot;
import com.google.firebase.firestore.model.DatabaseId;
import com.google.firebase.firestore.model.Document;
import com.google.firebase.firestore.model.DocumentKey;
import com.google.firebase.firestore.model.ResourcePath;
Expand All @@ -57,17 +59,15 @@
* in test mocks. Subclassing is not supported in production code and new SDK releases may break
* code that does so.
*/
public class DocumentReference {
public final class DocumentReference {

private final DocumentKey key;

private final FirebaseFirestore firestore;

DocumentReference(DocumentKey key, FirebaseFirestore firestore) {
this.key = checkNotNull(key);
// TODO: We should checkNotNull(firestore), but tests are currently cheating
// and setting it to null.
this.firestore = firestore;
this.firestore = checkNotNull(firestore);
}

/** @hide */
Expand Down Expand Up @@ -120,6 +120,15 @@ public String getPath() {
return key.getPath().canonicalString();
}

@RestrictTo(RestrictTo.Scope.LIBRARY)
@NonNull
public String getFullPath() {
DatabaseId databaseId = firestore.getDatabaseId();
return String.format(
"projects/%s/databases/%s/documents/%s",
databaseId.getProjectId(), databaseId.getDatabaseId(), getPath());
}

/**
* Gets a {@code CollectionReference} instance that refers to the subcollection at the specified
* path relative to this document.
Expand Down Expand Up @@ -564,6 +573,12 @@ public int hashCode() {
return result;
}

@NonNull
@Override
public String toString() {
return "DocumentReference{" + "key=" + key + ", firestore=" + firestore + '}';
}

private com.google.firebase.firestore.core.Query asQuery() {
return com.google.firebase.firestore.core.Query.atPath(key.getPath());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@ public int hashCode() {
return hash;
}

@NonNull
@Override
public String toString() {
return "DocumentSnapshot{" + "key=" + key + ", metadata=" + metadata + ", doc=" + doc + '}';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static com.google.firebase.firestore.util.Preconditions.checkNotNull;

import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
Expand All @@ -33,15 +34,18 @@ public final class FieldPath {

private final com.google.firebase.firestore.model.FieldPath internalPath;

private FieldPath(List<String> segments) {
private FieldPath(@NonNull List<String> segments) {
this.internalPath = com.google.firebase.firestore.model.FieldPath.fromSegments(segments);
}

private FieldPath(com.google.firebase.firestore.model.FieldPath internalPath) {
private FieldPath(@NonNull com.google.firebase.firestore.model.FieldPath internalPath) {
this.internalPath = internalPath;
}

com.google.firebase.firestore.model.FieldPath getInternalPath() {
/** @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY)
@NonNull
public com.google.firebase.firestore.model.FieldPath getInternalPath() {
return internalPath;
}

Expand Down Expand Up @@ -78,7 +82,9 @@ public static FieldPath documentId() {
}

/** Parses a field path string into a {@code FieldPath}, treating dots as separators. */
static FieldPath fromDotSeparatedPath(@NonNull String path) {
@RestrictTo(RestrictTo.Scope.LIBRARY)
@NonNull
public static FieldPath fromDotSeparatedPath(@NonNull String path) {
checkNotNull(path, "Provided field path must not be null.");
checkArgument(
!RESERVED.matcher(path).find(), "Use FieldPath.of() for field names containing '~*/[]'.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -850,10 +850,12 @@ <T> T callClient(Function<FirestoreClient, T> call) {
return clientProvider.call(call);
}

@NonNull
DatabaseId getDatabaseId() {
return databaseId;
}

@NonNull
UserDataReader getUserDataReader() {
return userDataReader;
}
Expand Down Expand Up @@ -881,4 +883,15 @@ void validateReference(DocumentReference docRef) {
static void setClientLanguage(@NonNull String languageToken) {
FirestoreChannel.setClientLanguage(languageToken);
}

/**
* Build a new Pipeline
*
* @return {@code PipelineSource} for this Firestore instance.
*/
@NonNull
public PipelineSource pipeline() {
clientProvider.ensureConfigured();
return new PipelineSource(this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import com.google.firebase.Firebase
import com.google.firebase.FirebaseApp
import com.google.firebase.components.Component
import com.google.firebase.components.ComponentRegistrar
import com.google.firebase.firestore.*
import com.google.firebase.firestore.util.Executors.BACKGROUND_EXECUTOR
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.awaitClose
Expand Down
Loading
Loading