適用対象: NoSQL
Azure Cosmos DB for NoSQL の地理空間データを使用すると、位置情報を格納し、次のような一般的なクエリを実行できます。
- 定義された領域内に場所があるかどうかを検出する
- 2 つの場所間の距離を測定する
- 進路が場所や領域と交差するかどうかを判断する
このガイドでは、地理空間データを作成して、データのインデックスを作成し、コンテナー内のデータに対してクエリを実行するプロセスについて説明します。
前提条件
- 既存の 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 ファイルを指定します。 このセクションでは、新しく作成されたコンテナーにカスタムの空間インデックスを使用します。
ターミナルを開きます。
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>"cosmicworksを使用して、az cosmosdb sql database createという新しいデータベースを作成します。az cosmosdb sql database create \ --resource-group "<resource-group-name>" \ --account-name "<nosql-account-name>" \ --name "cosmicworks" \ --throughput 400index-policy.json という新しい JSON ファイルを作成し、次の JSON オブジェクトをファイルに追加します。
{ "indexingMode": "consistent", "automatic": true, "includedPaths": [ { "path": "/*" } ], "excludedPaths": [ { "path": "/\"_etag\"/?" } ], "spatialIndexes": [ { "path": "/location/*", "types": [ "Point", "Polygon" ] } ] }az cosmosdb sql container createを使用して、locationsのパーティション キー パスを持つ、/regionという新しいコンテナーを作成します。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 コンソール アプリケーションの作成
.NET SDK for Azure Cosmos DB for NoSQL には、一般的な GeoJSON オブジェクトのクラスが用意されています。 この SDK を使用して、地理的オブジェクトをコンテナーに追加するプロセスを効率化します。
空のディレクトリでターミナルを開きます。
dotnet newコマンドとコンソール テンプレートを使用して、新しい .NET アプリケーションを作成します。dotnet new consoleMicrosoft.Azure.Cosmosコマンドを使用して、dotnet add packageNuGet パッケージをインポートします。dotnet add package Microsoft.Azure.Cosmos --version 3.*警告
Entity Framework は現在、Azure Cosmos DB for NoSQL の空間データをサポートしていません。 厳密に型指定された GeoJSON のサポートには、Azure Cosmos DB for NoSQL SDK のいずれかを使用します。
Azure.IdentityNuGet パッケージをインポートします。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 Microsoft.Azure.Cosmos; using Microsoft.Azure.Cosmos.Linq; using Microsoft.Azure.Cosmos.Spatial;Azure.Identity名前空間に対する別の using ディレクティブを追加します。using Azure.Identity;credentialという名前のDefaultAzureCredential型の新しい変数を作成します。DefaultAzureCredential credential = new();Azure Cosmos DB for NoSQL アカウント エンドポイントを含む
endpointという文字列変数を作成します。string endpoint = "<nosql-account-endpoint>";新しい
CosmosClientクラスのインスタンスを作成し、connectionStringを渡してusing ステートメントでラップします。using CosmosClient client = new (connectionString);cosmicworks/locationsの後にCosmosClient.GetDatabaseを使用して、Azure Cosmos DB for NoSQL アカウントで先ほど作成したコンテナー (Database.GetContainer) への参照を取得します。 結果をcontainerという名前の変数に格納します。var container = client.GetDatabase("cosmicworks").GetContainer("locations");Program.cs ファイルを保存します。
地理空間のデータの追加
.NET SDK では、一般的な GeoJSON オブジェクトを表す複数の型が Microsoft.Azure.Cosmos.Spatial 名前空間に含まれています。 これらの型により、コンテナー内の項目に新しい位置情報を追加するプロセスが効率化されます。
Office.cs という名前の新しいファイルを作成します。 そのファイルで、using ディレクティブを
Microsoft.Azure.Cosmos.Spatialに追加して、次のプロパティを持つOfficeレコード型を作成します。タイプ 説明 既定値 id string一意識別子 name stringオフィスの名前 location PointGeoJSON の地理的位置 カテゴリー stringパーティション キー値 business-officeusing Microsoft.Azure.Cosmos.Spatial; public record Office( string id, string name, Point location, string category = "business-office" );注
このレコードには、GeoJSON 内の特定の位置を表す
Pointプロパティが含まれています。 詳細については、GeoJSON の Point に関するページを参照してください。Region.cs という名前の別のファイルを新規作成します。 次のプロパティを使用して、
Regionという別のレコード型を追加します。タイプ 説明 既定値 id string一意識別子 name stringオフィスの名前 location PolygonGeoJSON の地理的形状 カテゴリー stringパーティション キー値 business-regionusing Microsoft.Azure.Cosmos.Spatial; public record Region( string id, string name, Polygon location, string category = "business-region" );注
このレコードには、GeoJSON 内の複数の場所の間に描画された線で構成される図形を表す
Polygonプロパティが含まれています。 詳細については、GeoJSON の Polygon に関するページを参照してください。Result.cs という名前の別のファイルを新規作成します。 次の 2 つのプロパティを使用して、
Resultというレコード型を追加します。タイプ 説明 name string一致した結果の名前 distanceKilometers decimalキロメートル単位の距離 public record Result( string name, decimal distanceKilometers );Office.cs、Region.cs、Result.cs の各ファイルを保存します。
Program.cs ファイルをもう一度開きます。
Polygonという名前の変数に、新しいmainCampusPolygonを作成します。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), }) } );Polygon、固有識別子
Region、および名前mainCampusRegionを使用して、1000という名前の新しいMain Campus変数を作成します。Region mainCampusRegion = new ("1000", "Main Campus", mainCampusPolygon);Container.UpsertItemAsyncを使用してコンテナーにリージョンを追加します。 リージョンの情報をコンソールに書き込みます。await container.UpsertItemAsync<Region>(mainCampusRegion); Console.WriteLine($"[UPSERT ITEM]\t{mainCampusRegion}");ヒント
このガイドでは、一意の識別子間の競合を引き起こさずにスクリプトを複数回実行できるように、insert ではなく upsert を使用します。 upsert 操作の詳細については、項目の作成に関するページを参照してください。
Pointという名前の新しいheadquartersPoint変数を作成します。 その変数を使用し、Officeという名前の新しいheadquartersOffice変数を、ポイント、一意識別子0001、および名前Headquartersを用いて作成します。Point headquartersPoint = new (-122.12827, 47.63980); Office headquartersOffice = new ("0001", "Headquarters", headquartersPoint);Pointという名前の別のresearchPoint変数を作成します。 その変数を使用して、対応するポイントと一意識別子Officeそして名前researchOfficeにより、0002という名前の別のResearch and Development変数を作成します。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 名前空間の型は、ST_DISTANCE などの組み込み関数を使うために、NoSQL のパラメーター化クエリへの入力として使用できます。
Program.cs ファイルを開きます。
このセクションでは、Point 間の距離を測定するために、クエリを使って
stringという名前の新しいnosql変数を作成します。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 ";QueryDefinition変数をパラメーターとして使用して、queryという名前の新しいnosqlString変数を作成します。 その後、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 ファイルを開きます。
Regionの一意識別子を持つコンテナーから項目1000を取得して、regionという変数に格納します。Region region = await container.ReadItemAsync<Region>("1000", new PartitionKey("business-region"));Container.GetItemLinqQueryable<>メソッドを使用し、LINQ をクエリ可能にしてから、次の 3 つのアクションを実行して LINQ クエリを円滑に構築します。Queryable.Where<>拡張メソッドを使用して、categoryと同等の"business-office"を持つ項目のみをフィルター処理する。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>();重要
この例では、オフィスの location プロパティに Point があり、リージョンの location プロパティに Polygon があります。
ST_WITHINでは、オフィスの Point が当該のリージョンの Polygon 内にあるかどうかを判断します。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を使用し、ターミナルでアプリケーションを最後に 1 回実行します。 出力に 2 つ目の LINQ ベースのクエリに関する結果が含まれていることを確認します。dotnet run[IN REGION] Office { id = 0001, name = Headquarters, location = Microsoft.Azure.Cosmos.Spatial.Point, category = business-office }
リソースをクリーンアップする
このガイドを完了したら、データベースを削除します。
ターミナルを開き、アカウントとリソース グループの名前のシェル変数を作成します。
# 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"