Azure Cosmos DB for NoSQL における GeoJSON の位置情報データのインデックス作成とクエリ
適用対象: 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>"
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 コンソール アプリケーションの作成
.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.*
警告
Entity Framework は現在、Azure Cosmos DB for NoSQL の空間データをサポートしていません。 厳密に型指定された GeoJSON のサポートには、Azure Cosmos DB for NoSQL SDK のいずれかを使用します。
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 ディレクティブを追加します。using Azure.Identity;
credential
という名前のDefaultAzureCredential
型の新しい変数を作成します。DefaultAzureCredential credential = new();
Azure Cosmos DB for NoSQL アカウント エンドポイントを含む
endpoint
という文字列変数を作成します。string endpoint = "<nosql-account-endpoint>";
connectionString
を渡して using ステートメントにラップするCosmosClient
クラスの新しいインスタンスを作成します。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 では、一般的な GeoJSON オブジェクトを表す複数の型が Microsoft.Azure.Cosmos.Spatial
名前空間に含まれています。 これらの型により、コンテナー内の項目に新しい位置情報を追加するプロセスが効率化されます。
Office.cs という名前の新しいファイルを作成します。 そのファイルで、using ディレクティブを
Microsoft.Azure.Cosmos.Spatial
に追加して、次のプロパティを持つOffice
レコード型を作成します。Type 説明 既定値 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" );
注意
このレコードには、GeoJSON 内の特定の位置を表す
Point
プロパティが含まれています。 詳細については、GeoJSON の Point に関するページを参照してください。Region.cs という名前の別のファイルを新規作成します。 次のプロパティを使用して、
Region
という別のレコード型を追加します。Type 説明 既定値 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" );
注意
このレコードには、GeoJSON 内の複数の場所の間に描画された線で構成される図形を表す
Polygon
プロパティが含まれています。 詳細については、GeoJSON の Polygon に関するページを参照してください。Result.cs という名前の別のファイルを新規作成します。 次の 2 つのプロパティを使用して、
Result
というレコード型を追加します。Type 説明 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), }) } );
Polygon を使用した
mainCampusRegion
という名前の新しいRegion
変数、一意識別子の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 操作の詳細については、項目の作成に関するページを参照してください。
headquartersPoint
という名前の新しいPoint
変数を作成します。 その変数を使用し、Point、一意識別子の0001
、Headquarters
という名前を使って、headquartersOffice
という新しいOffice
変数を作成します。Point headquartersPoint = new (-122.12827, 47.63980); Office headquartersOffice = new ("0001", "Headquarters", headquartersPoint);
researchPoint
という名前の別のPoint
変数を作成します。 その変数を使用し、対応する 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
名前空間の型は、ST_DISTANCE
などの組み込み関数を使うために、NoSQL のパラメーター化クエリへの入力として使用できます。
Program.cs ファイルを開きます。
このセクションでは、Point 間の距離を測定するために、クエリを使って
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<>
拡張メソッドを使用して、"business-office"
と同等のcategory
を持つ項目のみをフィルター処理する。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>();
重要
この例では、オフィスの 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"