evaluateMessage(@Nullable FDBReco
}
}
+ /**
+ * Evaluates the case where no value can be extracted for this field. This method is called from
+ * {@link #evaluateMessage} when either the input {@code message} is {@code null}, or the field is absent on the
+ * message and its {@link #nullStandin} is not {@code NOT_NULL} (i.e., the type-default substitution does not apply).
+ *
+ * The result depends on the fan type and the null standin: {@code FanOut} emits no entries, {@code Concatenate}
+ * and {@code None} emit a single entry carrying either the {@code NullStandin} (indexable null) or, for
+ * {@code Concatenate} with a {@code NOT_NULL} standin, an empty list (the proto default for a repeated field).
+ */
private List getNullResult() {
- // As opposed to default value, in order to get indexable NULL.
switch (fanType) {
case FanOut:
return Collections.emptyList();
case Concatenate:
- return Collections.singletonList(Key.Evaluated.scalar(Collections.emptyList()));
+ Key.Evaluated result = (nullStandin == Key.Evaluated.NullStandin.NOT_NULL)
+ ? Key.Evaluated.scalar(Collections.emptyList())
+ : Key.Evaluated.scalar(nullStandin);
+ return Collections.singletonList(result);
case None:
return Collections.singletonList(Key.Evaluated.scalar(nullStandin));
default:
diff --git a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/metadata/KeyExpressionTest.java b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/metadata/KeyExpressionTest.java
index 1fe4c2dd5f..c6f1f50f9a 100644
--- a/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/metadata/KeyExpressionTest.java
+++ b/fdb-record-layer-core/src/test/java/com/apple/foundationdb/record/metadata/KeyExpressionTest.java
@@ -334,9 +334,11 @@ void testConcatenateSingleRepeatedField() {
assertFalse(expression.createsDuplicates());
assertEquals(Collections.singletonList(scalar(Arrays.asList("Boxes", "Bowls"))),
evaluate(expression, plantsBoxesAndBowls));
+ // `repeat_me` has 0 repetitions: Yields the empty list.
assertEquals(Collections.singletonList(scalar(Collections.emptyList())),
evaluate(expression, emptyScalar));
- assertEquals(Collections.singletonList(scalar(Collections.emptyList())),
+ // Null record: Propagates the field’s NullStandin (default NULL).
+ assertEquals(Collections.singletonList(scalar(NULL)),
evaluate(expression, null));
}
@@ -348,9 +350,11 @@ void testFieldThenConcatenateRepeated() {
assertFalse(expression.createsDuplicates());
assertEquals(Collections.singletonList(Key.Evaluated.concatenate("Plants", Arrays.asList("Boxes", "Bowls"))),
evaluate(expression, plantsBoxesAndBowls));
+ // Both fields unset: The scalar `field` yields the NULL standin; concatenate yields the empty list.
assertEquals(Collections.singletonList(Key.Evaluated.concatenate(NULL, Collections.emptyList())),
evaluate(expression, emptyScalar));
- assertEquals(Collections.singletonList(Key.Evaluated.concatenate(NULL, Collections.emptyList())),
+ // Null record: Both parts propagate their null standin (default NULL) and yield NULL.
+ assertEquals(Collections.singletonList(Key.Evaluated.concatenate(NULL, NULL)),
evaluate(expression, null));
}
@@ -504,11 +508,14 @@ void testNestedThenRepeatsConcatenated() {
assertFalse(expression.createsDuplicates());
assertEquals(Collections.singletonList(scalar(Arrays.asList("lily", "rose"))),
evaluate(expression, matryoshkaDolls));
- assertEquals(Collections.singletonList(scalar(Collections.emptyList())),
+ // `nesty` absent: Concatenate propagates its NULL standin and yields NULL.
+ assertEquals(Collections.singletonList(scalar(NULL)),
evaluate(expression, emptyNested));
+ // `nesty` is present but `repeated_field` has 0 repetitions: Yields the empty-list result.
assertEquals(Collections.singletonList(scalar(Collections.emptyList())),
evaluate(expression, lonelyDoll));
- assertEquals(Collections.singletonList(scalar(Collections.emptyList())),
+ // Null record: Concatenate propagates its NULL standin and yields NULL.
+ assertEquals(Collections.singletonList(scalar(NULL)),
evaluate(expression, null));
}