Files
docdb/integration/aggregate_compat_test.go
zeekayandHanzo Dev 06d5eca873 Declare operations on the group that serves them
zip's registration is a verb method on the group, so an operation reads as the
group answering it rather than a function taking the group, and a route that is
not a typed operation says which method it answers with. The package-level form
and the Router interface it existed to accept any target through are both gone.

The toolchain and the pin move together, deliberately. Below zip v1.36.50 the
JSON encoder was selected on a build tag, so a repo whose go directive rises
first writes a wire that is neither the old release's nor the new one's until the
pin catches up.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-17 06:11:42 -07:00

2831 lines
77 KiB
Go

// Copyright 2021 DocDB Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package integration
import (
"math"
"slices"
"strings"
"testing"
"time"
"github.com/AlekSi/pointer"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/hanzoai/docdb/integration/setup"
"github.com/hanzoai/docdb/integration/shareddata"
)
// aggregateStagesCompatTestCase describes aggregation stages compatibility test case.
type aggregateStagesCompatTestCase struct {
pipeline bson.A // required, unspecified $sort appends bson.D{{"$sort", bson.D{{"_id", 1}}}} for non empty pipeline.
maxTime *time.Duration // optional, leave nil for unset maxTime
resultType CompatTestCaseResultType // defaults to NonEmptyResult
skip string // TODO https://github.com/hanzoai/docdb-DocumentDB/issues/1086
failsForDocDB string
failsProviders []shareddata.Provider // use only if failsForDocDB is set, defaults to all providers
}
// testAggregateStagesCompat tests aggregation stages compatibility test cases with all providers.
func testAggregateStagesCompat(t *testing.T, testCases map[string]aggregateStagesCompatTestCase) {
t.Helper()
testAggregateStagesCompatWithProviders(t, shareddata.AllProviders(), testCases)
}
// testAggregateStagesCompatWithProviders tests aggregation stages compatibility test cases with given providers.
func testAggregateStagesCompatWithProviders(t *testing.T, providers shareddata.Providers, testCases map[string]aggregateStagesCompatTestCase) {
t.Helper()
require.NotEmpty(t, providers)
s := setup.SetupCompatWithOpts(t, &setup.SetupCompatOpts{
Providers: providers,
})
ctx, targetCollections, compatCollections := s.Ctx, s.TargetCollections, s.CompatCollections
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
t.Helper()
if tc.skip != "" {
t.Skip(tc.skip)
}
t.Parallel()
pipeline := tc.pipeline
require.NotNil(t, pipeline, "pipeline should be set")
var hasSortStage bool
for _, stage := range pipeline {
stage, ok := stage.(bson.D)
if !ok {
continue
}
if _, hasSortStage = stage.Map()["$sort"]; hasSortStage {
break
}
}
if !hasSortStage && len(pipeline) > 0 {
// add sort stage to sort by _id because compat and target
// would be ordered differently otherwise.
pipeline = append(pipeline, bson.D{{"$sort", bson.D{{"_id", 1}}}})
}
opts := options.Aggregate()
if tc.maxTime != nil {
opts.SetMaxTime(*tc.maxTime)
}
failsProviders := make([]string, len(tc.failsProviders))
for i, p := range tc.failsProviders {
failsProviders[i] = p.Name()
}
var nonEmptyResults bool
for i := range targetCollections {
targetCollection := targetCollections[i]
compatCollection := compatCollections[i]
t.Run(targetCollection.Name(), func(tt *testing.T) {
tt.Helper()
// a workaround to get provider name by using the part after last `_`,
// e.g. `Doubles` from `TestQueryArrayCompatElemMatch_Doubles`
str := strings.Split(targetCollection.Name(), "_")
providerName := str[len(str)-1]
failsForCollection := len(tc.failsProviders) == 0 || slices.Contains(failsProviders, providerName)
var t testing.TB = tt
if tc.failsForDocDB != "" && failsForCollection {
t = setup.FailsForDocDB(tt, tc.failsForDocDB)
}
targetCursor, targetErr := targetCollection.Aggregate(ctx, pipeline, opts)
compatCursor, compatErr := compatCollection.Aggregate(ctx, pipeline, opts)
if targetCursor != nil {
defer targetCursor.Close(ctx)
}
if compatCursor != nil {
defer compatCursor.Close(ctx)
}
if targetErr != nil {
t.Logf("Target error: %v", targetErr)
t.Logf("Compat error: %v", compatErr)
// error messages are intentionally not compared
AssertMatchesCommandError(t, compatErr, targetErr)
return
}
require.NoError(t, compatErr, "compat error; target returned no error")
targetRes := FetchAll(t, ctx, targetCursor)
compatRes := FetchAll(t, ctx, compatCursor)
AssertEqualDocumentsSlice(t, compatRes, targetRes)
if len(targetRes) > 0 || len(compatRes) > 0 {
nonEmptyResults = true
}
})
}
switch tc.resultType {
case NonEmptyResult:
assert.True(t, nonEmptyResults, "expected non-empty results")
case EmptyResult:
if tc.failsForDocDB != "" {
return
}
assert.False(t, nonEmptyResults, "expected empty results")
default:
t.Fatalf("unknown result type %v", tc.resultType)
}
})
}
}
// aggregateCommandCompatTestCase describes aggregate compatibility test case.
type aggregateCommandCompatTestCase struct {
command bson.D // required
resultType CompatTestCaseResultType // defaults to NonEmptyResult
failsForDocDB string
}
// testAggregateCommandCompat tests aggregate pipeline compatibility test cases using one collection.
// Use testAggregateStagesCompat for testing stages of aggregation.
func testAggregateCommandCompat(t *testing.T, testCases map[string]aggregateCommandCompatTestCase) {
t.Helper()
s := setup.SetupCompatWithOpts(t, &setup.SetupCompatOpts{
Providers: []shareddata.Provider{shareddata.Int32s},
})
ctx := s.Ctx
targetCollection := s.TargetCollections[0]
compatCollection := s.CompatCollections[0]
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
t.Helper()
t.Parallel()
command := tc.command
require.NotNil(t, command, "command should be set")
var nonEmptyResults bool
t.Run(targetCollection.Name(), func(tt *testing.T) {
tt.Helper()
var t testing.TB = tt
if tc.failsForDocDB != "" {
t = setup.FailsForDocDB(tt, tc.failsForDocDB)
}
var targetRes, compatRes bson.D
targetErr := targetCollection.Database().RunCommand(ctx, command).Decode(&targetRes)
compatErr := compatCollection.Database().RunCommand(ctx, command).Decode(&compatRes)
if targetErr != nil {
t.Logf("Target error: %v", targetErr)
t.Logf("Compat error: %v", compatErr)
if _, ok := targetErr.(mongo.CommandError); ok { //nolint:errorlint // do not inspect error chain
// error messages are intentionally not compared
AssertMatchesCommandError(t, compatErr, targetErr)
} else {
// driver sent an error
require.Equal(t, compatErr, targetErr)
}
return
}
require.NoError(t, compatErr, "compat error; target returned no error")
AssertEqualDocuments(t, compatRes, targetRes)
if len(targetRes) > 0 || len(compatRes) > 0 {
nonEmptyResults = true
}
})
switch tc.resultType {
case NonEmptyResult:
assert.True(t, nonEmptyResults, "expected non-empty results")
case EmptyResult:
assert.False(t, nonEmptyResults, "expected empty results")
default:
t.Fatalf("unknown result type %v", tc.resultType)
}
})
}
}
func TestAggregateCommandCompat(t *testing.T) {
t.Parallel()
testCases := map[string]aggregateCommandCompatTestCase{
"CollectionAgnostic": {
command: bson.D{
{"aggregate", 1},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/349",
},
"FailedToParse": {
command: bson.D{
{"aggregate", 2},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/349",
},
"PipelineTypeMismatch": {
command: bson.D{
{"aggregate", "collection-name"},
{"pipeline", 1},
},
resultType: EmptyResult,
},
"StageTypeMismatch": {
command: bson.D{
{"aggregate", "collection-name"},
{"pipeline", bson.A{1}},
},
resultType: EmptyResult,
},
"InvalidStage": {
command: bson.D{
{"aggregate", "collection-name"},
{"pipeline", bson.A{"$invalid-stage"}},
},
resultType: EmptyResult,
},
"MaxTimeMSDoubleWholeNumber": {
command: bson.D{
{"aggregate", "collection-name"},
{"pipeline", bson.A{}},
{"cursor", bson.D{}},
{"maxTimeMS", float64(1000)},
},
},
}
testAggregateCommandCompat(t, testCases)
}
func TestAggregateCompatOptions(t *testing.T) {
t.Parallel()
providers := []shareddata.Provider{
// one provider is sufficient to test aggregate options
shareddata.Unsets,
}
testCases := map[string]aggregateStagesCompatTestCase{
"MaxTimeZero": {
pipeline: bson.A{},
maxTime: pointer.ToDuration(time.Duration(0)),
},
"MaxTime": {
pipeline: bson.A{},
maxTime: pointer.ToDuration(time.Second),
},
}
testAggregateStagesCompatWithProviders(t, providers, testCases)
}
func TestAggregateCompatStages(t *testing.T) {
t.Parallel()
testCases := map[string]aggregateStagesCompatTestCase{
"MatchAndCount": {
pipeline: bson.A{
bson.D{{"$match", bson.D{{"v", 42}}}},
bson.D{{"$count", "v"}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/396",
failsProviders: []shareddata.Provider{
shareddata.OverflowVergeDoubles,
shareddata.Strings,
shareddata.Binaries,
shareddata.ObjectIDs,
shareddata.Bools,
shareddata.DateTimes,
shareddata.Nulls,
shareddata.Regexes,
shareddata.Timestamps,
shareddata.Unsets,
shareddata.ObjectIDKeys,
shareddata.PostgresEdgeCases,
shareddata.DocumentsDoubles,
shareddata.DocumentsStrings,
shareddata.DocumentsDocuments,
shareddata.DocumentsDeeplyNested,
shareddata.ArrayStrings,
shareddata.ArrayDoubles,
shareddata.ArrayRegexes,
shareddata.ArrayDocuments,
shareddata.Mixed,
shareddata.ArrayAndDocuments,
},
},
"CountAndMatch": {
pipeline: bson.A{
bson.D{{"$count", "v"}},
bson.D{{"$match", bson.D{{"v", 1}}}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
},
}
testAggregateStagesCompat(t, testCases)
}
func TestAggregateCompatEmptyPipeline(t *testing.T) {
t.Parallel()
providers := []shareddata.Provider{
// for testing empty pipeline use a collection with single document,
// because sorting will not matter.
shareddata.Unsets,
}
testCases := map[string]aggregateStagesCompatTestCase{
"Empty": {
pipeline: bson.A{},
},
}
testAggregateStagesCompatWithProviders(t, providers, testCases)
}
func TestAggregateCompatCount(t *testing.T) {
t.Parallel()
testCases := map[string]aggregateStagesCompatTestCase{
"Value": {
pipeline: bson.A{bson.D{{"$count", "v"}}},
},
"NonExistent": {
pipeline: bson.A{bson.D{{"$count", "nonexistent"}}},
},
"CountGroupID": {
pipeline: bson.A{bson.D{{"$count", "_id"}}},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/354",
},
"CountNonString": {
pipeline: bson.A{bson.D{{"$count", 1}}},
resultType: EmptyResult,
},
"CountEmpty": {
pipeline: bson.A{bson.D{{"$count", ""}}},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/349",
},
"CountBadValue": {
pipeline: bson.A{bson.D{{"$count", "v.foo"}}},
resultType: EmptyResult,
},
"CountBadPrefix": {
pipeline: bson.A{bson.D{{"$count", "$foo"}}},
resultType: EmptyResult,
},
}
testAggregateStagesCompat(t, testCases)
}
func TestAggregateCompatGroupNonArrayProviders(t *testing.T) {
t.Parallel()
// Composites, ArrayStrings, ArrayInt32s, ArrayAndDocuments and Mixed are not included
// because the order in compat and target can be not deterministic.
// Aggregation assigns BSON array to output _id, and an array with
// descending sort use the greatest element for comparison causing
// multiple documents with the same greatest element the same order,
// so compat and target results in different order.
// https://github.com/hanzoai/docdb/issues/2185
//
// The Decimal128s are not included because they make tests flaky.
// TODO https://github.com/hanzoai/docdb-DocumentDB/issues/383
providers := shareddata.AllProviders().Remove(
shareddata.Composites, shareddata.Mixed,
shareddata.ArrayStrings, shareddata.ArrayInt32s, shareddata.ArrayAndDocuments,
shareddata.Decimal128s,
)
testCases := map[string]aggregateStagesCompatTestCase{
"DistinctValue": {
pipeline: bson.A{
// sort to assure the same type of values (while grouping 2 types with the same value,
// the first type in collection is chosen)
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$group", bson.D{
{"_id", "$v"},
}}},
// sort descending order, so ArrayDoubles has deterministic order.
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{
shareddata.Scalars,
shareddata.ArrayDocuments, // TODO https://github.com/hanzoai/docdb-DocumentDB/issues/385
},
},
"Distinct": {
pipeline: bson.A{
// sort collection to ensure the order is consistent
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", "$v"},
// set first _id of the collection as group's unique value
{"unique", bson.D{{"$first", "$_id"}}},
}}},
// ensure output is ordered by the _id of the collection, not _id of the group
// because _id of group can be an array
bson.D{{"$sort", bson.D{{"unique", 1}}}},
},
skip: "https://github.com/hanzoai/docdb/issues/2185",
},
"CountValue": {
pipeline: bson.A{
// sort to assure the same type of values (while grouping 2 types with the same value,
// the first type in collection is chosen)
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$group", bson.D{
{"_id", "$v"},
{"count", bson.D{{"$count", bson.D{}}}},
}}},
// sort descending order, so ArrayDoubles has deterministic order.
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{
shareddata.Scalars,
shareddata.ArrayDocuments, // TODO https://github.com/hanzoai/docdb-DocumentDB/issues/385
},
},
"LimitAfter": {
pipeline: bson.A{
// sort to assure the same type of values (while grouping 2 types with the same value,
// the first type in collection is chosen)
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$group", bson.D{{"_id", "$v"}}}},
// sort descending order, so ArrayDoubles has deterministic order.
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$limit", 5}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/385",
failsProviders: []shareddata.Provider{
shareddata.ArrayDocuments,
},
},
"LimitBefore": {
pipeline: bson.A{
// sort to assure the same type of values (while grouping 2 types with the same value,
// the first type in collection is chosen)
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$limit", 5}},
bson.D{{"$group", bson.D{{"_id", "$v"}}}},
// sort descending order, so ArrayDoubles has deterministic order.
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/385",
failsProviders: []shareddata.Provider{
shareddata.ArrayDocuments,
},
},
"SkipAfter": {
pipeline: bson.A{
// sort to assure the same type of values (while grouping 2 types with the same value,
// the first type in collection is chosen)
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$group", bson.D{{"_id", "$v"}}}},
// sort descending order, so ArrayDoubles has deterministic order.
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$skip", 2}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{
shareddata.Scalars,
shareddata.ArrayDocuments, // TODO https://github.com/hanzoai/docdb-DocumentDB/issues/385
},
},
"SkipBefore": {
pipeline: bson.A{
// the first type in collection is chosen)
// sort to assure the same type of values (while grouping 2 types with the same value,
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$skip", 2}},
bson.D{{"$group", bson.D{{"_id", "$v"}}}},
// sort descending order, so ArrayDoubles has deterministic order.
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{
shareddata.Scalars,
},
},
}
testAggregateStagesCompatWithProviders(t, providers, testCases)
}
func TestAggregateCompatGroup(t *testing.T) {
// TODO https://github.com/hanzoai/docdb-DocumentDB/issues/383
t.Skip("https://github.com/hanzoai/docdb-DocumentDB/issues/383")
t.Parallel()
testCases := map[string]aggregateStagesCompatTestCase{
"NullID": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
}}}},
},
"DistinctID": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$_id"},
}}}},
},
"IDExpression": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", bson.D{{"v", "$v"}}},
}}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{
shareddata.Scalars,
},
},
"IDExpressionNested": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", bson.D{{"nested", bson.D{{"v", "$v"}}}}},
}}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/388",
failsProviders: []shareddata.Provider{
shareddata.Scalars,
shareddata.Unsets,
shareddata.Mixed,
},
},
"IDExpressionDotNotation": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", bson.D{{"v", "$v.foo"}}},
}}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/388",
failsProviders: []shareddata.Provider{
shareddata.Scalars,
shareddata.Doubles,
shareddata.OverflowVergeDoubles,
shareddata.SmallDoubles,
shareddata.Strings,
shareddata.Binaries,
shareddata.ObjectIDs,
shareddata.Bools,
shareddata.DateTimes,
shareddata.Nulls,
shareddata.Regexes,
shareddata.Int32s,
shareddata.Timestamps,
shareddata.Int64s,
shareddata.ObjectIDKeys,
shareddata.DocumentsDoubles,
shareddata.DocumentsStrings,
shareddata.Mixed,
},
},
"IDExpressionNonExistentField": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", bson.D{{"missing", "$non-existent"}}},
}}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
},
"IDExpressionFields": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", bson.D{
{"v", "$v"},
{"foo", "$v.foo"},
}},
}}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/388",
failsProviders: []shareddata.Provider{
shareddata.Scalars,
shareddata.Unsets,
shareddata.Composites,
shareddata.DocumentsDoubles,
shareddata.DocumentsStrings,
shareddata.DocumentsDocuments,
shareddata.DocumentsDeeplyNested,
shareddata.PostgresEdgeCases,
shareddata.Mixed,
},
},
"IDExpressionNonExistentFields": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", bson.D{
{"missing1", "$non-existent1"},
{"missing2", "$non-existent2"},
}},
}}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/388",
},
"IDExpressionAndOperator": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", bson.D{
{"v", "$v"},
{"sum", bson.D{{"$sum", "$v"}}},
}},
}}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/388",
failsProviders: []shareddata.Provider{
shareddata.Unsets,
shareddata.Mixed,
shareddata.Scalars,
},
},
"IDInvalidExpressionAndOperator": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", bson.D{
{"v", "$"},
{"sum", bson.D{{"$sum", "$v"}}},
}},
}}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/390",
},
"IDExpressionAndInvalidOperator": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", bson.D{
{"v", "$v"},
{"sum", bson.D{{"$sum", "$v"}, {"second", "field"}}},
}},
}}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/389",
},
"IDDocument": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", bson.D{{"v", "v"}}},
}}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
},
"IDNestedDocument": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", bson.D{{"v", bson.D{{"nested", 1}}}}},
}}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
},
"NonExistentID": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$invalid"},
}}}},
},
"NonExpressionID": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "v"},
}}}},
},
"NonStringID": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", bson.A{}},
}}}},
},
"OperatorNameAsExpression": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$add"},
}}}},
},
"EmptyPath": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$"},
}}}},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/390",
},
"EmptyVariable": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$$"},
}}}},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/390",
},
"InvalidVariable$": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$$$"},
}}}},
resultType: EmptyResult,
},
"InvalidVariable$s": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$$$s"},
}}}},
resultType: EmptyResult,
},
"NonExistingVariable": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$$s"},
}}}},
resultType: EmptyResult,
skip: "https://github.com/hanzoai/docdb/issues/2275",
},
"SystemVariable": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$$NOW"},
}}}},
resultType: EmptyResult,
skip: "https://github.com/hanzoai/docdb/issues/2275",
},
"GroupInvalidFields": {
pipeline: bson.A{bson.D{{"$group", 1}}},
resultType: EmptyResult,
},
"EmptyGroup": {
pipeline: bson.A{bson.D{{"$group", bson.D{}}}},
resultType: EmptyResult,
},
"MissingID": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"bla", 1},
}}}},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/389",
},
"InvalidAccumulator": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
{"v", 1},
}}}},
resultType: EmptyResult,
},
"EmptyAccumulator": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
{"v", bson.D{}},
}}}},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/389",
},
"GroupMultipleAccumulator": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
{"v", bson.D{{"$count", "v"}, {"$count", "v"}}},
}}}},
resultType: EmptyResult,
},
"GroupInvalidAccumulator": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
{"v", bson.D{{"invalid", "v"}}},
}}}},
resultType: EmptyResult,
skip: "https://github.com/hanzoai/docdb/issues/2123",
},
"IDType": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", bson.D{{"$type", "$v"}}},
{"count", bson.D{{"$count", bson.D{}}}},
}}}},
},
"IDSum": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{{"_id", bson.D{{"$sum", "$v"}}}}}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{
shareddata.Scalars,
shareddata.Doubles,
},
},
"IDFieldSum": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{{"_id", bson.D{{"sum", bson.D{{"$sum", "$v"}}}}}}}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{
shareddata.Scalars,
shareddata.Doubles,
},
},
"IDNestedFieldSum": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{{"_id", bson.D{{"nested", bson.D{{"sum", bson.D{{"$sum", "$v"}}}}}}}}}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{
shareddata.Scalars,
shareddata.Doubles,
},
},
"IDSumNonExistentField": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{{"_id", bson.D{{"$sum", "$non-existent"}}}}}},
bson.D{{"$sort", bson.D{{"_id", 1}}}},
},
},
"IDSumInvalid": {
pipeline: bson.A{
bson.D{{"$group", bson.D{{"_id", bson.D{{"$sum", "$"}}}}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/390",
},
"IDSumRecursiveInvalid": {
pipeline: bson.A{
bson.D{{"$group", bson.D{{"_id", bson.D{{"$sum", bson.D{{"$sum", "$"}}}}}}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/390",
},
}
testAggregateStagesCompat(t, testCases)
}
func TestAggregateCompatGroupExpressionDotNotation(t *testing.T) {
t.Parallel()
// Use all providers after fixing $sort problem:
// TODO https://github.com/hanzoai/docdb/issues/2276
//
// Currently, providers Composites, DocumentsDeeplyNested, ArrayAndDocuments and Mixed
// cannot be used due to sorting difference.
// DocDB always sorts empty array is less than null.
// In compat, for `.sort()` an empty array is less than null.
// In compat, for aggregation `$sort` null is less than an empty array.
providers := shareddata.AllProviders().Remove(shareddata.Mixed, shareddata.Composites, shareddata.DocumentsDeeplyNested, shareddata.ArrayAndDocuments)
testCases := map[string]aggregateStagesCompatTestCase{
"NestedInDocument": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$v.foo"},
}}}},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/394",
failsProviders: shareddata.Providers{
shareddata.Scalars,
shareddata.Doubles,
shareddata.Decimal128s,
shareddata.OverflowVergeDoubles,
shareddata.SmallDoubles,
shareddata.Strings,
shareddata.Binaries,
shareddata.ObjectIDs,
shareddata.Bools,
shareddata.DateTimes,
shareddata.Nulls,
shareddata.Regexes,
shareddata.Int32s,
shareddata.Timestamps,
shareddata.Int64s,
shareddata.ObjectIDKeys,
shareddata.DocumentsDoubles,
shareddata.DocumentsStrings,
},
},
"DeeplyNested": { // Expect non-empty results for DocumentsDeeplyNested provider
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$v.a.b.c"},
}}}},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/394",
failsProviders: shareddata.Providers{
shareddata.Scalars,
shareddata.Doubles,
shareddata.Decimal128s,
shareddata.OverflowVergeDoubles,
shareddata.SmallDoubles,
shareddata.Strings,
shareddata.Binaries,
shareddata.ObjectIDs,
shareddata.Bools,
shareddata.DateTimes,
shareddata.Nulls,
shareddata.Regexes,
shareddata.Int32s,
shareddata.Timestamps,
shareddata.Int64s,
shareddata.ObjectIDKeys,
shareddata.DocumentsDoubles,
shareddata.DocumentsStrings,
},
},
"ArrayIndex": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$v.0"},
}}}},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/394",
failsProviders: shareddata.Providers{
shareddata.Scalars,
shareddata.Doubles,
shareddata.Decimal128s,
shareddata.OverflowVergeDoubles,
shareddata.SmallDoubles,
shareddata.Strings,
shareddata.Binaries,
shareddata.ObjectIDs,
shareddata.Bools,
shareddata.DateTimes,
shareddata.Nulls,
shareddata.Regexes,
shareddata.Int32s,
shareddata.Timestamps,
shareddata.Int64s,
shareddata.ObjectIDKeys,
shareddata.DocumentsDoubles,
shareddata.DocumentsStrings,
},
},
"NestedInArray": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$v.0.foo"},
}}}},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/394",
failsProviders: shareddata.Providers{
shareddata.Scalars,
shareddata.Doubles,
shareddata.Decimal128s,
shareddata.OverflowVergeDoubles,
shareddata.SmallDoubles,
shareddata.Strings,
shareddata.Binaries,
shareddata.ObjectIDs,
shareddata.Bools,
shareddata.DateTimes,
shareddata.Nulls,
shareddata.Regexes,
shareddata.Int32s,
shareddata.Timestamps,
shareddata.Int64s,
shareddata.ObjectIDKeys,
shareddata.DocumentsDoubles,
shareddata.DocumentsStrings,
},
},
"NonExistentChild": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$v.non.existent"},
}}}},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/394",
failsProviders: shareddata.Providers{
shareddata.Scalars,
shareddata.Doubles,
shareddata.Decimal128s,
shareddata.OverflowVergeDoubles,
shareddata.SmallDoubles,
shareddata.Strings,
shareddata.Binaries,
shareddata.ObjectIDs,
shareddata.Bools,
shareddata.DateTimes,
shareddata.Nulls,
shareddata.Regexes,
shareddata.Int32s,
shareddata.Timestamps,
shareddata.Int64s,
shareddata.ObjectIDKeys,
shareddata.DocumentsDoubles,
shareddata.DocumentsStrings,
},
},
}
testAggregateStagesCompatWithProviders(t, providers, testCases)
}
func TestAggregateCompatGroupExpressionNestedDotNotation(t *testing.T) {
t.Parallel()
// Merge the current function with TestAggregateCompatGroupExpressionDottedFields
// and use all providers when $sort problem is fixed:
// TODO https://github.com/hanzoai/docdb/issues/2276
providers := []shareddata.Provider{
shareddata.DocumentsDeeplyNested,
}
testCases := map[string]aggregateStagesCompatTestCase{
"DeeplyNested": { // Expect non-empty results for DocumentsDeeplyNested provider
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$v.a.b.c"},
}}}},
skip: "https://github.com/hanzoai/docdb/issues/2276",
// compat results [ { _id: null }, { _id: 12 }, { _id: { d: 123 } }, { _id: [ 1, 2 ] } ]
// target results [ { _id: null }, { _id: [ 1, 2 ] }, { _id: 12 }, { _id: { d: 123 } } ]
},
}
testAggregateStagesCompatWithProviders(t, providers, testCases)
}
func TestAggregateCompatGroupCount(t *testing.T) {
t.Parallel()
testCases := map[string]aggregateStagesCompatTestCase{
"CountNull": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
{"count", bson.D{{"$count", bson.D{}}}},
}}}},
},
"CountID": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$_id"},
{"count", bson.D{{"$count", bson.D{}}}},
}}}},
},
"TypeMismatch": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
{"count", bson.D{{"$count", ""}}},
}}}},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/390",
},
"NonEmptyExpression": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", nil},
{"count", bson.D{{"$count", bson.D{{"a", 1}}}}},
}}}},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/390",
},
"NonExistentField": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$nonexistent"},
{"count", bson.D{{"$count", bson.D{}}}},
}}}},
},
"Duplicate": {
pipeline: bson.A{bson.D{{"$group", bson.D{
{"_id", "$v"},
{"count", bson.D{{"$count", bson.D{}}}},
{"count", bson.D{{"$count", bson.D{}}}},
}}}},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/390",
},
}
testAggregateStagesCompat(t, testCases)
}
func TestAggregateCompatLimit(t *testing.T) {
t.Parallel()
testCases := map[string]aggregateStagesCompatTestCase{
"Zero": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$limit", 0}},
},
resultType: EmptyResult,
},
"One": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$limit", 1}},
},
},
"Five": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$limit", 5}},
},
},
"StringInt": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$limit", "5"}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/349",
},
"Double": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$limit", 4.5}},
},
resultType: EmptyResult,
},
"DoubleInt": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$limit", 5.0}},
},
},
"MaxInt64": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$limit", math.MaxInt64}},
},
},
"MinInt64": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$limit", math.MinInt64}},
},
resultType: EmptyResult,
},
"Negative": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$limit", -1}},
},
resultType: EmptyResult,
},
"NegativeDouble": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$limit", -2.1}},
},
resultType: EmptyResult,
},
"Document": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$limit", bson.D{}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/349",
},
"Int64Overflow": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$limit", float64(1 << 86)}},
},
resultType: EmptyResult,
},
"AfterMatch": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$match", bson.D{{"v", "foo"}}}},
bson.D{{"$limit", 1}},
},
},
"BeforeMatch": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$limit", 1}},
bson.D{{"$match", bson.D{{"v", "foo"}}}},
},
resultType: EmptyResult,
},
"NoSortAfterMatch": {
pipeline: bson.A{
bson.D{{"$match", bson.D{{"v", "foo"}}}},
bson.D{{"$limit", 100}},
},
},
"NoSortBeforeMatch": {
pipeline: bson.A{
bson.D{{"$limit", 100}},
bson.D{{"$match", bson.D{{"v", "foo"}}}},
},
},
}
testAggregateStagesCompat(t, testCases)
}
func TestAggregateCompatGroupSum(t *testing.T) {
t.Parallel()
providers := shareddata.AllProviders().
// skipped due to https://github.com/hanzoai/docdb/issues/2185.
Remove(shareddata.Composites).
Remove(shareddata.ArrayStrings).
Remove(shareddata.ArrayInt32s).
Remove(shareddata.Mixed).
Remove(shareddata.ArrayAndDocuments).
// Handle $sum of doubles near max precision.
// TODO https://github.com/hanzoai/docdb/issues/2300
Remove(shareddata.Doubles).
// TODO https://github.com/hanzoai/docdb/issues/2616
Remove(shareddata.ArrayDocuments)
testCases := map[string]aggregateStagesCompatTestCase{
"GroupNullID": {
pipeline: bson.A{
// Without $sort, the sum of large values results different in compat and target.
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 shareddata collections.
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{shareddata.Scalars, shareddata.Int64s, shareddata.Int32s},
},
"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}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{shareddata.Scalars},
},
"EmptyString": {
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}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{shareddata.Scalars},
},
"NonExpression": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", nil},
{"sum", bson.D{{"$sum", "v"}}},
}}},
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
},
"NonExistent": {
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}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{shareddata.Scalars},
},
"Document": {
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}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{shareddata.Scalars},
},
"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"}}}},
}}},
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/390",
},
"Int32": {
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}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{shareddata.Scalars},
},
"MaxInt32": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", "$v"},
{"sum", bson.D{{"$sum", math.MaxInt32}}},
}}},
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{shareddata.Scalars},
},
"NegativeInt32": {
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}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{shareddata.Scalars},
},
"Int64": {
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}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{shareddata.Scalars},
},
"Double": {
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}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{shareddata.Scalars},
},
"MaxDouble": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", "$v"},
{"sum", bson.D{{"$sum", math.MaxFloat64}}},
}}},
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{shareddata.Scalars},
},
"Bool": {
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}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/383",
failsProviders: []shareddata.Provider{shareddata.Scalars},
},
"Duplicate": {
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"}}},
}}},
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/390",
},
"RecursiveOperator": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$group", bson.D{
{"_id", "$_id"},
// first $sum is accumulator operator, second $sum is operator
{"sum", bson.D{{"$sum", bson.D{{"$sum", "$v"}}}}},
}}},
bson.D{{"$sort", bson.D{{"_id", -1}}}},
},
},
"RecursiveInvalid": {
pipeline: bson.A{
bson.D{{"$group", bson.D{{"sum", bson.D{{"$sum", bson.D{{"v", "$v"}}}}}}}},
},
resultType: EmptyResult,
},
"RecursiveArrayInvalid": {
pipeline: bson.A{
bson.D{{"$group", bson.D{{"sum", bson.D{{"$sum", bson.D{{"$type", bson.A{"1", "2"}}}}}}}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/389",
},
"RecursiveOperatorNonExistent": {
pipeline: bson.A{
bson.D{{"$group", bson.D{
{"_id", "$_id"},
// first $sum is accumulator operator, second $sum is operator
{"sum", bson.D{{"$sum", bson.D{{"$non-existent", "$v"}}}}},
}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/389",
},
}
testAggregateStagesCompatWithProviders(t, providers, testCases)
}
func TestAggregateCompatMatch(t *testing.T) {
t.Parallel()
// TODO https://github.com/hanzoai/docdb/issues/2291
providers := shareddata.AllProviders().Remove(shareddata.ArrayAndDocuments)
testCases := map[string]aggregateStagesCompatTestCase{
"ID": {
pipeline: bson.A{bson.D{{"$match", bson.D{{"_id", "string"}}}}},
},
"Int": {
pipeline: bson.A{
bson.D{{"$match", bson.D{{"v", 42}}}},
},
},
"String": {
pipeline: bson.A{
bson.D{{"$match", bson.D{{"v", "foo"}}}},
},
},
"Document": {
pipeline: bson.A{bson.D{{"$match", bson.D{{"v", bson.D{{"foo", int32(42)}}}}}}},
},
"Array": {
pipeline: bson.A{bson.D{{"$match", bson.D{{"v", bson.A{int32(42)}}}}}},
},
"Regex": {
pipeline: bson.A{bson.D{{"$match", bson.D{{"v", bson.D{{"$eq", primitive.Regex{Pattern: "foo", Options: "i"}}}}}}}},
},
"Empty": {
pipeline: bson.A{
bson.D{{"$match", bson.D{}}},
},
},
"DotNotationDocument": {
pipeline: bson.A{
bson.D{{"$match", bson.D{{"v.foo", int32(42)}}}},
},
},
"DotNotationArray": {
pipeline: bson.A{
bson.D{{"$match", bson.D{{"v.0", int32(42)}}}},
},
},
"MatchBadValue": {
pipeline: bson.A{bson.D{{"$match", 1}}},
resultType: EmptyResult,
},
"SumValue": {
pipeline: bson.A{
bson.D{{"$match", bson.D{{"$expr", bson.D{{"$sum", "$v"}}}}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/362",
failsProviders: []shareddata.Provider{shareddata.Decimal128s, shareddata.Doubles, shareddata.Int64s, shareddata.Scalars},
},
}
testAggregateStagesCompatWithProviders(t, providers, testCases)
}
func TestAggregateCompatSort(t *testing.T) {
t.Parallel()
testCases := map[string]aggregateStagesCompatTestCase{
"AscendingID": {
pipeline: bson.A{bson.D{{"$sort", bson.D{{"_id", 1}}}}},
},
"DescendingID": {
pipeline: bson.A{bson.D{{"$sort", bson.D{{"_id", -1}}}}},
},
"AscendingValue": {
pipeline: bson.A{bson.D{{"$sort", bson.D{
{"v", 1},
{"_id", 1}, // sort by _id when v is the same.
}}}},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/355",
failsProviders: []shareddata.Provider{shareddata.ArrayStrings, shareddata.Composites},
},
"DescendingValue": {
pipeline: bson.A{bson.D{{"$sort", bson.D{
{"v", -1},
{"_id", 1}, // sort by _id when v is the same.
}}}},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/355",
failsProviders: []shareddata.Provider{shareddata.ArrayStrings, shareddata.Composites, shareddata.Mixed},
},
"AscendingValueDescendingID": {
pipeline: bson.A{bson.D{{"$sort", bson.D{
{"v", 1},
{"_id", -1},
}}}},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/355",
failsProviders: []shareddata.Provider{shareddata.ArrayStrings, shareddata.Composites, shareddata.Mixed},
},
"DescendingValueDescendingID": {
pipeline: bson.A{bson.D{{"$sort", bson.D{
{"v", -1},
{"_id", -1},
}}}},
},
// TODO https://github.com/hanzoai/docdb-DocumentDB/issues/355
// "DotNotationIndex": {
// pipeline: bson.A{bson.D{{"$sort", bson.D{
// {"v.0", 1},
// {"_id", 1}, // sort by _id when v is the same.
// }}}},
// },
"DotNotationNonExistent": {
pipeline: bson.A{bson.D{{"$sort", bson.D{
{"invalid.foo", 1},
{"_id", 1}, // sort by _id when v is the same.
}}}},
},
"DotNotationMissingField": {
pipeline: bson.A{bson.D{{"$sort", bson.D{
{"v..foo", 1},
}}}},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/349",
},
"SortBadExpression": {
pipeline: bson.A{bson.D{{"$sort", 1}}},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/349",
},
"SortBadOrder": {
pipeline: bson.A{bson.D{{"$sort", bson.D{{"_id", 0}}}}},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/349",
},
"SortMissingKey": {
pipeline: bson.A{bson.D{{"$sort", bson.D{}}}},
resultType: EmptyResult,
},
"BadDollarStart": {
pipeline: bson.A{bson.D{{"$sort", bson.D{{"$v.foo", 1}}}}},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/354",
},
}
testAggregateStagesCompat(t, testCases)
}
func TestAggregateCompatSortDotNotation(t *testing.T) {
t.Parallel()
providers := shareddata.AllProviders().
// TODO https://github.com/hanzoai/docdb/issues/2617
Remove(shareddata.ArrayDocuments)
testCases := map[string]aggregateStagesCompatTestCase{
"DotNotation": {
pipeline: bson.A{bson.D{{"$sort", bson.D{
{"v.foo", 1},
{"_id", 1}, // sort by _id when v is the same.
}}}},
},
}
testAggregateStagesCompatWithProviders(t, providers, testCases)
}
func TestAggregateCompatUnwind(t *testing.T) {
t.Parallel()
testCases := map[string]aggregateStagesCompatTestCase{
"Simple": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$unwind", "$v"}},
},
},
"NonExistent": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$unwind", "$non-existent"}},
},
resultType: EmptyResult,
},
"Invalid": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$unwind", "invalid"}},
},
resultType: EmptyResult,
},
"DotNotation": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$unwind", "$v.foo"}},
},
},
"DotNotationNonExistent": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$unwind", "$v.non-existent"}},
},
resultType: EmptyResult,
},
"ArrayDotNotation": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$unwind", "$v.0"}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/367",
failsProviders: shareddata.Providers{
shareddata.ArrayStrings,
shareddata.ArrayAndDocuments,
shareddata.ArrayDoubles,
shareddata.ArrayDocuments,
shareddata.ArrayStrings,
shareddata.ArrayInt32s,
shareddata.ArrayInt64s,
shareddata.ArrayRegexes,
shareddata.Composites,
},
},
"ArrayDotNotationKey": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$unwind", "$v.0.foo"}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/367",
failsProviders: shareddata.Providers{
shareddata.ArrayAndDocuments,
shareddata.ArrayDocuments,
},
},
"Null": {
pipeline: bson.A{bson.D{{"$unwind", nil}}},
resultType: EmptyResult,
},
"ID": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$unwind", "$_id"}},
},
},
"NameAsExpression": {
pipeline: bson.A{bson.D{{"$unwind", "$add"}}},
resultType: EmptyResult,
},
"EmptyPath": {
pipeline: bson.A{bson.D{{"$unwind", "$"}}},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/349",
},
"EmptyVariable": {
pipeline: bson.A{bson.D{{"$unwind", "$$"}}},
resultType: EmptyResult,
},
"InvalidVariable$": {
pipeline: bson.A{bson.D{{"$unwind", "$$$"}}},
resultType: EmptyResult,
},
"InvalidVariable$s": {
pipeline: bson.A{bson.D{{"$unwind", "$$$s"}}},
resultType: EmptyResult,
},
"NonExistingVariable": {
pipeline: bson.A{bson.D{{"$unwind", "$$s"}}},
resultType: EmptyResult,
},
"SystemVariable": {
pipeline: bson.A{bson.D{{"$unwind", "$$NOW"}}},
resultType: EmptyResult,
},
"Empty": {
pipeline: bson.A{bson.D{{"$unwind", ""}}},
resultType: EmptyResult,
},
"Number": {
pipeline: bson.A{bson.D{{"$unwind", 42}}},
resultType: EmptyResult,
},
"Array": {
pipeline: bson.A{bson.D{{"$unwind", bson.A{"$v"}}}},
resultType: EmptyResult,
},
}
testAggregateStagesCompat(t, testCases)
}
func TestAggregateCompatSkip(t *testing.T) {
t.Parallel()
testCases := map[string]aggregateStagesCompatTestCase{
"Document": {
pipeline: bson.A{bson.D{{"$skip", bson.D{}}}},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/349",
},
"Zero": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$skip", int32(0)}},
},
},
"One": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$skip", int32(1)}},
},
},
"SkipAll": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$skip", int32(1000)}},
},
resultType: EmptyResult,
},
"StringInt": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$skip", "1"}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/349",
},
"NegativeValue": {
pipeline: bson.A{bson.D{{"$skip", int32(-1)}}},
resultType: EmptyResult,
},
"NegativeDouble": {
pipeline: bson.A{bson.D{{"$skip", -3.2}}},
resultType: EmptyResult,
},
"MaxInt64": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$skip", math.MaxInt64}},
},
resultType: EmptyResult,
},
"MinInt64": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$skip", math.MinInt64}},
},
resultType: EmptyResult,
},
"Int64Overflow": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$skip", float64(1 << 86)}},
},
resultType: EmptyResult,
},
"AfterMatch": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$match", bson.D{{"v", "foo"}}}},
bson.D{{"$skip", int32(1)}},
},
},
"BeforeMatch": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$skip", int32(1)}},
bson.D{{"$match", bson.D{{"v", "foo"}}}},
},
},
}
testAggregateStagesCompat(t, testCases)
}
func TestAggregateCompatProject(t *testing.T) {
t.Parallel()
testCases := map[string]aggregateStagesCompatTestCase{
"InvalidType": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", "invalid"}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/368",
},
"ZeroOperators": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"v", bson.D{}}}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/369",
},
"TwoOperators": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"v", bson.D{{"$type", "foo"}, {"$sum", 1}}}}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/368",
},
"DollarSignField": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"$v", 1}}}},
},
resultType: EmptyResult,
},
"Include1Field": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"v", int32(1)}}}},
},
},
"Exclude1Field": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"v", int64(0)}}}},
},
},
"IncludeID": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"_id", 1.42}}}},
},
},
"ExcludeID": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"_id", false}}}},
},
},
"Include2Fields": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"foo", 1.24}, {"bar", true}}}},
},
},
"Exclude2Fields": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"foo", int32(0)}, {"bar", false}}}},
},
},
"Include1FieldExclude1Field": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"foo", int32(0)}, {"bar", true}}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/368",
},
"Exclude1FieldInclude1Field": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"foo", int32(1)}, {"bar", false}}}},
},
resultType: EmptyResult,
},
"IncludeFieldExcludeID": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"_id", false}, {"v", true}}}},
},
},
"ExcludeFieldIncludeID": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"_id", true}, {"v", false}}}},
},
},
"ExcludeFieldExcludeID": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"_id", false}, {"v", false}}}},
},
},
"IncludeFieldIncludeID": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"_id", true}, {"v", true}}}},
},
},
"Assign1Field": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"foo", primitive.NewObjectID()}}}},
},
},
"AssignID": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"_id", primitive.Binary{Subtype: 0x80, Data: []byte{42, 0, 13}}}}}},
},
},
"Assign1FieldIncludeID": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"_id", true}, {"foo", primitive.NewDateTimeFromTime(time.Unix(0, 0))}}}},
},
},
"Assign2FieldsIncludeID": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"_id", true}, {"foo", nil}, {"bar", "qux"}}}},
},
},
"Assign1FieldExcludeID": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"_id", false}, {"foo", primitive.Regex{Pattern: "^fo"}}}}},
},
},
"DotNotationInclude": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"_id", true},
{"v.foo", true},
}}},
},
},
"DotNotationIncludeTwo": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"_id", true},
{"v.foo", true},
{"v.array", true},
}}},
},
},
"DotNotationExclude": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"_id", true},
{"v.foo", false},
}}},
},
},
"DotNotationExcludeTwo": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"_id", true},
{"v.foo", false},
{"v.array", false},
}}},
},
},
"DotNotationExcludeSecondLevel": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"_id", true},
{"v.array.42", false},
}}},
},
},
"DotNotationIncludeExclude": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"_id", true},
{"v.foo", true},
{"v.array.42", false},
}}},
},
resultType: EmptyResult,
},
"EmptyDocument": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"foo", bson.D{}}}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/369",
},
"Document": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"foo", bson.D{{"v", "foo"}}}}}},
},
},
"IDDocument": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"_id", bson.D{{"v", "foo"}}}}}},
},
},
"IDType": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"_id", bson.D{{"$type", "$v"}}}}}},
},
},
"DocumentAndValue": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{
{"foo", bson.D{{"v", "foo"}}},
{"v", 1},
}}},
},
},
"Type": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", "$v"}}}}}},
},
},
"TypeNonExistent": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", "$foo"}}}}}},
},
},
"TypeDotNotation": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", "$v.foo"}}}}}},
},
},
"TypeRecursive": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", bson.D{{"$type", "$v"}}}}}}}},
},
},
"TypeRecursiveNonExistent": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", bson.D{{"$non-existent", "$v"}}}}}}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/368",
},
"TypeRecursiveInvalid": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", bson.D{{"v", "$v"}}}}}}}},
},
},
"TypeRecursiveArrayInvalid": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", bson.D{{"$type", bson.A{"1", "2"}}}}}}}}},
},
resultType: EmptyResult,
},
"TypeInt": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", int32(42)}}}}}},
},
},
"TypeLong": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", int64(42)}}}}}},
},
},
"TypeString": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", "42"}}}}}},
},
},
"TypeDocument": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", bson.D{{"foo", "bar"}}}}}}}},
},
},
"TypeEmpty": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", bson.D{}}}}}}},
},
},
"TypeArraySingleItem": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", bson.A{int32(42)}}}}}}},
},
},
"TypeArray": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", bson.A{"foo", "bar"}}}}}}},
},
resultType: EmptyResult,
},
"TypeNestedArray": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", bson.A{bson.A{"foo", "bar"}}}}}}}},
},
},
"TypeObjectID": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", primitive.NewObjectID()}}}}}},
},
},
"TypeBool": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"type", bson.D{{"$type", true}}}}}},
},
},
"ProjectManyOperators": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$project", bson.D{{"$type", "foo"}, {"$op", "foo"}}}},
},
resultType: EmptyResult,
},
}
testAggregateStagesCompat(t, testCases)
}
func TestAggregateCompatProjectSum(t *testing.T) {
t.Parallel()
testCases := map[string]aggregateStagesCompatTestCase{
"Value": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", "$v"}}},
}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/1078",
failsProviders: shareddata.Providers{shareddata.Decimal128s},
},
"DotNotation": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", "$v.foo"}}},
}}},
},
},
"ArrayDotNotation": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", "$v.0.foo"}}},
}}},
},
},
"Int": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", int32(2)}}},
}}},
},
},
"Long": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", int64(3)}}},
}}},
},
},
"Double": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", float64(4)}}},
}}},
},
},
"EmptyString": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", ""}}},
}}},
},
},
"ArrayEmptyVariable": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", bson.A{"$"}}}},
}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/369",
},
"ArrayValue": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", bson.A{"$v"}}}},
}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/1078",
failsProviders: shareddata.Providers{shareddata.Decimal128s},
},
"ArrayTwoValues": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", bson.A{"$v", "$v"}}}},
}}},
},
},
"ArrayValueInt": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", bson.A{"$v", int32(1)}}}},
}}},
},
},
"ArrayIntLongDoubleStringBool": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", bson.A{int32(2), int64(3), float64(4), "not-expression", true}}}},
}}},
},
},
"RecursiveValue": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sumsum", bson.D{{"$sum", bson.D{{"$sum", "$v"}}}}},
}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/1078",
failsProviders: shareddata.Providers{shareddata.Decimal128s},
},
"RecursiveArrayValue": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sumsum", bson.D{{"$sum", bson.D{{"$sum", bson.A{"$v"}}}}}},
}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/1078",
failsProviders: shareddata.Providers{shareddata.Decimal128s},
},
"ArrayValueRecursiveInt": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", bson.A{"$v", bson.D{{"$sum", int32(2)}}}}}},
}}},
},
},
"ArrayValueAndRecursiveValue": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", bson.A{"$v", bson.D{{"$sum", "$v"}}}}}},
}}},
},
},
"ArrayValueAndRecursiveArray": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", bson.A{"$v", bson.D{{"$sum", bson.A{"$v"}}}}}}},
}}},
},
},
"Type": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sumtype", bson.D{{"$sum", bson.D{{"$type", "$v"}}}}},
}}},
},
},
"RecursiveEmptyVariable": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", bson.D{{"$sum", "$$$"}}}}},
}}},
},
resultType: EmptyResult,
},
"MultipleRecursiveEmptyVariable": {
pipeline: bson.A{
bson.D{{"$project", bson.D{
{"sum", bson.D{{"$sum", bson.D{{"$sum", bson.D{{"$sum", "$$$"}}}}}}},
}}},
},
resultType: EmptyResult,
},
}
testAggregateStagesCompat(t, testCases)
}
func TestAggregateCompatAddFields(t *testing.T) {
t.Parallel()
testCases := map[string]aggregateStagesCompatTestCase{
"InvalidTypeString": {
pipeline: bson.A{
bson.D{{"$addFields", "invalid"}},
},
resultType: EmptyResult,
},
"InvalidTypeBool": {
pipeline: bson.A{
bson.D{{"$addFields", false}},
},
resultType: EmptyResult,
},
"InvalidTypeArray": {
pipeline: bson.A{
bson.D{{"$addFields", bson.A{}}},
},
resultType: EmptyResult,
},
"InvalidTypeInt32": {
pipeline: bson.A{
bson.D{{"$addFields", int32(1)}},
},
resultType: EmptyResult,
},
"InvalidTypeInt64": {
pipeline: bson.A{
bson.D{{"$addFields", int64(1)}},
},
resultType: EmptyResult,
},
"InvalidTypeFloat32": {
pipeline: bson.A{
bson.D{{"$addFields", float32(1)}},
},
resultType: EmptyResult,
},
"InvalidTypeFloat64": {
pipeline: bson.A{
bson.D{{"$addFields", float64(1)}},
},
resultType: EmptyResult,
},
"InvalidTypeNull": {
pipeline: bson.A{
bson.D{{"$addFields", nil}},
},
resultType: EmptyResult,
},
"Include1Field": {
pipeline: bson.A{
bson.D{{"$addFields", bson.D{{"newField", int32(1)}}}},
},
},
"Include2Fields": {
pipeline: bson.A{
bson.D{{"$addFields", bson.D{{"newField1", int32(1)}, {"newField2", int32(2)}}}},
},
},
"Include2Stages": {
pipeline: bson.A{
bson.D{{"$addFields", bson.D{{"newField1", int32(1)}}}},
bson.D{{"$addFields", bson.D{{"newField2", int32(2)}}}},
},
},
"IncludeDocument": {
pipeline: bson.A{
bson.D{{"$addFields", bson.D{{"newField", bson.D{{"doc", int32(1)}}}}}},
},
},
"IncludeNestedDocument": {
pipeline: bson.A{
bson.D{{"$addFields", bson.D{{"newField", bson.D{{"doc", bson.D{{"nested", int32(1)}}}}}}}},
},
},
"IncludeArray": {
pipeline: bson.A{
bson.D{{"$addFields", bson.D{{"newField", bson.A{bson.D{{"elem", int32(1)}}}}}}},
},
},
"UnsupportedExpressionObject": {
pipeline: bson.A{
bson.D{{"$addFields", bson.D{{"newField1", bson.D{{"$sum", 1}}}}}},
},
resultType: EmptyResult,
skip: "https://github.com/hanzoai/docdb/issues/1413",
},
"UnsupportedExpressionArray": {
pipeline: bson.A{
bson.D{{"$addFields", bson.D{{"newField1", bson.A{bson.D{{"$sum", 1}}}}}}},
},
resultType: EmptyResult,
skip: "https://github.com/hanzoai/docdb/issues/1413",
},
"InvalidOperator": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"value", bson.D{{"$invalid-operator", "foo"}}}}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/349",
},
"Type": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", "$v"}}}}}},
},
},
"TypeNonExistent": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", "$foo"}}}}}},
},
},
"TypeDotNotation": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", "$v.foo"}}}}}},
},
},
"TypeRecursive": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", bson.D{{"$type", "$v"}}}}}}}},
},
},
"TypeRecursiveNonExistent": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", bson.D{{"$non-existent", "$v"}}}}}}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/349",
},
"TypeRecursiveInvalid": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", bson.D{{"v", "$v"}}}}}}}},
},
},
"TypeInt": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", int32(42)}}}}}},
},
},
"TypeLong": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", int64(42)}}}}}},
},
},
"TypeString": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", "42"}}}}}},
},
},
"TypeDocument": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", bson.D{{"foo", "bar"}}}}}}}},
},
},
"TypeEmpty": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", bson.D{}}}}}}},
},
},
"MultipleOperators": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", "foo"}, {"$operator", "foo"}}}}}},
},
resultType: EmptyResult,
},
"MultipleOperatorFirst": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", "foo"}, {"not-operator", "foo"}}}}}},
},
resultType: EmptyResult,
},
"MultipleOperatorLast": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"not-operator", "foo"}, {"$type", "foo"}}}}}},
},
resultType: EmptyResult,
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/349",
},
"TypeArraySingleItem": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", bson.A{int32(42)}}}}}}},
},
},
"TypeArray": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", bson.A{"foo", "bar"}}}}}}},
},
resultType: EmptyResult,
},
"TypeNestedArray": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", bson.A{bson.A{"foo", "bar"}}}}}}}},
},
},
"TypeObjectID": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", primitive.NewObjectID()}}}}}},
},
},
"TypeBool": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", -1}}}},
bson.D{{"$addFields", bson.D{{"type", bson.D{{"$type", true}}}}}},
},
},
"SumValue": {
pipeline: bson.A{
bson.D{{"$addFields", bson.D{
{"sum", bson.D{{"$sum", "$v"}}},
}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/1078",
failsProviders: shareddata.Providers{shareddata.Decimal128s},
},
}
testAggregateStagesCompat(t, testCases)
}
func TestAggregateCompatSet(t *testing.T) {
t.Parallel()
testCases := map[string]aggregateStagesCompatTestCase{
"InvalidTypeString": {
pipeline: bson.A{
bson.D{{"$set", "invalid"}},
},
resultType: EmptyResult,
},
"InvalidTypeBool": {
pipeline: bson.A{
bson.D{{"$set", false}},
},
resultType: EmptyResult,
},
"InvalidTypeArray": {
pipeline: bson.A{
bson.D{{"$set", bson.A{}}},
},
resultType: EmptyResult,
},
"InvalidTypeInt32": {
pipeline: bson.A{
bson.D{{"$set", int32(1)}},
},
resultType: EmptyResult,
},
"InvalidTypeInt64": {
pipeline: bson.A{
bson.D{{"$set", int64(1)}},
},
resultType: EmptyResult,
},
"InvalidTypeFloat32": {
pipeline: bson.A{
bson.D{{"$set", float32(1)}},
},
resultType: EmptyResult,
},
"InvalidTypeFloat64": {
pipeline: bson.A{
bson.D{{"$set", float64(1)}},
},
resultType: EmptyResult,
},
"InvalidTypeNull": {
pipeline: bson.A{
bson.D{{"$set", nil}},
},
resultType: EmptyResult,
},
"Include1Field": {
pipeline: bson.A{
bson.D{{"$set", bson.D{{"newField", int32(1)}}}},
},
},
"Include2Fields": {
pipeline: bson.A{
bson.D{{"$set", bson.D{{"newField1", int32(1)}, {"newField2", int32(2)}}}},
},
},
"Include2Stages": {
pipeline: bson.A{
bson.D{{"$set", bson.D{{"newField1", int32(1)}}}},
bson.D{{"$set", bson.D{{"newField2", int32(2)}}}},
},
},
"IncludeDocument": {
pipeline: bson.A{
bson.D{{"$set", bson.D{{"newField", bson.D{{"doc", int32(1)}}}}}},
},
},
"IncludeNestedDocument": {
pipeline: bson.A{
bson.D{{"$set", bson.D{{"newField", bson.D{{"doc", bson.D{{"nested", int32(1)}}}}}}}},
},
},
"IncludeArray": {
pipeline: bson.A{
bson.D{{"$set", bson.D{{"newField", bson.A{bson.D{{"elem", int32(1)}}}}}}},
},
},
"UnsupportedExpressionObject": {
pipeline: bson.A{
bson.D{{"$set", bson.D{{"newField1", bson.D{{"$sum", 1}}}}}},
},
resultType: EmptyResult,
skip: "https://github.com/hanzoai/docdb/issues/1413",
},
"UnsupportedExpressionArray": {
pipeline: bson.A{
bson.D{{"$set", bson.D{{"newField1", bson.A{bson.D{{"$sum", 1}}}}}}},
},
resultType: EmptyResult,
skip: "https://github.com/hanzoai/docdb/issues/1413",
},
"SumValue": {
pipeline: bson.A{
bson.D{{"$set", bson.D{
{"sum", bson.D{{"$sum", "$v"}}},
}}},
},
failsForDocDB: "https://github.com/hanzoai/docdb-DocumentDB/issues/1078",
failsProviders: shareddata.Providers{shareddata.Decimal128s},
},
}
testAggregateStagesCompat(t, testCases)
}
func TestAggregateCompatUnset(t *testing.T) {
t.Parallel()
testCases := map[string]aggregateStagesCompatTestCase{
"InvalidTypeArray": {
pipeline: bson.A{
bson.D{{"$unset", bson.A{1, 2, 3}}},
},
resultType: EmptyResult,
},
"EmptyArray": {
pipeline: bson.A{
bson.D{{"$unset", bson.A{}}},
},
resultType: EmptyResult,
},
"EmptyString": {
pipeline: bson.A{
bson.D{{"$unset", ""}},
},
resultType: EmptyResult,
},
"ArrayWithEmptyString": {
pipeline: bson.A{
bson.D{{"$unset", bson.A{""}}},
},
resultType: EmptyResult,
},
"InvalidTypeArrayWithDifferentTypes": {
pipeline: bson.A{
bson.D{{"$unset", bson.A{"v", 42, false}}},
},
resultType: EmptyResult,
},
"InvalidType": {
pipeline: bson.A{
bson.D{{"$unset", false}},
},
resultType: EmptyResult,
},
"Unset1Field": {
pipeline: bson.A{
bson.D{{"$unset", "v"}},
},
},
"UnsetID": {
pipeline: bson.A{
bson.D{{"$sort", bson.D{{"_id", 1}}}},
bson.D{{"$unset", "_id"}},
},
},
"Unset2Fields": {
pipeline: bson.A{
bson.D{{"$unset", bson.A{"_id", "v"}}},
},
},
"DotNotationUnset": {
pipeline: bson.A{
bson.D{{"$unset", "v.foo"}},
},
},
"DotNotationUnsetTwo": {
pipeline: bson.A{
bson.D{{"$unset", bson.A{"v.foo", "v.array"}}},
},
},
"DotNotationUnsetSecondLevel": {
pipeline: bson.A{
bson.D{{"$unset", "v.array.42"}},
},
},
}
testAggregateStagesCompat(t, testCases)
}