-
Notifications
You must be signed in to change notification settings - Fork 53
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: Add support for aggregate filters on inline arrays #622
Changes from all commits
9bc12d3
eee8aad
c13172b
fac4858
ce8d4dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -151,3 +151,7 @@ func (t *Targetable) cloneTo(index int) *Targetable { | |
OrderBy: t.OrderBy, | ||
} | ||
} | ||
|
||
func (t *Targetable) AsTargetable() (*Targetable, bool) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: in the file
question: I can see the benefit in turning the embedded type There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Good shout - will check/add (might already do)
This is deliberate and desirable IMO
The user should not have a choice here, or have to worry about it.
Maybe - I'm 50-50 here, there are better ways of handling this in other languages. Inheritance in data-structures is something I like quite a lot, but there is no good way of doing this in Go atm (maybe when we get generics, but even then I'm not sure due to Golangs embedding mechanic - I never looked at whether they allow generic constraints to account for embedded types). I don't think this is worth really paying much attention to atm. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Rejected on further look. Targetable and AggregateTarget are not Requestable and should not implement that interface (other than accidentally), that they implement some of their functions is coincidence and not a requirement (this coincidence is used here, but not required). |
||
return t, true | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -98,14 +98,54 @@ func (n *countNode) Next() (bool, error) { | |
switch v.Kind() { | ||
// v.Len will panic if v is not one of these types, we don't want it to panic | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: I feel like this entire for loop can be more simpler, consider the following:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The suggestion contains less code duplication, but I'm not sure it is simpler, and I'm really not a fan of Breaking up the switch cases breaks up the code-flow and makes reading it harder IMO. You also dont want to move This can be shrunk when we have generics, but until then I'm happy as-is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: you don't have to do a type switch. You can simply get the value of the property and pass it to for _, source := range n.aggregateMapping {
// track if property is a Slice or Array
isIterable := false
property := n.currentValue.Fields[source.Index]
v := reflect.ValueOf(property)
// isValueOfPropertyValid is true if v is one of the reflect types that can call v.Len() without panic.
switch v.Kind() {
case reflect.Array, reflect.Slice:
isIterable = true
case reflect.Chan, reflect.Map, reflect.String:
default:
continue
}
// v.Len() will panic if isValueOfPropertyValid is false.
if source.Filter == nil {
count = count + v.Len()
continue
}
if isIterable {
for i := 0; i < v.Len(); i++ {
passed, err := mapper.RunFilter(v.Index(i).Interface(), source.Filter)
if err != nil {
return false, err
}
if passed {
count += 1
}
}
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. :) I tried something very similar briefly (is a cleaner way that avoids Again, this is trivial to shrink with generics (avoiding the runtime cost of reflect/casting/etc) - so I really don't think it matters at all. Do you have a strong preference to using something similar to the above in the short-term, or are you happy to wait for generics? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't have too strong of a preference, but since you are already changing the function it would be nice to clean it up until we get generics. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The difference in cost between changing it now (I have no plans to do so unless you guys really want), and changing it in a month or two is minimal. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Your initial version works and is readable. I don't like the repetition but if you think it will be cleaned up with generics in the near future, then just go with whatever you prefer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ticket added in case I forget/get-lazy #633 |
||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: | ||
length := v.Len() | ||
// For now, we only support count filters internally to support averages | ||
// so this is fine here now, but may need to be moved later once external | ||
// count filter support is added. | ||
if count > 0 && source.Filter != nil { | ||
docArray, isDocArray := property.([]core.Doc) | ||
if isDocArray { | ||
for _, doc := range docArray { | ||
if source.Filter != nil { | ||
switch array := property.(type) { | ||
case []core.Doc: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking forward to getting generics lol |
||
for _, doc := range array { | ||
passed, err := mapper.RunFilter(doc, source.Filter) | ||
if err != nil { | ||
return false, err | ||
} | ||
if passed { | ||
count += 1 | ||
} | ||
} | ||
|
||
case []bool: | ||
for _, doc := range array { | ||
passed, err := mapper.RunFilter(doc, source.Filter) | ||
if err != nil { | ||
return false, err | ||
} | ||
if passed { | ||
count += 1 | ||
} | ||
} | ||
|
||
case []int64: | ||
for _, doc := range array { | ||
passed, err := mapper.RunFilter(doc, source.Filter) | ||
if err != nil { | ||
return false, err | ||
} | ||
if passed { | ||
count += 1 | ||
} | ||
} | ||
|
||
case []float64: | ||
for _, doc := range array { | ||
passed, err := mapper.RunFilter(doc, source.Filter) | ||
if err != nil { | ||
return false, err | ||
} | ||
if passed { | ||
count += 1 | ||
} | ||
} | ||
|
||
case []string: | ||
for _, doc := range array { | ||
passed, err := mapper.RunFilter(doc, source.Filter) | ||
if err != nil { | ||
return false, err | ||
|
@@ -116,7 +156,7 @@ func (n *countNode) Next() (bool, error) { | |
} | ||
} | ||
} else { | ||
count = count + length | ||
count = count + v.Len() | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -223,10 +223,24 @@ func (n *sumNode) Next() (bool, error) { | |
} | ||
case []int64: | ||
for _, childItem := range childCollection { | ||
passed, err := mapper.RunFilter(childItem, source.Filter) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: perhaps can be simplified too similar to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see my response to that comment. I'll apply anything that comes out of that convo here though if suitable |
||
if err != nil { | ||
return false, err | ||
} | ||
if !passed { | ||
continue | ||
} | ||
sum += float64(childItem) | ||
} | ||
case []float64: | ||
for _, childItem := range childCollection { | ||
passed, err := mapper.RunFilter(childItem, source.Filter) | ||
if err != nil { | ||
return false, err | ||
} | ||
if !passed { | ||
continue | ||
} | ||
sum += childItem | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -291,15 +291,28 @@ func (g *Generator) createExpandedFieldAggregate( | |||||||
) { | ||||||||
for _, aggregateTarget := range f.Args { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: Maybe the for loop can be one less nested-level with:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would expand the scope of |
||||||||
target := aggregateTarget.Name() | ||||||||
var targetType string | ||||||||
var filterTypeName string | ||||||||
if target == parserTypes.GroupFieldName { | ||||||||
targetType = obj.Name() | ||||||||
filterTypeName = obj.Name() + "FilterArg" | ||||||||
} else { | ||||||||
targetType = obj.Fields()[target].Type.Name() | ||||||||
filterType := obj.Fields()[target].Type | ||||||||
if list, isList := filterType.(*gql.List); isList && gql.IsLeafType(list.OfType) { | ||||||||
// If it is a list of leaf types - the filter is just the set of OperatorBlocks | ||||||||
// that are supported by this type - there can be no field selections. | ||||||||
if notNull, isNotNull := list.OfType.(*gql.NonNull); isNotNull { | ||||||||
// GQL does not support '!' in type names, and so we have to manipulate the | ||||||||
// underlying name like this if it is a nullable type. | ||||||||
filterTypeName = fmt.Sprintf("NotNull%sOperatorBlock", notNull.OfType.Name()) | ||||||||
} else { | ||||||||
filterTypeName = genTypeName(list.OfType, "OperatorBlock") | ||||||||
} | ||||||||
} else { | ||||||||
filterTypeName = filterType.Name() + "FilterArg" | ||||||||
} | ||||||||
} | ||||||||
|
||||||||
expandedField := &gql.InputObjectFieldConfig{ | ||||||||
Type: g.manager.schema.TypeMap()[targetType+"FilterArg"], | ||||||||
Type: g.manager.schema.TypeMap()[filterTypeName], | ||||||||
} | ||||||||
aggregateTarget.Type.(*gql.InputObject).AddFieldConfig("filter", expandedField) | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggest out of scope, and there are a few of these dotted about in here that can be cleaned up in one go. |
||||||||
} | ||||||||
|
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: you changed it to
core.Doc
previously and now back tointerface{}
. We are no longer just comparingcore.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.
Nope, this now needs to handle inline arrays (noted in commit message)
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.
well not explicitly 😉
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.
Unless I fluffed up the commit contents, the message is: