diff --git a/firebase-firestore/CHANGELOG.md b/firebase-firestore/CHANGELOG.md index 9e913179941..1cb1ab4ebf6 100644 --- a/firebase-firestore/CHANGELOG.md +++ b/firebase-firestore/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +- [feature] Added support for `parent` Pipeline expression. + [#7999](https://github.com/firebase/firebase-android-sdk/pull/7999) - [feature] Added support for Pipeline expressions `ifNull` and `coalesce`. [#7976](https://github.com/firebase/firebase-android-sdk/pull/7976) - [feature] Added support for `timestampTruncate`, `timestampDiff`, and `timestampExtract` Pipeline expressions. diff --git a/firebase-firestore/api.txt b/firebase-firestore/api.txt index 9b1836326f0..20227de22fd 100644 --- a/firebase-firestore/api.txt +++ b/firebase-firestore/api.txt @@ -1211,6 +1211,10 @@ package com.google.firebase.firestore.pipeline { method public final com.google.firebase.firestore.pipeline.BooleanExpression notEqualAny(java.util.List values); method public static final com.google.firebase.firestore.pipeline.Expression nullValue(); method public static final com.google.firebase.firestore.pipeline.BooleanExpression or(com.google.firebase.firestore.pipeline.BooleanExpression condition, com.google.firebase.firestore.pipeline.BooleanExpression... conditions); + method public final com.google.firebase.firestore.pipeline.Expression parent(); + method public static final com.google.firebase.firestore.pipeline.Expression parent(com.google.firebase.firestore.DocumentReference docRef); + method public static final com.google.firebase.firestore.pipeline.Expression parent(com.google.firebase.firestore.pipeline.Expression documentPath); + method public static final com.google.firebase.firestore.pipeline.Expression parent(String documentPath); method public final com.google.firebase.firestore.pipeline.Expression pow(com.google.firebase.firestore.pipeline.Expression exponent); method public static final com.google.firebase.firestore.pipeline.Expression pow(com.google.firebase.firestore.pipeline.Expression numericExpr, com.google.firebase.firestore.pipeline.Expression exponent); method public static final com.google.firebase.firestore.pipeline.Expression pow(com.google.firebase.firestore.pipeline.Expression numericExpr, Number exponent); @@ -1709,6 +1713,9 @@ package com.google.firebase.firestore.pipeline { method public com.google.firebase.firestore.pipeline.BooleanExpression notEqualAny(String fieldName, java.util.List values); method public com.google.firebase.firestore.pipeline.Expression nullValue(); method public com.google.firebase.firestore.pipeline.BooleanExpression or(com.google.firebase.firestore.pipeline.BooleanExpression condition, com.google.firebase.firestore.pipeline.BooleanExpression... conditions); + method public com.google.firebase.firestore.pipeline.Expression parent(com.google.firebase.firestore.DocumentReference docRef); + method public com.google.firebase.firestore.pipeline.Expression parent(com.google.firebase.firestore.pipeline.Expression documentPath); + method public com.google.firebase.firestore.pipeline.Expression parent(String documentPath); method public com.google.firebase.firestore.pipeline.Expression pow(com.google.firebase.firestore.pipeline.Expression numericExpr, com.google.firebase.firestore.pipeline.Expression exponent); method public com.google.firebase.firestore.pipeline.Expression pow(com.google.firebase.firestore.pipeline.Expression numericExpr, Number exponent); method public com.google.firebase.firestore.pipeline.Expression pow(String numericField, com.google.firebase.firestore.pipeline.Expression exponent); diff --git a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/PipelineTest.java b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/PipelineTest.java index a0cda07c7a0..19ec9d98746 100644 --- a/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/PipelineTest.java +++ b/firebase-firestore/src/androidTest/java/com/google/firebase/firestore/PipelineTest.java @@ -55,6 +55,7 @@ import static com.google.firebase.firestore.pipeline.Expression.notEqual; import static com.google.firebase.firestore.pipeline.Expression.nullValue; import static com.google.firebase.firestore.pipeline.Expression.or; +import static com.google.firebase.firestore.pipeline.Expression.parent; import static com.google.firebase.firestore.pipeline.Expression.rand; import static com.google.firebase.firestore.pipeline.Expression.split; import static com.google.firebase.firestore.pipeline.Expression.startsWith; @@ -3674,6 +3675,32 @@ public void testSupportsDocumentId() { .containsExactly(ImmutableMap.of("docId", "book4")); } + @Test + public void testSupportsParent() { + DocumentReference reviewRef = + randomCol.document("book4").collection("reviews").document("review1"); + + Task execute = + firestore + .pipeline() + .collection(randomCol.getPath()) + .limit(1) + .select( + parent(constant(reviewRef)).alias("parentRefStatic"), + constant(reviewRef).parent().alias("parentRefInstance")) + .select( + field("parentRefStatic").documentId().alias("parentIdStatic"), + field("parentRefInstance").documentId().alias("parentIdInstance")) + .execute(); + + assertThat(waitFor(execute).getResults()) + .comparingElementsUsing(DATA_CORRESPONDENCE) + .containsExactly( + ImmutableMap.of( + "parentIdStatic", "book4", + "parentIdInstance", "book4")); + } + @Test public void testDocumentsAsSource() { Task execute = diff --git a/firebase-firestore/src/main/java/com/google/firebase/firestore/pipeline/expressions.kt b/firebase-firestore/src/main/java/com/google/firebase/firestore/pipeline/expressions.kt index 160654298e5..d6dbd02accf 100644 --- a/firebase-firestore/src/main/java/com/google/firebase/firestore/pipeline/expressions.kt +++ b/firebase-firestore/src/main/java/com/google/firebase/firestore/pipeline/expressions.kt @@ -7431,6 +7431,47 @@ abstract class Expression internal constructor() { */ @JvmStatic fun documentId(docRef: DocumentReference): Expression = documentId(constant(docRef)) + /** + * Creates an expression that returns the parent document reference of a document reference. + * + * ```kotlin + * // Get the parent document reference of a document reference. + * parent(field("__path__")) + * ``` + * + * @param documentPath An expression evaluating to a document reference. + * @return A new [Expression] representing the parent operation. + */ + @JvmStatic + fun parent(documentPath: Expression): Expression = + FunctionExpression("parent", notImplemented, documentPath) + + /** + * Creates an expression that returns the parent document reference of a document reference. + * + * ```kotlin + * // Get the parent document reference of a document reference. + * parent("projects/p/databases/d/documents/c/d") + * ``` + * + * @param documentPath A string path to get the parent from. + * @return A new [Expression] representing the parent operation. + */ + @JvmStatic fun parent(documentPath: String): Expression = parent(constant(documentPath)) + + /** + * Creates an expression that returns the parent document reference of a document reference. + * + * ```kotlin + * // Get the parent document reference of a document reference. + * parent(myDocumentReference) + * ``` + * + * @param docRef A [DocumentReference] to get the parent from. + * @return A new [Expression] representing the parent operation. + */ + @JvmStatic fun parent(docRef: DocumentReference): Expression = parent(constant(docRef)) + /** * Creates an expression that retrieves the value of a variable bound via [Pipeline.define]. * @@ -7884,6 +7925,19 @@ abstract class Expression internal constructor() { */ fun documentId(): Expression = Companion.documentId(this) + /** + * Creates an expression that returns the parent document reference of this document reference + * expression. + * + * ```kotlin + * // Get the parent document reference of the 'path' field. + * field("path").parent() + * ``` + * + * @return A new [Expression] representing the parent operation. + */ + fun parent(): Expression = Companion.parent(this) + /** * Creates an expression that returns the collection ID from this path expression. *