> execute(@Nonnull Pipeline pipeline) {
try (TraceUtil.Scope ignored = transactionTraceContext.makeCurrent()) {
- return pipeline.execute(transactionId, null);
+ return pipeline.execute(PipelineOptions.DEFAULT, transactionId, null);
}
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UserDataConverter.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UserDataConverter.java
index 1d64b772d..9fee83ede 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UserDataConverter.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UserDataConverter.java
@@ -43,6 +43,7 @@
class UserDataConverter {
static final Value NULL_VALUE = Value.newBuilder().setNullValue(NullValue.NULL_VALUE).build();
+
private static final Logger LOGGER = Logger.getLogger(UserDataConverter.class.getName());
/** Controls the behavior for field deletes. */
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/AbstractOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/AbstractOptions.java
new file mode 100644
index 000000000..2d7a64a3f
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/AbstractOptions.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.firestore.pipeline.stages;
+
+import static com.google.cloud.firestore.PipelineUtils.encodeValue;
+
+import com.google.cloud.firestore.PipelineUtils;
+import com.google.cloud.firestore.pipeline.expressions.Expr;
+import com.google.cloud.firestore.pipeline.expressions.Field;
+import com.google.cloud.firestore.pipeline.expressions.FunctionUtils;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.firestore.v1.Value;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Parent class to Pipeline and Stage options.
+ *
+ * Provides a base set of `wither` methods for adding undefined options.
+ *
Standardizes structure of options for uniform encoding and handling.
+ *
Intentionally package-private to prevent extension outside of library.
+ *
+ * @param Subclass type.
+ */
+abstract class AbstractOptions {
+
+ protected final InternalOptions options;
+
+ AbstractOptions(InternalOptions options) {
+ this.options = options;
+ }
+
+ abstract T self(InternalOptions options);
+
+ public final T with(String key, String value) {
+ return with(key, encodeValue(value));
+ }
+
+ public final T with(String key, boolean value) {
+ return with(key, encodeValue(value));
+ }
+
+ public final T with(String key, long value) {
+ return with(key, encodeValue(value));
+ }
+
+ public final T with(String key, double value) {
+ return with(key, encodeValue(value));
+ }
+
+ public final T with(String key, Field value) {
+ return with(key, value.toProto());
+ }
+
+ protected final T with(String key, Value value) {
+ return self(options.with(key, value));
+ }
+
+ protected final T with(String key, String[] values) {
+ return self(options.with(key, Arrays.stream(values).map(PipelineUtils::encodeValue)::iterator));
+ }
+
+ protected final T with(String key, List extends Expr> expressions) {
+ return self(options.with(key, Lists.transform(expressions, FunctionUtils::exprToValue)));
+ }
+
+ protected final T with(String key, AbstractOptions> subSection) {
+ return self(options.with(key, subSection.options));
+ }
+
+ public final T withSection(String key, GenericOptions subSection) {
+ return with(key, subSection);
+ }
+
+ final ImmutableMap toMap() {
+ return options.options;
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/AddFields.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/AddFields.java
index bec9411bd..b7963fbb5 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/AddFields.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/AddFields.java
@@ -20,22 +20,23 @@
import com.google.api.core.InternalApi;
import com.google.cloud.firestore.pipeline.expressions.Expr;
-import com.google.firestore.v1.Pipeline;
+import com.google.firestore.v1.Value;
+import java.util.Collections;
import java.util.Map;
@InternalApi
public final class AddFields extends Stage {
- private static final String name = "add_fields";
private final Map fields;
@InternalApi
public AddFields(Map fields) {
+ super("add_fields", InternalOptions.EMPTY);
this.fields = fields;
}
@Override
- Pipeline.Stage toStageProto() {
- return Pipeline.Stage.newBuilder().setName(name).addArgs(encodeValue(fields)).build();
+ Iterable toStageArgs() {
+ return Collections.singletonList(encodeValue(fields));
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Aggregate.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Aggregate.java
index 520c274b2..327c232da 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Aggregate.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Aggregate.java
@@ -24,53 +24,57 @@
import com.google.cloud.firestore.pipeline.expressions.Expr;
import com.google.cloud.firestore.pipeline.expressions.ExprWithAlias;
import com.google.cloud.firestore.pipeline.expressions.Selectable;
-import com.google.firestore.v1.Pipeline;
+import com.google.firestore.v1.Value;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
+import javax.annotation.Nonnull;
@BetaApi
public final class Aggregate extends Stage {
- private static final String name = "aggregate";
private final Map groups;
private final Map accumulators;
@BetaApi
public Aggregate withGroups(String... fields) {
- return new Aggregate(PipelineUtils.fieldNamesToMap(fields), this.accumulators);
+ return new Aggregate(PipelineUtils.fieldNamesToMap(fields), this.accumulators, AggregateOptions.DEFAULT);
}
@BetaApi
public Aggregate withGroups(Selectable... selectables) {
- return new Aggregate(PipelineUtils.selectablesToMap(selectables), this.accumulators);
+ return new Aggregate(PipelineUtils.selectablesToMap(selectables), this.accumulators, AggregateOptions.DEFAULT);
}
@BetaApi
public static Aggregate withAccumulators(ExprWithAlias... accumulators) {
- if (accumulators.length == 0) {
- throw new IllegalArgumentException(
- "Must specify at least one accumulator for aggregate() stage. There is a distinct() stage if only distinct group values are needed.");
- }
-
return new Aggregate(
Collections.emptyMap(),
Arrays.stream(accumulators)
- .collect(Collectors.toMap(ExprWithAlias::getAlias, ExprWithAlias::getExpr)));
+ .collect(Collectors.toMap(ExprWithAlias::getAlias, ExprWithAlias::getExpr)),
+ AggregateOptions.DEFAULT);
}
- private Aggregate(Map groups, Map accumulators) {
+ @BetaApi
+ public Aggregate withOptions(@Nonnull AggregateOptions options) {
+ return new Aggregate(groups, accumulators, options);
+ }
+
+ private Aggregate(Map groups, Map accumulators,
+ AggregateOptions options) {
+ super("aggregate", options.options);
+ if (accumulators.isEmpty()) {
+ throw new IllegalArgumentException(
+ "Must specify at least one accumulator for aggregate() stage. There is a distinct() stage if only distinct group values are needed.");
+ }
+
this.groups = groups;
this.accumulators = accumulators;
}
@Override
- Pipeline.Stage toStageProto() {
- return Pipeline.Stage.newBuilder()
- .setName(name)
- .addArgs(encodeValue(accumulators))
- .addArgs(encodeValue(groups))
- .build();
+ Iterable toStageArgs() {
+ return Arrays.asList(encodeValue(accumulators), encodeValue(groups));
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/AggregateHints.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/AggregateHints.java
new file mode 100644
index 000000000..8ca2a3dd1
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/AggregateHints.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.firestore.pipeline.stages;
+
+public final class AggregateHints extends AbstractOptions {
+
+ public static AggregateHints DEFAULT = new AggregateHints(InternalOptions.EMPTY);
+
+ public AggregateHints(InternalOptions options) {
+ super(options);
+ }
+
+ @Override
+ AggregateHints self(InternalOptions options) {
+ return new AggregateHints(options);
+ }
+
+ public AggregateHints withForceStreamableEnabled() {
+ return with("force_streamable", true);
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/AggregateOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/AggregateOptions.java
new file mode 100644
index 000000000..69f576d6d
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/AggregateOptions.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.firestore.pipeline.stages;
+
+public final class AggregateOptions extends AbstractOptions {
+
+ public static AggregateOptions DEFAULT = new AggregateOptions(InternalOptions.EMPTY);
+
+ public AggregateOptions(InternalOptions options) {
+ super(options);
+ }
+
+ @Override
+ AggregateOptions self(InternalOptions options) {
+ return new AggregateOptions(options);
+ }
+
+ public AggregateOptions withHints(AggregateHints hints) {
+ return with("hints", hints);
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Collection.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Collection.java
index 1dbcfd028..0d049b0ee 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Collection.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Collection.java
@@ -17,18 +17,17 @@
package com.google.cloud.firestore.pipeline.stages;
import com.google.api.core.InternalApi;
-import com.google.firestore.v1.Pipeline;
import com.google.firestore.v1.Value;
+import java.util.Collections;
import javax.annotation.Nonnull;
@InternalApi
public final class Collection extends Stage {
- private static final String name = "collection";
@Nonnull private final String path;
- @InternalApi
- public Collection(@Nonnull String path) {
+ public Collection(@Nonnull String path, CollectionOptions options) {
+ super("collection", options.options);
if (!path.startsWith("/")) {
this.path = "/" + path;
} else {
@@ -36,11 +35,12 @@ public Collection(@Nonnull String path) {
}
}
+ public Collection withOptions(CollectionOptions options) {
+ return new Collection(path, options);
+ }
+
@Override
- Pipeline.Stage toStageProto() {
- return Pipeline.Stage.newBuilder()
- .setName(name)
- .addArgs(Value.newBuilder().setReferenceValue(path).build())
- .build();
+ Iterable toStageArgs() {
+ return Collections.singleton(Value.newBuilder().setReferenceValue(path).build());
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/CollectionGroup.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/CollectionGroup.java
index 186b06c09..5df5d4df9 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/CollectionGroup.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/CollectionGroup.java
@@ -19,26 +19,28 @@
import static com.google.cloud.firestore.PipelineUtils.encodeValue;
import com.google.api.core.InternalApi;
-import com.google.firestore.v1.Pipeline;
+import com.google.common.collect.ImmutableList;
import com.google.firestore.v1.Value;
@InternalApi
public final class CollectionGroup extends Stage {
- private static final String name = "collection_group";
private final String collectionId;
@InternalApi
- public CollectionGroup(String collectionId) {
+ public CollectionGroup(String collectionId, CollectionGroupOptions options) {
+ super("collection_group", options.options);
this.collectionId = collectionId;
}
+ public CollectionGroup withOptions(CollectionGroupOptions options) {
+ return new CollectionGroup(collectionId, options);
+ }
+
@Override
- Pipeline.Stage toStageProto() {
- return Pipeline.Stage.newBuilder()
- .setName(name)
- .addArgs(Value.newBuilder().setReferenceValue("").build())
- .addArgs(encodeValue(collectionId))
- .build();
+ Iterable toStageArgs() {
+ return ImmutableList.of(
+ Value.newBuilder().setReferenceValue("").build(),
+ encodeValue(collectionId));
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/CollectionGroupOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/CollectionGroupOptions.java
new file mode 100644
index 000000000..44f82baf1
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/CollectionGroupOptions.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.firestore.pipeline.stages;
+
+public final class CollectionGroupOptions extends AbstractOptions {
+
+ public static final CollectionGroupOptions DEFAULT = new CollectionGroupOptions(InternalOptions.EMPTY);
+
+ CollectionGroupOptions(InternalOptions options) {
+ super(options);
+ }
+
+ @Override
+ CollectionGroupOptions self(InternalOptions options) {
+ return new CollectionGroupOptions(options);
+ }
+
+ public CollectionGroupOptions withHints(CollectionHints hints) {
+ return with("hints", hints);
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/CollectionHints.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/CollectionHints.java
new file mode 100644
index 000000000..c96927974
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/CollectionHints.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.firestore.pipeline.stages;
+
+public final class CollectionHints extends AbstractOptions {
+
+ public static CollectionHints DEFAULT = new CollectionHints(InternalOptions.EMPTY);
+
+ CollectionHints(InternalOptions options) {
+ super(options);
+ }
+
+ @Override
+ CollectionHints self(InternalOptions options) {
+ return new CollectionHints(options);
+ }
+
+ public CollectionHints withForceIndex(String value) {
+ return with("forceIndex", value);
+ }
+
+ public CollectionHints withIgnoreIndexFields(String... values) {
+ return with("ignore_index_fields", values);
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/CollectionOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/CollectionOptions.java
new file mode 100644
index 000000000..9e9006a78
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/CollectionOptions.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.firestore.pipeline.stages;
+
+public final class CollectionOptions extends AbstractOptions {
+
+ public static final CollectionOptions DEFAULT = new CollectionOptions(InternalOptions.EMPTY);
+
+ CollectionOptions(InternalOptions options) {
+ super(options);
+ }
+
+ @Override
+ CollectionOptions self(InternalOptions options) {
+ return new CollectionOptions(options);
+ }
+
+ public CollectionOptions withHints(CollectionHints hints) {
+ return with("hints", hints);
+ }
+
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Database.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Database.java
index 8fd98ca03..121cde8f5 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Database.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Database.java
@@ -17,17 +17,19 @@
package com.google.cloud.firestore.pipeline.stages;
import com.google.api.core.InternalApi;
-import com.google.firestore.v1.Pipeline;
+import com.google.firestore.v1.Value;
+import java.util.Collections;
@InternalApi
public final class Database extends Stage {
- private static final String name = "database";
@InternalApi
- public Database() {}
+ public Database() {
+ super("database", InternalOptions.EMPTY);
+ }
@Override
- Pipeline.Stage toStageProto() {
- return Pipeline.Stage.newBuilder().setName(name).build();
+ Iterable toStageArgs() {
+ return Collections.emptyList();
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Distinct.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Distinct.java
index eafbdce68..03e7b486a 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Distinct.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Distinct.java
@@ -21,22 +21,23 @@
import com.google.api.core.BetaApi;
import com.google.api.core.InternalApi;
import com.google.cloud.firestore.pipeline.expressions.Expr;
-import com.google.firestore.v1.Pipeline;
+import com.google.firestore.v1.Value;
+import java.util.Collections;
import java.util.Map;
@BetaApi
public final class Distinct extends Stage {
- private static final String name = "distinct";
private final Map groups;
@InternalApi
public Distinct(Map groups) {
+ super("distinct", InternalOptions.EMPTY);
this.groups = groups;
}
@Override
- Pipeline.Stage toStageProto() {
- return Pipeline.Stage.newBuilder().setName(name).addArgs(encodeValue(groups)).build();
+ Iterable toStageArgs() {
+ return Collections.singletonList(encodeValue(groups));
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Documents.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Documents.java
index f20d79898..5a26a35d9 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Documents.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Documents.java
@@ -18,7 +18,9 @@
import com.google.api.core.InternalApi;
import com.google.cloud.firestore.DocumentReference;
-import com.google.firestore.v1.Pipeline;
+import com.google.cloud.firestore.PipelineUtils;
+import com.google.common.collect.Iterables;
+import com.google.firestore.v1.Value;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@@ -26,11 +28,11 @@
@InternalApi
public final class Documents extends Stage {
- private static final String name = "documents";
private List documents;
@InternalApi
Documents(List documents) {
+ super("documents", InternalOptions.EMPTY);
this.documents = documents;
}
@@ -41,11 +43,7 @@ public static Documents of(DocumentReference... documents) {
}
@Override
- Pipeline.Stage toStageProto() {
- Pipeline.Stage.Builder builder = Pipeline.Stage.newBuilder().setName(name);
- for (String document : documents) {
- builder.addArgsBuilder().setStringValue(document);
- }
- return builder.build();
+ Iterable toStageArgs() {
+ return Iterables.transform(documents, PipelineUtils::encodeValue);
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/FindNearest.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/FindNearest.java
index 2b41de29b..9cc75339b 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/FindNearest.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/FindNearest.java
@@ -21,105 +21,47 @@
import com.google.api.core.BetaApi;
import com.google.api.core.InternalApi;
import com.google.cloud.firestore.pipeline.expressions.Expr;
-import com.google.firestore.v1.Pipeline;
+import com.google.common.collect.ImmutableList;
+import com.google.firestore.v1.Value;
@BetaApi
public final class FindNearest extends Stage {
- public interface DistanceMeasure {
+ public final static class DistanceMeasure {
- enum Type {
- EUCLIDEAN,
- COSINE,
- DOT_PRODUCT
- }
-
- static DistanceMeasure euclidean() {
- return new EuclideanDistanceMeasure();
- }
-
- static DistanceMeasure cosine() {
- return new CosineDistanceMeasure();
- }
-
- static DistanceMeasure dotProduct() {
- return new DotProductDistanceMeasure();
- }
-
- static DistanceMeasure generic(String name) {
- return new GenericDistanceMeasure(name);
- }
-
- @InternalApi
- String toProtoString();
- }
-
- public static class EuclideanDistanceMeasure implements DistanceMeasure {
+ final String protoString;
- @Override
- @InternalApi
- public String toProtoString() {
- return "euclidean";
+ private DistanceMeasure(String protoString) {
+ this.protoString = protoString;
}
- }
-
- public static class CosineDistanceMeasure implements DistanceMeasure {
-
- @Override
- @InternalApi
- public String toProtoString() {
- return "cosine";
- }
- }
-
- public static class DotProductDistanceMeasure implements DistanceMeasure {
-
- @Override
- @InternalApi
- public String toProtoString() {
- return "dot_product";
- }
- }
-
- public static class GenericDistanceMeasure implements DistanceMeasure {
-
- String name;
- public GenericDistanceMeasure(String name) {
- this.name = name;
+ public static final DistanceMeasure EUCLIDEAN = new DistanceMeasure("euclidean");
+ public static final DistanceMeasure COSINE = new DistanceMeasure("cosine");
+ public static final DistanceMeasure DOT_PRODUCT = new DistanceMeasure("dot_product");
+ public static DistanceMeasure generic(String name) {
+ return new DistanceMeasure(name);
}
- @Override
- @InternalApi
- public String toProtoString() {
- return name;
+ Value toProto() {
+ return encodeValue(protoString);
}
}
- private static final String name = "find_nearest";
private final Expr property;
private final double[] vector;
private final DistanceMeasure distanceMeasure;
- private final FindNearestOptions options;
@InternalApi
public FindNearest(
Expr property, double[] vector, DistanceMeasure distanceMeasure, FindNearestOptions options) {
+ super("find_nearest", options.options);
this.property = property;
this.vector = vector;
this.distanceMeasure = distanceMeasure;
- this.options = options;
}
@Override
- Pipeline.Stage toStageProto() {
- return Pipeline.Stage.newBuilder()
- .setName(name)
- .addArgs(encodeValue(property))
- .addArgs(encodeValue(vector))
- .addArgs(encodeValue(distanceMeasure.toProtoString()))
- .putOptions("limit", encodeValue(options.getLimit()))
- .putOptions("distance_field", encodeValue(options.getDistanceField()))
- .build();
+ Iterable toStageArgs() {
+ return ImmutableList.of(encodeValue(property), encodeValue(vector), distanceMeasure.toProto());
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/FindNearestOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/FindNearestOptions.java
index 1c57ac76e..03c0652c0 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/FindNearestOptions.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/FindNearestOptions.java
@@ -18,50 +18,30 @@
import com.google.api.core.BetaApi;
import com.google.cloud.firestore.pipeline.expressions.Field;
-import javax.annotation.Nullable;
@BetaApi
-public class FindNearestOptions {
+public final class FindNearestOptions extends AbstractOptions {
- @Nullable private final Long limit;
+ public static FindNearestOptions DEFAULT = new FindNearestOptions(InternalOptions.EMPTY);
- @Nullable private final Field distanceField;
-
- private FindNearestOptions(Long limit, Field distanceField) {
- this.limit = limit;
- this.distanceField = distanceField;
+ private FindNearestOptions(InternalOptions options) {
+ super(options);
}
- public static Builder builder() {
- return new Builder();
+ @Override
+ FindNearestOptions self(InternalOptions options) {
+ return new FindNearestOptions(options);
}
- @Nullable
- public Long getLimit() {
- return limit;
+ public FindNearestOptions withLimit(long limit) {
+ return with("limit", limit);
}
- @Nullable
- public Field getDistanceField() {
- return distanceField;
+ public FindNearestOptions withDistanceField(Field distanceField) {
+ return with("distance_field", distanceField);
}
- public static class Builder {
- @Nullable private Long limit;
- @Nullable private Field distanceField;
-
- public Builder limit(Long limit) {
- this.limit = limit;
- return this;
- }
-
- public Builder distanceField(Field distanceField) {
- this.distanceField = distanceField;
- return this;
- }
-
- public FindNearestOptions build() {
- return new FindNearestOptions(limit, distanceField);
- }
+ public FindNearestOptions withDistanceField(String distanceField) {
+ return withDistanceField(Field.of(distanceField));
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/GenericOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/GenericOptions.java
new file mode 100644
index 000000000..f05e5f0ac
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/GenericOptions.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.firestore.pipeline.stages;
+
+import static com.google.cloud.firestore.PipelineUtils.encodeValue;
+
+import com.google.cloud.firestore.pipeline.expressions.Field;
+
+public final class GenericOptions extends AbstractOptions {
+
+ public static GenericOptions DEFAULT = new GenericOptions(InternalOptions.EMPTY);
+
+ public static GenericOptions of(String key, String value) {
+ return new GenericOptions(InternalOptions.of(key, encodeValue(value)));
+ }
+
+ public static GenericOptions of(String key, boolean value) {
+ return new GenericOptions(InternalOptions.of(key, encodeValue(value)));
+ }
+
+ public static GenericOptions of(String key, long value) {
+ return new GenericOptions(InternalOptions.of(key, encodeValue(value)));
+ }
+
+ public static GenericOptions of(String key, double value) {
+ return new GenericOptions(InternalOptions.of(key, encodeValue(value)));
+ }
+
+ public static GenericOptions of(String key, Field value) {
+ return new GenericOptions(InternalOptions.of(key, value.toProto()));
+ }
+
+ GenericOptions(InternalOptions options) {
+ super(options);
+ }
+
+ @Override
+ protected GenericOptions self(InternalOptions options) {
+ return new GenericOptions(options);
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/GenericStage.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/GenericStage.java
index d2595f12c..dc0adf00a 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/GenericStage.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/GenericStage.java
@@ -19,27 +19,24 @@
import static com.google.cloud.firestore.PipelineUtils.encodeValue;
import com.google.api.core.InternalApi;
-import com.google.firestore.v1.Pipeline;
+import com.google.cloud.firestore.PipelineUtils;
+import com.google.common.collect.Iterables;
+import com.google.firestore.v1.Value;
import java.util.List;
@InternalApi
public final class GenericStage extends Stage {
- private final String name;
private List params;
@InternalApi
- public GenericStage(String name, List params) {
- this.name = name;
+ public GenericStage(String name, List params, GenericOptions optionalParams) {
+ super(name, optionalParams.options);
this.params = params;
}
@Override
- Pipeline.Stage toStageProto() {
- Pipeline.Stage.Builder builder = Pipeline.Stage.newBuilder().setName(name);
- for (Object param : params) {
- builder.addArgs(encodeValue(param));
- }
- return builder.build();
+ Iterable toStageArgs() {
+ return Iterables.transform(params, PipelineUtils::encodeValue);
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/InternalOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/InternalOptions.java
new file mode 100644
index 000000000..bad786c64
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/InternalOptions.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.firestore.pipeline.stages;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.firestore.v1.ArrayValue;
+import com.google.firestore.v1.MapValue;
+import com.google.firestore.v1.Value;
+
+/**
+ * Wither style Key/Value options object.
+ *
+ * Basic `wither` functionality built upon `ImmutableMap`. Exposes methods to
+ * construct, augment, and encode Kay/Value pairs. The wrapped collection
+ * `ImmutableMap` is an implementation detail, not to be exposed, since more
+ * efficient implementations are possible.
+ */
+final class InternalOptions {
+
+ public static final InternalOptions EMPTY = new InternalOptions(ImmutableMap.of());
+
+ final ImmutableMap options;
+
+ InternalOptions(ImmutableMap options) {
+ this.options = options;
+ }
+
+ public static InternalOptions of(String key, Value value) {
+ return new InternalOptions(ImmutableMap.of(key, value));
+ }
+
+ InternalOptions with(String key, Value value) {
+ ImmutableMap.Builder builder = ImmutableMap.builderWithExpectedSize(
+ options.size() + 1);
+ builder.putAll(options);
+ builder.put(key, value);
+ return new InternalOptions(builder.buildKeepingLast());
+ }
+
+ InternalOptions with(String key, Iterable values) {
+ ArrayValue arrayValue = ArrayValue.newBuilder().addAllValues(values).build();
+ return with(key, Value.newBuilder().setArrayValue(arrayValue).build());
+ }
+
+ InternalOptions with(String key, InternalOptions value) {
+ return with(key, value.toValue());
+ }
+
+ private Value toValue() {
+ MapValue mapValue = MapValue.newBuilder().putAllFields(options).build();
+ return Value.newBuilder().setMapValue(mapValue).build();
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Limit.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Limit.java
index 5d73400ff..8723b980c 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Limit.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Limit.java
@@ -19,21 +19,22 @@
import static com.google.cloud.firestore.PipelineUtils.encodeValue;
import com.google.api.core.InternalApi;
-import com.google.firestore.v1.Pipeline;
+import com.google.firestore.v1.Value;
+import java.util.Collections;
@InternalApi
public final class Limit extends Stage {
- private static final String name = "limit";
private final int limit;
@InternalApi
public Limit(int limit) {
+ super("limit", InternalOptions.EMPTY);
this.limit = limit;
}
@Override
- Pipeline.Stage toStageProto() {
- return Pipeline.Stage.newBuilder().setName(name).addArgs(encodeValue(limit)).build();
+ Iterable toStageArgs() {
+ return Collections.singletonList(encodeValue(limit));
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Offset.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Offset.java
index 49bb0aed4..63a96812a 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Offset.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Offset.java
@@ -19,21 +19,22 @@
import static com.google.cloud.firestore.PipelineUtils.encodeValue;
import com.google.api.core.InternalApi;
-import com.google.firestore.v1.Pipeline;
+import com.google.firestore.v1.Value;
+import java.util.Collections;
@InternalApi
public final class Offset extends Stage {
- private static final String name = "offset";
private final int offset;
@InternalApi
public Offset(int offset) {
+ super("offset", InternalOptions.EMPTY);
this.offset = offset;
}
@Override
- Pipeline.Stage toStageProto() {
- return Pipeline.Stage.newBuilder().setName(name).addArgs(encodeValue(offset)).build();
+ Iterable toStageArgs() {
+ return Collections.singletonList(encodeValue(offset));
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/PipelineOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/PipelineOptions.java
new file mode 100644
index 000000000..591c7b465
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/PipelineOptions.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.firestore.pipeline.stages;
+
+import com.google.cloud.firestore.PipelineUtils;
+import com.google.firestore.v1.Value;
+
+public final class PipelineOptions extends AbstractOptions {
+
+ public static PipelineOptions DEFAULT = new PipelineOptions(InternalOptions.EMPTY);
+
+ public enum ExecutionMode {
+ EXECUTE("execute"),
+ EXPLAIN("explain"),
+ PROFILE("profile");
+
+ private final Value value;
+ ExecutionMode(String profile) {
+ value = PipelineUtils.encodeValue(profile);
+ }
+ }
+
+ PipelineOptions(InternalOptions options) {
+ super(options);
+ }
+
+ @Override
+ PipelineOptions self(InternalOptions options) {
+ return new PipelineOptions(options);
+ }
+
+ public PipelineOptions withExecutionMode(ExecutionMode mode) {
+ return with("execution_mode", mode.value);
+ }
+
+ public PipelineOptions withIndexRecommendationEnabled() {
+ return with("index_recommendation", true);
+ }
+
+ public PipelineOptions withShowAlternativePlanEnabled() {
+ return with("show_alternative_plans", true);
+ }
+
+ public PipelineOptions withRedactEnabled() {
+ return with("redact", true);
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/RemoveFields.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/RemoveFields.java
index 794f28a8f..613f1bf0e 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/RemoveFields.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/RemoveFields.java
@@ -20,25 +20,22 @@
import com.google.cloud.firestore.PipelineUtils;
import com.google.cloud.firestore.pipeline.expressions.Field;
import com.google.common.collect.ImmutableList;
-import com.google.firestore.v1.Pipeline;
+import com.google.common.collect.Iterables;
+import com.google.firestore.v1.Value;
@InternalApi
public final class RemoveFields extends Stage {
- private static final String name = "remove_fields";
private final ImmutableList fields;
@InternalApi
public RemoveFields(ImmutableList fields) {
+ super("remove_fields", InternalOptions.EMPTY);
this.fields = fields;
}
@Override
- Pipeline.Stage toStageProto() {
- Pipeline.Stage.Builder builder = Pipeline.Stage.newBuilder().setName(name);
- for (Field field : fields) {
- builder.addArgs(PipelineUtils.encodeValue(field));
- }
- return builder.build();
+ Iterable toStageArgs() {
+ return Iterables.transform(fields, PipelineUtils::encodeValue);
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Replace.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Replace.java
index 6be322564..d4f99f368 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Replace.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Replace.java
@@ -19,13 +19,12 @@
import static com.google.cloud.firestore.PipelineUtils.encodeValue;
import com.google.cloud.firestore.pipeline.expressions.Selectable;
-import com.google.firestore.v1.Pipeline;
+import com.google.common.collect.ImmutableList;
import com.google.firestore.v1.Value;
import javax.annotation.Nonnull;
public class Replace extends Stage {
- private static final String name = "replace";
private final Selectable field;
private final Mode mode;
@@ -46,16 +45,13 @@ public Replace(@Nonnull Selectable field) {
}
public Replace(@Nonnull Selectable field, @Nonnull Mode mode) {
+ super("replace", InternalOptions.EMPTY);
this.field = field;
this.mode = mode;
}
@Override
- Pipeline.Stage toStageProto() {
- return Pipeline.Stage.newBuilder()
- .setName(name)
- .addArgs(encodeValue(field))
- .addArgs(mode.value)
- .build();
+ Iterable toStageArgs() {
+ return ImmutableList.of(encodeValue(field), mode.value);
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Sample.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Sample.java
index f30597a21..9115fe203 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Sample.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Sample.java
@@ -16,21 +16,54 @@
package com.google.cloud.firestore.pipeline.stages;
+import static com.google.cloud.firestore.PipelineUtils.encodeValue;
+
+import com.google.api.core.BetaApi;
import com.google.api.core.InternalApi;
-import com.google.firestore.v1.Pipeline;
+import com.google.common.collect.ImmutableList;
+import com.google.firestore.v1.Value;
+import javax.annotation.Nonnull;
public final class Sample extends Stage {
- private static final String name = "sample";
- private final SampleOptions options;
+ private final Number size;
+ private final Mode mode;
+
+ public enum Mode {
+ DOCUMENTS(encodeValue("documents")),
+ PERCENT(encodeValue("percent"));
+
+ public final Value value;
+
+ Mode(Value value) {
+ this.value = value;
+ }
+ }
+
+ @BetaApi
+ public static Sample withPercentage(double percentage) {
+ return new Sample(percentage, Mode.PERCENT, SampleOptions.DEFAULT);
+ }
+
+ @BetaApi
+ public static Sample withDocLimit(int documents) {
+ return new Sample(documents, Mode.DOCUMENTS, SampleOptions.DEFAULT);
+ }
+
+ @BetaApi
+ public Sample withOptions(@Nonnull SampleOptions options) {
+ return new Sample(size, mode, options);
+ }
@InternalApi
- public Sample(SampleOptions options) {
- this.options = options;
+ private Sample(Number size, Mode mode, SampleOptions options) {
+ super("sample", options.options);
+ this.size = size;
+ this.mode = mode;
}
@Override
- Pipeline.Stage toStageProto() {
- return Pipeline.Stage.newBuilder().setName(name).addAllArgs(options.getProtoArgs()).build();
+ Iterable toStageArgs() {
+ return ImmutableList.of(encodeValue(size), mode.value);
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/SampleOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/SampleOptions.java
index fe7524dd6..c57a59d7a 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/SampleOptions.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/SampleOptions.java
@@ -16,41 +16,16 @@
package com.google.cloud.firestore.pipeline.stages;
-import static com.google.cloud.firestore.PipelineUtils.encodeValue;
+public final class SampleOptions extends AbstractOptions {
-import com.google.common.collect.ImmutableList;
-import com.google.firestore.v1.Value;
+ public static SampleOptions DEFAULT = new SampleOptions(InternalOptions.EMPTY);
-public class SampleOptions {
-
- private final Number n;
- private final Mode mode;
-
- private SampleOptions(Number n, Mode mode) {
- this.n = n;
- this.mode = mode;
- }
-
- public enum Mode {
- DOCUMENTS(Value.newBuilder().setStringValue("documents").build()),
- PERCENT(Value.newBuilder().setStringValue("percent").build());
-
- public final Value value;
-
- Mode(Value value) {
- this.value = value;
- }
- }
-
- public static SampleOptions percentage(double percentage) {
- return new SampleOptions(percentage, Mode.PERCENT);
- }
-
- public static SampleOptions docLimit(int documents) {
- return new SampleOptions(documents, Mode.DOCUMENTS);
+ public SampleOptions(InternalOptions options) {
+ super(options);
}
- Iterable getProtoArgs() {
- return ImmutableList.of(encodeValue(n), mode.value);
+ @Override
+ SampleOptions self(InternalOptions options) {
+ return new SampleOptions(options);
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Select.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Select.java
index 74763f9b0..311608a23 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Select.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Select.java
@@ -20,22 +20,23 @@
import com.google.api.core.InternalApi;
import com.google.cloud.firestore.pipeline.expressions.Expr;
-import com.google.firestore.v1.Pipeline;
+import com.google.firestore.v1.Value;
+import java.util.Collections;
import java.util.Map;
@InternalApi
public final class Select extends Stage {
- private static final String name = "select";
private final Map projections;
@InternalApi
public Select(Map projections) {
+ super("select", InternalOptions.EMPTY);
this.projections = projections;
}
@Override
- Pipeline.Stage toStageProto() {
- return Pipeline.Stage.newBuilder().setName(name).addArgs(encodeValue(projections)).build();
+ Iterable toStageArgs() {
+ return Collections.singletonList(encodeValue(projections));
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Sort.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Sort.java
index 70d92750f..c89266118 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Sort.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Sort.java
@@ -18,8 +18,9 @@
import com.google.api.core.InternalApi;
import com.google.cloud.firestore.pipeline.expressions.Ordering;
-import com.google.common.collect.Lists;
-import com.google.firestore.v1.Pipeline;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.firestore.v1.Value;
import java.util.List;
public final class Sort extends Stage {
@@ -28,16 +29,13 @@ public final class Sort extends Stage {
private final List orders;
@InternalApi
- public Sort(Ordering... orders) {
- this.orders = Lists.newArrayList(orders);
+ public Sort(ImmutableList orders) {
+ super("sort", InternalOptions.EMPTY);
+ this.orders = orders;
}
@Override
- Pipeline.Stage toStageProto() {
- Pipeline.Stage.Builder builder = Pipeline.Stage.newBuilder().setName(name);
- for (Ordering order : orders) {
- builder.addArgs(order.toProto());
- }
- return builder.build();
+ Iterable toStageArgs() {
+ return Iterables.transform(orders, Ordering::toProto);
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Stage.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Stage.java
index a148d126b..8f82195f7 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Stage.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Stage.java
@@ -16,11 +16,28 @@
package com.google.cloud.firestore.pipeline.stages;
+import com.google.firestore.v1.Pipeline;
+import com.google.firestore.v1.Value;
+
/** Parent to all stages. */
public abstract class Stage {
+ protected final String name;
+ final InternalOptions options;
+
/** Constructor is package-private to prevent extension. */
- Stage() {}
+ Stage(String name, InternalOptions options) {
+ this.name = name;
+ this.options = options;
+ }
+
+ final Pipeline.Stage toStageProto() {
+ return Pipeline.Stage.newBuilder()
+ .setName(name)
+ .addAllArgs(toStageArgs())
+ .putAllOptions(options.options)
+ .build();
+ }
- abstract com.google.firestore.v1.Pipeline.Stage toStageProto();
+ abstract Iterable toStageArgs();
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/StageUtils.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/StageUtils.java
index d538e3dd1..bcb7e5ee0 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/StageUtils.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/StageUtils.java
@@ -17,6 +17,8 @@
package com.google.cloud.firestore.pipeline.stages;
import com.google.api.core.InternalApi;
+import com.google.common.collect.ImmutableMap;
+import com.google.firestore.v1.Value;
@InternalApi
public final class StageUtils {
@@ -24,4 +26,10 @@ public final class StageUtils {
public static com.google.firestore.v1.Pipeline.Stage toStageProto(Stage stage) {
return stage.toStageProto();
}
+
+ @SuppressWarnings("ClassEscapesDefinedScope")
+ @InternalApi
+ public static ImmutableMap toMap(AbstractOptions> options) {
+ return options.options.options;
+ }
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Union.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Union.java
index 4014e926a..d54a650f4 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Union.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Union.java
@@ -17,20 +17,20 @@
package com.google.cloud.firestore.pipeline.stages;
import com.google.cloud.firestore.Pipeline;
+import com.google.firestore.v1.Value;
+import java.util.Collections;
-public class Union extends Stage {
+public final class Union extends Stage {
- private static final String name = "union";
private final Pipeline other;
public Union(Pipeline other) {
+ super("union", InternalOptions.EMPTY);
this.other = other;
}
@Override
- com.google.firestore.v1.Pipeline.Stage toStageProto() {
- com.google.firestore.v1.Pipeline.Stage.Builder builder =
- com.google.firestore.v1.Pipeline.Stage.newBuilder().setName(name);
- return builder.addArgs(other.toProtoValue()).build();
+ Iterable toStageArgs() {
+ return Collections.singletonList(other.toProtoValue());
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Unnest.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Unnest.java
index aaddc12b2..3d7a3cfee 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Unnest.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Unnest.java
@@ -19,32 +19,30 @@
import static com.google.cloud.firestore.PipelineUtils.encodeValue;
import com.google.cloud.firestore.pipeline.expressions.Field;
-import com.google.firestore.v1.Pipeline;
+import com.google.common.collect.ImmutableList;
+import com.google.firestore.v1.Value;
import javax.annotation.Nonnull;
-public class Unnest extends Stage {
+public final class Unnest extends Stage {
- private static final String name = "unnest";
private final Field field;
- private final UnnestOptions options;
+ private final String alias;
- public Unnest(Field field) {
+ public Unnest(@Nonnull Field field, @Nonnull String alias) {
+ super("unnest", InternalOptions.EMPTY);
this.field = field;
- this.options = null;
+ this.alias = alias;
}
- public Unnest(@Nonnull Field field, @Nonnull UnnestOptions options) {
+ public Unnest(@Nonnull Field field, @Nonnull String alias, @Nonnull UnnestOptions options) {
+ super("unnest", options.options);
this.field = field;
- this.options = options;
+ this.alias = alias;
}
@Override
- Pipeline.Stage toStageProto() {
- Pipeline.Stage.Builder builder =
- Pipeline.Stage.newBuilder().setName(name).addArgs(encodeValue(field));
- if (options != null) {
- builder.addArgs(encodeValue(options.indexField));
- }
- return builder.build();
+ Iterable toStageArgs() {
+ return ImmutableList.of(encodeValue(field), encodeValue(alias));
}
+
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/UnnestOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/UnnestOptions.java
index 3ca12d792..59ab0cb2d 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/UnnestOptions.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/UnnestOptions.java
@@ -18,15 +18,20 @@
import javax.annotation.Nonnull;
-public class UnnestOptions {
+public final class UnnestOptions extends AbstractOptions {
- final String indexField;
+ public static UnnestOptions DEFAULT = new UnnestOptions(InternalOptions.EMPTY);
- public static UnnestOptions indexField(@Nonnull String indexField) {
- return new UnnestOptions(indexField);
+ public UnnestOptions withIndexField(@Nonnull String indexField) {
+ return with("index_field", indexField);
}
- private UnnestOptions(String indexField) {
- this.indexField = indexField;
+ @Override
+ UnnestOptions self(InternalOptions options) {
+ return new UnnestOptions(options);
+ }
+
+ private UnnestOptions(InternalOptions options) {
+ super(options);
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Where.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Where.java
index 36da8f12e..02511e4fa 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Where.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/stages/Where.java
@@ -20,21 +20,22 @@
import com.google.api.core.InternalApi;
import com.google.cloud.firestore.pipeline.expressions.FilterCondition;
-import com.google.firestore.v1.Pipeline;
+import com.google.firestore.v1.Value;
+import java.util.Collections;
@InternalApi
public final class Where extends Stage {
- private static final String name = "where";
private final FilterCondition condition;
@InternalApi
public Where(FilterCondition condition) {
+ super("where", InternalOptions.EMPTY);
this.condition = condition;
}
@Override
- Pipeline.Stage toStageProto() {
- return Pipeline.Stage.newBuilder().setName(name).addArgs(encodeValue(condition)).build();
+ Iterable toStageArgs() {
+ return Collections.singletonList(encodeValue(condition));
}
}
diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITPipelineTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITPipelineTest.java
index 550d04fa1..822d21834 100644
--- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITPipelineTest.java
+++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITPipelineTest.java
@@ -41,6 +41,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
+import com.google.api.core.ApiFuture;
import com.google.cloud.firestore.CollectionReference;
import com.google.cloud.firestore.LocalFirestoreHelper;
import com.google.cloud.firestore.Pipeline;
@@ -49,7 +50,15 @@
import com.google.cloud.firestore.pipeline.expressions.Field;
import com.google.cloud.firestore.pipeline.expressions.Function;
import com.google.cloud.firestore.pipeline.stages.Aggregate;
-import com.google.cloud.firestore.pipeline.stages.SampleOptions;
+import com.google.cloud.firestore.pipeline.stages.AggregateHints;
+import com.google.cloud.firestore.pipeline.stages.AggregateOptions;
+import com.google.cloud.firestore.pipeline.stages.CollectionHints;
+import com.google.cloud.firestore.pipeline.stages.CollectionOptions;
+import com.google.cloud.firestore.pipeline.stages.FindNearest;
+import com.google.cloud.firestore.pipeline.stages.FindNearestOptions;
+import com.google.cloud.firestore.pipeline.stages.PipelineOptions;
+import com.google.cloud.firestore.pipeline.stages.PipelineOptions.ExecutionMode;
+import com.google.cloud.firestore.pipeline.stages.Sample;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
@@ -261,7 +270,7 @@ public void testAggregates() throws Exception {
firestore
.pipeline()
.collection(collection.getPath())
- .aggregate(countAll().as("count"))
+ .aggregate(AggregateOptions.DEFAULT, countAll().as("count"))
.execute()
.get();
assertThat(data(results)).isEqualTo(Lists.newArrayList(map("count", 10L)));
@@ -1137,7 +1146,7 @@ public void testSampleLimit() throws Exception {
@Test
public void testSamplePercentage() throws Exception {
List results =
- collection.pipeline().sample(SampleOptions.percentage(0.6)).execute().get();
+ collection.pipeline().sample(Sample.withPercentage(0.6)).execute().get();
assertThat(results).hasSize(6);
}
@@ -1156,10 +1165,48 @@ public void testUnnest() throws Exception {
collection
.pipeline()
.where(eq(Field.of("title"), "The Hitchhiker's Guide to the Galaxy"))
- .unnest("tags")
+ .unnest("tags", "tag")
.execute()
.get();
assertThat(results).hasSize(3);
}
+
+ @Test
+ public void testOptions() {
+ // This is just example of execute and stage options.
+ PipelineOptions opts = PipelineOptions.DEFAULT
+ .withIndexRecommendationEnabled()
+ .withExecutionMode(ExecutionMode.PROFILE);
+
+ double[] vector = {1.0, 2.0, 3.0};
+
+ Pipeline pipeline = firestore.pipeline()
+ .collection(
+ "/k",
+ // Remove Hints overload - can be added later.
+ CollectionOptions.DEFAULT
+ .withHints(CollectionHints.DEFAULT
+ .withForceIndex("abcdef")
+ .with("foo", "bar"))
+ .with("foo", "bar")
+ )
+ .findNearest("topicVectors", vector, FindNearest.DistanceMeasure.COSINE,
+ FindNearestOptions.DEFAULT
+ .withLimit(10)
+ .withDistanceField("distance")
+ .with("foo", "bar"))
+ .aggregate(
+ Aggregate
+ .withAccumulators(avg("rating").as("avg_rating"))
+ .withGroups("genre")
+ .withOptions(AggregateOptions.DEFAULT
+ .withHints(AggregateHints.DEFAULT
+ .withForceStreamableEnabled()
+ .with("foo", "bar"))
+ .with("foo", "bar"))
+ );
+
+ pipeline.execute(opts);
+ }
}