vespa
vespa copied to clipboard
invalid json rendering for results with -inf relevance score
-Infinity is not a valid number in json. Rendering it as 0.0 was reverted after a short thinking period. Maybe we can use 'null'?
make test
current behavior (via jackson's writeNumberField) is to render -inf as a string "-Infinity", which is legal JSON but not a number (as most users would expect).
I suggest rendering a number which is easy for a human to spot as an "invalid marker" (-1.0 or -999999999 for example). 0.0 is a valid (and common) actual relevance number so we should probably not use that.
Seems like using null
is used for both NaN and +/-Infinity when emitting JSON in JavaScript: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
I've also seen suggestions of using +/-1e9999 or similar massive exponents that will (most likely) implicitly roll over to infinity by the receiving parser. Pros: it's still a number. Cons: kinda smelly.
extra con for -1e9999: if the parser is try-hard you could end up with expensive handling of NaN/Inf values (BigDecimal and friends)
I think we should keep current behavior for now.
Fixing this specifically for relevance is easy, diff below. But we should probably wait until Vespa 8, or some customer really needs this fix. Since it's jackson that produces current behavior, it's possible/ that JSON consumers already has handling of it (or is willing to add that). If we make the change, we should also make sure to do it when rendering rank features and other structured data where doubles with possible overflows are rendered to JSON.
diff --git a/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java b/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java
index 31f8194..1e4233c 100644
--- a/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java
+++ b/container-search/src/main/java/com/yahoo/search/rendering/JsonRenderer.java
@@ -329,7 +329,13 @@ public class JsonRenderer extends AsynchronousSectionedRenderer<Result> {
if (id != null)
generator.writeStringField(ID, id);
- generator.writeNumberField(RELEVANCE, hit.getRelevance().getScore());
+ generator.writeFieldName(RELEVANCE);
+ double rv = hit.getRelevance().getScore();
+ if (Double.isNaN(rv) || Double.isInfinite(rv)) {
+ generator.writeNull();
+ } else {
+ generator.writeNumber(rv);
+ }
if (hit.types().size() > 0) {
generator.writeArrayFieldStart(TYPES);