diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CosmosQueryExecutionContextFactory.cs b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CosmosQueryExecutionContextFactory.cs index 21b512166e..fec5fa4d4a 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CosmosQueryExecutionContextFactory.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/Pipeline/CosmosQueryExecutionContextFactory.cs @@ -248,6 +248,7 @@ private static async Task> TryCreateCoreContextAsy cosmosQueryContext.ResourceTypeEnum, partitionKeyDefinition, inputParameters.PartitionKey != null, + containerQueryProperties.GeospatialType, cosmosQueryContext.UseSystemPrefix, createQueryPipelineTrace, cancellationToken); diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/QueryClient/ContainerQueryProperties.cs b/Microsoft.Azure.Cosmos/src/Query/Core/QueryClient/ContainerQueryProperties.cs index 4d80412388..0baaceb9c6 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/QueryClient/ContainerQueryProperties.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/QueryClient/ContainerQueryProperties.cs @@ -11,15 +11,18 @@ internal readonly struct ContainerQueryProperties public ContainerQueryProperties( string resourceId, string effectivePartitionKeyString, - PartitionKeyDefinition partitionKeyDefinition) + PartitionKeyDefinition partitionKeyDefinition, + Cosmos.GeospatialType geospatialType) { this.ResourceId = resourceId; this.EffectivePartitionKeyString = effectivePartitionKeyString; this.PartitionKeyDefinition = partitionKeyDefinition; + this.GeospatialType = geospatialType; } public string ResourceId { get; } public string EffectivePartitionKeyString { get; } public PartitionKeyDefinition PartitionKeyDefinition { get; } + public Cosmos.GeospatialType GeospatialType { get; } } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/QueryClient/CosmosQueryClient.cs b/Microsoft.Azure.Cosmos/src/Query/Core/QueryClient/CosmosQueryClient.cs index e066ec2140..c2bcf91619 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/QueryClient/CosmosQueryClient.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/QueryClient/CosmosQueryClient.cs @@ -47,6 +47,7 @@ public abstract Task> TryGetPartitionedQ bool hasLogicalPartitionKey, bool allowDCount, bool useSystemPrefix, + Cosmos.GeospatialType geospatialType, CancellationToken cancellationToken); public abstract Task> ExecuteItemQueryAsync( diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPartitionProvider.cs b/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPartitionProvider.cs index 8d09a074c9..6e7b0b4a8f 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPartitionProvider.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPartitionProvider.cs @@ -118,7 +118,8 @@ public TryCatch TryGetPartitionedQueryExecutionIn bool allowNonValueAggregateQuery, bool hasLogicalPartitionKey, bool allowDCount, - bool useSystemPrefix) + bool useSystemPrefix, + GeospatialType geospatialType) { TryCatch tryGetInternalQueryInfo = this.TryGetPartitionedQueryExecutionInfoInternal( querySpecJsonString: querySpecJsonString, @@ -128,7 +129,8 @@ public TryCatch TryGetPartitionedQueryExecutionIn allowNonValueAggregateQuery: allowNonValueAggregateQuery, hasLogicalPartitionKey: hasLogicalPartitionKey, allowDCount: allowDCount, - useSystemPrefix: useSystemPrefix); + useSystemPrefix: useSystemPrefix, + geospatialType: geospatialType); if (!tryGetInternalQueryInfo.Succeeded) { return TryCatch.FromException(tryGetInternalQueryInfo.Exception); @@ -169,7 +171,8 @@ internal TryCatch TryGetPartitionedQueryE bool allowNonValueAggregateQuery, bool hasLogicalPartitionKey, bool allowDCount, - bool useSystemPrefix) + bool useSystemPrefix, + GeospatialType geospatialType) { if (querySpecJsonString == null || partitionKeyDefinition == null) { @@ -200,7 +203,6 @@ internal TryCatch TryGetPartitionedQueryE } PartitionKind partitionKind = partitionKeyDefinition.Kind; - GeospatialType defaultGeopatialType = GeospatialType.Geography; this.Initialize(); @@ -219,7 +221,7 @@ internal TryCatch TryGetPartitionedQueryE bIsContinuationExpected = Convert.ToInt32(isContinuationExpected), bRequireFormattableOrderByQuery = Convert.ToInt32(requireFormattableOrderByQuery), bUseSystemPrefix = Convert.ToInt32(useSystemPrefix), - eGeospatialType = Convert.ToInt32(defaultGeopatialType), + eGeospatialType = Convert.ToInt32(geospatialType), ePartitionKind = Convert.ToInt32(partitionKind) }; diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanHandler.cs b/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanHandler.cs index e6f8ec06bf..30be0a4903 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanHandler.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanHandler.cs @@ -29,6 +29,7 @@ public async Task> TryGetQueryPlanAsync( QueryFeatures supportedQueryFeatures, bool hasLogicalPartitionKey, bool useSystemPrefix, + GeospatialType geospatialType, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -49,6 +50,7 @@ public async Task> TryGetQueryPlanAsync( partitionKeyDefinition, hasLogicalPartitionKey, useSystemPrefix, + geospatialType, cancellationToken); if (!tryGetQueryInfo.Succeeded) { @@ -76,6 +78,7 @@ public async Task> TryGetQueryPlanAsync( PartitionKeyDefinition partitionKeyDefinition, bool hasLogicalPartitionKey, bool useSystemPrefix, + GeospatialType geospatialType, CancellationToken cancellationToken = default) { if (sqlQuerySpec == null) @@ -96,6 +99,7 @@ public async Task> TryGetQueryPlanAsync( partitionKeyDefinition, hasLogicalPartitionKey, useSystemPrefix, + geospatialType, cancellationToken); if (tryGetQueryInfo.Failed) { @@ -114,6 +118,7 @@ private Task> TryGetQueryInfoAsync( PartitionKeyDefinition partitionKeyDefinition, bool hasLogicalPartitionKey, bool useSystemPrefix, + Cosmos.GeospatialType geospatialType, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); @@ -128,6 +133,7 @@ private Task> TryGetQueryInfoAsync( hasLogicalPartitionKey: hasLogicalPartitionKey, allowDCount: true, useSystemPrefix: useSystemPrefix, + geospatialType: geospatialType, cancellationToken: cancellationToken); } diff --git a/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanRetriever.cs b/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanRetriever.cs index 6a6d7d5777..32e061dfd8 100644 --- a/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanRetriever.cs +++ b/Microsoft.Azure.Cosmos/src/Query/Core/QueryPlan/QueryPlanRetriever.cs @@ -38,6 +38,7 @@ public static async Task GetQueryPlanWithServiceI Documents.ResourceType resourceType, PartitionKeyDefinition partitionKeyDefinition, bool hasLogicalPartitionKey, + GeospatialType geospatialType, bool useSystemPrefix, ITrace trace, CancellationToken cancellationToken = default) @@ -70,6 +71,7 @@ public static async Task GetQueryPlanWithServiceI QueryPlanRetriever.SupportedQueryFeatures, hasLogicalPartitionKey, useSystemPrefix, + geospatialType, cancellationToken); if (!tryGetQueryPlan.Succeeded) diff --git a/Microsoft.Azure.Cosmos/src/Query/v2Query/DefaultDocumentQueryExecutionContext.cs b/Microsoft.Azure.Cosmos/src/Query/v2Query/DefaultDocumentQueryExecutionContext.cs index e3425f3199..b53c3c5c71 100644 --- a/Microsoft.Azure.Cosmos/src/Query/v2Query/DefaultDocumentQueryExecutionContext.cs +++ b/Microsoft.Azure.Cosmos/src/Query/v2Query/DefaultDocumentQueryExecutionContext.cs @@ -319,6 +319,7 @@ private static bool ServiceInteropAvailable() partitionKeyDefinition: partitionKeyDefinition, queryPartitionProvider: queryPartitionProvider, clientApiVersion: version, + geospatialType: collection.GeospatialConfig.GeospatialType, out QueryInfo _); } else if (request.Properties != null && request.Properties.TryGetValue( diff --git a/Microsoft.Azure.Cosmos/src/Query/v2Query/DocumentQueryExecutionContextBase.cs b/Microsoft.Azure.Cosmos/src/Query/v2Query/DocumentQueryExecutionContextBase.cs index 52458f35f1..ec4e6d9185 100644 --- a/Microsoft.Azure.Cosmos/src/Query/v2Query/DocumentQueryExecutionContextBase.cs +++ b/Microsoft.Azure.Cosmos/src/Query/v2Query/DocumentQueryExecutionContextBase.cs @@ -160,6 +160,7 @@ public async Task GetPartitionedQueryExecutionInf bool allowNonValueAggregateQuery, bool hasLogicalPartitionKey, bool allowDCount, + Cosmos.GeospatialType geospatialType, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -174,6 +175,7 @@ public async Task GetPartitionedQueryExecutionInf allowNonValueAggregateQuery: allowNonValueAggregateQuery, hasLogicalPartitionKey: hasLogicalPartitionKey, allowDCount: allowDCount, + geospatialType: geospatialType, useSystemPrefix: false); if (!tryGetPartitionedQueryExecutionInfo.Succeeded) { diff --git a/Microsoft.Azure.Cosmos/src/Query/v2Query/DocumentQueryExecutionContextFactory.cs b/Microsoft.Azure.Cosmos/src/Query/v2Query/DocumentQueryExecutionContextFactory.cs index 63f17406c5..f7861f5a2c 100644 --- a/Microsoft.Azure.Cosmos/src/Query/v2Query/DocumentQueryExecutionContextFactory.cs +++ b/Microsoft.Azure.Cosmos/src/Query/v2Query/DocumentQueryExecutionContextFactory.cs @@ -104,6 +104,7 @@ public static async Task CreateDocumentQueryExec allowNonValueAggregateQuery: true, hasLogicalPartitionKey: feedOptions.PartitionKey != null, allowDCount: true, + geospatialType: collection.GeospatialConfig.GeospatialType, cancellationToken: token); if (DocumentQueryExecutionContextFactory.ShouldCreateSpecializedDocumentQueryExecutionContext( diff --git a/Microsoft.Azure.Cosmos/src/Query/v3Query/CosmosQueryClientCore.cs b/Microsoft.Azure.Cosmos/src/Query/v3Query/CosmosQueryClientCore.cs index e541f51774..138f32c593 100644 --- a/Microsoft.Azure.Cosmos/src/Query/v3Query/CosmosQueryClientCore.cs +++ b/Microsoft.Azure.Cosmos/src/Query/v3Query/CosmosQueryClientCore.cs @@ -74,7 +74,8 @@ public override async Task GetCachedContainerQueryProp return new ContainerQueryProperties( containerProperties.ResourceId, effectivePartitionKeyString, - containerProperties.PartitionKey); + containerProperties.PartitionKey, + containerProperties.GeospatialConfig.GeospatialType); } public override async Task> TryGetPartitionedQueryExecutionInfoAsync( @@ -87,6 +88,7 @@ public override async Task> TryGetPartit bool hasLogicalPartitionKey, bool allowDCount, bool useSystemPrefix, + Cosmos.GeospatialType geospatialType, CancellationToken cancellationToken) { string queryString = null; @@ -109,7 +111,8 @@ public override async Task> TryGetPartit allowNonValueAggregateQuery: allowNonValueAggregateQuery, hasLogicalPartitionKey: hasLogicalPartitionKey, allowDCount: allowDCount, - useSystemPrefix: useSystemPrefix); + useSystemPrefix: useSystemPrefix, + geospatialType: geospatialType); } public override async Task> ExecuteItemQueryAsync( diff --git a/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerCore.Items.cs b/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerCore.Items.cs index 60b8db6776..0018eca9b7 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerCore.Items.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerCore.Items.cs @@ -351,6 +351,7 @@ public override async Task TryExecuteQueryAsync( string continuationToken, FeedRangeInternal feedRangeInternal, QueryRequestOptions requestOptions, + GeospatialType geospatialType, CancellationToken cancellationToken = default) { if (queryDefinition == null) @@ -418,6 +419,7 @@ public override async Task TryExecuteQueryAsync( partitionKeyDefinition, requestOptions.PartitionKey.HasValue, useSystemPrefix: QueryIterator.IsSystemPrefixExpected(requestOptions), + geospatialType: geospatialType, cancellationToken); if (tryGetQueryInfoAndIfSupported.Failed) diff --git a/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerInternal.cs b/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerInternal.cs index e6d21ba01a..920fc5d694 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerInternal.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Container/ContainerInternal.cs @@ -71,6 +71,7 @@ public abstract Task TryExecuteQueryAsync( string continuationToken, FeedRangeInternal feedRangeInternal, QueryRequestOptions requestOptions, + GeospatialType geospatialType, CancellationToken cancellationToken = default); public abstract FeedIterator GetStandByFeedIterator( diff --git a/Microsoft.Azure.Cosmos/src/Routing/PartitionRoutingHelper.cs b/Microsoft.Azure.Cosmos/src/Routing/PartitionRoutingHelper.cs index a91c1dba5f..68f6214f17 100644 --- a/Microsoft.Azure.Cosmos/src/Routing/PartitionRoutingHelper.cs +++ b/Microsoft.Azure.Cosmos/src/Routing/PartitionRoutingHelper.cs @@ -37,6 +37,7 @@ public static IReadOnlyList> GetProvidedPartitionKeyRanges( PartitionKeyDefinition partitionKeyDefinition, QueryPartitionProvider queryPartitionProvider, string clientApiVersion, + Cosmos.GeospatialType geospatialType, out QueryInfo queryInfo) { if (querySpecJsonString == null) @@ -62,7 +63,8 @@ public static IReadOnlyList> GetProvidedPartitionKeyRanges( allowNonValueAggregateQuery: allowNonValueAggregates, hasLogicalPartitionKey: hasLogicalPartitionKey, allowDCount: allowDCount, - useSystemPrefix: useSystemPrefix); + useSystemPrefix: useSystemPrefix, + geospatialType: geospatialType); if (!tryGetPartitionQueryExecutionInfo.Succeeded) { throw new BadRequestException(tryGetPartitionQueryExecutionInfo.Exception); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs index ca6f1495f5..da6d79c333 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs @@ -1589,7 +1589,8 @@ public async Task ItemEpkQuerySingleKeyRangeValidation() ContainerQueryProperties containerQueryProperties = new ContainerQueryProperties( containerResponse.Resource.ResourceId, null, - containerResponse.Resource.PartitionKey); + containerResponse.Resource.PartitionKey, + containerResponse.Resource.GeospatialConfig.GeospatialType); // There should only be one range since the EPK option is set. List partitionKeyRanges = await CosmosQueryExecutionContextFactory.GetTargetPartitionKeyRangesAsync( diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/FeedToken/QueryFeedTokenTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/FeedToken/QueryFeedTokenTests.cs index 8e36c206f1..1e26ef0738 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/FeedToken/QueryFeedTokenTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/FeedToken/QueryFeedTokenTests.cs @@ -55,7 +55,8 @@ public async Task GetTargetPartitionKeyRangesAsyncWithFeedRange() ContainerQueryProperties containerQueryProperties = new ContainerQueryProperties( containerResponse.Resource.ResourceId, null, - containerResponse.Resource.PartitionKey); + containerResponse.Resource.PartitionKey, + containerResponse.Resource.GeospatialConfig.GeospatialType); IReadOnlyList feedTokens = await container.GetFeedRangesAsync(); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/PartitioningQueryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/PartitioningQueryTests.cs index c925c95d67..ed61b8fc8e 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/PartitioningQueryTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/PartitioningQueryTests.cs @@ -489,5 +489,90 @@ async Task ImplementationAsync( Assert.IsTrue(actualPartitionKeyValues.SetEquals(args.ExpectedPartitionKeyValues)); } } + + [TestMethod] + public async Task TestGeospatial() + { + string[] inputDocs = new[] + { + @"{""id"":""documentId1"",""key"":""A"",""prop"":3,""shortArray"":[{""a"":5}]}", + @"{""id"":""documentId2"",""key"":""A"",""prop"":2,""shortArray"":[{""a"":6}]}", + @"{""id"":""documentId3"",""key"":""A"",""prop"":1,""shortArray"":[{""a"":7}]}", + @"{""id"":""documentId4"",""key"":5,""prop"":3,""shortArray"":[{""a"":5}]}", + @"{""id"":""documentId5"",""key"":5,""prop"":2,""shortArray"":[{""a"":6}]}", + @"{""id"":""documentId6"",""key"":5,""prop"":1,""shortArray"":[{""a"":7}]}", + @"{""id"":""documentId10"",""prop"":3,""shortArray"":[{""a"":5}]}", + @"{""id"":""documentId11"",""prop"":2,""shortArray"":[{""a"":6}]}", + @"{""id"":""documentId12"",""prop"":1,""shortArray"":[{""a"":7}]}", + }; + + string All = "documentId4,documentId5,documentId6,documentId10,documentId11,documentId12,documentId1,documentId2,documentId3"; + string None = string.Empty; + var testVariations = new[] + { + new + { + Query = "SELECT c.id FROM c WHERE ST_DISTANCE({'type': 'Polygon', 'coordinates': [[[35, 10], [45, 45], [15, 40], [10, 20], [35, 10]], [[20, 30], [35, 35], [30, 20], [20, 30]]]}, {'type': 'Point', 'coordinates': [30, 10]}) > 66408.034483", + Expected = new Dictionary + { + { Cosmos.GeospatialType.Geography, All }, + { Cosmos.GeospatialType.Geometry, None }, + } + }, + new + { + Query = "SELECT c.id FROM c WHERE ST_ISVALID({'type': 'Polygon', 'coordinates': [[[-1000, 1000], [1000, 1000], [1000, 4000], [-1000, 4000], [-1000, 1000]]]})", + Expected = new Dictionary + { + { Cosmos.GeospatialType.Geography, None }, + { Cosmos.GeospatialType.Geometry, All }, + } + }, + new + { + Query = "SELECT * FROM c WHERE NOT ST_WITHIN({'type': 'Point', 'coordinates': [0, 40]}, {'type':'Polygon','coordinates':[[[-60,20], [70,20], [70,70], [-60,70], [-60,20]]]})", + Expected = new Dictionary + { + { Cosmos.GeospatialType.Geography, All }, + { Cosmos.GeospatialType.Geometry, None }, + } + }, + }; + + foreach (Cosmos.GeospatialType geospatialType in new[] { Cosmos.GeospatialType.Geography, Cosmos.GeospatialType.Geometry }) + { + await this.CreateIngestQueryDeleteAsync( + ConnectionModes.Direct, + CollectionTypes.MultiPartition, + inputDocs, + ImplementationAsync, + "/key", + geospatialType: geospatialType); + + async Task ImplementationAsync(Container container, IReadOnlyList documents) + { + foreach (var testVariation in testVariations) + { + string expectedResult = string.Join(",", testVariation.Expected[geospatialType]); + + FeedIterator resultSetIterator = container.GetItemQueryIterator( + queryText: testVariation.Query, + requestOptions: new QueryRequestOptions()); + + List result = new List(); + while (resultSetIterator.HasMoreResults) + { + result.AddRange(await resultSetIterator.ReadNextAsync()); + } + + string resultDocIds = string.Join(",", result.Select(doc => doc.Id)); + Assert.AreEqual( + expectedResult, + resultDocIds, + $"{Environment.NewLine}Query failed for geospatial type '{geospatialType}'{Environment.NewLine}{testVariation.Query}"); + } + } + } + } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/QueryTestsBase.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/QueryTestsBase.cs index f40015473a..269e8448bd 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/QueryTestsBase.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/QueryTestsBase.cs @@ -113,12 +113,14 @@ private async Task> GetPartitionKeyRanges(Conta private async Task CreateMultiPartitionContainer( string partitionKey = "/id", - Microsoft.Azure.Cosmos.IndexingPolicy indexingPolicy = null) + Microsoft.Azure.Cosmos.IndexingPolicy indexingPolicy = null, + Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography) { ContainerResponse containerResponse = await this.CreatePartitionedContainer( throughput: 25000, partitionKey: partitionKey, - indexingPolicy: indexingPolicy); + indexingPolicy: indexingPolicy, + geospatialType); IReadOnlyList ranges = await this.GetPartitionKeyRanges(containerResponse); Assert.IsTrue( @@ -130,12 +132,14 @@ private async Task CreateMultiPartitionContainer( private async Task CreateSinglePartitionContainer( string partitionKey = "/id", - Microsoft.Azure.Cosmos.IndexingPolicy indexingPolicy = null) + Microsoft.Azure.Cosmos.IndexingPolicy indexingPolicy = null, + Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography) { ContainerResponse containerResponse = await this.CreatePartitionedContainer( throughput: 4000, partitionKey: partitionKey, - indexingPolicy: indexingPolicy); + indexingPolicy: indexingPolicy, + geospatialType: geospatialType); Assert.IsNotNull(containerResponse); Assert.AreEqual(HttpStatusCode.Created, containerResponse.StatusCode); @@ -149,13 +153,15 @@ private async Task CreateSinglePartitionContainer( } private async Task CreateNonPartitionedContainerAsync( - Microsoft.Azure.Cosmos.IndexingPolicy indexingPolicy = null) + Microsoft.Azure.Cosmos.IndexingPolicy indexingPolicy = null, + Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography) { string containerName = Guid.NewGuid().ToString() + "container"; await NonPartitionedContainerHelper.CreateNonPartitionedContainer( this.database, containerName, - indexingPolicy == null ? null : JsonConvert.SerializeObject(indexingPolicy)); + indexingPolicy == null ? null : JsonConvert.SerializeObject(indexingPolicy), + geospatialType); return this.database.GetContainer(containerName); } @@ -163,7 +169,8 @@ await NonPartitionedContainerHelper.CreateNonPartitionedContainer( private async Task CreatePartitionedContainer( int throughput, string partitionKey = "/id", - Microsoft.Azure.Cosmos.IndexingPolicy indexingPolicy = null) + Microsoft.Azure.Cosmos.IndexingPolicy indexingPolicy = null, + Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography) { // Assert that database exists (race deletes are possible when used concurrently) ResponseMessage responseMessage = await this.database.ReadStreamAsync(); @@ -192,7 +199,8 @@ private async Task CreatePartitionedContainer( { Paths = new Collection { partitionKey }, Kind = PartitionKind.Hash - } + }, + GeospatialConfig = new Cosmos.GeospatialConfig(geospatialType) }, // This throughput needs to be about half the max with multi master // otherwise it will create about twice as many partitions. @@ -208,50 +216,57 @@ private async Task CreatePartitionedContainer( private Task<(Container, IReadOnlyList)> CreateNonPartitionedContainerAndIngestDocumentsAsync( IEnumerable documents, - Cosmos.IndexingPolicy indexingPolicy = null) + Cosmos.IndexingPolicy indexingPolicy = null, + Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography) { return this.CreateContainerAndIngestDocumentsAsync( CollectionTypes.NonPartitioned, documents, partitionKey: null, - indexingPolicy: indexingPolicy); + indexingPolicy: indexingPolicy, + geospatialType: geospatialType); } private Task<(Container, IReadOnlyList)> CreateSinglePartitionContainerAndIngestDocumentsAsync( IEnumerable documents, string partitionKey = "/id", - Cosmos.IndexingPolicy indexingPolicy = null) + Cosmos.IndexingPolicy indexingPolicy = null, + Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography) { return this.CreateContainerAndIngestDocumentsAsync( CollectionTypes.SinglePartition, documents, partitionKey, - indexingPolicy); + indexingPolicy, + geospatialType); } private Task<(Container, IReadOnlyList)> CreateMultiPartitionContainerAndIngestDocumentsAsync( IEnumerable documents, string partitionKey = "/id", - Cosmos.IndexingPolicy indexingPolicy = null) + Cosmos.IndexingPolicy indexingPolicy = null, + Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography) { return this.CreateContainerAndIngestDocumentsAsync( CollectionTypes.MultiPartition, documents, partitionKey, - indexingPolicy); + indexingPolicy, + geospatialType); } private async Task<(Container, IReadOnlyList)> CreateContainerAndIngestDocumentsAsync( CollectionTypes collectionType, IEnumerable documents, string partitionKey = "/id", - Cosmos.IndexingPolicy indexingPolicy = null) + Cosmos.IndexingPolicy indexingPolicy = null, + Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography) { Container container = collectionType switch { - CollectionTypes.NonPartitioned => await this.CreateNonPartitionedContainerAsync(indexingPolicy), - CollectionTypes.SinglePartition => await this.CreateSinglePartitionContainer(partitionKey, indexingPolicy), - CollectionTypes.MultiPartition => await this.CreateMultiPartitionContainer(partitionKey, indexingPolicy), + CollectionTypes.NonPartitioned => await this.CreateNonPartitionedContainerAsync(indexingPolicy, geospatialType), + CollectionTypes.SinglePartition => await this.CreateSinglePartitionContainer(partitionKey, indexingPolicy, geospatialType), + CollectionTypes.MultiPartition => await this.CreateMultiPartitionContainer(partitionKey, indexingPolicy, geospatialType), _ => throw new ArgumentException($"Unknown {nameof(CollectionTypes)} : {collectionType}"), }; List insertedDocuments = new List(); @@ -369,7 +384,8 @@ internal Task CreateIngestQueryDeleteAsync( Query query, string partitionKey = "/id", Cosmos.IndexingPolicy indexingPolicy = null, - CosmosClientFactory cosmosClientFactory = null) + CosmosClientFactory cosmosClientFactory = null, + Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography) { Task queryWrapper(Container container, IReadOnlyList inputDocuments, object throwaway) { @@ -384,7 +400,8 @@ Task queryWrapper(Container container, IReadOnlyList inputDocument null, partitionKey, indexingPolicy, - cosmosClientFactory); + cosmosClientFactory, + geospatialType); } internal Task CreateIngestQueryDeleteAsync( @@ -395,7 +412,8 @@ internal Task CreateIngestQueryDeleteAsync( T testArgs, string partitionKey = "/id", Cosmos.IndexingPolicy indexingPolicy = null, - CosmosClientFactory cosmosClientFactory = null) + CosmosClientFactory cosmosClientFactory = null, + Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography) { return this.CreateIngestQueryDeleteAsync( connectionModes, @@ -405,7 +423,8 @@ internal Task CreateIngestQueryDeleteAsync( cosmosClientFactory ?? this.CreateDefaultCosmosClient, testArgs, partitionKey, - indexingPolicy); + indexingPolicy, + geospatialType); } /// @@ -434,7 +453,8 @@ internal async Task CreateIngestQueryDeleteAsync( CosmosClientFactory cosmosClientFactory, T testArgs, string partitionKey = "/id", - Cosmos.IndexingPolicy indexingPolicy = null) + Cosmos.IndexingPolicy indexingPolicy = null, + Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography) { try { @@ -450,15 +470,18 @@ internal async Task CreateIngestQueryDeleteAsync( { CollectionTypes.NonPartitioned => this.CreateNonPartitionedContainerAndIngestDocumentsAsync( documents, - indexingPolicy), + indexingPolicy, + geospatialType), CollectionTypes.SinglePartition => this.CreateSinglePartitionContainerAndIngestDocumentsAsync( documents, partitionKey, - indexingPolicy), + indexingPolicy, + geospatialType), CollectionTypes.MultiPartition => this.CreateMultiPartitionContainerAndIngestDocumentsAsync( documents, partitionKey, - indexingPolicy), + indexingPolicy, + geospatialType), _ => throw new ArgumentException($"Unknown {nameof(CollectionTypes)} : {collectionType}"), }; collectionsAndDocuments.Add(await createContainerTask); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/SanityQueryTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/SanityQueryTests.cs index bc04ba6ebe..60ab6f9fe4 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/SanityQueryTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Query/SanityQueryTests.cs @@ -604,7 +604,8 @@ private async Task TestTryExecuteQueryHelper( MaxItemCount = maxItemCount, }, feedRangeInternal: null, - continuationToken: continuationToken); + continuationToken: continuationToken, + geospatialType: Cosmos.GeospatialType.Geography); if (canSupportExpected) { @@ -637,7 +638,8 @@ private async Task TestTryExecuteQueryHelper( MaxItemCount = 1, }, feedRangeInternal: null, - continuationToken: null); + continuationToken: null, + geospatialType: Cosmos.GeospatialType.Geography); Assert.IsTrue(tryExecuteQueryResult is ContainerInternal.FailedToGetQueryPlanResult); } @@ -653,7 +655,8 @@ private async Task TestTryExecuteQueryHelper( MaxItemCount = 1, }, feedRangeInternal: new FeedRangePartitionKeyRange("0"), // filtering on a PkRangeId. - continuationToken: null); + continuationToken: null, + geospatialType: Cosmos.GeospatialType.Geography); Assert.IsTrue(tryExecuteQueryResult is ContainerInternal.QueryPlanIsSupportedResult); ContainerInternal.QueryPlanIsSupportedResult queryPlanIsSupportedResult = (ContainerInternal.QueryPlanIsSupportedResult)tryExecuteQueryResult; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/NonPartitionedContainerHelper.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/NonPartitionedContainerHelper.cs index 8748360c9a..0482695ec2 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/NonPartitionedContainerHelper.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Utils/NonPartitionedContainerHelper.cs @@ -18,7 +18,8 @@ internal static class NonPartitionedContainerHelper internal static async Task CreateNonPartitionedContainer( Cosmos.Database database, string containerId, - string indexingPolicy = null) + string indexingPolicy = null, + Cosmos.GeospatialType geospatialType = Cosmos.GeospatialType.Geography) { DocumentCollection documentCollection = new DocumentCollection() { @@ -30,6 +31,13 @@ internal static async Task CreateNonPartitionedContainer( documentCollection.IndexingPolicy = JsonConvert.DeserializeObject(indexingPolicy); } + documentCollection.GeospatialConfig = geospatialType switch + { + Cosmos.GeospatialType.Geography => new GeospatialConfig(GeospatialType.Geography), + Cosmos.GeospatialType.Geometry => new GeospatialConfig(GeospatialType.Geometry), + _ => throw new InvalidOperationException($"Unsupported geospatialType {geospatialType}") + }; + await NonPartitionedContainerHelper.CreateNonPartitionedContainer( database, documentCollection); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Query/ParsingBenchmark.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Query/ParsingBenchmark.cs index 9ac18a7068..2a17267604 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Query/ParsingBenchmark.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Performance.Tests/Query/ParsingBenchmark.cs @@ -126,7 +126,8 @@ private static void ParseUsingNativeParser(SqlQuerySpec sqlQuerySpec) allowNonValueAggregateQuery: true, hasLogicalPartitionKey: false, allowDCount: true, - useSystemPrefix: false); + useSystemPrefix: false, + geospatialType: Cosmos.GeospatialType.Geography); tryGetQueryPlan.ThrowIfFailed(); } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.Spatial.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.Spatial.xml new file mode 100644 index 0000000000..1d67b08708 --- /dev/null +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline/QueryPlanBaselineTests.Spatial.xml @@ -0,0 +1,548 @@ + + + + ST_DISTANCE Constant Foldable Geography + SELECT * FROM c WHERE ST_DISTANCE({'type': 'Polygon', 'coordinates': [[[35, 10], [45, 45], [15, 40], [10, 20], [35, 10]], [[20, 30], [35, 35], [30, 20], [20, 30]]]}, {'type': 'Point', 'coordinates': [30, 10]}) > 66408.034483 + + /key + + Hash + Geography + + + + + None + + + + + + + + + + False + + + + [[],"Infinity") + + + + + + + + + ST_DISTANCE Constant Foldable Geometry + SELECT * FROM c WHERE ST_DISTANCE({'type': 'Polygon', 'coordinates': [[[35, 10], [45, 45], [15, 40], [10, 20], [35, 10]], [[20, 30], [35, 35], [30, 20], [20, 30]]]}, {'type': 'Point', 'coordinates': [30, 10]}) > 66408.034483 + + /key + + Hash + Geometry + + + + + None + + + + + + + + + + False + + + + [[],[]] + + + + + + + + + ST_DISTANCE Geography + SELECT * FROM c WHERE ST_DISTANCE({'type': 'Polygon', 'coordinates': [[[35, 10], [45, 45], [15, 40], [10, 20], [35, 10]], [[20, 30], [35, 35], [30, 20], [20, 30]]]}, c.geojson) > 66408.034483 + + /key + + Hash + Geography + + + + + None + + + + + + + + + + False + + + + [[],"Infinity") + + + + + + + + + ST_DISTANCE Geometry + SELECT * FROM c WHERE ST_DISTANCE({'type': 'Polygon', 'coordinates': [[[35, 10], [45, 45], [15, 40], [10, 20], [35, 10]], [[20, 30], [35, 35], [30, 20], [20, 30]]]}, c.geojson) > 66408.034483 + + /key + + Hash + Geometry + + + + + None + + + + + + + + + + False + + + + [[],"Infinity") + + + + + + + + + ST_INTERSECTS Constant Foldable Geography + SELECT * FROM c WHERE ST_INTERSECTS({'type':'LineString', 'coordinates':[ [ 0, 0 ], [ 0, 40 ] ]}, {'type':'Polygon',"coordinates":[[[-60,20], [70,20], [70,70], [-60,70], [-60,20]]]}) + + /key + + Hash + Geography + + + + + None + + + + + + + + + + False + + + + [[],[]] + + + + + + + + + ST_INTERSECTS Constant Foldable Geometry + SELECT * FROM c WHERE ST_INTERSECTS({'type':'LineString', 'coordinates':[ [ 0, 0 ], [ 0, 40 ] ]}, {'type':'Polygon',"coordinates":[[[-60,20], [70,20], [70,70], [-60,70], [-60,20]]]}) + + /key + + Hash + Geometry + + + + + None + + + + + + + + + + False + + + + [[],"Infinity") + + + + + + + + + ST_INTERSECTS Geography + SELECT * FROM c WHERE ST_INTERSECTS(c.geojson, {'type':'Polygon',"coordinates":[[[-60,20], [70,20], [70,70], [-60,70], [-60,20]]]}) + + /key + + Hash + Geography + + + + + None + + + + + + + + + + False + + + + [[],"Infinity") + + + + + + + + + ST_INTERSECTS Geometry + SELECT * FROM c WHERE ST_INTERSECTS(c.geojson, {'type':'Polygon',"coordinates":[[[-60,20], [70,20], [70,70], [-60,70], [-60,20]]]}) + + /key + + Hash + Geometry + + + + + None + + + + + + + + + + False + + + + [[],"Infinity") + + + + + + + + + ST_ISVALID Constant Foldable Geography + SELECT * FROM c WHERE ST_ISVALID({'type': 'Polygon', 'coordinates': [[[-1000, 1000], [1000, 1000], [1000, 4000], [-1000, 4000], [-1000, 1000]]]}) + + /key + + Hash + Geography + + + + + None + + + + + + + + + + False + + + + [[],[]] + + + + + + + + + ST_ISVALID Constant Foldable Geometry + SELECT * FROM c WHERE ST_ISVALID({'type': 'Polygon', 'coordinates': [[[-1000, 1000], [1000, 1000], [1000, 4000], [-1000, 4000], [-1000, 1000]]]}) + + /key + + Hash + Geometry + + + + + None + + + + + + + + + + False + + + + [[],"Infinity") + + + + + + + + + ST_ISVALID Geography + SELECT * FROM c WHERE ST_ISVALID(c.geojson) + + /key + + Hash + Geography + + + + + None + + + + + + + + + + False + + + + [[],"Infinity") + + + + + + + + + ST_ISVALID Geometry + SELECT * FROM c WHERE ST_ISVALID(c.geojson) + + /key + + Hash + Geometry + + + + + None + + + + + + + + + + False + + + + [[],"Infinity") + + + + + + + + + ST_WITHIN Constant Foldable Geography + SELECT * FROM c WHERE NOT ST_WITHIN({'type': 'Point', 'coordinates': [0, 40]}, {'type':'Polygon','coordinates':[[[-60,20], [70,20], [70,70], [-60,70], [-60,20]]]}) + + /key + + Hash + Geography + + + + + None + + + + + + + + + + False + + + + [[],"Infinity") + + + + + + + + + ST_WITHIN Constant Foldable Geometry + SELECT * FROM c WHERE NOT ST_WITHIN({'type': 'Point', 'coordinates': [0, 40]}, {'type':'Polygon','coordinates':[[[-60,20], [70,20], [70,70], [-60,70], [-60,20]]]}) + + /key + + Hash + Geometry + + + + + None + + + + + + + + + + False + + + + [[],[]] + + + + + + + + + ST_WITHIN Geography + SELECT * FROM c WHERE NOT ST_WITHIN(c.geojson, + {'type':'Polygon','coordinates':[[[-60,20], [70,20], [70,70], [-60,70], [-60,20]]]}) + + /key + + Hash + Geography + + + + + None + + + + + + + + + + False + + + + [[],"Infinity") + + + + + + + + + ST_WITHIN Geometry + SELECT * FROM c WHERE NOT ST_WITHIN(c.geojson, + {'type':'Polygon','coordinates':[[[-60,20], [70,20], [70,70], [-60,70], [-60,20]]]}) + + /key + + Hash + Geometry + + + + + None + + + + + + + + + + False + + + + [[],"Infinity") + + + + + + + \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Microsoft.Azure.Cosmos.Tests.csproj b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Microsoft.Azure.Cosmos.Tests.csproj index 2fe8b7feec..ef7e193dbf 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Microsoft.Azure.Cosmos.Tests.csproj +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Microsoft.Azure.Cosmos.Tests.csproj @@ -222,6 +222,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OptimisticDirectExecutionQueryBaselineTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OptimisticDirectExecutionQueryBaselineTests.cs index c2ac15064d..960235dcae 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OptimisticDirectExecutionQueryBaselineTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/OptimisticDirectExecutionQueryBaselineTests.cs @@ -388,7 +388,8 @@ private static PartitionedQueryExecutionInfo GetPartitionedQueryExecutionInfo(st allowNonValueAggregateQuery: true, hasLogicalPartitionKey: false, allowDCount: true, - useSystemPrefix: false); + useSystemPrefix: false, + geospatialType: Cosmos.GeospatialType.Geography); return tryGetQueryPlan.Result; } @@ -611,7 +612,7 @@ public override Task> TryGetOverlappingRangesAs throw new NotImplementedException(); } - public override Task> TryGetPartitionedQueryExecutionInfoAsync(SqlQuerySpec sqlQuerySpec, ResourceType resourceType, PartitionKeyDefinition partitionKeyDefinition, bool requireFormattableOrderByQuery, bool isContinuationExpected, bool allowNonValueAggregateQuery, bool hasLogicalPartitionKey, bool allowDCount, bool useSystemPrefix, CancellationToken cancellationToken) + public override Task> TryGetPartitionedQueryExecutionInfoAsync(SqlQuerySpec sqlQuerySpec, ResourceType resourceType, PartitionKeyDefinition partitionKeyDefinition, bool requireFormattableOrderByQuery, bool isContinuationExpected, bool allowNonValueAggregateQuery, bool hasLogicalPartitionKey, bool allowDCount, bool useSystemPrefix, Cosmos.GeospatialType geospatialType, CancellationToken cancellationToken) { throw new NotImplementedException(); } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/FullPipelineTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/FullPipelineTests.cs index e1cc776d4f..0f8f6f2be7 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/FullPipelineTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/Pipeline/FullPipelineTests.cs @@ -421,7 +421,8 @@ private static QueryInfo GetQueryPlan(string query) allowNonValueAggregateQuery: true, allowDCount: true, hasLogicalPartitionKey: false, - useSystemPrefix: false); + useSystemPrefix: false, + geospatialType: Cosmos.GeospatialType.Geography); info.ThrowIfFailed(); return info.Result.QueryInfo; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPartitionProviderTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPartitionProviderTests.cs index ccd7a81dfd..e4e6f48846 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPartitionProviderTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPartitionProviderTests.cs @@ -39,7 +39,8 @@ public void TestQueryPartitionProviderUpdate() allowNonValueAggregateQuery: true, hasLogicalPartitionKey: false, allowDCount: true, - useSystemPrefix: false); + useSystemPrefix: false, + geospatialType: Cosmos.GeospatialType.Geography); Assert.IsTrue(tryGetQueryPlan.Failed); Assert.IsTrue(tryGetQueryPlan.Exception.ToString().Contains("The SQL query text exceeded the maximum limit of 5 characters")); @@ -54,7 +55,8 @@ public void TestQueryPartitionProviderUpdate() allowNonValueAggregateQuery: true, hasLogicalPartitionKey: false, allowDCount: true, - useSystemPrefix: false); + useSystemPrefix: false, + geospatialType: Cosmos.GeospatialType.Geography); Assert.IsTrue(tryGetQueryPlan.Succeeded); } 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 dbdace2923..79d674d7b9 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 @@ -15,6 +15,7 @@ using System.Linq; using Microsoft.Azure.Cosmos.Query.Core.Pipeline.Aggregate; using Microsoft.Azure.Cosmos.Query.Core.Pipeline.CrossPartition.OrderBy; + using Microsoft.Azure.Cosmos.Serialization.HybridRow.Schemas; /// /// Tests for . @@ -1292,6 +1293,44 @@ public void SystemFunctions() this.ExecuteTestSuite(testVariations); } + [TestMethod] + [Owner("adityasa")] + public void Spatial() + { + var variations = new[] + { + // ST_DISTANCE + new { Description = @"ST_DISTANCE Constant Foldable", Query = @"SELECT * FROM c WHERE ST_DISTANCE({'type': 'Polygon', 'coordinates': [[[35, 10], [45, 45], [15, 40], [10, 20], [35, 10]], [[20, 30], [35, 35], [30, 20], [20, 30]]]}, {'type': 'Point', 'coordinates': [30, 10]}) > 66408.034483" }, + new { Description = @"ST_DISTANCE", Query = @"SELECT * FROM c WHERE ST_DISTANCE({'type': 'Polygon', 'coordinates': [[[35, 10], [45, 45], [15, 40], [10, 20], [35, 10]], [[20, 30], [35, 35], [30, 20], [20, 30]]]}, c.geojson) > 66408.034483" }, + + // ST_INTERSECTS + new { Description = @"ST_INTERSECTS Constant Foldable", Query = @"SELECT * FROM c WHERE ST_INTERSECTS({'type':'LineString', 'coordinates':[ [ 0, 0 ], [ 0, 40 ] ]}, {'type':'Polygon',""coordinates"":[[[-60,20], [70,20], [70,70], [-60,70], [-60,20]]]})" }, + new { Description = @"ST_INTERSECTS", Query = @"SELECT * FROM c WHERE ST_INTERSECTS(c.geojson, {'type':'Polygon',""coordinates"":[[[-60,20], [70,20], [70,70], [-60,70], [-60,20]]]})" }, + + // ST_ISVALID + new { Description = @"ST_ISVALID Constant Foldable", Query = @"SELECT * FROM c WHERE ST_ISVALID({'type': 'Polygon', 'coordinates': [[[-1000, 1000], [1000, 1000], [1000, 4000], [-1000, 4000], [-1000, 1000]]]})" }, + new { Description = @"ST_ISVALID", Query = @"SELECT * FROM c WHERE ST_ISVALID(c.geojson)" }, + + // ST_WITHIN + new { Description = @"ST_WITHIN Constant Foldable", Query = @"SELECT * FROM c WHERE NOT ST_WITHIN({'type': 'Point', 'coordinates': [0, 40]}, {'type':'Polygon','coordinates':[[[-60,20], [70,20], [70,70], [-60,70], [-60,20]]]})" }, + new { Description = @"ST_WITHIN", Query = @"SELECT * FROM c WHERE NOT ST_WITHIN(c.geojson, {'type':'Polygon','coordinates':[[[-60,20], [70,20], [70,70], [-60,70], [-60,20]]]})" }, + }; + + List testVariations = variations + .SelectMany(variation => + { + PartitionKeyDefinition pkDefinitions = CreateHashPartitionKey("/key"); + return new List + { + new QueryPlanBaselineTestInput($"{variation.Description} Geography", pkDefinitions, new SqlQuerySpec(variation.Query)) { GeospatialType = Cosmos.GeospatialType.Geography }, + new QueryPlanBaselineTestInput($"{variation.Description} Geometry", pkDefinitions, new SqlQuerySpec(variation.Query)) { GeospatialType = Cosmos.GeospatialType.Geometry } + }; + }) + .ToList(); + + this.ExecuteTestSuite(testVariations); + } + private static PartitionKeyDefinition CreateHashPartitionKey( params string[] partitionKeys) => new PartitionKeyDefinition() { @@ -1386,7 +1425,8 @@ public override QueryPlanBaselineTestOutput ExecuteTest(QueryPlanBaselineTestInp allowNonValueAggregateQuery: true, hasLogicalPartitionKey: false, allowDCount: true, - useSystemPrefix: false); + useSystemPrefix: false, + geospatialType: input.GeospatialType ?? Cosmos.GeospatialType.Geography); if (info.Failed) { @@ -1401,6 +1441,7 @@ public sealed class QueryPlanBaselineTestInput : BaselineTestInput { internal PartitionKeyDefinition PartitionKeyDefinition { get; set; } internal SqlQuerySpec SqlQuerySpec { get; set; } + internal Cosmos.GeospatialType? GeospatialType { get; set; } internal QueryPlanBaselineTestInput( string description, @@ -1434,6 +1475,11 @@ public override void SerializeAsXml(XmlWriter xmlWriter) this.PartitionKeyDefinition.Kind == PartitionKind.MultiHash ? "MultiHash" : "Range")); } + if (this.GeospatialType != null) + { + xmlWriter.WriteElementString("GeospatialType", this.GeospatialType.Value.ToString()); + } + if (this.SqlQuerySpec.ShouldSerializeParameters()) { xmlWriter.WriteStartElement("QueryParameters"); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPlanRetrieverTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPlanRetrieverTests.cs index ffad405d08..47a681eb1a 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPlanRetrieverTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Query/QueryPlanRetrieverTests.cs @@ -38,6 +38,7 @@ public async Task ServiceInterop_BadRequestContainsInnerException() It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny())).ReturnsAsync(TryCatch.FromException(innerException)); Mock trace = new Mock(); @@ -47,6 +48,7 @@ public async Task ServiceInterop_BadRequestContainsInnerException() ResourceType.Document, new Documents.PartitionKeyDefinition() { Paths = new Collection() { "/id" } }, hasLogicalPartitionKey: false, + geospatialType: Cosmos.GeospatialType.Geography, useSystemPrefix: false, trace.Object, default)); @@ -73,6 +75,7 @@ public async Task ServiceInterop_BadRequestContainsOriginalCosmosException() It.IsAny(), It.IsAny(), It.IsAny(), + It.IsAny(), It.IsAny())).ReturnsAsync(TryCatch.FromException(expectedException)); Mock trace = new Mock(); @@ -82,6 +85,7 @@ public async Task ServiceInterop_BadRequestContainsOriginalCosmosException() ResourceType.Document, new Documents.PartitionKeyDefinition() { Paths = new Collection() { "/id" } }, hasLogicalPartitionKey: false, + geospatialType: Cosmos.GeospatialType.Geography, useSystemPrefix: false, trace.Object, default)); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Routing/PartitionRoutingHelperTest.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Routing/PartitionRoutingHelperTest.cs index 0201b92327..35ffaa65bb 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Routing/PartitionRoutingHelperTest.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Routing/PartitionRoutingHelperTest.cs @@ -348,6 +348,7 @@ FROM c partitionKeyDefinition: new PartitionKeyDefinition { Paths = new Collection { testcase.PartitionKey }, Kind = PartitionKind.Hash }, queryPartitionProvider: QueryPartitionProviderTestInstance.Object, clientApiVersion: testcase.ClientApiVersion, + geospatialType: Cosmos.GeospatialType.Geography, out QueryInfo info); Assert.Fail(); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceWriterBaselineTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceWriterBaselineTests.cs index 2421060b65..eace2eb9df 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceWriterBaselineTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceWriterBaselineTests.cs @@ -815,7 +815,8 @@ private static QueryInfo GetQueryPlan(string query) allowNonValueAggregateQuery: true, hasLogicalPartitionKey: false, allowDCount: true, - useSystemPrefix: false); + useSystemPrefix: false, + geospatialType: Cosmos.GeospatialType.Geography); info.ThrowIfFailed(); return info.Result.QueryInfo;