Skip to content

Commit

Permalink
feat: Add support for sum aggregate (sourcenetwork#121)
Browse files Browse the repository at this point in the history
* Remove unnessecary object deconstruction

* Correct count test description

* Handle count generation errors

* Replace query.Count struct with query.PropertyTransformation

Makes it easier to share code between aggregates if they are parsed into the same struct

* Generalize aggregate alias magic

New implementation should happily support other aggregates out of the box

* Move count plan planning to expand aggregate plans function

* Extract hidden join logic to generic function

* Add support for sum aggregate
  • Loading branch information
AndrewSisley authored Jan 30, 2022
1 parent 37d7b62 commit 3db855e
Show file tree
Hide file tree
Showing 12 changed files with 847 additions and 131 deletions.
2 changes: 1 addition & 1 deletion db/tests/query/inline_array/with_count_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func TestQueryInlineIntegerArrayWithsWithCountAndEmptyArray(t *testing.T) {

func TestQueryInlineIntegerArrayWithsWithCountAndPopulatedArray(t *testing.T) {
test := testUtils.QueryTestCase{
Description: "Simple inline array with no filter, count of empty integer array",
Description: "Simple inline array with no filter, count of integer array",
Query: `query {
users {
Name
Expand Down
178 changes: 178 additions & 0 deletions db/tests/query/inline_array/with_sum_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// Copyright 2020 Source Inc.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
package inline_array

import (
"testing"

testUtils "github.com/sourcenetwork/defradb/db/tests"
)

func TestQueryInlineIntegerArrayWithsWithSumAndNullArray(t *testing.T) {
test := testUtils.QueryTestCase{
Description: "Simple inline array with no filter, sum of nil integer array",
Query: `query {
users {
Name
_sum(field: {FavouriteIntegers: {}})
}
}`,
Docs: map[int][]string{
0: {
(`{
"Name": "John",
"FavouriteIntegers": null
}`)},
},
Results: []map[string]interface{}{
{
"Name": "John",
"_sum": int64(0),
},
},
}

executeTestCase(t, test)
}

func TestQueryInlineIntegerArrayWithsWithSumAndEmptyArray(t *testing.T) {
test := testUtils.QueryTestCase{
Description: "Simple inline array with no filter, sum of empty integer array",
Query: `query {
users {
Name
_sum(field: {FavouriteIntegers: {}})
}
}`,
Docs: map[int][]string{
0: {
(`{
"Name": "John",
"FavouriteIntegers": []
}`)},
},
Results: []map[string]interface{}{
{
"Name": "John",
"_sum": int64(0),
},
},
}

executeTestCase(t, test)
}

func TestQueryInlineIntegerArrayWithsWithSumAndPopulatedArray(t *testing.T) {
test := testUtils.QueryTestCase{
Description: "Simple inline array with no filter, sum of integer array",
Query: `query {
users {
Name
_sum(field: {FavouriteIntegers: {}})
}
}`,
Docs: map[int][]string{
0: {
(`{
"Name": "Shahzad",
"FavouriteIntegers": [-1, 2, -1, 1, 0]
}`)},
},
Results: []map[string]interface{}{
{
"Name": "Shahzad",
"_sum": int64(1),
},
},
}

executeTestCase(t, test)
}

func TestQueryInlineFloatArrayWithsWithSumAndNullArray(t *testing.T) {
test := testUtils.QueryTestCase{
Description: "Simple inline array with no filter, sum of nil float array",
Query: `query {
users {
Name
_sum(field: {FavouriteFloats: {}})
}
}`,
Docs: map[int][]string{
0: {
(`{
"Name": "John",
"FavouriteFloats": null
}`)},
},
Results: []map[string]interface{}{
{
"Name": "John",
"_sum": float64(0),
},
},
}

executeTestCase(t, test)
}

func TestQueryInlineFloatArrayWithsWithSumAndEmptyArray(t *testing.T) {
test := testUtils.QueryTestCase{
Description: "Simple inline array with no filter, sum of empty float array",
Query: `query {
users {
Name
_sum(field: {FavouriteFloats: {}})
}
}`,
Docs: map[int][]string{
0: {
(`{
"Name": "John",
"FavouriteFloats": []
}`)},
},
Results: []map[string]interface{}{
{
"Name": "John",
"_sum": float64(0),
},
},
}

executeTestCase(t, test)
}

func TestQueryInlineFloatArrayWithsWithSumAndPopulatedArray(t *testing.T) {
test := testUtils.QueryTestCase{
Description: "Simple inline array with no filter, sum of float array",
Query: `query {
users {
Name
_sum(field: {FavouriteFloats: {}})
}
}`,
Docs: map[int][]string{
0: {
(`{
"Name": "John",
"FavouriteFloats": [3.1425, 0.00000000001, 10]
}`)},
},
Results: []map[string]interface{}{
{
"Name": "John",
"_sum": float64(13.14250000001),
},
},
}

executeTestCase(t, test)
}
1 change: 1 addition & 0 deletions db/tests/query/simple/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var userCollectionGQLSchema = (`
type users {
Name: String
Age: Int
HeightM: Float
Verified: Boolean
}
`)
Expand Down
201 changes: 201 additions & 0 deletions db/tests/query/simple/with_group_sum_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
// Copyright 2020 Source Inc.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
package simple

// TODO!!!!! once scalar are merged, this should be capable of summing int/float arrays - likely needs some tweaks in generator.go and query.go

import (
"testing"

testUtils "github.com/sourcenetwork/defradb/db/tests"
)

func TestQuerySimpleWithGroupByStringWithoutRenderedGroupAndSumOfUndefined(t *testing.T) {
test := testUtils.QueryTestCase{
Description: "Simple query with sum on unspecified field",
Query: `query {
users (groupBy: [Name]) {
Name
_sum
}
}`,
Docs: map[int][]string{
0: {
(`{
"Name": "John",
"Age": 32
}`)},
},
Results: []map[string]interface{}{
{
"Name": "John",
"_sum": int64(0),
},
},
}

executeTestCase(t, test)
}

func TestQuerySimpleWithGroupByStringWithoutRenderedGroupAndChildIntegerSum(t *testing.T) {
test := testUtils.QueryTestCase{
Description: "Simple query with group by string, sum on non-rendered group integer value",
Query: `query {
users(groupBy: [Name]) {
Name
_sum(field: {_group: Age})
}
}`,
Docs: map[int][]string{
0: {
(`{
"Name": "John",
"Age": 32
}`),
(`{
"Name": "John",
"Age": 38
}`),
// It is important to test negative values here, due to the auto-typing of numbers
(`{
"Name": "Alice",
"Age": -19
}`)},
},
Results: []map[string]interface{}{
{
"Name": "John",
"_sum": int64(70),
},
{
"Name": "Alice",
"_sum": int64(-19),
},
},
}

executeTestCase(t, test)
}

func TestQuerySimpleWithGroupByStringWithoutRenderedGroupAndChildNilSum(t *testing.T) {
test := testUtils.QueryTestCase{
Description: "Simple query with group by string, sum on non-rendered group nil and integer values",
Query: `query {
users(groupBy: [Name]) {
Name
_sum(field: {_group: Age})
}
}`,
Docs: map[int][]string{
0: {
(`{
"Name": "John",
"Age": 32
}`),
// Age is undefined here
(`{
"Name": "John"
}`),
(`{
"Name": "Alice",
"Age": 19
}`)},
},
Results: []map[string]interface{}{
{
"Name": "Alice",
"_sum": int64(19),
},
{
"Name": "John",
"_sum": int64(32),
},
},
}

executeTestCase(t, test)
}

func TestQuerySimpleWithGroupByStringWithoutRenderedGroupAndChildEmptyFloatSum(t *testing.T) {
test := testUtils.QueryTestCase{
Description: "Simple query with group by string, sum on non-rendered group float (default) value",
Query: `query {
users(groupBy: [Name]) {
Name
_sum(field: {_group: HeightM})
}
}`,
Docs: map[int][]string{
0: {
(`{
"Name": "John",
"HeightM": 1.82
}`),
(`{
"Name": "John",
"HeightM": 1.89
}`),
(`{
"Name": "Alice"
}`)},
},
Results: []map[string]interface{}{
{
"Name": "John",
"_sum": float64(3.71),
},
{
"Name": "Alice",
"_sum": float64(0),
},
},
}

executeTestCase(t, test)
}

func TestQuerySimpleWithGroupByStringWithoutRenderedGroupAndChildFloatSum(t *testing.T) {
test := testUtils.QueryTestCase{
Description: "Simple query with group by string, sum on non-rendered group float value",
Query: `query {
users(groupBy: [Name]) {
Name
_sum(field: {_group: HeightM})
}
}`,
Docs: map[int][]string{
0: {
(`{
"Name": "John",
"HeightM": 1.82
}`),
(`{
"Name": "John",
"HeightM": 1.89
}`),
(`{
"Name": "Alice",
"HeightM": 2.04
}`)},
},
Results: []map[string]interface{}{
{
"Name": "John",
"_sum": float64(3.71),
},
{
"Name": "Alice",
"_sum": float64(2.04),
},
},
}

executeTestCase(t, test)
}
Loading

0 comments on commit 3db855e

Please sign in to comment.