Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,21 @@
import com.google.firebase.firestore.pipeline.AggregateStage;
import com.google.firebase.firestore.pipeline.CollectionHints;
import com.google.firebase.firestore.pipeline.CollectionSourceOptions;
import com.google.firebase.firestore.pipeline.ConflictResolution;
import com.google.firebase.firestore.pipeline.DeleteOptions;
import com.google.firebase.firestore.pipeline.DeleteReturn;
import com.google.firebase.firestore.pipeline.DeleteStage;
import com.google.firebase.firestore.pipeline.Expression;
import com.google.firebase.firestore.pipeline.Field;
import com.google.firebase.firestore.pipeline.FindNearestOptions;
import com.google.firebase.firestore.pipeline.FindNearestStage;
import com.google.firebase.firestore.pipeline.InsertOptions;
import com.google.firebase.firestore.pipeline.InsertStage;
import com.google.firebase.firestore.pipeline.RawStage;
import com.google.firebase.firestore.pipeline.UnnestOptions;
import com.google.firebase.firestore.pipeline.UpsertOptions;
import com.google.firebase.firestore.pipeline.UpsertReturn;
import com.google.firebase.firestore.pipeline.UpsertStage;
import com.google.firebase.firestore.testutil.IntegrationTestUtil;
import java.util.Calendar;
import java.util.Collections;
Expand Down Expand Up @@ -2431,6 +2440,92 @@ public void disallowDuplicateAliasesInDistinct() {
assertThat(exception.getMessage()).contains("Duplicate alias: 'dup'");
}

@Test
public void testDelete() {
firestore
.pipeline()
.collection(randomCol)
.where(equal("title", "The Hitchhiker's Guide to the Galaxy"))
.delete()
.execute();

firestore
.pipeline()
.collection(randomCol)
.where(equal("title", "The Hitchhiker's Guide to the Galaxy"))
.delete(
new DeleteStage().withReturns(DeleteReturn.DOCUMENT_ID),
new DeleteOptions().withTransactional(true).with("batch_size", 42))
.execute();
}

@Test
public void testUpsert() {
// Upsert inplace without any transformations, essentially noop.
firestore.pipeline().collection(randomCol).upsert().execute();

// Backup the collection
firestore
.pipeline()
.collection(randomCol)
.upsert(firestore.collection("books_backup"))
.execute();

firestore
.pipeline()
.collection(randomCol)
.where(equal("title", "The Hitchhiker's Guide to the Galaxy"))
// Note, this new field will get upserted into target collections as well
.addFields(currentTimestamp().alias("timestamp"))
.upsert(
UpsertStage.withCollection(firestore.collection("books_backup"))
.withTransformations(
// Nulls out existing rating field
nullValue().alias("rating"),
// Replace title with its uppercase string
field("title").toUpper().alias("title"),
// Create a new field timestamp_seconds
field("timestamp").timestampToUnixSeconds().alias("timestamp_seconds"))
.withReturns(UpsertReturn.DOCUMENT_ID),
new UpsertOptions()
.withTransactional(true)
.withConflictResolution(ConflictResolution.MERGE)
.with("batch_size", 42))
.execute();
}

@Test
public void testInsert() {
// COMPILATION error, insert has to have a target collection
// firestore.pipeline().collection(randomCol).insert().execute();

// Backup the collection
firestore
.pipeline()
.collection(randomCol)
.insert(firestore.collection("books_backup"))
.execute();

firestore
.pipeline()
.collection(randomCol)
.where(equal("title", "The Hitchhiker's Guide to the Galaxy"))
// Note, this new field will get inserted into target collections as well
.addFields(currentTimestamp().alias("timestamp"))
.insert(
InsertStage.withCollection(firestore.collection("books_backup"))
.withTransformations(
// Nulls out existing rating field
nullValue().alias("rating"),
// Replace title with its uppercase string
field("title").toUpper().alias("title"),
// Create a new field timestamp_seconds
field("timestamp").timestampToUnixSeconds().alias("timestamp_seconds"))
.withReturns(UpsertReturn.DOCUMENT_ID),
new InsertOptions().withTransactional(true).with("batch_size", 42))
.execute();
}

static <T> Map.Entry<String, T> entry(String key, T value) {
return new Map.Entry<String, T>() {
private String k = key;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import com.google.firebase.firestore.pipeline.CollectionGroupSource
import com.google.firebase.firestore.pipeline.CollectionSource
import com.google.firebase.firestore.pipeline.CollectionSourceOptions
import com.google.firebase.firestore.pipeline.DatabaseSource
import com.google.firebase.firestore.pipeline.DeleteOptions
import com.google.firebase.firestore.pipeline.DeleteStage
import com.google.firebase.firestore.pipeline.DistinctStage
import com.google.firebase.firestore.pipeline.DocumentsSource
import com.google.firebase.firestore.pipeline.Expression
Expand All @@ -43,6 +45,8 @@ import com.google.firebase.firestore.pipeline.Field
import com.google.firebase.firestore.pipeline.FindNearestOptions
import com.google.firebase.firestore.pipeline.FindNearestStage
import com.google.firebase.firestore.pipeline.FunctionExpression
import com.google.firebase.firestore.pipeline.InsertOptions
import com.google.firebase.firestore.pipeline.InsertStage
import com.google.firebase.firestore.pipeline.InternalOptions
import com.google.firebase.firestore.pipeline.LimitStage
import com.google.firebase.firestore.pipeline.OffsetStage
Expand All @@ -58,6 +62,8 @@ import com.google.firebase.firestore.pipeline.Stage
import com.google.firebase.firestore.pipeline.UnionStage
import com.google.firebase.firestore.pipeline.UnnestOptions
import com.google.firebase.firestore.pipeline.UnnestStage
import com.google.firebase.firestore.pipeline.UpsertOptions
import com.google.firebase.firestore.pipeline.UpsertStage
import com.google.firebase.firestore.pipeline.WhereStage
import com.google.firebase.firestore.remote.RemoteSerializer
import com.google.firebase.firestore.util.Logger
Expand Down Expand Up @@ -897,6 +903,16 @@ internal constructor(
* @return A new [Pipeline] object with this stage appended to the stage list.
*/
fun unnest(unnestStage: UnnestStage): Pipeline = append(unnestStage)

fun delete(): Pipeline = append(DeleteStage())
fun delete(deleteStage: DeleteStage, options: DeleteOptions): Pipeline = append(deleteStage)
fun upsert(): Pipeline = append(UpsertStage())
fun upsert(collection: CollectionReference): Pipeline = append(UpsertStage())
fun upsert(upsertStage: UpsertStage, upsertOptions: UpsertOptions): Pipeline =
append(UpsertStage())
fun insert(collection: CollectionReference): Pipeline = append(InsertStage())
fun insert(insertStage: InsertStage, insertOptions: InsertOptions): Pipeline =
append(InsertStage())
}

/** Start of a Firestore Pipeline */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package com.google.firebase.firestore.pipeline

import com.google.firebase.firestore.CollectionReference
import com.google.firebase.firestore.UserDataReader
import com.google.firebase.firestore.VectorValue
import com.google.firebase.firestore.model.Document
Expand Down Expand Up @@ -1308,3 +1309,117 @@ class UnnestOptions private constructor(options: InternalOptions) :
return UnnestOptions(options)
}
}

enum class DeleteReturn {
EMPTY,
DOCUMENT_ID
}

abstract class WriteOptions<T : AbstractOptions<T>>(options: InternalOptions) :
AbstractOptions<T>(options) {
/** Creates a new, empty `UnnestOptions` object. */
internal constructor() : this(InternalOptions.EMPTY)

fun withTransactional(boolean: Boolean): T {
TODO("Not yet implemented")
}
}

class DeleteStage internal constructor(options: InternalOptions = InternalOptions.EMPTY) :
Stage<DeleteStage>("delete", options) {
companion object {
@JvmStatic fun withReturns(returns: DeleteReturn) = DeleteStage()
}
override fun self(options: InternalOptions) = DeleteStage(options)
override fun canonicalId(): String {
TODO("Not yet implemented")
}

override fun args(userDataReader: UserDataReader): Sequence<Value> = sequenceOf()
}

class DeleteOptions private constructor(options: InternalOptions) :
WriteOptions<DeleteOptions>(options) {
/** Creates a new, empty `UnnestOptions` object. */
constructor() : this(InternalOptions.EMPTY)

public override fun self(options: InternalOptions): DeleteOptions {
return DeleteOptions(options)
}
}

enum class UpsertReturn {
EMPTY,
DOCUMENT_ID
}

class UpsertStage internal constructor(options: InternalOptions = InternalOptions.EMPTY) :
Stage<UpsertStage>("upsert", options) {
companion object {
@JvmStatic
fun withCollection(target: CollectionReference): UpsertStage {
TODO("Not yet implemented")
}
}
override fun self(options: InternalOptions) = UpsertStage(options)
override fun canonicalId(): String {
TODO("Not yet implemented")
}

override fun args(userDataReader: UserDataReader): Sequence<Value> = sequenceOf()
fun withReturns(returns: UpsertReturn) = UpsertStage()
fun withTransformations(vararg transformations: Selectable): UpsertStage {
TODO("Not yet implemented")
}
}

enum class ConflictResolution {
OVERWRITE,
MERGE,
FAIL,
KEEP
}

class UpsertOptions private constructor(options: InternalOptions) :
WriteOptions<UpsertOptions>(options) {
/** Creates a new, empty `UnnestOptions` object. */
constructor() : this(InternalOptions.EMPTY)

public override fun self(options: InternalOptions): UpsertOptions {
return UpsertOptions(options)
}

fun withConflictResolution(resolution: ConflictResolution): UpsertOptions {
TODO("Not yet implemented")
}
}

class InsertStage internal constructor(options: InternalOptions = InternalOptions.EMPTY) :
Stage<InsertStage>("insert", options) {
companion object {
@JvmStatic
fun withCollection(target: CollectionReference): InsertStage {
TODO("Not yet implemented")
}
}
override fun self(options: InternalOptions) = InsertStage(options)
override fun canonicalId(): String {
TODO("Not yet implemented")
}

override fun args(userDataReader: UserDataReader): Sequence<Value> = sequenceOf()
fun withReturns(returns: UpsertReturn) = InsertStage()
fun withTransformations(vararg transformations: Selectable): InsertStage {
TODO("Not yet implemented")
}
}

class InsertOptions private constructor(options: InternalOptions) :
WriteOptions<InsertOptions>(options) {
/** Creates a new, empty `UnnestOptions` object. */
constructor() : this(InternalOptions.EMPTY)

public override fun self(options: InternalOptions): InsertOptions {
return InsertOptions(options)
}
}
Loading