-
Notifications
You must be signed in to change notification settings - Fork 51
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
feat: Filter alias target #3201
feat: Filter alias target #3201
Conversation
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## develop #3201 +/- ##
===========================================
+ Coverage 77.41% 77.57% +0.15%
===========================================
Files 375 375
Lines 34756 34790 +34
===========================================
+ Hits 26906 26986 +80
+ Misses 6237 6203 -34
+ Partials 1613 1601 -12
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 17 files with indirect coverage changes Continue to review full report in Codecov by Sentry.
|
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.
Aggregate targets are not included in this PR as they require more changes.
question: I thought the aggregate filters were of the same type as normal filters, in which case they will now have a (presumably broken) alias
property on them. Have you checked what happens when someone tries to use it, and when do you plan on adding support for it?
praise: Looks really good Keenan, just a couple of localised suggestions for you - will be really good to have this in - we've been talking about it for a long time now lol and the JSON-type solution is really nice IMO :)
@@ -85,6 +85,9 @@ func (k *ObjectProperty) GetProp(data any) any { | |||
if data == nil { | |||
return nil | |||
} | |||
if _, ok := data.(core.Doc); ok { |
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.
question: Why are you checking for this particular type?
suggestion: Depending on the answer to the above question, it might be better to instead check the success of the cast below:
object, ok := data.(map[string]any)
if !ok {
return nil // this can happen when an alias target does not exist
}
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 past reviews you had a preference for specific type checks instead of a single type assertion. I thought I could anticipate this one before review.
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.
My preferences probably vary by context. Here you are casting to the type you actually want the line below, it doesn't seem to make sense to care about some other type as well.
@@ -992,6 +992,12 @@ func resolveInnerFilterDependencies( | |||
newFields := []Requestable{} | |||
|
|||
for key, value := range source { | |||
// alias fields are guarenteed to be resolved | |||
// because they refer to existing fields |
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.
praise: Thanks for this explanation :)
@@ -93,20 +93,20 @@ func parseFilterFieldsForDescriptionMap( | |||
fields := make([]client.FieldDefinition, 0) | |||
for k, v := range conditions { | |||
switch k { | |||
case "_or", "_and": | |||
case request.FilterOpOr, request.FilterOpAnd: |
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.
praise: Thanks for these cleanups :)
}, | ||
testUtils.Request{ | ||
Request: `query { | ||
Users(filter: {_alias: {}}) { |
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.
question: Is this consistent with the behaviour of other 'empty' filter properties?
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, there's no existing tests but I double checked
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.
Nice, thanks Keenan :)
}, | ||
testUtils.Request{ | ||
Request: `query { | ||
Users(filter: {_alias: null}) { |
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.
question: I thought we could make input params not-null without making them required?
suggestion: If so, please make the alias
param not-null, and change this test so that it expects an error :)
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 think we can have not null inputs but we need a default value. There will probably be a lot of other inputs that need a similar update.
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.
Ah okay, must be a memory of the variable stuff then - thanks :)
|
||
func TestQuerySimple_WithNonAliasedField_Succeeds(t *testing.T) { | ||
test := testUtils.TestCase{ | ||
Description: "Simple query with non aliased filter", |
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.
praise: Thank you for adding this test, and the ones for the compound filters!
}, | ||
testUtils.Request{ | ||
Request: `query { | ||
Users(filter: {_alias: {UserAge: {_eq: 21}}}) { |
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.
suggestion: Would it not be better to error in this case? This looks like it would mainly come about through a user-programming error, in which case they would likely much prefer an error over a silent failure.
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 have a preference to not return errors from otherwise validly typed queries. I also don't think the added complexity of checking aliases would be worth the benefits.
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.
The added complexity for us should pretty minimal no (might even be just be converting a continue
to a return NewErrorFoo
)? And the potential time/money savings for our users could be very large.
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 you see a solution that simple I'd definitely consider it.
It would be really difficult to tell whether a filter property is targeting a relationship field or a nested JSON property without adding a good amount of new validation logic.
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 found a kind of simple solution for this. Let me know what you think.
Doc: `{ | ||
"name": "Painted House", | ||
"rating": 4.9, | ||
"author_id": "bae-e1ea288f-09fa-55fa-b0b5-0ac8941ea35b" |
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.
todo: Please use DocIndex
for these IDs instead of strings, it reduces the maintenance and readability overhead quite a bit IMO, it will also force the test to create the docs in a 'correct' order :)
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.
done
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.
It's a nice feature. Good job Keenan.
I left some minor suggestions/concerns.
internal/connor/connor.go
Outdated
@@ -62,7 +66,7 @@ func matchWith(op string, conditions, data any) (bool, error) { | |||
return anyOp(conditions, data) | |||
case AllOp: | |||
return all(conditions, data) | |||
case EqualOp: | |||
case EqualOp, request.FilterOpAlias: |
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.
suggestion: I think it's fine to duplicate the const
for _alias
here and avoid the added dependency.
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.
done
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.
LGTM! Good work, Keenan.
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.
LGTM - thanks Keenan :)
Remaining suggestion from me is very optional.
|
||
func (k *ObjectProperty) GetOperatorOrDefault(defaultOp string) string { | ||
return defaultOp | ||
_, ok := data.(core.Doc) |
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.
suggestion: Made me smile a little to see you do this again in this PR - and I'm curious as to why/where you think I prefer this 😁 (are you thinking back to me preferring nil checks vs failed casting?)
My suggestion is the same as the last, code is simpler IMO, and avoids having to care about an extra, somewhat irrelevant type:
docMap, ok := data.(map[string]any)
if !ok {
return nil, defaultOp, NewErrFieldOrAliasNotFound(k.Name)
}
return docMap[k.Name], defaultOp, nil
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 think I burned that one into my brain. Should be fixed now.
bug bash result: ran all tests from integration tests and many other related tests. Everything is good. |
Relevant issue(s)
Resolves #3194
Description
This PR adds alias targeting in filters.
Aggregate targets are not included in this PR as they require more changes.
Tasks
How has this been tested?
Added and updated integration tests.
Specify the platform(s) on which this was tested: