Skip to content
This repository was archived by the owner on May 8, 2026. It is now read-only.

Commit 4cf8b9d

Browse files
committed
Initial implementation of search API
1 parent f50a9e5 commit 4cf8b9d

9 files changed

Lines changed: 515 additions & 9 deletions

File tree

google-cloud-firestore/src/main/java/com/google/cloud/firestore/Pipeline.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import com.google.cloud.firestore.pipeline.stages.RemoveFields;
5252
import com.google.cloud.firestore.pipeline.stages.ReplaceWith;
5353
import com.google.cloud.firestore.pipeline.stages.Sample;
54+
import com.google.cloud.firestore.pipeline.stages.Search;
5455
import com.google.cloud.firestore.pipeline.stages.Select;
5556
import com.google.cloud.firestore.pipeline.stages.Sort;
5657
import com.google.cloud.firestore.pipeline.stages.Stage;
@@ -219,6 +220,21 @@ private Pipeline append(Stage stage) {
219220
return new Pipeline(this.rpcContext, stages.append(stage));
220221
}
221222

223+
/**
224+
* Adds a search stage to the Pipeline.
225+
*
226+
* <p>This must be the first stage of the pipeline.
227+
*
228+
* <p>A limited set of expressions are supported in the search stage.
229+
*
230+
* @param searchStage An object that specifies how search is performed.
231+
* @return A new {@code Pipeline} object with this stage appended to the stage list.
232+
*/
233+
@BetaApi
234+
public Pipeline search(Search searchStage) {
235+
return append(searchStage);
236+
}
237+
222238
/**
223239
* Adds new fields to outputs from previous stages.
224240
*
@@ -1237,9 +1253,9 @@ public void onError(Throwable t) {
12371253
}
12381254

12391255
@InternalApi
1240-
private com.google.firestore.v1.Pipeline toProto() {
1256+
public com.google.firestore.v1.Pipeline toProto() {
12411257
return com.google.firestore.v1.Pipeline.newBuilder()
1242-
.addAllStages(stages.transform(StageUtils::toStageProto))
1258+
.addAllStages(stages.transform(Stage::toStageProto))
12431259
.build();
12441260
}
12451261

google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/expressions/BooleanFunctionExpression.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ class BooleanFunctionExpression extends BooleanExpression {
3636
this(new FunctionExpression(name, java.util.Arrays.asList(params)));
3737
}
3838

39+
BooleanFunctionExpression(
40+
String name, List<? extends Expression> params, java.util.Map<String, Value> options) {
41+
this(new FunctionExpression(name, params, options));
42+
}
43+
3944
@Override
4045
Value toProto() {
4146
return expr.toProto();

google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/expressions/Expression.java

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public static Expression constant(Timestamp value) {
120120
*/
121121
@BetaApi
122122
public static BooleanExpression constant(Boolean value) {
123-
return equal(new Constant(value), true);
123+
return new BooleanConstant(new Constant(value));
124124
}
125125

126126
/**
@@ -3390,6 +3390,155 @@ public static BooleanExpression isError(Expression expr) {
33903390
return new BooleanFunctionExpression("is_error", expr);
33913391
}
33923392

3393+
/**
3394+
* Evaluates to the distance in meters between the location in the specified field and the query
3395+
* location.
3396+
*
3397+
* <p>This Expression can only be used within a {@code Search} stage.
3398+
*
3399+
* @param fieldName Specifies the field in the document which contains the first {@link GeoPoint}
3400+
* for distance computation.
3401+
* @param location Compute distance to this {@link GeoPoint}.
3402+
* @return A new {@link Expression} representing the geoDistance operation.
3403+
*/
3404+
@BetaApi
3405+
public static Expression geoDistance(String fieldName, GeoPoint location) {
3406+
return geoDistance(field(fieldName), location);
3407+
}
3408+
3409+
/**
3410+
* Evaluates to the distance in meters between the location in the specified field and the query
3411+
* location.
3412+
*
3413+
* <p>This Expression can only be used within a {@code Search} stage.
3414+
*
3415+
* @param field Specifies the field in the document which contains the first {@link GeoPoint} for
3416+
* distance computation.
3417+
* @param location Compute distance to this {@link GeoPoint}.
3418+
* @return A new {@link Expression} representing the geoDistance operation.
3419+
*/
3420+
@BetaApi
3421+
public static Expression geoDistance(Field field, GeoPoint location) {
3422+
return new FunctionExpression("geo_distance", java.util.Arrays.asList(field, constant(location)));
3423+
}
3424+
3425+
/**
3426+
* Perform a full-text search on all indexed search fields in the document.
3427+
*
3428+
* <p>This Expression can only be used within a {@code Search} stage.
3429+
*
3430+
* @param rquery Define the search query using the search DTS.
3431+
* @return A new {@link BooleanExpression} representing the documentMatches operation.
3432+
*/
3433+
@BetaApi
3434+
public static BooleanExpression documentMatches(String rquery) {
3435+
return new BooleanFunctionExpression("document_matches", constant(rquery));
3436+
}
3437+
3438+
/**
3439+
* Perform a full-text search on the specified field.
3440+
*
3441+
* <p>This Expression can only be used within a {@code Search} stage.
3442+
*
3443+
* @param fieldName Perform search on this field.
3444+
* @param rquery Define the search query using the rquery DTS.
3445+
*/
3446+
@InternalApi
3447+
static BooleanExpression matches(String fieldName, String rquery) {
3448+
return matches(field(fieldName), rquery);
3449+
}
3450+
3451+
/**
3452+
* Perform a full-text search on the specified field.
3453+
*
3454+
* <p>This Expression can only be used within a {@code Search} stage.
3455+
*
3456+
* @param field Perform search on this field.
3457+
* @param rquery Define the search query using the rquery DTS.
3458+
*/
3459+
@InternalApi
3460+
static BooleanExpression matches(Field field, String rquery) {
3461+
return new BooleanFunctionExpression("matches", field, constant(rquery));
3462+
}
3463+
3464+
/**
3465+
* Evaluates to the search score that reflects the topicality of the document to all of the text
3466+
* predicates ({@code matches} and {@code documentMatches}) in the search query.
3467+
*
3468+
* <p>This Expression can only be used within a {@code Search} stage.
3469+
*
3470+
* @return A new {@link Expression} representing the score operation.
3471+
*/
3472+
@BetaApi
3473+
public static Expression score() {
3474+
return new FunctionExpression("score", com.google.common.collect.ImmutableList.of());
3475+
}
3476+
3477+
/**
3478+
* Evaluates to an HTML-formatted text snippet that highlights terms matching the search query in
3479+
* {@code <b>bold</b>}.
3480+
*
3481+
* <p>This Expression can only be used within a {@code Search} stage.
3482+
*
3483+
* @param fieldName Search the specified field for matching terms.
3484+
* @param rquery Define the search query using the search DTS.
3485+
* @return A new {@link Expression} representing the snippet operation.
3486+
*/
3487+
@BetaApi
3488+
public static Expression snippet(String fieldName, String rquery) {
3489+
return new FunctionExpression(
3490+
"snippet", java.util.Arrays.asList(field(fieldName), constant(rquery)));
3491+
}
3492+
3493+
/**
3494+
* Evaluates to an HTML-formatted text snippet that highlights terms matching the search query in
3495+
* {@code <b>bold</b>}.
3496+
*
3497+
* <p>This Expression can only be used within a {@code Search} stage.
3498+
*
3499+
* @param rquery Define the search query using the search DTS.
3500+
* @return A new {@link Expression} representing the snippet operation.
3501+
*/
3502+
@BetaApi
3503+
public final Expression snippet(String rquery) {
3504+
return new FunctionExpression(
3505+
"snippet",
3506+
java.util.Arrays.asList(this, constant(rquery)),
3507+
java.util.Collections.singletonMap(
3508+
"query", com.google.cloud.firestore.PipelineUtils.encodeValue(rquery)));
3509+
}
3510+
3511+
@InternalApi
3512+
static BooleanExpression between(String fieldName, Expression lowerBound, Expression upperBound) {
3513+
return between(field(fieldName), lowerBound, upperBound);
3514+
}
3515+
3516+
@InternalApi
3517+
static BooleanExpression between(String fieldName, Object lowerBound, Object upperBound) {
3518+
return between(fieldName, toExprOrConstant(lowerBound), toExprOrConstant(upperBound));
3519+
}
3520+
3521+
@InternalApi
3522+
static BooleanExpression between(
3523+
Expression expression, Expression lowerBound, Expression upperBound) {
3524+
return new BooleanFunctionExpression("between", expression, lowerBound, upperBound);
3525+
}
3526+
3527+
@InternalApi
3528+
static BooleanExpression between(Expression expression, Object lowerBound, Object upperBound) {
3529+
return between(expression, toExprOrConstant(lowerBound), toExprOrConstant(upperBound));
3530+
}
3531+
3532+
@InternalApi
3533+
public final BooleanExpression between(Expression lowerBound, Expression upperBound) {
3534+
return Expression.between(this, lowerBound, upperBound);
3535+
}
3536+
3537+
@InternalApi
3538+
public final BooleanExpression between(Object lowerBound, Object upperBound) {
3539+
return Expression.between(this, lowerBound, upperBound);
3540+
}
3541+
33933542
// Other Utility Functions
33943543
/**
33953544
* Creates an expression that returns the document ID from a path.

google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/expressions/Field.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,32 @@ public Value toProto() {
9090
return Value.newBuilder().setFieldReferenceValue(path.toString()).build();
9191
}
9292

93+
/**
94+
* Evaluates to the distance in meters between the location specified by this field and the query
95+
* location.
96+
*
97+
* <p>This Expression can only be used within a {@code Search} stage.
98+
*
99+
* @param location Compute distance to this {@link com.google.cloud.firestore.GeoPoint}.
100+
* @return A new {@link Expression} representing the geoDistance operation.
101+
*/
102+
@BetaApi
103+
public Expression geoDistance(com.google.cloud.firestore.GeoPoint location) {
104+
return Expression.geoDistance(this, location);
105+
}
106+
107+
/**
108+
* Perform a full-text search on this field.
109+
*
110+
* <p>This Expression can only be used within a {@code Search} stage.
111+
*
112+
* @param rquery Define the search query using the rquery DTS.
113+
*/
114+
@InternalApi
115+
BooleanExpression matches(String rquery) {
116+
return Expression.matches(this, rquery);
117+
}
118+
93119
@Override
94120
public boolean equals(Object o) {
95121
if (this == o) {

google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/expressions/FunctionExpression.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,17 @@
2828
public class FunctionExpression extends Expression {
2929
private final String name;
3030
private final List<Expression> params;
31+
private final java.util.Map<String, Value> options;
3132

3233
FunctionExpression(String name, List<? extends Expression> params) {
34+
this(name, params, java.util.Collections.emptyMap());
35+
}
36+
37+
FunctionExpression(
38+
String name, List<? extends Expression> params, java.util.Map<String, Value> options) {
3339
this.name = name;
34-
this.params = Collections.unmodifiableList(params);
40+
this.params = java.util.Collections.unmodifiableList(params);
41+
this.options = java.util.Collections.unmodifiableMap(options);
3542
}
3643

3744
@InternalApi
@@ -44,7 +51,8 @@ Value toProto() {
4451
.addAllArgs(
4552
this.params.stream()
4653
.map(FunctionUtils::exprToValue)
47-
.collect(Collectors.toList())))
54+
.collect(Collectors.toList()))
55+
.putAllOptions(this.options))
4856
.build();
4957
}
5058

@@ -53,11 +61,13 @@ public boolean equals(Object o) {
5361
if (this == o) return true;
5462
if (o == null || getClass() != o.getClass()) return false;
5563
FunctionExpression that = (FunctionExpression) o;
56-
return Objects.equal(name, that.name) && Objects.equal(params, that.params);
64+
return java.util.Objects.equals(name, that.name)
65+
&& java.util.Objects.equals(params, that.params)
66+
&& java.util.Objects.equals(options, that.options);
5767
}
5868

5969
@Override
6070
public int hashCode() {
61-
return Objects.hashCode(name, params);
71+
return java.util.Objects.hash(name, params, options);
6272
}
6373
}

google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/expressions/Selectable.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,24 @@
1919
import com.google.api.core.BetaApi;
2020

2121
@BetaApi
22-
public interface Selectable {}
22+
public interface Selectable {
23+
/**
24+
* Converts an object to a {@link Selectable}.
25+
*
26+
* @param o The object to convert. Supported types: {@link Selectable}, {@link String}, {@link
27+
* com.google.cloud.firestore.FieldPath}.
28+
* @return The converted {@link Selectable}.
29+
*/
30+
@com.google.api.core.InternalApi
31+
static Selectable toSelectable(Object o) {
32+
if (o instanceof Selectable) {
33+
return (Selectable) o;
34+
} else if (o instanceof String) {
35+
return Expression.field((String) o);
36+
} else if (o instanceof com.google.cloud.firestore.FieldPath) {
37+
return Expression.field((com.google.cloud.firestore.FieldPath) o);
38+
} else {
39+
throw new IllegalArgumentException("Unknown Selectable type: " + o.getClass().getName());
40+
}
41+
}
42+
}

0 commit comments

Comments
 (0)