diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/expressions/Expression.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/expressions/Expression.java index 695051c20..2c512bd65 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/expressions/Expression.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/pipeline/expressions/Expression.java @@ -3583,6 +3583,41 @@ public static Expression type(String fieldName) { return type(field(fieldName)); } + /** + * Creates an expression that checks if the result of an expression is of the given type. + * + *

Supported values for {@code type} are: "null", "array", "boolean", "bytes", "timestamp", + * "geo_point", "number", "int32", "int64", "float64", "decimal128", "map", "reference", "string", + * "vector", "max_key", "min_key", "object_id", "regex", and "request_timestamp". + * + * @param expr The expression to check the type of. + * @param type The type to check for. + * @return A new {@link BooleanExpression} that evaluates to true if the expression's result is of + * the given type, false otherwise. + */ + @BetaApi + public static BooleanExpression isType(Expression expr, String type) { + return new BooleanFunctionExpression("is_type", ImmutableList.of(expr, constant(type))); + } + + /** + * Creates an expression that checks if the value of a field is of the given type. + * + *

Supported values for {@code type} are: "null", "array", "boolean", "bytes", "timestamp", + * "geo_point", "number", "int32", "int64", "float64", "decimal128", "map", "reference", "string", + * "vector", "max_key", "min_key", "object_id", "regex", and "request_timestamp". + * + * @param fieldName The name of the field to check the type of. + * @param type The type to check for. + * @return A new {@link BooleanExpression} that evaluates to true if the expression's result is of + * the given type, false otherwise. + */ + @BetaApi + public static BooleanExpression isType(String fieldName, String type) { + return new BooleanFunctionExpression( + "is_type", ImmutableList.of(field(fieldName), constant(type))); + } + // Numeric Operations /** * Creates an expression that rounds {@code numericExpr} to nearest integer. @@ -5629,4 +5664,20 @@ public final Expression collectionId() { public final Expression type() { return type(this); } + + /** + * Creates an expression that checks if the result of this expression is of the given type. + * + *

Supported values for {@code type} are: "null", "array", "boolean", "bytes", "timestamp", + * "geo_point", "number", "int32", "int64", "float64", "decimal128", "map", "reference", "string", + * "vector", "max_key", "min_key", "object_id", "regex", and "request_timestamp". + * + * @param type The type to check for. + * @return A new {@link BooleanExpression} that evaluates to true if the expression's result is of + * the given type, false otherwise. + */ + @BetaApi + public final BooleanExpression isType(String type) { + return isType(this, type); + } } 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 e14fa8a94..acdb68c3f 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 @@ -3500,6 +3500,89 @@ public void testType() throws Exception { "vector")); } + @Test + public void testIsType() throws Exception { + List results = + firestore + .pipeline() + .collection(collection.getPath()) + .replaceWith( + Expression.map( + map( + "int", + 1, + "float", + 1.1, + "str", + "a string", + "bool", + true, + "null", + null, + "geoPoint", + new GeoPoint(0.1, 0.2), + "timestamp", + Timestamp.ofTimeSecondsAndNanos(123456, 0), + "bytes", + com.google.cloud.firestore.Blob.fromBytes(new byte[] {1, 2, 3}), + "docRef", + collection.document("bar"), + "vector", + vector(new double[] {1.0, 2.0, 3.0}), + "map", + Expression.map(map("numberK", 1, "stringK", "a string")), + "array", + array(1, 2, true)))) + .select( + Expression.isType("int", "int64").as("isInt64"), + Expression.isType("int", "number").as("isInt64IsNumber"), + Expression.isType("int", "decimal128").as("isInt64IsDecimal128"), + Expression.isType("float", "float64").as("isFloat64"), + Expression.isType("float", "number").as("isFloat64IsNumber"), + Expression.isType("float", "decimal128").as("isFloat64IsDecimal128"), + Expression.isType("str", "string").as("isStr"), + Expression.isType("str", "int64").as("isStrNum"), + Expression.isType("int", "string").as("isNumStr"), + Expression.isType("bool", "boolean").as("isBool"), + Expression.isType("null", "null").as("isNull"), + Expression.isType("geoPoint", "geo_point").as("isGeoPoint"), + Expression.isType("timestamp", "timestamp").as("isTimestamp"), + Expression.isType("bytes", "bytes").as("isBytes"), + Expression.isType("docRef", "reference").as("isDocRef"), + Expression.isType("vector", "vector").as("isVector"), + Expression.isType("map", "map").as("isMap"), + Expression.isType("array", "array").as("isArray"), + Expression.isType(constant(1), "int64").as("exprIsInt64"), + field("int").isType("int64").as("staticIsInt64")) + .limit(1) + .execute() + .get() + .getResults(); + assertThat(data(results)) + .containsExactly( + map( + "isInt64", true, + "isInt64IsNumber", true, + "isInt64IsDecimal128", false, + "isFloat64", true, + "isFloat64IsNumber", true, + "isFloat64IsDecimal128", false, + "isStr", true, + "isStrNum", false, + "isNumStr", false, + "isBool", true, + "isNull", true, + "isGeoPoint", true, + "isTimestamp", true, + "isBytes", true, + "isDocRef", true, + "isVector", true, + "isMap", true, + "isArray", true, + "exprIsInt64", true, + "staticIsInt64", true)); + } + @Test public void testExplainWithError() { assumeFalse(