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

refactor: Rework sum and count nodes to make use of generics #757

Merged
merged 3 commits into from
Aug 24, 2022

Conversation

AndrewSisley
Copy link
Contributor

Relevant issue(s)

Resolves #633

Description

Rework sum and count node type switches to make use of generics. Switches changed in this PR will be expanded with new cases shortly and I'd rather not write them twice :)

Specify the platform(s) on which this was tested:

  • Debian Linux

@AndrewSisley AndrewSisley added area/query Related to the query component refactor This issue specific to or requires *notable* refactoring of existing codebases and components action/no-benchmark Skips the action that runs the benchmark. labels Aug 23, 2022
@AndrewSisley AndrewSisley added this to the DefraDB v0.3.1 milestone Aug 23, 2022
@AndrewSisley AndrewSisley requested a review from a team August 23, 2022 21:41
@AndrewSisley AndrewSisley self-assigned this Aug 23, 2022
@AndrewSisley AndrewSisley changed the title refactor: Rework sum and count node type switches to make use of generics refactor: Rework sum and count nodes to make use of generics Aug 23, 2022
@codecov
Copy link

codecov bot commented Aug 23, 2022

Codecov Report

Merging #757 (e862f65) into develop (d59592f) will increase coverage by 0.04%.
The diff coverage is 76.92%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop     #757      +/-   ##
===========================================
+ Coverage    58.02%   58.07%   +0.04%     
===========================================
  Files          144      144              
  Lines        16593    16578      -15     
===========================================
- Hits          9628     9627       -1     
+ Misses        6052     6043       -9     
+ Partials       913      908       -5     
Impacted Files Coverage Δ
query/graphql/planner/count.go 91.35% <72.72%> (+7.51%) ⬆️
query/graphql/planner/sum.go 80.95% <80.00%> (+3.37%) ⬆️

@@ -99,62 +99,28 @@ func (n *countNode) Next() (bool, error) {
// v.Len will panic if v is not one of these types, we don't want it to panic
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
if source.Filter != nil {
var arrayCount int
var err error
switch array := property.(type) {
Copy link
Member

Choose a reason for hiding this comment

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

question: Wondering if there is a possibility to replace all the cases with:

				case []core.Doc, []bool, []int64, []float64, []string:
					arrayCount, err = countItems(array, source.Filter)
				}

Type inference issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, some langs let you do that but I couldn't find a way in Go (including in the reflect package in case they'd added some generic funcs) - would have been nice

Copy link
Member

Choose a reason for hiding this comment

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

If not possible then don't worry about it. Still makes things a lot nicer than before.

@@ -165,4 +131,18 @@ func (n *countNode) Next() (bool, error) {
return true, nil
}

func countItems[T any](collection []T, filter *mapper.Filter) (int, error) {
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: Define the type constraint rather than using any.

Suggested change
func countItems[T any](collection []T, filter *mapper.Filter) (int, error) {
type Countable interface {
core.Doc | ~bool | ~int64 | ~float64 | ~string
}
func countItems[T Countable](collection []T, filter *mapper.Filter) (int, error) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why? This function will work with any type of T

Copy link
Member

Choose a reason for hiding this comment

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

Is that the desired behavior? thought we specifically wanted to only handle the cases defined in the switches and any other element type is to be treated like any error or default case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes it is desired. Why would you block future cases and couple the function to specific types?

Copy link
Member

Choose a reason for hiding this comment

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

Well it's normal to want to limit some functionality for a specific unsupported type, I was wondering if perhaps the reason we had explicit cases was maybe that we wanted to limit count/sum only to these cases. However, I understand now that we need the explicit cases because we need the actual type information instead of interface{} (through type switch).

As this is desired (and assuming also desired for the sum items), this is resolved.

jsimnz
jsimnz previously requested changes Aug 23, 2022
Copy link
Member

@jsimnz jsimnz left a comment

Choose a reason for hiding this comment

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

Quick thought regarding type constraints to simplify sum

@@ -257,4 +242,19 @@ func (n *sumNode) Next() (bool, error) {
return true, nil
}

func sumItems[T any](collection []T, filter *mapper.Filter, toFloat func(T) float64) (float64, error) {
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: Instead of using T any we can limit the type constraint to "numbers"

eg

import "golang.org/x/exp/constraints"

type Number interface {
  constraints.Integer | constraint.Float
}

Then you wouldn't have to do toFloat, then you can convert the final results sum into a float64 return float64(sum) and since sum is guranteed to be a number, the compiler will be happy with that conversion. So you can drop the argument dependancy entirely

Copy link
Contributor Author

Choose a reason for hiding this comment

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

toFloat also takes child documents, not just numbers - what you are suggesting is not possible without reverting 1 out of the 3 current cases

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Lol, I wanted to scream when I read 'golang.org/x/exp/constraints' - Golang really really loves doing stuff that is known at compile time at runtime... Why would you want to box values of the generic type?? (I assume at least given that it is an interface)

Copy link
Member

Choose a reason for hiding this comment

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

Its not really box values of generic types, theres a new special syntax for type constraints that re-uses the interface semantics

ie

type Number interface {
   ~int
}

is special type constraint attached to the named interface Number.

Copy link
Member

Choose a reason for hiding this comment

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

As for sub documents, wouldn't it make sense to do the subdoc stuff outside of sumItems? Since the toFloat function for []code.Doc does child-access stuff, and also uses index contexts outside of its scope. Seems simipler to seperate the two, and not have the toFloat func.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

theres a new special syntax for type constraints that re-uses the interface semantics

Ah okay, and I checked - it doesn't seem to behave like a pointer either, bit odd to use interface for that though - looks like you can't use them as interfaces either (compiler blocks it)

As for sub documents, wouldn't it make sense to do the subdoc stuff outside of sumItems?

I didn't think so when I wrote it, I would rather have everything go through the same logic and keep as much as possible out of the switch. Would also prefer to leave the function unconstrained - reduces the risk of having to change it in the future

Copy link
Contributor Author

Choose a reason for hiding this comment

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

:) Now I'm hopeful for unions lol (if not rust-style enums)

@@ -165,4 +131,18 @@ func (n *countNode) Next() (bool, error) {
return true, nil
}

func countItems[T any](collection []T, filter *mapper.Filter) (int, error) {
Copy link
Member

Choose a reason for hiding this comment

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

extreme nitpick: change parameter name from collection as its used a lot in a specific context (ie collections in the DB) :)

Copy link
Contributor Author

@AndrewSisley AndrewSisley Aug 23, 2022

Choose a reason for hiding this comment

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

lol, fair enough, will do

  • rename param (and for sum)

@AndrewSisley AndrewSisley requested a review from jsimnz August 23, 2022 22:49
Copy link
Member

@shahzadlone shahzadlone left a comment

Choose a reason for hiding this comment

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

LGTM.

@AndrewSisley AndrewSisley force-pushed the sisley/refactor/I633-sum-count-generic-refactor branch from 4a5714c to e862f65 Compare August 24, 2022 15:39
@AndrewSisley AndrewSisley dismissed jsimnz’s stale review August 24, 2022 15:51

coversations appear resolved and I need this code in to progress on the more important task - any comments you may add here can be applied to that branch

@AndrewSisley AndrewSisley merged commit aa92a70 into develop Aug 24, 2022
@AndrewSisley AndrewSisley deleted the sisley/refactor/I633-sum-count-generic-refactor branch August 24, 2022 15:51
shahzadlone pushed a commit to shahzadlone/defradb that referenced this pull request Feb 23, 2024
…etwork#757)

* Refactor count node to make use of generics

* Refactor sum node to make use of generics
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
action/no-benchmark Skips the action that runs the benchmark. area/query Related to the query component refactor This issue specific to or requires *notable* refactoring of existing codebases and components
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Rework sum and count node type switches to make use of generics
3 participants