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

Support $sum accumulator of $group aggregation #2292

Merged
merged 14 commits into from
Mar 29, 2023
Prev Previous commit
Next Next commit
more impl
  • Loading branch information
chilagrow committed Mar 28, 2023
commit 36cc33e034b16757520ccf73d56c359dc94fd309
166 changes: 116 additions & 50 deletions integration/aggregate_compat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ func TestAggregateCompatGroupDeterministicCollections(t *testing.T) {
// shareddata.Scalars,

shareddata.Doubles,
shareddata.BigDoubles,
shareddata.OverflowVergeDoubles,
shareddata.SmallDoubles,
shareddata.Strings,
shareddata.Binaries,
Expand Down Expand Up @@ -551,7 +551,7 @@ func TestAggregateCompatGroupDotNotation(t *testing.T) {
shareddata.Scalars,

shareddata.Doubles,
shareddata.BigDoubles,
shareddata.OverflowVergeDoubles,
shareddata.SmallDoubles,
shareddata.Strings,
shareddata.Binaries,
Expand Down Expand Up @@ -625,7 +625,7 @@ func TestAggregateCompatGroupDocDotNotation(t *testing.T) {
shareddata.Scalars,

shareddata.Doubles,
shareddata.BigDoubles,
shareddata.OverflowVergeDoubles,
shareddata.SmallDoubles,
shareddata.Strings,
shareddata.Binaries,
Expand Down Expand Up @@ -716,13 +716,15 @@ func TestAggregateCompatGroupCount(t *testing.T) {
}

func TestAggregateCompatGroupSum(t *testing.T) {
// Scalars and BigDoubles are skipped as they produce `Infinity`.
// Doubles is skipped as they produce wrong result due to inaccurate precision.
// Composites, ArrayStrings, ArrayInt32s, Mixed and ArrayAndDocuments are skipped due to
// https://github.com/FerretDB/FerretDB/issues/2185.
providers := []shareddata.Provider{
// shareddata.Scalars,
shareddata.Scalars,

// TODO: handle doubles close to max precision in doubles.
// shareddata.Doubles,
// shareddata.BigDoubles,
shareddata.OverflowVergeDoubles,
shareddata.SmallDoubles,
shareddata.Strings,
shareddata.Binaries,
Expand All @@ -737,90 +739,154 @@ func TestAggregateCompatGroupSum(t *testing.T) {
shareddata.Unsets,
shareddata.ObjectIDKeys,

shareddata.Composites,
// shareddata.Composites,
shareddata.PostgresEdgeCases,

shareddata.DocumentsDoubles,
shareddata.DocumentsStrings,
shareddata.DocumentsDocuments,

shareddata.ArrayStrings,
// shareddata.ArrayStrings,
shareddata.ArrayDoubles,
shareddata.ArrayInt32s,
// shareddata.ArrayInt32s,
shareddata.ArrayRegexes,
shareddata.ArrayDocuments,

shareddata.Mixed,
shareddata.ArrayAndDocuments,
// shareddata.Mixed,
// shareddata.ArrayAndDocuments,
}

testCases := map[string]aggregateStagesCompatTestCase{
"Value": {
"GroupNullID": {
pipeline: bson.A{
// Without $sort sum of large values results in wrong result.
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", nil},
{"sum", bson.D{{"$sum", "$v"}}},
}}},
// Without $sort documents are ordered not the same.
// Descending sort is used because it is more unique than
// ascending sort for shared data.
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
},
"GroupByID": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", "$_id"},
{"sum", bson.D{{"$sum", "$v"}}},
}}},
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
},
"GroupByValue": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", "$v"},
{"sum", bson.D{{"$sum", "$v"}}},
}}},
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
},
"EmptyString": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
{"count", bson.D{{"$sum", ""}}},
}}}},
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", "$v"},
{"sum", bson.D{{"$sum", ""}}},
}}},
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
},
"NonExpression": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
{"sum", bson.D{{"$sum", "v"}}},
}}}},
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", nil},
{"sum", bson.D{{"$sum", "v"}}},
}}}},
},
"NonExistent": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
{"sum", bson.D{{"$sum", "$non-existent"}}},
}}}},
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", "$v"},
{"sum", bson.D{{"$sum", "$non-existent"}}},
}}},
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
},
"Document": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
{"sum", bson.D{{"$sum", bson.D{}}}},
}}}},
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},

bson.D{{"$group", bson.D{
{"_id", "$v"},
{"sum", bson.D{{"$sum", bson.D{}}}},
}}},
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
},
"ArraySum": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
{"sum", bson.D{{"$sum", bson.A{"$v", "$c"}}}},
}}}},
"Array": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", "$v"},
{"sum", bson.D{{"$sum", bson.A{"$v", "$c"}}}},
}}}},
resultType: emptyResult,
},
"Int32": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
{"sum", bson.D{{"$sum", int32(1)}}}}}}},
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", "$v"},
{"sum", bson.D{{"$sum", int32(1)}}},
}}},
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
},
"Int64": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
{"sum", bson.D{{"$sum", int64(20)}}}}}}},
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", "$v"},
{"sum", bson.D{{"$sum", int64(20)}}},
}}},
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
},
"Double": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
{"sum", bson.D{{"$sum", 43.7}}}}}}},
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", "$v"},
{"sum", bson.D{{"$sum", 43.7}}},
}}},
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
},
"Bool": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
{"sum", bson.D{{"$sum", true}}}}}}},
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", "$v"},
{"sum", bson.D{{"$sum", true}}},
}}},
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
},
"Duplicate": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$v"},
{"sum", bson.D{{"$sum", "$v"}}},
{"sum", bson.D{{"$sum", "$s"}}},
}}}},
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", "$v"},
{"sum", bson.D{{"$sum", "$v"}}},
{"sum", bson.D{{"$sum", "$s"}}},
}}}},
resultType: emptyResult,
},
}
Expand Down
18 changes: 10 additions & 8 deletions integration/shareddata/scalars.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,13 +163,13 @@ var Doubles = &Values[string]{
},
}

// BigDoubles contains double values which would overflow on
// OverflowVergeDoubles contains double values which would overflow on
// numeric update operation such as $mul. Upon such,
// target returns error and compat returns +INF or -INF.
// BigDoubles may be excluded on such update tests and tested
// OverflowVergeDoubles may be excluded on such update tests and tested
// in diff tests https://github.com/FerretDB/dance.
var BigDoubles = &Values[string]{
name: "BigDoubles",
var OverflowVergeDoubles = &Values[string]{
chilagrow marked this conversation as resolved.
Show resolved Hide resolved
name: "OverflowVergeDoubles",
backends: []string{"ferretdb-pg", "ferretdb-tigris", "mongodb"},
validators: map[string]map[string]any{
"ferretdb-tigris": {
Expand All @@ -183,7 +183,7 @@ var BigDoubles = &Values[string]{
}

// SmallDoubles contains double values that does not go close to
// maximum precision for tests.
// the maximum safe precision for tests.
var SmallDoubles = &Values[string]{
name: "SmallDoubles",
backends: []string{"ferretdb-pg", "ferretdb-tigris", "mongodb"},
Expand All @@ -193,9 +193,11 @@ var SmallDoubles = &Values[string]{
},
},
data: map[string]any{
"double": 42.13,
"double-whole": 42.0,
"double-smallest": math.SmallestNonzeroFloat64,
"double": 42.13,
"double-whole": 42.0,
"double-1": 4080.1234,
"double-2": 1048560.0099,
"double-3": 268435440.2,
},
}

Expand Down
2 changes: 1 addition & 1 deletion integration/shareddata/shareddata.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func AllProviders() Providers {
Scalars,

Doubles,
BigDoubles,
OverflowVergeDoubles,
SmallDoubles,
Strings,
Binaries,
Expand Down
4 changes: 2 additions & 2 deletions integration/update_field_compat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1023,9 +1023,9 @@ func TestUpdateFieldCompatMul(t *testing.T) {
t.Parallel()

providers := shareddata.AllProviders().
// BigDoubles and Scalars contain numbers that produces +INF on compat,
// OverflowVergeDoubles and Scalars contain numbers that produces +INF on compat,
// validation error on target upon $mul operation.
Remove("BigDoubles", "Scalars")
Remove("OverflowVergeDoubles", "Scalars")

testCases := map[string]updateCompatTestCase{
"Int32": {
Expand Down
Loading