Skip to content

Commit 9553abc

Browse files
committed
Add options
1 parent 1ccea4d commit 9553abc

File tree

4 files changed

+219
-31
lines changed

4 files changed

+219
-31
lines changed

firebase-firestore/api.txt

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -428,21 +428,26 @@ package com.google.firebase.firestore {
428428
method public com.google.firebase.firestore.Pipeline distinct(java.lang.Object... groups);
429429
method public com.google.firebase.firestore.Pipeline distinct(java.lang.String... groups);
430430
method public com.google.android.gms.tasks.Task<com.google.firebase.firestore.PipelineSnapshot> execute();
431+
method public com.google.firebase.firestore.Pipeline findNearest(com.google.firebase.firestore.pipeline.Expr property, double[] vector, com.google.firebase.firestore.pipeline.FindNearestStage.DistanceMeasure distanceMeasure);
432+
method public com.google.firebase.firestore.Pipeline findNearest(com.google.firebase.firestore.pipeline.Expr property, double[] vector, com.google.firebase.firestore.pipeline.FindNearestStage.DistanceMeasure distanceMeasure, com.google.firebase.firestore.pipeline.FindNearestOptions options);
431433
method public com.google.firebase.firestore.Pipeline genericStage(String name, java.lang.Object... params);
432434
method public com.google.firebase.firestore.Pipeline limit(long limit);
433435
method public com.google.firebase.firestore.Pipeline offset(long offset);
434436
method public com.google.firebase.firestore.Pipeline removeFields(com.google.firebase.firestore.pipeline.Field... fields);
435437
method public com.google.firebase.firestore.Pipeline removeFields(java.lang.String... fields);
436438
method public com.google.firebase.firestore.Pipeline replace(com.google.firebase.firestore.pipeline.Selectable field);
437439
method public com.google.firebase.firestore.Pipeline replace(String field);
440+
method public com.google.firebase.firestore.Pipeline sample(com.google.firebase.firestore.pipeline.SampleStage sample);
438441
method public com.google.firebase.firestore.Pipeline sample(int documents);
439442
method public com.google.firebase.firestore.Pipeline select(com.google.firebase.firestore.pipeline.Selectable... fields);
440443
method public com.google.firebase.firestore.Pipeline select(java.lang.Object... fields);
441444
method public com.google.firebase.firestore.Pipeline select(java.lang.String... fields);
442445
method public com.google.firebase.firestore.Pipeline sort(com.google.firebase.firestore.pipeline.Ordering... orders);
443446
method public com.google.firebase.firestore.Pipeline union(com.google.firebase.firestore.Pipeline other);
444447
method public com.google.firebase.firestore.Pipeline unnest(com.google.firebase.firestore.pipeline.Selectable selectable);
445-
method public com.google.firebase.firestore.Pipeline unnest(String field);
448+
method public com.google.firebase.firestore.Pipeline unnest(com.google.firebase.firestore.pipeline.Selectable selectable, com.google.firebase.firestore.pipeline.UnnestOptions options);
449+
method public com.google.firebase.firestore.Pipeline unnest(String field, String alias);
450+
method public com.google.firebase.firestore.Pipeline unnest(String field, String alias, com.google.firebase.firestore.pipeline.UnnestOptions options);
446451
method public com.google.firebase.firestore.Pipeline where(com.google.firebase.firestore.pipeline.BooleanExpr condition);
447452
}
448453

@@ -658,6 +663,15 @@ package com.google.firebase.firestore.ktx {
658663

659664
package com.google.firebase.firestore.pipeline {
660665

666+
public abstract class AbstractOptions<T extends com.google.firebase.firestore.pipeline.AbstractOptions<T>> {
667+
method public final T with(String key, boolean value);
668+
method public final T with(String key, com.google.firebase.firestore.pipeline.Field value);
669+
method public final T with(String key, double value);
670+
method protected final T with(String key, error.NonExistentClass value);
671+
method public final T with(String key, String value);
672+
method public final T with(String key, long value);
673+
}
674+
661675
public final class AggregateExpr {
662676
method public com.google.firebase.firestore.pipeline.AggregateWithAlias as(String alias);
663677
method public static com.google.firebase.firestore.pipeline.AggregateExpr avg(com.google.firebase.firestore.pipeline.Expr expr);
@@ -860,8 +874,31 @@ package com.google.firebase.firestore.pipeline {
860874
method public com.google.firebase.firestore.pipeline.Field of(String name);
861875
}
862876

863-
public final class FindNearestOptions {
864-
method public java.util.Map<java.lang.String,error.NonExistentClass> toProto();
877+
public final class FindNearestOptions extends com.google.firebase.firestore.pipeline.AbstractOptions<com.google.firebase.firestore.pipeline.FindNearestOptions> {
878+
method public com.google.firebase.firestore.pipeline.FindNearestOptions withDistanceField(com.google.firebase.firestore.pipeline.Field distanceField);
879+
method public com.google.firebase.firestore.pipeline.FindNearestOptions withDistanceField(String distanceField);
880+
method public com.google.firebase.firestore.pipeline.FindNearestOptions withLimit(long limit);
881+
field public static final com.google.firebase.firestore.pipeline.FindNearestOptions.Companion Companion;
882+
field public static final com.google.firebase.firestore.pipeline.FindNearestOptions DEFAULT;
883+
}
884+
885+
public static final class FindNearestOptions.Companion {
886+
}
887+
888+
public final class FindNearestStage extends com.google.firebase.firestore.pipeline.Stage {
889+
}
890+
891+
public static final class FindNearestStage.DistanceMeasure {
892+
field public static final com.google.firebase.firestore.pipeline.FindNearestStage.DistanceMeasure.Companion Companion;
893+
}
894+
895+
public static final class FindNearestStage.DistanceMeasure.Companion {
896+
method public error.NonExistentClass getCOSINE();
897+
method public error.NonExistentClass getDOT_PRODUCT();
898+
method public error.NonExistentClass getEUCLIDEAN();
899+
property public final error.NonExistentClass COSINE;
900+
property public final error.NonExistentClass DOT_PRODUCT;
901+
property public final error.NonExistentClass EUCLIDEAN;
865902
}
866903

867904
public class Function extends com.google.firebase.firestore.pipeline.Expr {
@@ -1237,12 +1274,43 @@ package com.google.firebase.firestore.pipeline {
12371274
method public com.google.firebase.firestore.pipeline.Ordering descending(String fieldName);
12381275
}
12391276

1277+
public final class SampleStage extends com.google.firebase.firestore.pipeline.Stage {
1278+
method public static com.google.firebase.firestore.pipeline.SampleStage withDocLimit(int documents);
1279+
method public static com.google.firebase.firestore.pipeline.SampleStage withPercentage(double percentage);
1280+
field public static final com.google.firebase.firestore.pipeline.SampleStage.Companion Companion;
1281+
}
1282+
1283+
public static final class SampleStage.Companion {
1284+
method public com.google.firebase.firestore.pipeline.SampleStage withDocLimit(int documents);
1285+
method public com.google.firebase.firestore.pipeline.SampleStage withPercentage(double percentage);
1286+
}
1287+
1288+
public static final class SampleStage.Mode {
1289+
field public static final com.google.firebase.firestore.pipeline.SampleStage.Mode.Companion Companion;
1290+
}
1291+
1292+
public static final class SampleStage.Mode.Companion {
1293+
method public error.NonExistentClass getDOCUMENTS();
1294+
method public error.NonExistentClass getPERCENT();
1295+
property public final error.NonExistentClass DOCUMENTS;
1296+
property public final error.NonExistentClass PERCENT;
1297+
}
1298+
12401299
public abstract class Selectable extends com.google.firebase.firestore.pipeline.Expr {
12411300
ctor public Selectable();
12421301
}
12431302

12441303
public abstract class Stage {
12451304
}
12461305

1306+
public final class UnnestOptions extends com.google.firebase.firestore.pipeline.AbstractOptions<com.google.firebase.firestore.pipeline.UnnestOptions> {
1307+
method public com.google.firebase.firestore.pipeline.UnnestOptions withIndexField(String indexField);
1308+
field public static final com.google.firebase.firestore.pipeline.UnnestOptions.Companion Companion;
1309+
field public static final com.google.firebase.firestore.pipeline.UnnestOptions DEFAULT;
1310+
}
1311+
1312+
public static final class UnnestOptions.Companion {
1313+
}
1314+
12471315
}
12481316

firebase-firestore/src/main/java/com/google/firebase/firestore/Pipeline.kt

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ import com.google.firebase.firestore.pipeline.CollectionSource
2929
import com.google.firebase.firestore.pipeline.DatabaseSource
3030
import com.google.firebase.firestore.pipeline.DistinctStage
3131
import com.google.firebase.firestore.pipeline.DocumentsSource
32+
import com.google.firebase.firestore.pipeline.Expr
3233
import com.google.firebase.firestore.pipeline.Field
34+
import com.google.firebase.firestore.pipeline.FindNearestOptions
35+
import com.google.firebase.firestore.pipeline.FindNearestStage
3336
import com.google.firebase.firestore.pipeline.GenericArg
3437
import com.google.firebase.firestore.pipeline.GenericStage
3538
import com.google.firebase.firestore.pipeline.LimitStage
@@ -43,6 +46,7 @@ import com.google.firebase.firestore.pipeline.Selectable
4346
import com.google.firebase.firestore.pipeline.SortStage
4447
import com.google.firebase.firestore.pipeline.Stage
4548
import com.google.firebase.firestore.pipeline.UnionStage
49+
import com.google.firebase.firestore.pipeline.UnnestOptions
4650
import com.google.firebase.firestore.pipeline.UnnestStage
4751
import com.google.firebase.firestore.pipeline.WhereStage
4852
import com.google.firebase.firestore.util.Preconditions
@@ -134,27 +138,39 @@ internal constructor(
134138

135139
fun aggregate(aggregateStage: AggregateStage): Pipeline = append(aggregateStage)
136140

137-
// fun findNearest()
141+
fun findNearest(
142+
property: Expr,
143+
vector: DoubleArray,
144+
distanceMeasure: FindNearestStage.DistanceMeasure
145+
) = append(FindNearestStage(property, vector, distanceMeasure, FindNearestOptions.DEFAULT))
146+
147+
fun findNearest(
148+
property: Expr,
149+
vector: DoubleArray,
150+
distanceMeasure: FindNearestStage.DistanceMeasure,
151+
options: FindNearestOptions
152+
) = append(FindNearestStage(property, vector, distanceMeasure, options))
138153

139154
fun replace(field: String): Pipeline = replace(Field.of(field))
140155

141156
fun replace(field: Selectable): Pipeline =
142157
append(ReplaceStage(field, ReplaceStage.Mode.FULL_REPLACE))
143158

144-
fun sample(documents: Int): Pipeline = append(SampleStage(documents, SampleStage.Mode.DOCUMENTS))
159+
fun sample(documents: Int): Pipeline = append(SampleStage.withDocLimit(documents))
145160

146-
// fun sample(options: SampleOptions): Pipeline
161+
fun sample(sample: SampleStage): Pipeline = append(sample)
147162

148163
fun union(other: Pipeline): Pipeline = append(UnionStage(other))
149164

150-
fun unnest(field: String): Pipeline = unnest(Field.of(field))
165+
fun unnest(field: String, alias: String): Pipeline = unnest(Field.of(field).`as`(alias))
151166

152-
// fun unnest(field: String, options: UnnestOptions): Pipeline = unnest(Field.of(field), options)
167+
fun unnest(field: String, alias: String, options: UnnestOptions): Pipeline =
168+
unnest(Field.of(field).`as`(alias), options)
153169

154170
fun unnest(selectable: Selectable): Pipeline = append(UnnestStage(selectable))
155171

156-
// fun unnest(selectable: Selectable, options: UnnestOptions): Pipeline =
157-
// append(UnnestStage(selectable))
172+
fun unnest(selectable: Selectable, options: UnnestOptions): Pipeline =
173+
append(UnnestStage(selectable))
158174

159175
private inner class ObserverSnapshotTask : PipelineResultObserver {
160176
private val taskCompletionSource = TaskCompletionSource<PipelineSnapshot>()
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.firestore.pipeline
16+
17+
import com.google.common.collect.ImmutableMap
18+
import com.google.firebase.firestore.model.Values
19+
import com.google.firestore.v1.ArrayValue
20+
import com.google.firestore.v1.MapValue
21+
import com.google.firestore.v1.Value
22+
23+
/**
24+
* Wither style Key/Value options object.
25+
*
26+
* Basic `wither` functionality built upon `ImmutableMap<String></String>, Value>`. Exposes methods
27+
* to construct, augment, and encode Kay/Value pairs. The wrapped collection
28+
* `ImmutableMap<String></String>, Value>` is an implementation detail, not to be exposed, since
29+
* more efficient implementations are possible.
30+
*/
31+
internal class InternalOptions
32+
internal constructor(private val options: ImmutableMap<String, Value>) {
33+
internal fun with(key: String, value: Value): InternalOptions {
34+
val builder = ImmutableMap.builderWithExpectedSize<String, Value>(options.size + 1)
35+
builder.putAll(options)
36+
builder.put(key, value)
37+
return InternalOptions(builder.buildKeepingLast())
38+
}
39+
40+
internal fun with(key: String, values: Iterable<Value>): InternalOptions {
41+
val arrayValue = ArrayValue.newBuilder().addAllValues(values).build()
42+
return with(key, Value.newBuilder().setArrayValue(arrayValue).build())
43+
}
44+
45+
internal fun with(key: String, value: InternalOptions): InternalOptions {
46+
return with(key, value.toValue())
47+
}
48+
49+
internal fun forEach(f: (String, Value) -> Unit) = options.forEach(f)
50+
51+
private fun toValue(): Value {
52+
val mapValue = MapValue.newBuilder().putAllFields(options).build()
53+
return Value.newBuilder().setMapValue(mapValue).build()
54+
}
55+
56+
internal companion object {
57+
internal val EMPTY: InternalOptions = InternalOptions(ImmutableMap.of())
58+
59+
internal fun of(key: String, value: Value): InternalOptions {
60+
return InternalOptions(ImmutableMap.of(key, value))
61+
}
62+
}
63+
}
64+
65+
abstract class AbstractOptions<T : AbstractOptions<T>>
66+
internal constructor(internal val options: InternalOptions) {
67+
68+
internal abstract fun self(options: InternalOptions): T
69+
70+
protected fun with(key: String, value: Value): T = self(options.with(key, value))
71+
72+
fun with(key: String, value: String): T = with(key, Values.encodeValue(value))
73+
74+
fun with(key: String, value: Boolean): T = with(key, Values.encodeValue(value))
75+
76+
fun with(key: String, value: Long): T = with(key, Values.encodeValue(value))
77+
78+
fun with(key: String, value: Double): T = with(key, Values.encodeValue(value))
79+
80+
fun with(key: String, value: Field): T = with(key, value.toProto())
81+
}

firebase-firestore/src/main/java/com/google/firebase/firestore/pipeline/stage.kt

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,22 @@
1414

1515
package com.google.firebase.firestore.pipeline
1616

17-
import com.google.common.collect.ImmutableMap
1817
import com.google.firebase.firestore.UserDataReader
1918
import com.google.firebase.firestore.model.Values.encodeValue
2019
import com.google.firebase.firestore.model.Values.encodeVectorValue
20+
import com.google.firebase.firestore.pipeline.Field.Companion.of
2121
import com.google.firestore.v1.Pipeline
2222
import com.google.firestore.v1.Value
2323

2424
abstract class Stage
25-
internal constructor(private val name: String, private val options: Map<String, Value>) {
26-
internal constructor(name: String) : this(name, emptyMap())
25+
private constructor(private val name: String, private val options: InternalOptions) {
26+
internal constructor(name: String) : this(name, InternalOptions.EMPTY)
27+
internal constructor(name: String, options: AbstractOptions<*>) : this(name, options.options)
2728
internal fun toProtoStage(userDataReader: UserDataReader): Pipeline.Stage {
2829
val builder = Pipeline.Stage.newBuilder()
2930
builder.setName(name)
3031
args(userDataReader).forEach { arg -> builder.addArgs(arg) }
31-
builder.putAllOptions(options)
32+
options.forEach(builder::putOptions)
3233
return builder.build()
3334
}
3435
internal abstract fun args(userDataReader: UserDataReader): Sequence<Value>
@@ -147,13 +148,13 @@ internal class WhereStage internal constructor(private val condition: BooleanExp
147148
sequenceOf(condition.toProto(userDataReader))
148149
}
149150

150-
internal class FindNearestStage
151+
class FindNearestStage
151152
internal constructor(
152153
private val property: Expr,
153154
private val vector: DoubleArray,
154155
private val distanceMeasure: DistanceMeasure,
155-
private val options: FindNearestOptions
156-
) : Stage("find_nearest", options.toProto()) {
156+
options: FindNearestOptions
157+
) : Stage("find_nearest", options) {
157158

158159
class DistanceMeasure private constructor(internal val proto: Value) {
159160
private constructor(protoString: String) : this(encodeValue(protoString))
@@ -168,18 +169,21 @@ internal constructor(
168169
sequenceOf(property.toProto(userDataReader), encodeVectorValue(vector), distanceMeasure.proto)
169170
}
170171

171-
class FindNearestOptions
172-
internal constructor(private val limit: Long?, private val distanceField: Field?) {
173-
fun toProto(): Map<String, Value> {
174-
val builder = ImmutableMap.builder<String, Value>()
175-
if (limit != null) {
176-
builder.put("limit", encodeValue(limit))
177-
}
178-
if (distanceField != null) {
179-
builder.put("distance_field", distanceField.toProto())
180-
}
181-
return builder.build()
172+
class FindNearestOptions private constructor(options: InternalOptions) :
173+
AbstractOptions<FindNearestOptions>(options) {
174+
companion object {
175+
@JvmField val DEFAULT = FindNearestOptions(InternalOptions.EMPTY)
182176
}
177+
178+
override fun self(options: InternalOptions): FindNearestOptions = FindNearestOptions(options)
179+
180+
fun withLimit(limit: Long): FindNearestOptions = with("limit", limit)
181+
182+
fun withDistanceField(distanceField: Field): FindNearestOptions =
183+
with("distance_field", distanceField)
184+
185+
fun withDistanceField(distanceField: String): FindNearestOptions =
186+
withDistanceField(of(distanceField))
183187
}
184188

185189
internal class LimitStage internal constructor(private val limit: Long) : Stage("limit") {
@@ -230,7 +234,7 @@ internal constructor(private val field: Selectable, private val mode: Mode) : St
230234
sequenceOf(field.toProto(userDataReader), mode.proto)
231235
}
232236

233-
internal class SampleStage internal constructor(private val size: Number, private val mode: Mode) :
237+
class SampleStage private constructor(private val size: Number, private val mode: Mode) :
234238
Stage("sample") {
235239
class Mode private constructor(internal val proto: Value) {
236240
private constructor(protoString: String) : this(encodeValue(protoString))
@@ -239,6 +243,11 @@ internal class SampleStage internal constructor(private val size: Number, privat
239243
val PERCENT = Mode("percent")
240244
}
241245
}
246+
companion object {
247+
@JvmStatic fun withPercentage(percentage: Double) = SampleStage(percentage, Mode.PERCENT)
248+
249+
@JvmStatic fun withDocLimit(documents: Int) = SampleStage(documents, Mode.DOCUMENTS)
250+
}
242251
override fun args(userDataReader: UserDataReader): Sequence<Value> =
243252
sequenceOf(encodeValue(size), mode.proto)
244253
}
@@ -249,8 +258,22 @@ internal constructor(private val other: com.google.firebase.firestore.Pipeline)
249258
sequenceOf(Value.newBuilder().setPipelineValue(other.toPipelineProto()).build())
250259
}
251260

252-
internal class UnnestStage internal constructor(private val selectable: Selectable) :
253-
Stage("unnest") {
261+
internal class UnnestStage
262+
internal constructor(private val selectable: Selectable, options: UnnestOptions) :
263+
Stage("unnest", options) {
264+
internal constructor(selectable: Selectable) : this(selectable, UnnestOptions.DEFAULT)
254265
override fun args(userDataReader: UserDataReader): Sequence<Value> =
255266
sequenceOf(encodeValue(selectable.getAlias()), selectable.toProto(userDataReader))
256267
}
268+
269+
class UnnestOptions private constructor(options: InternalOptions) :
270+
AbstractOptions<UnnestOptions>(options) {
271+
272+
fun withIndexField(indexField: String): UnnestOptions = with("index_field", indexField)
273+
274+
override fun self(options: InternalOptions) = UnnestOptions(options)
275+
276+
companion object {
277+
@JvmField val DEFAULT: UnnestOptions = UnnestOptions(InternalOptions.EMPTY)
278+
}
279+
}

0 commit comments

Comments
 (0)