Repository navigation
Exposing optional replaceMissingValueWith in lookup function and macros - #14956
Conversation
6602841 to
ff8b736
Compare
ff8b736 to
d39cd68
Compare
| FROM sales | ||
| GROUP BY 1 | ||
| ``` | ||
| Lookup function also accepts the 3rd argument called `$replaceMissingValueWith` as constant string, if you value is missing given lookups for queried key then lookup function return result value from `replaceMissingValueWith` |
There was a problem hiding this comment.
nit. The lookup function also accepts the 3rd argument called $replaceMissingValueWith as a constant string. If your value is missing a lookup for the queried key, the lookup function returns the result value from replaceMissingValueWith
| if (args.size() > 2) { | ||
| final Expr missingValExpr = args.get(2); | ||
| if (missingValExpr.isLiteral()) { | ||
| return missingValExpr.getLiteralValue().toString(); |
There was a problem hiding this comment.
Q. when used in sqlCompatibleMode if there is a value like empty string should it be converted to null ?
There was a problem hiding this comment.
Nope, regardless of the sqlCompatibleMode, replaceMissingValueWith is literal value passed by user and feature expect to replace it as it is.
72624ed to
0eb390c
Compare
| public Expr apply(final List<Expr> args) | ||
| { | ||
| validationHelperCheckArgumentCount(args, 2); | ||
| validationHelperCheckMinArgumentCount(args, 2); |
There was a problem hiding this comment.
this should use maybe validationHelperCheckArgumentRange since it requires 2 or 3 arguments exactly
| return missingValExpr.getLiteralValue().toString(); | ||
| } | ||
| } | ||
| return null; |
There was a problem hiding this comment.
I think if you are not going to handle a dynamic argument for the 'missing value' parameter, this should be an exception if the argument is not a literal. There is validationHelperCheckArgIsLiteral that can be called if we only want to support this argument being a constant.
Silently returning null seems incorrect
There was a problem hiding this comment.
added validationHelperCheckArgIsLiteral check
| The lookup function also accepts the 3rd argument called `replaceMissingValueWith` as a constant string. If your value is missing a lookup for the queried key, the lookup function returns the result value from `replaceMissingValueWith` | ||
| For example: | ||
| ``` | ||
| LOOKUP(store, 'store_to_country', 'NA') | ||
| ``` | ||
| If value is missing from `store_to_country` lookup for given key 'store' then it will return `NA`. |
There was a problem hiding this comment.
you should probably indicate that this optional 3rd argument must be a constant.
math-expr.md, sql-functions.md, and sql-scalar.md should also be updated since they all document this function signature
c5ab145 to
d112231
Compare
| @Test | ||
| public void testTooFewArgs() | ||
| { | ||
| expectException(IllegalArgumentException.class, "Function[lookup] requires 2 to 3 arguments"); |
There was a problem hiding this comment.
i suppose if the range is 1 we could change this messaging to be like 2 or 3 but we don't have to worry about that in this PR
There was a problem hiding this comment.
I think Range 1, will also throw the same message.
| if (args.size() > 2) { | ||
| final Expr missingValExpr = args.get(2); | ||
| validationHelperCheckArgIsLiteral(missingValExpr, "third argument"); | ||
| return String.valueOf(missingValExpr.getLiteralValue()); |
There was a problem hiding this comment.
hmm, sorry I didn't notice this earlier, I think this should use Evals.asString so that if the value is null it doesn't become "null" as in https://github.com/apache/druid/pull/14956/files#diff-989c5e92f71c63c48629a62588a78bec198184ff896791058509176dffe91338R54
There was a problem hiding this comment.
fixed, retained the original expression here. also checking literal in macro and added test to make sure that null literalValue is passed correctly same as native layer.
| if (missingValExpr.isLiteral()) { | ||
| return missingValExpr.getLiteralValue().toString(); | ||
| } |
There was a problem hiding this comment.
this should maybe have a similar validation to the native layer to make sure that the argument doesn't silently become null in the event that the expression is null
There was a problem hiding this comment.
Added a test in CalciteQueryTest to make sure that null is wired up correctly to native extraction fn.
modified the returned value to(String) missingValExpr.getLiteralValue()
| if (inputExpressions.size() > 2) { | ||
| final Expr missingValExpr = plannerContext.parseExpression(inputExpressions.get(2).getExpression()); | ||
| if (missingValExpr.isLiteral()) { | ||
| return missingValExpr.getLiteralValue().toString(); |
There was a problem hiding this comment.
can this argument be null? (add a test with the argument as NULL if it parses correctly, ignore if not)
There was a problem hiding this comment.
yes it can be null, newly added test validates the correct behavior.
Thank you
| SqlTypeFamily.CHARACTER, | ||
| SqlTypeFamily.CHARACTER | ||
| ) | ||
| .requiredOperandCount(2) |
There was a problem hiding this comment.
Since the lookup expr only supports a literal "missing value replacement", this should include .literalOperands(2) (meaning operand 2 must be a literal).
| |`CHARACTER_LENGTH(expr)`|Alias for `LENGTH`.| | ||
| |`STRLEN(expr)`|Alias for `LENGTH`.| | ||
| |`LOOKUP(expr, lookupName)`|Look up `expr` in a registered [query-time lookup table](lookups.md). Note that lookups can also be queried directly using the [`lookup` schema](sql.md#from).| | ||
| |`LOOKUP(expr, lookupName, [replaceMissingValueWith])`|Look up `expr` in a registered [query-time lookup table](lookups.md). Note that lookups can also be queried directly using the [`lookup` schema](sql.md#from).| |
There was a problem hiding this comment.
Update the text to say what replaceMissingValueWith does, and to point out that it must be a constant.
| FN_NAME, | ||
| arg.stringify(), | ||
| lookupExpr.stringify(), | ||
| replaceMissingValueWith |
There was a problem hiding this comment.
This doesn't handle escaping properly. replaceMissingValueWith might contain ' itself, or newlines, etc.
The simplest way to fix this is to keep the expr around and stringify() it here, like we do with lookupExpr (which is also required to be a literal).
d112231 to
3fbd640
Compare
3fbd640 to
bd0cac9
Compare
| |format|format(pattern[, args...]) returns a string formatted in the manner of Java's [String.format](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#format-java.lang.String-java.lang.Object...-).| | ||
| |like|like(expr, pattern[, escape]) is equivalent to SQL `expr LIKE pattern`| | ||
| |lookup|lookup(expr, lookup-name) looks up expr in a registered [query-time lookup](../querying/lookups.md)| | ||
| |lookup|lookup(expr, lookup-name[,replaceMissingValueWith]) looks up expr in a registered [query-time lookup](../querying/lookups.md)| |
There was a problem hiding this comment.
nit: should probably specify replaceMissingValueWith must be a constant
| false, | ||
| null, | ||
| replaceMissingValueWith != null && replaceMissingValueWith.isLiteral() | ||
| ? (String) replaceMissingValueWith.getLiteralValue() |
There was a problem hiding this comment.
I suppose since other types can also be literals it might be safer to use Evals.asString instead of casting
| if (inputExpressions.size() > 2) { | ||
| final Expr missingValExpr = plannerContext.parseExpression(inputExpressions.get(2).getExpression()); | ||
| if (missingValExpr.isLiteral()) { | ||
| return (String) missingValExpr.getLiteralValue(); |
There was a problem hiding this comment.
otoh, this one is probably fine as is and doesn't need to use Evals.asString like the native layer should since the operand type checker specifies the 3rd argument as character type, though it would also probably be harmless
| .build() | ||
| ), | ||
| ImmutableList.of( | ||
| new Object[]{"Missing_Value", NullHandling.sqlCompatible() ? null : "", 5L}, |
There was a problem hiding this comment.
nit: can use NullHandling.defaultStringValue()
Co-authored-by: Clint Wylie <cjwylie@gmail.com>
Co-authored-by: Clint Wylie <cjwylie@gmail.com>
…os (apache#14956) * Exposing optional replaceMissingValueWith in lookup function and macros * args range validation * Updating docs * Addressing comments * Update docs/querying/sql-scalar.md Co-authored-by: Clint Wylie <cjwylie@gmail.com> * Update docs/querying/sql-functions.md Co-authored-by: Clint Wylie <cjwylie@gmail.com> * Addressing comments --------- Co-authored-by: Clint Wylie <cjwylie@gmail.com>
…os (apache#14956) * Exposing optional replaceMissingValueWith in lookup function and macros * args range validation * Updating docs * Addressing comments * Update docs/querying/sql-scalar.md Co-authored-by: Clint Wylie <cjwylie@gmail.com> * Update docs/querying/sql-functions.md Co-authored-by: Clint Wylie <cjwylie@gmail.com> * Addressing comments --------- Co-authored-by: Clint Wylie <cjwylie@gmail.com>
Description
Adding the optional 3rd argument replaceMissingValueWith to lookup extractor function that replaces the missing value from given lookups. Default value for replaceMissingValueWith is null. It is already supported in native layer. Just exposing this value to populated in SQL layer as well. On same line, updated the lookup macros to support replaceMissingValueWith.
Release note
Lookup function also accepts the 3rd argument called '$replaceMissingValueWith' as constant string, if you value is missing given lookups for queried key then lookup function return result value from replaceMissingValueWith eg:
LOOKUP(store, 'store_to_country', 'NA')
If value is missing from store_to_country lookup for given key 'store' then it will return 'NA'.
This PR has: