你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
索引和查询 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 $resourceGroupName \ --account-name $accountName \ --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 $resourceGroupName \ --account-name $accountName \ --database-name "cosmicworks" \ --name "locations" \ --partition-key-path "/category" \ --idx @index-policy.json
使用
az cosmosdb keys list
检索帐户的主要连接字符串。az cosmosdb keys list \ --resource-group $resourceGroupName \ --name $accountName \ --type "connection-strings" \ --query "connectionStrings[?keyKind == \`Primary\`].connectionString" \ --output tsv
提示
若要查看帐户的所有可能的连接字符串,请使用
az cosmosdb keys list --resource-group $resourceGroupName --name $accountName --type "connection-strings"
。记录连接字符串。 本指南稍后将使用此凭据。
创建 .NET SDK 控制台应用程序
.NET SDK for Azure Cosmos DB for NoSQL 为常见的 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 支持。
使用
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;
使用本指南前面记录的连接字符串添加名为 *
connectionString
的字符串变量。string connectionString = "<your-account-connection-string>"
创建
CosmosClient
类的新实例,从而传入connectionString
并将其包装在 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
在终端中运行应用程序。 请注意,应用程序运行的输出包括 3 个新创建的项的相关信息。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 可查询项,并执行下面 3 个操作来顺场地生成 LINQ 查询:使用
Queryable.Where<>
扩展方法筛选找到category
等效于"business-office"
的项。再次使用
Queryable.Where<>
,通过Geometry.Within()
筛选找到region
变量的location
属性中的位置。使用
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 $resourceGroupName \ --account-name $accountName \ --name "cosmicworks"