Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,100 @@ public static Expression ifAbsent(String ifFieldName, Object elseValue) {
return ifAbsent(field(ifFieldName), toExprOrConstant(elseValue));
}

/**
* Creates an expression that returns a default value if an expression evaluates to null.
*
* <p>Note: This function provides a fallback for both absent and explicit null values. In
* contrast, {@link ifAbsent} only triggers for missing fields.
*
* @param ifExpr The expression to check.
* @param elseExpression The default value.
* @return A new {@link Expression} representing the ifNull operation.
*/
@BetaApi
public static Expression ifNull(Expression ifExpr, Expression elseExpression) {
return new FunctionExpression("if_null", ImmutableList.of(ifExpr, elseExpression));
}

/**
* Creates an expression that returns a default value if an expression evaluates to null.
*
* <p>Note: This function provides a fallback for both absent and explicit null values. In
* contrast, {@link ifAbsent} only triggers for missing fields.
*
* @param ifExpr The expression to check.
* @param elseValue The default value.
* @return A new {@link Expression} representing the ifNull operation.
*/
@BetaApi
public static Expression ifNull(Expression ifExpr, Object elseValue) {
return ifNull(ifExpr, toExprOrConstant(elseValue));
}

/**
* Creates an expression that returns a default value if a field is null.
*
* <p>Note: This function provides a fallback for both absent and explicit null values. In
* contrast, {@link ifAbsent} only triggers for missing fields.
*
* @param ifFieldName The field to check.
* @param elseExpression The default value.
* @return A new {@link Expression} representing the ifNull operation.
*/
@BetaApi
public static Expression ifNull(String ifFieldName, Expression elseExpression) {
return ifNull(field(ifFieldName), elseExpression);
}

/**
* Creates an expression that returns a default value if a field is null.
*
* <p>Note: This function provides a fallback for both absent and explicit null values. In
* contrast, {@link ifAbsent} only triggers for missing fields.
*
* @param ifFieldName The field to check.
* @param elseValue The default value.
* @return A new {@link Expression} representing the ifNull operation.
*/
@BetaApi
public static Expression ifNull(String ifFieldName, Object elseValue) {
return ifNull(field(ifFieldName), toExprOrConstant(elseValue));
}

/**
* Returns the first non-null, non-absent argument, without evaluating the rest of the arguments.
* When all arguments are null or absent, returns the last argument.
*
* @param expression The first expression to check for null.
* @param replacement The fallback expression or value if the first one is null.
* @param others Optional additional expressions to check if previous ones are null.
* @return A new {@link Expression} representing the coalesce operation.
*/
@BetaApi
public static Expression coalesce(Expression expression, Object replacement, Object... others) {
ImmutableList.Builder<Expression> args = ImmutableList.builder();
args.add(expression);
args.add(toExprOrConstant(replacement));
for (Object other : others) {
args.add(toExprOrConstant(other));
}
return new FunctionExpression("coalesce", args.build());
}

/**
* Returns the first non-null, non-absent argument, without evaluating the rest of the arguments.
* When all arguments are null or absent, returns the last argument.
*
* @param firstFieldName The name of the first field to check for null.
* @param replacement The fallback expression or value if the first one is null.
* @param others Optional additional expressions to check if previous ones are null.
* @return A new {@link Expression} representing the coalesce operation.
*/
@BetaApi
public static Expression coalesce(String firstFieldName, Object replacement, Object... others) {
return coalesce(field(firstFieldName), replacement, others);
}

/**
* Creates an expression that joins the elements of an array into a string.
*
Expand Down Expand Up @@ -4206,6 +4300,33 @@ public Expression ifAbsent(Object elseValue) {
return Expression.ifAbsent(this, elseValue);
}

/**
* Creates an expression that returns a default value if this expression evaluates null.
*
* <p>Note: This function provides a fallback for both absent and explicit null values. In
* contrast, {@link ifAbsent} only triggers for missing fields.
*
* @param elseValue The default value.
* @return A new {@link Expression} representing the ifNull operation.
*/
@BetaApi
public Expression ifNull(Object elseValue) {
return Expression.ifNull(this, elseValue);
}

/**
* Returns the first non-null, non-absent argument, without evaluating the rest of the arguments.
* When all arguments are null or absent, returns the last argument.
*
* @param second The next expression or literal to evaluate.
* @param others Additional expressions or literals to evaluate.
* @return A new {@link Expression} representing the coalesce operation.
*/
@BetaApi
public Expression coalesce(Object second, Object... others) {
return Expression.coalesce(this, second, others);
}

/**
* Creates an expression that joins the elements of this array expression into a string.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2662,6 +2662,92 @@ public void testIfAbsent() throws Exception {
assertThat(data(results)).containsExactly(map("res", "Frank Herbert"));
}

@Test
public void testIfNull() throws Exception {
List<PipelineResult> results =
firestore
.pipeline()
.collection(collection.getPath())
.limit(1)
.replaceWith(Expression.map(map("title", "foo", "name", null)))
.select(
Expression.ifNull("title", "default title").as("staticMethod"),
field("title").ifNull("default title").as("instanceMethod"),
field("name").ifNull(field("title")).as("nameOrTitle"),
field("name").ifNull("default name").as("fieldIsNull"),
field("absent").ifNull("default name").as("fieldIsAbsent"))
.execute()
.get()
.getResults();

assertThat(data(results))
.containsExactly(
map(
"staticMethod", "foo",
"instanceMethod", "foo",
"nameOrTitle", "foo",
"fieldIsNull", "default name",
"fieldIsAbsent", "default name"));
}

@Test
public void testCoalesce() throws Exception {
assumeFalse(
"Coalesce is not supported against the emulator.",
isRunningAgainstFirestoreEmulator(firestore));

List<PipelineResult> results =
firestore
.pipeline()
.collection(collection.getPath())
.limit(1)
.replaceWith(
Expression.map(
map(
"numberValue",
1L,
"stringValue",
"hello",
"booleanValue",
false,
"nullValue",
null,
"nullValue2",
null)))
.select(
Expression.coalesce(field("numberValue"), field("stringValue")).as("staticMethod"),
field("numberValue").coalesce(field("stringValue")).as("instanceMethod"),
Expression.coalesce(field("nullValue"), field("stringValue")).as("firstIsNull"),
Expression.coalesce(field("nullValue"), field("nullValue2"), field("booleanValue"))
.as("lastIsNotNull"),
Expression.coalesce(field("nullValue"), field("nullValue2")).as("allFieldsNull"),
Expression.coalesce(field("nullValue"), field("nullValue2"), constant("default"))
.as("allFieldsNullWithDefault"),
Expression.coalesce(field("absentField"), field("numberValue"), constant("default"))
.as("withAbsentField"))
.execute()
.get()
.getResults();

assertThat(data(results))
.containsExactly(
map(
"staticMethod",
1L,
"instanceMethod",
1L,
"firstIsNull",
"hello",
"lastIsNotNull",
false,
"allFieldsNull",
null,
"allFieldsNullWithDefault",
"default",
"withAbsentField",
1L));
}

@Test
public void testJoin() throws Exception {
// Test join with a constant delimiter
Expand Down
Loading