diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/AggregateOperator.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/AggregateOperator.cs index fdd52e516b..86fccaef9d 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/AggregateOperator.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/AggregateOperator.cs @@ -8,6 +8,8 @@ internal enum AggregateOperator { Average, Count, + MakeList, + MakeSet, Max, Min, Sum, diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MakeListAggregator.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MakeListAggregator.cs new file mode 100644 index 0000000000..7ab664bdcb --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MakeListAggregator.cs @@ -0,0 +1,70 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Query.Core.Pipeline.Aggregate.Aggregators +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + using System.Linq; + using System.Text; + using Microsoft.Azure.Cosmos.CosmosElements; + using Microsoft.Azure.Cosmos.CosmosElements.Numbers; + using Microsoft.Azure.Cosmos.Query.Core.Exceptions; + using Microsoft.Azure.Cosmos.Query.Core.Monads; + + internal sealed class MakeListAggregator : IAggregator + { + private readonly List globalList; + + private MakeListAggregator(CosmosArray initialList) + { + this.globalList = new List(); + + this.Aggregate(initialList); + } + + public void Aggregate(CosmosElement localList) + { + if (!(localList is CosmosArray cosmosArray)) + { + throw new ArgumentException($"{nameof(localList)} must be an array."); + } + + this.globalList.AddRange(cosmosArray.ToList()); + } + + public CosmosElement GetResult() + { + return CosmosArray.Create(this.globalList); + } + + public static TryCatch TryCreate(CosmosElement continuationToken) + { + CosmosArray partialList; + if (continuationToken != null) + { + if (!(continuationToken is CosmosArray cosmosPartialList)) + { + return TryCatch.FromException( + new MalformedContinuationTokenException($@"Invalid MakeList continuation token: ""{continuationToken}"".")); + } + + partialList = cosmosPartialList; + } + else + { + partialList = CosmosArray.Empty; + } + + return TryCatch.FromResult(new MakeListAggregator(initialList: partialList)); + } + + public CosmosElement GetCosmosElementContinuationToken() + { + return this.GetResult(); + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MakeSetAggregator.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MakeSetAggregator.cs new file mode 100644 index 0000000000..79d6a9485e --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/MakeSetAggregator.cs @@ -0,0 +1,75 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Query.Core.Pipeline.Aggregate.Aggregators +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + using System.Linq; + using System.Text; + using Microsoft.Azure.Cosmos.CosmosElements; + using Microsoft.Azure.Cosmos.CosmosElements.Numbers; + using Microsoft.Azure.Cosmos.Query.Core.Exceptions; + using Microsoft.Azure.Cosmos.Query.Core.Monads; + + internal sealed class MakeSetAggregator : IAggregator + { + private readonly HashSet globalSet; + + private MakeSetAggregator(CosmosArray initialSet) + { + this.globalSet = new HashSet(); + + this.Aggregate(initialSet); + } + + public void Aggregate(CosmosElement localSet) + { + if (!(localSet is CosmosArray cosmosArray)) + { + throw new ArgumentException($"{nameof(localSet)} must be an array."); + } + + this.globalSet.UnionWith(cosmosArray.ToList()); + } + + public CosmosElement GetResult() + { + return CosmosArray.Create(this.globalSet); + } + + public string GetContinuationToken() + { + return this.globalSet.ToString(); + } + + public static TryCatch TryCreate(CosmosElement continuationToken) + { + CosmosArray partialSet; + if (continuationToken != null) + { + if (!(continuationToken is CosmosArray cosmosPartialSet)) + { + return TryCatch.FromException( + new MalformedContinuationTokenException($@"Invalid MakeSet continuation token: ""{continuationToken}"".")); + } + + partialSet = cosmosPartialSet; + } + else + { + partialSet = CosmosArray.Empty; + } + + return TryCatch.FromResult(new MakeSetAggregator(initialSet: partialSet)); + } + + public CosmosElement GetCosmosElementContinuationToken() + { + return this.GetResult(); + } + } +} diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/SingleGroupAggregator.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/SingleGroupAggregator.cs index 806b311462..a9c47dacfb 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/SingleGroupAggregator.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/Aggregate/Aggregators/SingleGroupAggregator.cs @@ -370,6 +370,14 @@ public static TryCatch TryCreate( tryCreateAggregator = CountAggregator.TryCreate(continuationToken); break; + case AggregateOperator.MakeList: + tryCreateAggregator = MakeListAggregator.TryCreate(continuationToken); + break; + + case AggregateOperator.MakeSet: + tryCreateAggregator = MakeSetAggregator.TryCreate(continuationToken); + break; + case AggregateOperator.Max: tryCreateAggregator = MinMaxAggregator.TryCreateMaxAggregator(continuationToken); break; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/AggregateQueryTests.AggregateMixedTypes_baseline.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/AggregateQueryTests.AggregateMixedTypes_baseline.xml index e7f3e96c19..fadf094505 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/AggregateQueryTests.AggregateMixedTypes_baseline.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/AggregateQueryTests.AggregateMixedTypes_baseline.xml @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + makeListResult = inputDocuments + .Select(doc => + { + if (!doc.TryGetValue(aggregateTestArgs.PartitionKey, out CosmosElement cosmosElement)) + { + Assert.Fail("Failed to get partition key from document"); + } + + return cosmosElement; + }) + .ToList(); + + IReadOnlyList makeSetResult = makeListResult.Distinct().ToList(); + AggregateQueryArguments[] aggregateQueryArgumentsList = new AggregateQueryArguments[] { new AggregateQueryArguments( @@ -237,6 +298,16 @@ private static AggregateQueryArguments[] CreateAggregateQueryArguments( aggregateOperator: "COUNT", expectedValue: CosmosNumber64.Create(inputDocuments.Count()), predicate: "true"), + new AggregateQueryArguments( + aggregateOperator: "MAKELIST", + expectedValue: CosmosArray.Create(makeListResult), + predicate: "true", + ignoreResultOrder: true), + new AggregateQueryArguments( + aggregateOperator: "MAKESET", + expectedValue: CosmosArray.Create(makeSetResult), + predicate: "true", + ignoreResultOrder: true), new AggregateQueryArguments( aggregateOperator: "MAX", expectedValue: CosmosString.Create("xyz"), @@ -286,16 +357,18 @@ public AggregateTestArgs( private readonly struct AggregateQueryArguments { - public AggregateQueryArguments(string aggregateOperator, CosmosElement expectedValue, string predicate) + public AggregateQueryArguments(string aggregateOperator, CosmosElement expectedValue, string predicate, bool ignoreResultOrder=false) { this.AggregateOperator = aggregateOperator; this.ExpectedValue = expectedValue; this.Predicate = predicate; + this.IgnoreResultOrder = ignoreResultOrder; } public string AggregateOperator { get; } public CosmosElement ExpectedValue { get; } public string Predicate { get; } + public bool IgnoreResultOrder { get; } public override string ToString() { @@ -392,6 +465,32 @@ async Task ImplementationAsync( Assert.Fail($"Something went wrong with query: {query}, ex: {ex}"); } } + + string[] arrayAggregateQueries = new string[] + { + $"SELECT VALUE MAKELIST(c.{uniqueField}) FROM c WHERE c.{uniqueField} = {valueOfInterest}", + $"SELECT VALUE MAKESET(c.{uniqueField}) FROM c WHERE c.{uniqueField} = {valueOfInterest}", + }; + + foreach (string query in arrayAggregateQueries) + { + try + { + List items = await QueryTestsBase.RunQueryAsync( + container, + query, + new QueryRequestOptions() + { + MaxConcurrency = 10, + }); + + Assert.IsTrue((items.Count() == 1) && (items.Single() is CosmosArray result) && result.Equals(CosmosArray.Create(CosmosNumber64.Create(valueOfInterest)))); + } + catch (Exception ex) + { + Assert.Fail($"Something went wrong with query: {query}, ex: {ex}"); + } + } } } @@ -527,43 +626,48 @@ private async Task TestQueryCrossPartitionAggregateFunctionsWithMixedTypesHelper args.UndefinedKey }; - string[] aggregateOperators = new string[] { "AVG", "MIN", "MAX", "SUM", "COUNT" }; + string[] aggregateOperators = new string[] { "AVG", "MIN", "MAX", "SUM", "COUNT", "MAKELIST", "MAKESET" }; string[] typeCheckFunctions = new string[] { "IS_ARRAY", "IS_BOOL", "IS_NULL", "IS_NUMBER", "IS_OBJECT", "IS_STRING", "IS_DEFINED", "IS_PRIMITIVE" }; - List queries = new List(); + List<(string, bool)> queries = new List<(string, bool)>(); foreach (string aggregateOperator in aggregateOperators) { + bool ignoreResultOrder = aggregateOperator.Equals("MAKELIST") || aggregateOperator.Equals("MAKESET"); foreach (string typeCheckFunction in typeCheckFunctions) { queries.Add( - $@" + ($@" SELECT VALUE {aggregateOperator} (c.{field}) FROM c WHERE {typeCheckFunction}(c.{field}) - "); + ", + ignoreResultOrder)); } foreach (string typeOnlyPartitionKey in typeOnlyPartitionKeys) { queries.Add( - $@" + ($@" SELECT VALUE {aggregateOperator} (c.{field}) FROM c WHERE c.{partitionKey} = ""{typeOnlyPartitionKey}"" - "); + ", + ignoreResultOrder)); } }; // mixing primitive and non primitives foreach (string minmaxop in new string[] { "MIN", "MAX" }) { + bool ignoreResultOrder = false; foreach (string key in new string[] { args.OneObjectKey, args.OneArrayKey }) { queries.Add( - $@" + ($@" SELECT VALUE {minmaxop} (c.{field}) FROM c WHERE c.{partitionKey} IN (""{key}"", ""{args.DoubleOnlyKey}"") - "); + ", + ignoreResultOrder)); } } @@ -582,7 +686,7 @@ FROM c { writer.WriteStartDocument(); writer.WriteStartElement("Results"); - foreach (string query in queries) + foreach ( (string query, bool ignoreResultOrder) in queries) { string formattedQuery = string.Join( Environment.NewLine, @@ -609,10 +713,18 @@ FROM c { Assert.AreEqual(1, items.Count); CosmosElement aggregateResult = items.First(); - if(aggregateResult is not CosmosUndefined) { - writer.WriteCData(items.Single().ToString()); + if (ignoreResultOrder && (aggregateResult is CosmosArray aggregateResultArray)) + { + CosmosElement[] normalizedAggregateResult = aggregateResultArray.ToArray(); + Array.Sort(normalizedAggregateResult); + writer.WriteCData(CosmosArray.Create(normalizedAggregateResult).ToString()); + } + else + { + writer.WriteCData(items.Single().ToString()); + } } } @@ -996,5 +1108,104 @@ private async Task TestNonValueAggregates( } } } + + [TestMethod] + public async Task TestArrayAggregatesWithContinuationTokenAsync() + { + await this.TestArrayAggregatesWithContinuationToken(100); + + // using 2048 + 1 documents here to ensure list size hits continuation token limit of 16KB + // We aggregate c.age (integers) which has 8 bytes, 16KB / 8B = 2048 + await this.TestArrayAggregatesWithContinuationToken(2049); + } + + private async Task TestArrayAggregatesWithContinuationToken(int numDocuments) + { + int seed = 135749376; + + Random rand = new Random(seed); + List people = new List(); + + for (int i = 0; i < numDocuments; i++) + { + // Generate random people + Person person = PersonGenerator.GetRandomPerson(rand); + for (int j = 0; j < rand.Next(0, 4); j++) + { + // Force an exact duplicate + people.Add(person); + } + } + + List documents = new List(); + // Shuffle them so they end up in different pages + people = people.OrderBy((person) => Guid.NewGuid()).ToList(); + foreach (Person person in people) + { + documents.Add(JsonConvert.SerializeObject(person)); + } + + await this.CreateIngestQueryDeleteAsync( + ConnectionModes.Direct | ConnectionModes.Gateway, + CollectionTypes.MultiPartition, + documents, + ImplementationAsync, + "/id"); + + async static Task ImplementationAsync(Container container, IReadOnlyList documents) + { + foreach (string[] queriesToCompare in new string[][] + { + new string[]{ "SELECT VALUE c.age FROM c", "SELECT VALUE MakeList(c.age) FROM c" }, + new string[]{ "SELECT DISTINCT VALUE c.age FROM c ORDER BY c.age", "SELECT VALUE MakeSet(c.age) FROM c" }, + }) + { + string queryWithoutAggregate = queriesToCompare[0]; + List expectedDocuments = await QueryTestsBase.RunQueryCombinationsAsync( + container, + queryWithoutAggregate, + new QueryRequestOptions() + { + MaxConcurrency = 10, + MaxItemCount = 100, + }, + QueryDrainingMode.ContinuationToken | QueryDrainingMode.HoldState); + + CosmosElement[] normalizedExpectedResult = expectedDocuments.ToArray(); + Array.Sort(normalizedExpectedResult); + + CosmosArray normalizedExpectedCosmosArray = CosmosArray.Create(normalizedExpectedResult); + + int[] pageSizes = (documents.Count() < 1000) ? new int[] { 1, 10, 100 } : new int[] { 100 }; + foreach (int pageSize in pageSizes) + { + string queryWithAggregate = queriesToCompare[1]; + List actualDocuments = await QueryTestsBase.RunQueryCombinationsAsync( + container, + queryWithAggregate, + new QueryRequestOptions() + { + MaxConcurrency = 10, + MaxItemCount = pageSize + }, + QueryDrainingMode.ContinuationToken | QueryDrainingMode.HoldState | QueryDrainingMode.CosmosElementContinuationToken); + + CosmosElement aggregateResult = actualDocuments.First(); + CosmosArray normalizedActualCosmosArray = null; + if (aggregateResult is CosmosArray actualCosmosArray) + { + CosmosElement[] normalizedActualArray = actualCosmosArray.ToArray(); + Array.Sort(normalizedActualArray); + normalizedActualCosmosArray = CosmosArray.Create(normalizedActualArray); + } + + Assert.AreEqual( + expected: normalizedExpectedCosmosArray, + actual: normalizedActualCosmosArray, + message: $"Documents didn't match for {queryWithAggregate} on a Partitioned container"); + } + } + } + } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/GroupByQueryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/GroupByQueryTests.cs index f2257bbf34..0287b2d0d4 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/GroupByQueryTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/GroupByQueryTests.cs @@ -2,6 +2,8 @@ { using System; using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Drawing.Printing; using System.Linq; using System.Threading.Tasks; using Microsoft.Azure.Cosmos.CosmosElements; @@ -499,26 +501,23 @@ FROM c { foreach (int maxItemCount in new int[] { 1, 5, 10 }) { + QueryRequestOptions queryRequestOptions = new QueryRequestOptions() + { + MaxConcurrency = 2, + MaxItemCount = maxItemCount, + MaxBufferedItemCount = 100, + }; + List actualWithoutContinuationTokens = await QueryTestsBase.QueryWithoutContinuationTokensAsync( container, query, - new QueryRequestOptions() - { - MaxConcurrency = 2, - MaxItemCount = maxItemCount, - MaxBufferedItemCount = 100, - }); + queryRequestOptions); HashSet actualWithoutContinuationTokensSet = new HashSet(actualWithoutContinuationTokens); List actualWithTryGetContinuationTokens = await QueryTestsBase.QueryWithCosmosElementContinuationTokenAsync( container, query, - new QueryRequestOptions() - { - MaxConcurrency = 2, - MaxItemCount = maxItemCount, - MaxBufferedItemCount = 100, - }); + queryRequestOptions); HashSet actualWithTryGetContinuationTokensSet = new HashSet(actualWithTryGetContinuationTokens); Assert.IsTrue( @@ -537,23 +536,138 @@ FROM c } } - // Test that continuation token is blocked + List<(string, List)> queryAndExpectedResultsListArrayAggregates = new List<(string, List)>() + { + ( + "SELECT c.name AS Name, MAKELIST(c.age) AS AgeList FROM c GROUP BY c.name", + documents + .GroupBy(document => document["name"]) + .Select(grouping => CosmosObject.Create( + new Dictionary() + { + { "Name", grouping.Key }, + { "AgeList", CosmosArray.Create(grouping.Select(document => document["age"]))} + })) + .ToList() + ), + ( + "SELECT c.name AS Name, MAKESET(c.age) AS AgeSet FROM c GROUP BY c.name", + documents + .GroupBy(document => document["name"]) + .Select(grouping => CosmosObject.Create( + new Dictionary() + { + { "Name", grouping.Key }, + { "AgeSet", CosmosArray.Create(grouping.Select(document => document["age"]).Distinct())} + })) + .ToList() + ) + }; + + // Test query correctness. + foreach ((string query, List expectedResults) in queryAndExpectedResultsListArrayAggregates) { - try + foreach (int maxItemCount in new int[] { 1, 5, 10 }) { - List actual = await QueryTestsBase.QueryWithContinuationTokensAsync( + QueryRequestOptions queryRequestOptions = new QueryRequestOptions() + { + MaxConcurrency = 2, + MaxItemCount = maxItemCount, + MaxBufferedItemCount = 100, + }; + + List actualWithoutContinuationTokens = await QueryTestsBase.QueryWithoutContinuationTokensAsync( container, - "SELECT c.age FROM c GROUP BY c.age", - new QueryRequestOptions() - { - MaxConcurrency = 2, - MaxItemCount = 1 - }); - Assert.Fail("Expected an error when trying to drain a GROUP BY query with continuation tokens."); + query, + queryRequestOptions); + this.NormalizeGroupByArrayAggregateResults(actualWithoutContinuationTokens); + HashSet actualWithoutContinuationTokensSet = new HashSet(actualWithoutContinuationTokens); + + List actualWithTryGetContinuationTokens = await QueryTestsBase.QueryWithCosmosElementContinuationTokenAsync( + container, + query, + queryRequestOptions); + this.NormalizeGroupByArrayAggregateResults(actualWithTryGetContinuationTokens); + HashSet actualWithTryGetContinuationTokensSet = new HashSet(actualWithTryGetContinuationTokens); + + List actualWithCombinations = await QueryTestsBase.RunQueryCombinationsAsync( + container, + query, + queryRequestOptions, + QueryDrainingMode.HoldState | QueryDrainingMode.CosmosElementContinuationToken); + this.NormalizeGroupByArrayAggregateResults(actualWithCombinations); + HashSet actualWithCombinationsSet = new HashSet(actualWithCombinations); + + Assert.IsTrue( + actualWithoutContinuationTokensSet.SetEquals(actualWithTryGetContinuationTokensSet), + $"Results did not match for query: {query} with maxItemCount: {maxItemCount}" + + $"ActualWithoutContinuationTokens: {JsonConvert.SerializeObject(actualWithoutContinuationTokensSet)}" + + $"ActualWithTryGetContinuationTokens: {JsonConvert.SerializeObject(actualWithTryGetContinuationTokensSet)}"); + + Assert.IsTrue( + actualWithoutContinuationTokensSet.SetEquals(actualWithCombinationsSet), + $"Results did not match for query: {query} with maxItemCount: {maxItemCount}" + + $"ActualWithoutContinuationTokens: {JsonConvert.SerializeObject(actualWithoutContinuationTokensSet)}" + + $"ActualWithCombinations: {JsonConvert.SerializeObject(actualWithCombinationsSet)}"); + + this.NormalizeGroupByArrayAggregateResults(expectedResults); + HashSet expectedSet = new HashSet(expectedResults); + + Assert.IsTrue( + actualWithoutContinuationTokensSet.SetEquals(expectedSet), + $"Results did not match for query: {query} with maxItemCount: {maxItemCount}" + + $"Actual {JsonConvert.SerializeObject(actualWithoutContinuationTokensSet)}" + + $"Expected: {JsonConvert.SerializeObject(expectedSet)}"); } - catch (Exception) + } + + // Test that continuation token is blocked + + try + { + List actual = await QueryTestsBase.QueryWithContinuationTokensAsync( + container, + "SELECT c.age FROM c GROUP BY c.age", + new QueryRequestOptions() + { + MaxConcurrency = 2, + MaxItemCount = 1 + }); + Assert.Fail("Expected an error when trying to drain a GROUP BY query with continuation tokens."); + } + catch (Exception) + { + } + } + + private void NormalizeGroupByArrayAggregateResults( + List results) + { + for (int i = 0; i < results.Count; i++) + { + if (results[i] is not CosmosObject) { + Assert.Fail("This function assumes results are CosmosObjects (ie, not from a SELECT VALUE query)."); } + + Dictionary normalizedResult = new Dictionary(); + + CosmosObject resultObject = (CosmosObject)results[i]; + foreach (KeyValuePair kvp in resultObject) + { + if (kvp.Value is CosmosArray cosmosArray) + { + CosmosElement[] normalizedArray = cosmosArray.ToArray(); + Array.Sort(normalizedArray); + normalizedResult.Add(kvp.Key, CosmosArray.Create(normalizedArray)); + } + else + { + normalizedResult.Add(kvp.Key, kvp.Value); + } + } + + results[i] = CosmosObject.Create(normalizedResult); } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.Aggregates.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.Aggregates.xml index 7648693ffa..a333b20cb4 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.Aggregates.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.Aggregates.xml @@ -248,6 +248,78 @@ FROM c]]> + + + + + + MAKELIST + SELECT VALUE MAKELIST(c.blah) FROM c + + /key + + Hash + + + + + None + + + + + + + + MakeList + + + + True + + + + [[],"Infinity") + + + + + + + + + MAKESET + SELECT VALUE MAKESET(c.blah) FROM c + + /key + + Hash + + + + + None + + + + + + + + MakeSet + + + + True + + + + [[],"Infinity") + + + diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.NonValueAggregates.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.NonValueAggregates.xml index 29991e9575..dfbfbcf15c 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.NonValueAggregates.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.NonValueAggregates.xml @@ -190,6 +190,84 @@ FROM c]]> + + + + + + Single Aggregate (MAKELIST) Without 'VALUE' and Without alias. + SELECT MAKELIST(c.blah) FROM c + + Hash + + + + + None + + + + + + + + + + $1 + MakeList + + + + $1 + + False + + + + [[],"Infinity") + + + + + + + + + Single Aggregate (MAKESET) Without 'VALUE' and Without alias. + SELECT MAKESET(c.blah) FROM c + + Hash + + + + + None + + + + + + + + + + $1 + MakeSet + + + + $1 + + False + + + + [[],"Infinity") + + + @@ -385,6 +463,84 @@ FROM c]]> + + + + + + Single Aggregate (MAKELIST) Without 'VALUE' and With alias. + SELECT MAKELIST(c.blah) as makelist_blah FROM c + + Hash + + + + + None + + + + + + + + + + makelist_blah + MakeList + + + + makelist_blah + + False + + + + [[],"Infinity") + + + + + + + + + Single Aggregate (MAKESET) Without 'VALUE' and With alias. + SELECT MAKESET(c.blah) as makeset_blah FROM c + + Hash + + + + + None + + + + + + + + + + makeset_blah + MakeSet + + + + makeset_blah + + False + + + + [[],"Infinity") + + + @@ -625,6 +781,102 @@ FROM c]]> + + + + + + Multiple Aggregates (MAKELIST) With alias. + + SELECT + MAKELIST(c.blah) as makelist_blah, + MAKELIST(c.blah) as makelist_blah2 + FROM c + + Hash + + + + + None + + + + + + + + + + makelist_blah + MakeList + + + makelist_blah2 + MakeList + + + + makelist_blah + makelist_blah2 + + False + + + + [[],"Infinity") + + + + + + + + + Multiple Aggregates (MAKESET) With alias. + + SELECT + MAKESET(c.blah) as makeset_blah, + MAKESET(c.blah) as makeset_blah2 + FROM c + + Hash + + + + + None + + + + + + + + + + makeset_blah + MakeSet + + + makeset_blah2 + MakeSet + + + + makeset_blah + makeset_blah2 + + False + + + + [[],"Infinity") + + + @@ -775,11 +1027,107 @@ FROM c]]> - Multiple Aggregates (MAX) Without alias. + Multiple Aggregates (MAX) Without alias. + + SELECT + MAX(c.blah), + MAX(c.blah) + FROM c + + Hash + + + + + None + + + + + + + + + + $1 + Max + + + $2 + Max + + + + $1 + $2 + + False + + + + [[],"Infinity") + + + + + + + + + Multiple Aggregates (AVG) Without alias. + + SELECT + AVG(c.blah), + AVG(c.blah) + FROM c + + Hash + + + + + None + + + + + + + + + + $1 + Average + + + $2 + Average + + + + $1 + $2 + + False + + + + [[],"Infinity") + + + + + + + + + Multiple Aggregates (MAKELIST) Without alias. SELECT - MAX(c.blah), - MAX(c.blah) + MAKELIST(c.blah), + MAKELIST(c.blah) FROM c Hash @@ -798,11 +1146,11 @@ FROM c]]> $1 - Max + MakeList $2 - Max + MakeList @@ -816,18 +1164,18 @@ FROM c]]> [[],"Infinity") - - Multiple Aggregates (AVG) Without alias. + Multiple Aggregates (MAKESET) Without alias. SELECT - AVG(c.blah), - AVG(c.blah) + MAKESET(c.blah), + MAKESET(c.blah) FROM c Hash @@ -846,11 +1194,11 @@ FROM c]]> $1 - Average + MakeSet $2 - Average + MakeSet @@ -864,7 +1212,7 @@ FROM c]]> [[],"Infinity") - @@ -1345,6 +1693,198 @@ FROM c]]> + + + + + + Multiple Aggregates (MAKELIST) mixed alias. + + SELECT + MAKELIST(c.blah) as makelist_blah, + MAKELIST(c.blah) + FROM c + + Hash + + + + + None + + + + + + + + + + makelist_blah + MakeList + + + $1 + MakeList + + + + makelist_blah + $1 + + False + + + + [[],"Infinity") + + + + + + + + + Multiple Aggregates (MAKELIST) mixed alias. + + SELECT + MAKELIST(c.blah), + MAKELIST(c.blah) as makelist_blah + FROM c + + Hash + + + + + None + + + + + + + + + + makelist_blah + MakeList + + + $1 + MakeList + + + + $1 + makelist_blah + + False + + + + [[],"Infinity") + + + + + + + + + Multiple Aggregates (MAKESET) mixed alias. + + SELECT + MAKESET(c.blah) as makeset_blah, + MAKESET(c.blah) + FROM c + + Hash + + + + + None + + + + + + + + + + makeset_blah + MakeSet + + + $1 + MakeSet + + + + makeset_blah + $1 + + False + + + + [[],"Infinity") + + + + + + + + + Multiple Aggregates (MAKESET) mixed alias. + + SELECT + MAKESET(c.blah), + MAKESET(c.blah) as makeset_blah + FROM c + + Hash + + + + + None + + + + + + + + + + makeset_blah + MakeSet + + + $1 + MakeSet + + + + $1 + makeset_blah + + False + + + + [[],"Infinity") + + + @@ -1615,6 +2155,114 @@ FROM c]]> + + + + + + Multiple Aggregates (MAKELIST) interleaved aliases. + + SELECT + MAKELIST(c.blah) as makelist_blah, + MAKELIST(c.blah), + MAKELIST(c.blah) as makelist_blah2 + FROM c + + Hash + + + + + None + + + + + + + + + + makelist_blah + MakeList + + + makelist_blah2 + MakeList + + + $1 + MakeList + + + + makelist_blah + $1 + makelist_blah2 + + False + + + + [[],"Infinity") + + + + + + + + + Multiple Aggregates (MAKESET) interleaved aliases. + + SELECT + MAKESET(c.blah) as makeset_blah, + MAKESET(c.blah), + MAKESET(c.blah) as makeset_blah2 + FROM c + + Hash + + + + + None + + + + + + + + + + makeset_blah + MakeSet + + + $1 + MakeSet + + + makeset_blah2 + MakeSet + + + + makeset_blah + $1 + makeset_blah2 + + False + + + + [[],"Infinity") + + + diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPlanBaselineTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPlanBaselineTests.cs index 02121aa717..ef68af02bc 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPlanBaselineTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPlanBaselineTests.cs @@ -62,6 +62,16 @@ public void Aggregates() @"SELECT VALUE COUNT(c.blah) FROM c", @"/key"), + Hash( + @"MAKELIST", + @"SELECT VALUE MAKELIST(c.blah) FROM c", + @"/key"), + + Hash( + @"MAKESET", + @"SELECT VALUE MAKESET(c.blah) FROM c", + @"/key"), + Hash( @"COUNT 1", @"SELECT VALUE COUNT(1) FROM c", @@ -114,7 +124,9 @@ public void NonValueAggregates() "COUNT", "MIN", "MAX", - "AVG" + "AVG", + "MAKELIST", + "MAKESET" }; List testVariations = new List();