-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix : Add support for contains key and like operator #125
Conversation
Codecov Report
@@ Coverage Diff @@
## main #125 +/- ##
============================================
+ Coverage 81.06% 81.16% +0.09%
Complexity 568 568
============================================
Files 53 53
Lines 2192 2193 +1
Branches 235 235
============================================
+ Hits 1777 1780 +3
+ Misses 321 318 -3
- Partials 94 95 +1
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
...l/src/main/java/org/hypertrace/core/query/service/pinot/QueryRequestToPinotSQLConverter.java
Outdated
Show resolved
Hide resolved
&& value.getValue().getStringArrayCount() == 2) { | ||
literals.set(0, value.getValue().getStringArray(0)); | ||
literals.set(1, value.getValue().getStringArray(1)); | ||
} else if (value.getValue().getValueType() == ValueType.STRING) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So support for a string arg is the only functional change in this PR, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly nit changes till now. I thought like and contains_key will not work but with the new changes, they were working so just added unit tests for them till now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved
private LiteralConstant[] convertExpressionToMapLiterals(Expression expression) { | ||
LiteralConstant[] literals = new LiteralConstant[2]; | ||
private List<LiteralConstant> convertExpressionToMapLiterals(Expression expression) { | ||
List<String> literals = new ArrayList<>(List.of("", "")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're going to clean up the implementation, let's do it right and return an object rather than a list.
@Value
class MapKeyValueArguments {
LiteralConstant key;
Optional<LiteralConstant> value;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can Map.Entry<LiteralConstant, Optional<LiteralConstant>>
be used as tuple?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit on the fence. I probably wouldn't do it, but I wouldn't call it out on review. In general, we'd rather create a new class than use an Entry or Pair class, but in this case it's reasonable to use Entry since we're representing a key and value (Pair pretty much never makes sense, because left and right have no meaning). The reason I'd still lean against it is that we're kind of mixing a structural paradigm with an object-oriented one. What we're producing here "is a" map entry, but it would never be used in a map - we're just using an existing unrelated class because it has the structure we're looking for.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the key is an index like 0 or 1, then why do we need it to be a literalConstant ? Then at the places where we access using 0/1, we would need to create a literalConstant first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sarthak77 I didn't fully understand - Then at the places where we access using 0/1, we would need to create a literalConstant first
.
But, the proposal of https://github.com/hypertrace/query-service/pull/125/files#r769725462, makes it more readable and clear the explicit indexing based usage - x[0] or x.get(0).
Note: This will change a bit once you add support for handling attribute expression
in all the places. E.g the function convertExpressionToMapLiterals
will need lhs
expression get the key.
But, if we are modifying from the existing way, I would also suggest having a readable structure the way @aaron-steinfeld has suggested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then also it will still have rhs right ?
Like we can say Span.tags CONTAINS_KEY 'flags' or lhs CONTAINS_KEY rhs
Yes, you are right. Rhs is a key in map. I got confused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does the int key value be used? How will we get LiteralConsant, will we still have List? Based on aaron's suggestion, you can do something as below. And, you can access key literal constant directly from the object. e.g builder.append(convertLiteralToString(mapKeyValueArguments.getKey(), paramsBuilder));
Ok got it now. Earlier I thought 0 is key. But you are proposing that key should be Span.tags and value should be say span.kind.
For a map, there are two arguments one is key and the other is value - e.g x['key'] = 'value'
So, here Span.tags
is a map, the span.kind
is a key arg.
In the case of CONTAINS_KEY
and NOT_CONTAINS_KEY
, it will have only one key
arg. But, in the case of CONTAINS_KEY_VALUE
, it will have both key
and value
args, and so value
arg is optional in structure.
e.g Span.tags['span.kind'] = 'client'
, for this key
will be span.kind
and value
will be client
Should I make this change then ?
Yes, we can do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, we can wait then drop this whole thing. Assuming we deprecate CONTAINS_KEY_VALUE and remove the array around the contains_key arg, this whole function can just go away I think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. We can simplify that and move inline maybe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved
…ontains_key_support
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
private LiteralConstant[] convertExpressionToMapLiterals(Expression expression) { | ||
LiteralConstant[] literals = new LiteralConstant[2]; | ||
private List<LiteralConstant> convertExpressionToMapLiterals(Expression expression) { | ||
List<String> literals = new ArrayList<>(List.of("", "")); | ||
if (expression.getValueCase() == LITERAL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, do we throw an exception here if rhs is not literal ? As using the below design, we always expect the key. Also, we are always calling this method from such a case so it may prevent any error if some new case is added.
@Value class MapKeyValueArguments { LiteralConstant key; Optional<LiteralConstant> value; }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the existing main
code, we are defaulting to empty string ""
if the case is not LITERAL
. Here as well default to ""
string LiteralConstant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought optional was to deal with this default ""
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional value means that the map's value argument is not mandatory. Refer to this - #125 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, in a way, we can also extend as you suggested that we expect the key
argument should be present. if it's not there, we can throw the exception. And for the value, we can default to ""
Does the below make sense?
spanTags contains_key ''
List<LiteralConstant> kvp = convertExpressionToMapLiterals(filter.getRhs()); | ||
builder.append(convertExpressionToMapKeyColumn(filter.getLhs())); | ||
builder.append(" != "); | ||
builder.append(convertLiteralToString(kvp.get(MAP_KEY_INDEX), paramsBuilder)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CONTAINS_KEY and NOT_CONTAINS_KEY can really just have the same logic - assuming we translate the operators correctly in convertOperatorToString
(they're not currently, have a separate comment on that), the only difference between these and the default case is the conversion of the LHS (using convertExpressionToMapKeyColumn
instead of convertExpressionToString
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved
@@ -250,6 +257,7 @@ private String convertOperatorToString(Operator operator) { | |||
return "<="; | |||
case LIKE: | |||
return REGEX_OPERATOR; | |||
case NOT_CONTAINS_KEY: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These cases aren't right. It seems like they just got parked here so as not to throw since they're not used in the existing code and this is called prematurely. NOT_CONTAINS_KEY becomes !=
and CONTAINS_KEY becomes =
(the only difference being that we change the col translation to add on __keys). CONTAINS_KEYVALUE becomes multiple expressions, so could remain until removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved
This comment has been minimized.
This comment has been minimized.
} | ||
if (value.getValue().getValueType() == ValueType.STRING_ARRAY | ||
&& value.getValue().getStringArrayCount() == 2) { | ||
literals.set(0, value.getValue().getStringArray(0)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: if we're going to keep it unstructured and use a list, might as well use the same index constants to write this as we use to read it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't get you on this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We read these as via .get(MAP_KEY_INDEX)
etc., but we write them with plain literals that need to match those constants. Here, we should be using
literals.set(MAP_KEY_INDEX, value.getValue().getStringArray(MAP_KEY_INDEX));
Or better yet, using no constants and just use an object like we do in most java code as discussed earlier.
All relatively moot though as long as this function is slated for deletion in the near term.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok got it.
Ref issue : #122 , #109