索引和查询 Azure Cosmos DB for NoSQL 中的 GeoJSON 位置数据
适用范围: NoSQL
通过 Azure Cosmos DB for NoSQL 中的地理空间数据,可存储位置信息并执行常见查询,这包括但不限于:
- 查找某个位置是否在定义的区域内
- 测量两个位置之间的距离
- 确定路径是否与位置或区域相交
本指南逐步讲解创建地理空间数据、索引数据,然后在容器中查询数据的过程。
先决条件
- 一个现有的 Azure Cosmos DB for NoSQL 帐户。
- 如果没有 Azure 订阅,请 试用 Azure Cosmos DB for NoSQL。
- 如果已有 Azure 订阅,请 创建新的 Azure Cosmos DB for NoSQL 帐户。
- 最新版本的 .NET。
- 最新版本的 Azure CLI。
- 如果使用的是本地安装,请使用
az login
命令登录到 Azure CLI。
- 如果使用的是本地安装,请使用
创建容器和索引策略
所有容器都包含一个默认索引策略,该策略将成功为地理空间数据编制索引。 若要创建自定义索引策略,请创建一个帐户,并使用策略的配置指定 JSON 文件。 在本部分中,对新创建的容器使用自定义空间索引。
打开终端。
创建一个 shell 变量来存储 Azure Cosmos DB for NoSQL 帐户和资源组的名称。
# Variable for resource group name resourceGroupName="<name-of-your-resource-group>" # Variable for account name accountName="<name-of-your-account>"
使用
az cosmosdb sql database create
创建名为cosmicworks
的新数据库。az cosmosdb sql database create \ --resource-group "<resource-group-name>" \ --account-name "<nosql-account-name>" \ --name "cosmicworks" \ --throughput 400
创建名为 index-policy.json 的新 JSON 文件,并将以下 JSON 对象添加到该文件。
{ "indexingMode": "consistent", "automatic": true, "includedPaths": [ { "path": "/*" } ], "excludedPaths": [ { "path": "/\"_etag\"/?" } ], "spatialIndexes": [ { "path": "/location/*", "types": [ "Point", "Polygon" ] } ] }
使用
az cosmosdb sql container create
创建分区键路径为/region
的名为locations
的新容器。az cosmosdb sql container create \ --resource-group "<resource-group-name>" \ --account-name "<nosql-account-name>" \ --database-name "cosmicworks" \ --name "locations" \ --partition-key-path "/category" \ --idx @index-policy.json
最后,使用
az cosmosdb show
和 JMESPath 查询获取帐户的帐户终结点。az cosmosdb show \ --resource-group "<resource-group-name>" \ --name "<nosql-account-name>" \ --query "documentEndpoint"
记录帐户终结点,因为下一部分中需要此终结点。
创建 .NET SDK 控制台应用程序
适用于 Azure Cosmos DB for NoSQL 的 .NET SDK 为常见 GeoJSON 对象提供类。 使用此 SDK 可简化将地理对象添加到容器的过程。
在空目录中打开终端。
通过将
dotnet new
命令与控制台模板一起使用,创建一个新的 .NET 应用程序。dotnet new console
使用
dotnet add package
命令导入Microsoft.Azure.Cosmos
NuGet 包。dotnet add package Microsoft.Azure.Cosmos --version 3.*
警告
实体框架目前不支持 Azure Cosmos DB for NoSQL 中的空间数据。 使用其中一个 Azure Cosmos DB for NoSQL SDK 获取强类型 GeoJSON 支持。
导入
Azure.Identity
NuGet 包。dotnet add package Azure.Identity --version 1.*
使用
dotnet build
命令生成项目。dotnet build
在 .NET 控制台应用程序所在的同一目录中打开所选的集成开发人员环境 (IDE)。
打开新创建的 Program.cs 文件并删除任何现有代码。 为
Microsoft.Azure.Cosmos
、Microsoft.Azure.Cosmos.Linq
和Microsoft.Azure.Cosmos.Spatial
命名空间添加 using 指令。using Microsoft.Azure.Cosmos; using Microsoft.Azure.Cosmos.Linq; using Microsoft.Azure.Cosmos.Spatial;
使用指令为
Azure.Identity
命名空间再添加一个。using Azure.Identity;
创建一个名为
credential
、类型为DefaultAzureCredential
的新变量。DefaultAzureCredential credential = new();
使用 Azure Cosmos DB for NoSQL 帐户终结点创建名为
endpoint
的字符串变量。string endpoint = "<nosql-account-endpoint>";
创建传入
connectionString
的CosmosClient
类的新实例,并将其包装在 using 语句中。using CosmosClient client = new (connectionString);
通过使用
CosmosClient.GetDatabase
,然后使用Database.GetContainer
,在 Azure Cosmos DB for NoSQL 帐户中检索对先前创建的容器 (cosmicworks/locations
) 的引用。 将结果存储在名为container
的变量中。var container = client.GetDatabase("cosmicworks").GetContainer("locations");
保存 Program.cs 文件。
添加地理空间数据
.NET SDK 在 Microsoft.Azure.Cosmos.Spatial
命名空间中包含多个类型,用于表示常见的 GeoJSON 对象。 这些类型简化了向容器中的项添加新位置信息的过程。
创建名为 Office.cs 的新文件。 在文件中,将 using 指令添加到
Microsoft.Azure.Cosmos.Spatial
,然后使用以下属性创建Office
记录类型:类型 说明 默认值 id string
唯一标识符 name string
办公室名称 location Point
GeoJSON 地理点 category string
分区键值 business-office
using Microsoft.Azure.Cosmos.Spatial; public record Office( string id, string name, Point location, string category = "business-office" );
创建另一个名为 Region.cs 的新文件。 使用以下属性另外添加一个名为
Region
的记录类型:类型 说明 默认值 id string
唯一标识符 name string
办公室名称 location Polygon
GeoJSON 地理形状 category string
分区键值 business-region
using Microsoft.Azure.Cosmos.Spatial; public record Region( string id, string name, Polygon location, string category = "business-region" );
注意
此记录包含一个
Polygon
属性,该属性表示由在 GeoJSON 中的多个位置之间绘制的线条组成的形状。 有关详细信息,请参阅 GeoJSON 多边形。创建另一个名为 Result.cs 的新文件。 添加包含下面两个属性的名为
Result
的记录类型:类型 说明 name string
匹配结果的名称 distanceKilometers decimal
以公里为单位的距离 public record Result( string name, decimal distanceKilometers );
保存 Office.cs、Region.cs 和 Result.cs 文件。
再次打开 Program.cs 文件。
在名为
mainCampusPolygon
的变量中创建一个新的Polygon
。Polygon mainCampusPolygon = new ( new [] { new LinearRing(new [] { new Position(-122.13237, 47.64606), new Position(-122.13222, 47.63376), new Position(-122.11841, 47.64175), new Position(-122.12061, 47.64589), new Position(-122.13237, 47.64606), }) } );
使用多边形、唯一标识符
1000
和名称Main Campus
创建名为mainCampusRegion
的新Region
变量。Region mainCampusRegion = new ("1000", "Main Campus", mainCampusPolygon);
使用
Container.UpsertItemAsync
将区域添加到容器。 将区域的信息写入控制台。await container.UpsertItemAsync<Region>(mainCampusRegion); Console.WriteLine($"[UPSERT ITEM]\t{mainCampusRegion}");
提示
本指南使用“更新插入”而不是“插入”,因此可以多次运行脚本,而不会导致唯一标识符之间出现冲突。 有关更新插入操作的详细信息,请参阅 创建项。
创建名为
headquartersPoint
的新Point
变量。 使用该变量创建一个使用点、唯一标识符0001
和名称Headquarters
的,名为headquartersOffice
的新Office
变量。Point headquartersPoint = new (-122.12827, 47.63980); Office headquartersOffice = new ("0001", "Headquarters", headquartersPoint);
创建另一个名为
researchPoint
的Point
变量。 使用该变量创建另一个使用相应的点、唯一标识符0002
和名称Research and Development
的,名为researchOffice
的新Office
变量。Point researchPoint = new (-96.84369, 46.81298); Office researchOffice = new ("0002", "Research and Development", researchPoint);
创建
TransactionalBatch
以将这两个Office
变量作为单个事务更新插入。 然后,将两个办公室的信息写入控制台。TransactionalBatch officeBatch = container.CreateTransactionalBatch(new PartitionKey("business-office")); officeBatch.UpsertItem<Office>(headquartersOffice); officeBatch.UpsertItem<Office>(researchOffice); await officeBatch.ExecuteAsync(); Console.WriteLine($"[UPSERT ITEM]\t{headquartersOffice}"); Console.WriteLine($"[UPSERT ITEM]\t{researchOffice}");
注意
有关事务的详细信息,请参阅事务批处理操作。
保存 Program.cs 文件。
使用
dotnet run
在终端中运行应用程序。 请注意,应用程序运行的输出包括有关三个新创建的项的信息。dotnet run
[UPSERT ITEM] Region { id = 1000, name = Main Campus, location = Microsoft.Azure.Cosmos.Spatial.Polygon, category = business-region } [UPSERT ITEM] Office { id = 0001, name = Headquarters, location = Microsoft.Azure.Cosmos.Spatial.Point, category = business-office } [UPSERT ITEM] Office { id = 0002, name = Research and Development, location = Microsoft.Azure.Cosmos.Spatial.Point, category = business-office }
使用 NoSQL 查询以查询地理空间数据
Microsoft.Azure.Cosmos.Spatial
命名空间中的类型可用作 NoSQL 参数化查询的输入,以使用 ST_DISTANCE
等内置函数。
打开 Program.cs 文件。
使用在本部分中所用的查询新建一个名为
nosql
的string
变量,来测量点之间的距离。string nosqlString = @" SELECT o.name, NumberBin(distanceMeters / 1000, 0.01) AS distanceKilometers FROM offices o JOIN (SELECT VALUE ROUND(ST_DISTANCE(o.location, @compareLocation))) AS distanceMeters WHERE o.category = @partitionKey AND distanceMeters > @maxDistance ";
使用
nosqlString
变量作为参数创建名为query
的新QueryDefinition
变量。 然后,多次使用QueryDefinition.WithParameter
fluent 方法将这些参数添加到查询:值 @maxDistance 2000
@partitionKey "business-office"
@compareLocation new Point(-122.11758, 47.66901)
var query = new QueryDefinition(nosqlString) .WithParameter("@maxDistance", 2000) .WithParameter("@partitionKey", "business-office") .WithParameter("@compareLocation", new Point(-122.11758, 47.66901));
使用
Container.GetItemQueryIterator<>
、Result
泛型类型和query
变量创建新的迭代器。 然后,结合使用 while 和 foreach 循环来循环访问每个结果页面中的所有结果。 将每个结果输出到控制台。var distanceIterator = container.GetItemQueryIterator<Result>(query); while (distanceIterator.HasMoreResults) { var response = await distanceIterator.ReadNextAsync(); foreach (var result in response) { Console.WriteLine($"[DISTANCE KM]\t{result}"); } }
注意
若要详细了解如何枚举查询结果,请参阅查询项。
保存 Program.cs 文件。
使用
dotnet run
在终端中再次运行应用程序。 请注意,输出现在包含查询的结果。dotnet run
[DISTANCE KM] Result { name = Headquarters, distanceKilometers = 3.34 } [DISTANCE KM] Result { name = Research and Development, distanceKilometers = 1907.43 }
使用 LINQ 查询地理空间数据
.NET SDK 中的 LINQ to NoSQL 功能支持在查询表达式中包含地理空间类型。 此外,SDK 还包括映射到等效内置函数的扩展方法:
扩展方法 | 内置函数 |
---|---|
Distance() |
ST_DISTANCE |
Intersects() |
ST_INTERSECTS |
IsValid() |
ST_ISVALID |
IsValidDetailed() |
ST_ISVALIDDETAILED |
Within() |
ST_WITHIN |
打开 Program.cs 文件。
使用唯一标识符
1000
在容器中检索Region
项,并将它存储在名为region
的变量中。Region region = await container.ReadItemAsync<Region>("1000", new PartitionKey("business-region"));
使用
Container.GetItemLinqQueryable<>
方法获取可查询的 LINQ,并通过执行以下三项操作来流畅地生成 LINQ 查询:使用
Queryable.Where<>
扩展方法筛选为仅具有"business-office"
等效项的category
项。再次使用
Queryable.Where<>
筛选region
变量的location
属性中仅使用Geometry.Within()
的位置。使用
CosmosLinqExtensions.ToFeedIterator<>
将 LINQ 表达式转换为源迭代器。
var regionIterator = container.GetItemLinqQueryable<Office>() .Where(o => o.category == "business-office") .Where(o => o.location.Within(region.location)) .ToFeedIterator<Office>();
重要
在此示例中,办公室的位置属性具有点,区域的位置属性具有多边形。
ST_WITHIN
确定办公室的点是否在区域的多边形内。结合使用 while 和 foreach 循环来循环访问每个结果页面中的所有结果。 将每个结果输出到控制台。
while (regionIterator.HasMoreResults) { var response = await regionIterator.ReadNextAsync(); foreach (var office in response) { Console.WriteLine($"[IN REGION]\t{office}"); } }
保存 Program.cs 文件。
使用
dotnet run
在终端中最后运行一次应用程序。 请注意,输出现在包含第二个基于 LINQ 的查询的结果。dotnet run
[IN REGION] Office { id = 0001, name = Headquarters, location = Microsoft.Azure.Cosmos.Spatial.Point, category = business-office }
清理资源
完成本指南后,请移除你的数据库。
打开终端,为帐户和资源组的名称创建 shell 变量。
# Variable for resource group name resourceGroupName="<name-of-your-resource-group>" # Variable for account name accountName="<name-of-your-account>"
使用
az cosmosdb sql database delete
来移除数据库。az cosmosdb sql database delete \ --resource-group "<resource-group-name>" \ --account-name "<nosql-account-name>" \ --name "cosmicworks"