Skip to content
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

Merged
merged 6 commits into from
Dec 17, 2021

Conversation

sarthak77
Copy link
Member

@sarthak77 sarthak77 commented Dec 15, 2021

Ref issue : #122 , #109

@codecov
Copy link

codecov bot commented Dec 15, 2021

Codecov Report

Merging #125 (de8f2ce) into main (392d36b) will increase coverage by 0.09%.
The diff coverage is 92.85%.

Impacted file tree graph

@@             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     
Flag Coverage Δ
integration 81.16% <92.85%> (+0.09%) ⬆️
unit 79.03% <92.85%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
...service/pinot/QueryRequestToPinotSQLConverter.java 91.16% <91.30%> (+0.07%) ⬆️
...ypertrace/core/query/service/QueryRequestUtil.java 71.42% <100.00%> (-0.45%) ⬇️
.../query/service/pinot/PinotBasedRequestHandler.java 71.05% <0.00%> (+0.32%) ⬆️
...rtrace/core/query/service/QueryServiceStarter.java 58.33% <0.00%> (+2.77%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 392d36b...de8f2ce. Read the comment docs.

@github-actions

This comment has been minimized.

@sarthak77 sarthak77 changed the title Fix : Add support for contains key operator Fix : Add support for contains key and like operator Dec 15, 2021
@github-actions

This comment has been minimized.

&& 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) {
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Member Author

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("", ""));
Copy link
Contributor

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;
}

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?

Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor

@kotharironak kotharironak Dec 16, 2021

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.

Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved

@github-actions

This comment has been minimized.

@github-actions

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) {
Copy link
Member Author

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; }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

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.

Copy link
Member Author

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 ""

Copy link
Contributor

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)

Copy link
Contributor

@kotharironak kotharironak Dec 16, 2021

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));
Copy link
Contributor

@aaron-steinfeld aaron-steinfeld Dec 16, 2021

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)

Copy link
Member Author

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:
Copy link
Contributor

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved

@github-actions

This comment has been minimized.

}
if (value.getValue().getValueType() == ValueType.STRING_ARRAY
&& value.getValue().getStringArrayCount() == 2) {
literals.set(0, value.getValue().getStringArray(0));
Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok got it.

@sarthak77 sarthak77 merged commit 85bcb31 into main Dec 17, 2021
@sarthak77 sarthak77 deleted the fix_contains_key_support branch December 17, 2021 03:50
@github-actions
Copy link

Unit Test Results

  30 files  ±0    30 suites  ±0   9s ⏱️ ±0s
174 tests +3  174 ✔️ +3  0 💤 ±0  0 ❌ ±0 

Results for commit 85bcb31. ± Comparison against base commit 392d36b.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants