Indizieren und Abfragen von GeoJSON-Standortdaten in Azure Cosmos DB for NoSQL
GILT FÜR: NoSQL
Geodaten in Azure Cosmos DB for NoSQL ermöglichen es Ihnen, Standortinformationen zu speichern und allgemeine Abfragen durchzuführen, einschließlich, aber nicht beschränkt auf:
- Ermitteln, ob sich ein Standort innerhalb eines definierten Bereichs befindet
- Messen des Abstands zwischen zwei Standorten
- Bestimmen, ob ein Pfad eine Position oder einen Bereich schneidet
Dieser Leitfaden führt Sie durch den Prozess zum Erstellen und Indizieren von Geodaten sowie zum anschließenden Abfragen dieser Daten in einem Container.
Voraussetzungen
- Ein vorhandenes Azure Cosmos DB for NoSQL-Konto.
- Wenn Sie kein Azure-Abonnement besitzen, testen Sie Azure Cosmos DB for NoSQL kostenlos.
- Wenn Sie über ein Azure-Abonnement verfügen, erstellen Sie einen neuen Azure Cosmos DB for NoSQL-Cluster.
- Neueste Version von .NET.
- Neueste Version von Azure CLI.
- Wenn Sie eine lokale Installation verwenden, melden Sie sich mithilfe des Befehls
az login
bei der Azure CLI an.
- Wenn Sie eine lokale Installation verwenden, melden Sie sich mithilfe des Befehls
Erstellen eines Containers und einer Indizierungsrichtlinie
Alle Container enthalten eine standardmäßige Indizierungsrichtlinie, die Geodaten erfolgreich indiziert. Um eine benutzerdefinierte Indizierungsrichtlinie zu erstellen, erstellen Sie ein Konto, und geben Sie dann eine JSON-Datei mit der Konfiguration der Richtlinie an. In diesem Abschnitt wird ein benutzerdefinierter räumlicher Index für einen neu erstellten Container verwendet.
Öffnen Sie ein Terminal.
Erstellen Sie eine Shellvariable für den Namen Ihres Azure Cosmos DB for NoSQL-Kontos und ihre Ressourcengruppe.
# Variable for resource group name resourceGroupName="<name-of-your-resource-group>" # Variable for account name accountName="<name-of-your-account>"
Erstellen Sie mithilfe von „
az cosmosdb sql database create
“ eine neue Datenbank namens „cosmicworks
“.az cosmosdb sql database create \ --resource-group "<resource-group-name>" \ --account-name "<nosql-account-name>" \ --name "cosmicworks" \ --throughput 400
Erstellen Sie eine neue JSON-Datei namens index-policy.json, und fügen Sie der Datei das folgende JSON-Objekt hinzu.
{ "indexingMode": "consistent", "automatic": true, "includedPaths": [ { "path": "/*" } ], "excludedPaths": [ { "path": "/\"_etag\"/?" } ], "spatialIndexes": [ { "path": "/location/*", "types": [ "Point", "Polygon" ] } ] }
Verwenden Sie „
az cosmosdb sql container create
“, um einen neuen Container mit dem Namen „locations
“ und dem Partitionsschlüsselpfad „/region
“ zu erstellen.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
Rufen Sie schließlich den Kontoendpunkt für Ihr Konto mithilfe von
az cosmosdb show
und einer JMESPath-Abfrage ab.az cosmosdb show \ --resource-group "<resource-group-name>" \ --name "<nosql-account-name>" \ --query "documentEndpoint"
Notieren Sie den Kontoendpunkt, da Sie dies im nächsten Abschnitt benötigen.
Erstellen einer .NET-SDK-Konsolenanwendung
Das .NET-SDK für Azure Cosmos DB for NoSQL stellt Klassen für gängige GeoJSON-Objekte bereit. Verwenden Sie dieses SDK, um den Prozess des Hinzufügens geografischer Objekte zu Ihrem Container zu optimieren.
Öffnen Sie ein Terminal in einem leeren Verzeichnis.
Erstellen Sie eine neue .NET-Anwendung mithilfe des Befehls mit der
dotnet new
Vorlage Konsole.dotnet new console
Importieren Sie das
Microsoft.Azure.Cosmos
NuGet-Paket mithilfe des Befehlsdotnet add package
.dotnet add package Microsoft.Azure.Cosmos --version 3.*
Warnung
Entity Framework unterstützt derzeit keine räumlichen Daten in Azure Cosmos DB for NoSQL. Verwenden Sie eines der Azure Cosmos DB for NoSQL-SDKs für die Unterstützung von stark typisiertem GeoJSON.
Importieren Sie das
Azure.Identity
NuGet-Paket.dotnet add package Azure.Identity --version 1.*
Erstellen Sie das Projekt mit dem
dotnet build
-Befehl.dotnet build
Öffnen Sie die integrierte Entwicklerumgebung (Integrated Developer Environment, IDE) Ihrer Wahl im selben Verzeichnis wie Ihre .NET-Konsolenanwendung.
Öffnen Sie die neu erstellte Datei Program.cs, und löschen Sie den gesamten vorhandenen Code. Fügen Sie in den Namespaces „“
Microsoft.Azure.Cosmos
, „Microsoft.Azure.Cosmos.Linq
“ und „Microsoft.Azure.Cosmos.Spatial
“ Using-Direktiven hinzu.using Microsoft.Azure.Cosmos; using Microsoft.Azure.Cosmos.Linq; using Microsoft.Azure.Cosmos.Spatial;
Fügen Sie eine weitere Using-Direktive für den
Azure.Identity
Namespace hinzu.using Azure.Identity;
Erstellen Sie eine neue Variable mit dem Namen
credential
vom TypDefaultAzureCredential
.DefaultAzureCredential credential = new();
Erstellen Sie eine Zeichenfolgenvariable namens
endpoint
mit Ihrem Azure Cosmos DB für den NoSQL-Kontoendpunkt.string endpoint = "<nosql-account-endpoint>";
Erstellen Sie eine neue Instanz der Klasse „
CosmosClient
“, die an „connectionString
“ übergeben und von einer Using-Anweisung umschlossen wird.using CosmosClient client = new (connectionString);
Rufen Sie einen Verweis auf den zuvor erstellten Container (
cosmicworks/locations
) im Azure Cosmos DB for NoSQL-Konto ab, indem Sie erst „CosmosClient.GetDatabase
“ und dann „Database.GetContainer
“ verwenden. Speichern Sie das Ergebnis in einer Variable namenscontainer
.var container = client.GetDatabase("cosmicworks").GetContainer("locations");
Speichern Sie die Datei Program.cs.
Hinzufügen von Geodaten
Das .NET-SDK enthält mehrere Typen im Namespace „Microsoft.Azure.Cosmos.Spatial
“, um gängige GeoJSON-Objekte darzustellen. Diese Typen optimieren den Prozess zum Hinzufügen neuer Standortinformationen zu Elementen in einem Container.
Erstellen Sie eine neue Datei mit dem Namen Office.cs. Fügen Sie in der Datei eine Nutzungsanweisung zu
Microsoft.Azure.Cosmos.Spatial
hinzu, und erstellen Sie dann einenOffice
Datensatztyp mit den folgenden Eigenschaften:type BESCHREIBUNG Standardwert id string
Eindeutiger Bezeichner name string
Name des Büros location Point
Geografischer GeoJSON-Punkt category string
Partitionsschlüsselwert business-office
using Microsoft.Azure.Cosmos.Spatial; public record Office( string id, string name, Point location, string category = "business-office" );
Hinweis
Dieser Datensatz enthält eine „
Point
“-Eigenschaft, die eine bestimmte Position in GeoJSON darstellt. Weitere Informationen finden Sie unter GeoJSON-Punkt.Erstellen Sie eine weitere neue Datei namens Region.cs. Fügen Sie einen weiteren Datensatztyp namens „
Region
“ mit den folgenden Eigenschaften hinzu:type BESCHREIBUNG Standardwert id string
Eindeutiger Bezeichner name string
Name des Büros location Polygon
Geografische GeoJSON-Form category string
Partitionsschlüsselwert business-region
using Microsoft.Azure.Cosmos.Spatial; public record Region( string id, string name, Polygon location, string category = "business-region" );
Hinweis
Dieser Datensatz enthält eine „
Polygon
“-Eigenschaft, die eine aus Linien bestehende Form darstellt, die zwischen mehreren Positionen in GeoJSON gezeichnet wird. Weitere Informationen finden Sie unter GeoJSON-Polygon.Erstellen Sie eine weitere neue Datei namens Result.cs. Fügen Sie einen Datensatztyp namens „
Result
“ mit den folgenden beiden Eigenschaften hinzu:type BESCHREIBUNG name string
Name des übereinstimmenden Ergebnisses distanceKilometers decimal
Entfernung in Kilometern public record Result( string name, decimal distanceKilometers );
Speichern Sie die Dateien Office.cs, Region.cs und Result.cs ab.
Öffnen Sie die Datei Program.cs erneut.
Erstellen Sie eine neue „
Polygon
“ in einer Variablen mit dem Namen „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), }) } );
Erstellen Sie mithilfe des Polygons, des eindeutigen Bezeichners „
1000
“ und des Namens „Main Campus
“ eine neue „Region
“-Variable namens „mainCampusRegion
“.Region mainCampusRegion = new ("1000", "Main Campus", mainCampusPolygon);
Verwenden Sie „
Container.UpsertItemAsync
“, um die Region zum Container hinzuzufügen. Schreiben Sie die Informationen der Region in die Konsole.await container.UpsertItemAsync<Region>(mainCampusRegion); Console.WriteLine($"[UPSERT ITEM]\t{mainCampusRegion}");
Tipp
In diesem Leitfaden wird upsert anstelle von insert verwendet, damit Sie das Skript mehrmals ausführen können, ohne einen Konflikt zwischen eindeutigen Bezeichnern zu verursachen. Weitere Informationen zu Upsert-Vorgängen finden Sie unter Erstellen von Elementen.
Erstellen Sie eine neue „
Point
“-Variable namens „headquartersPoint
“. Verwenden Sie diese Variable, um mithilfe des Punkts, des eindeutigen Bezeichners „0001
“ und des Namens „Headquarters
“ eine neue „Office
“-Variable namens „headquartersOffice
“ zu erstellen.Point headquartersPoint = new (-122.12827, 47.63980); Office headquartersOffice = new ("0001", "Headquarters", headquartersPoint);
Erstellen Sie eine weitere „
Point
“-Variable mit dem Namen „researchPoint
“. Verwenden Sie diese Variable, um mithilfe des entsprechenden Punkts, des eindeutigen Bezeichners „0002
“ und des Namens „Research and Development
“ eine weitere „Office
“-Variable namens „researchOffice
“ zu erstellen.Point researchPoint = new (-96.84369, 46.81298); Office researchOffice = new ("0002", "Research and Development", researchPoint);
Erstellen Sie ein „
TransactionalBatch
“, um für beide „Office
“-Variablen ein Upsert als einzelne Transaktion auszuführen. Schreiben Sie dann die Informationen beider Büros in die Konsole.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}");
Hinweis
Weitere Informationen zu Transaktionen finden Sie unter Transaktionale Batchvorgänge.
Speichern Sie die Datei Program.cs.
Führen Sie die Anwendung mithilfe von „
dotnet run
“ in einem Terminal. Beachten Sie, dass die Ausgabe der Anwendungsausführung Informationen zu den drei neu erstellten Elementen enthält.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 }
Abfragen von Geodaten mithilfe einer NoSQL-Abfrage
Die Typen im Namespace „Microsoft.Azure.Cosmos.Spatial
“ können als Eingaben für eine parametrisierte NoSQL-Abfrage verwendet werden, um integrierte Funktionen wie „ST_DISTANCE
“ zu verwenden.
Öffnen Sie die Datei Program.cs.
Erstellen Sie eine neue „
string
“-Variable namens „nosql
“ mit der in diesem Abschnitt verwendeten Abfrage, um den Abstand zwischen Punkten zu messen.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 ";
Tipp
Diese Abfrage platziert die Geofunktion in einer Unterabfrage, um den Prozess der mehrfachen Wiederverwendung des bereits berechneten Werts in den Klauseln „
SELECT
“ und „WHERE
“ zu vereinfachen.Erstellen Sie eine neue „
QueryDefinition
“-Variable namens „query
“, indem Sie die Variable „nosqlString
“ als Parameter verwenden. Verwenden Sie dann die Fluent-Methode „QueryDefinition.WithParameter
“ mehrmals, um der Abfrage folgende Parameter hinzuzufügen:Wert @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));
Erstellen Sie mit „
Container.GetItemQueryIterator<>
“, dem generischen Typ „Result
“ und der Variablen „query
“ einen neuen Iterator. Verwenden Sie dann eine Kombination aus einer while- und einer foreach-Schleife, um alle Ergebnisse auf jeder Ergebnisseite zu durchlaufen. Geben Sie jedes Ergebnis an die Konsole aus.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}"); } }
Hinweis
Weitere Informationen zum Auflisten von Abfrageergebnissen finden Sie unter Abfrageelemente.
Speichern Sie die Datei Program.cs.
Führen Sie die Anwendung mithilfe von „
dotnet run
“ erneut in einem Terminal. Beachten Sie, dass die Ausgabe jetzt die Ergebnisse der Abfrage enthält.dotnet run
[DISTANCE KM] Result { name = Headquarters, distanceKilometers = 3.34 } [DISTANCE KM] Result { name = Research and Development, distanceKilometers = 1907.43 }
Abfragen von Geodaten mithilfe von LINQ
Die LINQ to NoSQL-Funktionalität im .NET-SDK unterstützt das Einschließen von räumlichen Typen in Abfrageausdrücken. Darüber hinaus enthält das SDK Erweiterungsmethoden, die gleichwertigen integrierten Funktionen zugeordnet sind:
Erweiterungsmethode | Integrierte Funktion |
---|---|
Distance() |
ST_DISTANCE |
Intersects() |
ST_INTERSECTS |
IsValid() |
ST_ISVALID |
IsValidDetailed() |
ST_ISVALIDDETAILED |
Within() |
ST_WITHIN |
Öffnen Sie die Datei Program.cs.
Rufen Sie das Element „
Region
“ aus dem Container mit dem eindeutigen Bezeichner „1000
“ ab, und speichern Sie es in einer Variablen mit dem Namen „region
“.Region region = await container.ReadItemAsync<Region>("1000", new PartitionKey("business-region"));
Verwenden Sie die „
Container.GetItemLinqQueryable<>
“-Methode, um eine LINQ-Abfrage abzurufen, und erstellen Sie die LINQ-Abfrage, indem Sie die folgenden drei Aktionen ausführen:Verwenden Sie die Erweiterungsmethode „
Queryable.Where<>
“, um nur nach Elementen mit einem „category
“, das äquivalent zu „"business-office"
“ ist, zu filtern.Verwenden Sie „
Queryable.Where<>
“ erneut, um mithilfe von „Geometry.Within()
“ nur Orte innerhalb von „region
“ der Variable „location
“ zu filtern.Übersetzen Sie den LINQ-Ausdruck mithilfe von „
CosmosLinqExtensions.ToFeedIterator<>
“ in einen Feediterator.
var regionIterator = container.GetItemLinqQueryable<Office>() .Where(o => o.category == "business-office") .Where(o => o.location.Within(region.location)) .ToFeedIterator<Office>();
Wichtig
In diesem Beispiel weist die „Location“-Eigenschaft des Büros einen Punkt und die „Location“-Eigenschaft der Region ein Polygon auf. „
ST_WITHIN
“ bestimmt, ob sich der Punkt des Büros innerhalb des Polygons der Region befindet.Verwenden Sie eine Kombination aus einer while- und einer foreach-Schleife, um alle Ergebnisse auf jeder Ergebnisseite zu durchlaufen. Geben Sie jedes Ergebnis an die Konsole aus.
while (regionIterator.HasMoreResults) { var response = await regionIterator.ReadNextAsync(); foreach (var office in response) { Console.WriteLine($"[IN REGION]\t{office}"); } }
Speichern Sie die Datei Program.cs.
Führen Sie die Anwendung mithilfe von „
dotnet run
“ ein letztes Mal in einem Terminal aus. Beachten Sie, dass die Ausgabe jetzt die Ergebnisse der zweiten auf LINQ basierenden Abfrage enthält.dotnet run
[IN REGION] Office { id = 0001, name = Headquarters, location = Microsoft.Azure.Cosmos.Spatial.Point, category = business-office }
Bereinigen von Ressourcen
Entfernen Sie Ihre Datenbank, nachdem Sie diesen Leitfaden abgeschlossen haben.
Öffnen Sie ein Terminal, und erstellen Sie eine Shellvariable für den Namen Ihres Kontos und Ihrer Ressourcengruppe.
# Variable for resource group name resourceGroupName="<name-of-your-resource-group>" # Variable for account name accountName="<name-of-your-account>"
Verwenden Sie „
az cosmosdb sql database delete
“, um die Datenbank zu entfernen.az cosmosdb sql database delete \ --resource-group "<resource-group-name>" \ --account-name "<nosql-account-name>" \ --name "cosmicworks"