Indicizzare ed eseguire query sui dati della posizione GeoJSON in Azure Cosmos DB per NoSQL
SI APPLICA A: NoSQL
I dati geospaziali in Azure Cosmos DB per NoSQL consentono di archiviare le informazioni sulla posizione ed eseguire query comuni, tra cui, a titolo esemplificativo:
- Cercare se una posizione si trova all'interno di un'area definita
- Misurare la distanza tra due posizioni
- Determinare se un percorso si interseca con una posizione o un'area
Questa guida illustra il processo di creazione di dati geospaziali, l’indicizzazione dei dati e quindi l’esecuzione di query sui dati in un contenitore.
Prerequisiti
- Un account Azure Cosmos DB per NoSQL già presente.
- Se non si ha un abbonamento ad Azure, prova gratuitamente Azure Cosmos DB per NoSQL.
- Se si ha già un abbonamento ad Azure, creare un nuovo account Azure Cosmos DB per NoSQL.
- Versione più recente di .NET.
- Versione più recente dell'interfaccia della riga di comando di Azure.
- Se si usa un'installazione locale, accedere all'interfaccia della riga di comando di Azure usando il comando
az login
.
- Se si usa un'installazione locale, accedere all'interfaccia della riga di comando di Azure usando il comando
Creare contenitori e criteri di indicizzazione
Tutti i contenitori comprendono un criterio di indicizzazione predefinito che indicizza correttamente i dati geospaziali. Per creare un criterio di indicizzazione personalizzato, creare un account e specificare un file JSON con la configurazione del criterio. In questa sezione viene usato un indice spaziale personalizzato per un contenitore creato recentemente.
Aprire un terminale.
Creare una variabile della shell per il nome dell'account Azure Cosmos DB per NoSQL e del gruppo di risorse.
# Variable for resource group name resourceGroupName="<name-of-your-resource-group>" # Variable for account name accountName="<name-of-your-account>"
Creare un nuovo database denominato
cosmicworks
usandoaz cosmosdb sql database create
.az cosmosdb sql database create \ --resource-group "<resource-group-name>" \ --account-name "<nosql-account-name>" \ --name "cosmicworks" \ --throughput 400
Creare un nuovo file JSON denominato index-policy.json e aggiungere l'oggetto JSON seguente al file.
{ "indexingMode": "consistent", "automatic": true, "includedPaths": [ { "path": "/*" } ], "excludedPaths": [ { "path": "/\"_etag\"/?" } ], "spatialIndexes": [ { "path": "/location/*", "types": [ "Point", "Polygon" ] } ] }
Usare
az cosmosdb sql container create
per creare un nuovo contenitore denominatolocations
con un percorso di chiave di partizione di/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
Infine, ottenere l'endpoint dell'account usando
az cosmosdb show
e una query JMESPath.az cosmosdb show \ --resource-group "<resource-group-name>" \ --name "<nosql-account-name>" \ --query "documentEndpoint"
Registrare l'endpoint dell'account perché sarà necessario nella sezione successiva.
Creare un'applicazione console .NET SDK
.NET SDK per Azure Cosmos DB per NoSQL fornisce classi per oggetti GeoJSON comuni. Usare questo SDK per velocizzare l’aggiunta di oggetti geografici al contenitore.
Aprire un terminale in una directory vuota.
Creare una nuova applicazione .NET usando il
dotnet new
comando con il modello di console .dotnet new console
Importare il
Microsoft.Azure.Cosmos
pacchetto NuGet usando ildotnet add package
comando .dotnet add package Microsoft.Azure.Cosmos --version 3.*
Avviso
Entity Framework non attualmente dati spaziali in Azure Cosmos DB per NoSQL. Usare uno degli SDK di Azure Cosmos DB per NoSQL per il supporto GeoJSON strettamente specificato.
Importare il
Azure.Identity
pacchetto NuGet.dotnet add package Azure.Identity --version 1.*
Creare il progetto con il
dotnet build
comando.dotnet build
Aprire l'ambiente di sviluppo integrato (IDE) preferito nella stessa directory dell'applicazione console .NET.
Aprire il file Program.cs appena creato ed eliminare qualsiasi codice esistente. Aggiungere delle direttive d’uso per gli spazi dei nomi
Microsoft.Azure.Cosmos
,Microsoft.Azure.Cosmos.Linq
eMicrosoft.Azure.Cosmos.Spatial
.using Microsoft.Azure.Cosmos; using Microsoft.Azure.Cosmos.Linq; using Microsoft.Azure.Cosmos.Spatial;
Aggiungere un'altra direttiva using per lo spazio dei
Azure.Identity
nomi .using Azure.Identity;
Creare una nuova variabile denominata
credential
di tipoDefaultAzureCredential
.DefaultAzureCredential credential = new();
Creare una variabile stringa denominata
endpoint
con l'endpoint dell'account Azure Cosmos DB per NoSQL.string endpoint = "<nosql-account-endpoint>";
Creare una nuova istanza della
CosmosClient
classe passandolaconnectionString
ed eseguendo il wrapping in un'istruzione d’uso.using CosmosClient client = new (connectionString);
Recuperare un riferimento al contenitore creato in precedenza (
cosmicworks/locations
) nell'account Azure Cosmos DB per NoSQL usandoCosmosClient.GetDatabase
e dopoDatabase.GetContainer
. Archiviare il risultato in una variabile denominatacontainer
.var container = client.GetDatabase("cosmicworks").GetContainer("locations");
Salvare il file Program.cs.
Aggiungere dati geospaziali
.NET SDK include più tipi nello Microsoft.Azure.Cosmos.Spatial
spazio dei nomi per rappresentare oggetti GeoJSON comuni. Questi tipi velocizzano l’aggiunta di nuove informazioni sulla posizione agli elementi in un contenitore.
Creare un nuovo file denominato Office.cs. Nel file aggiungere una direttiva using a
Microsoft.Azure.Cosmos.Spatial
e quindi creare unOffice
tipo di record con queste proprietà:Tipo Descrizione Default value id string
Identificatore univoco name string
Nome dell'ufficio location Point
Punto geografico GeoJSON category string
Valore chiave di partizione business-office
using Microsoft.Azure.Cosmos.Spatial; public record Office( string id, string name, Point location, string category = "business-office" );
Nota
Questo record include una
Point
proprietà che rappresenta una posizione specifica in GeoJSON. Per altre informazioni, vedere Punto GeoJSON.Creare un altro nuovo file denominato Region.cs. Aggiungere un altro tipo di record denominato
Region
con queste proprietà:Tipo Descrizione Default value id string
Identificatore univoco name string
Nome dell'ufficio location Polygon
Forma geografica GeoJSON category string
Valore chiave di partizione business-region
using Microsoft.Azure.Cosmos.Spatial; public record Region( string id, string name, Polygon location, string category = "business-region" );
Nota
Questo record include una
Polygon
proprietà che rappresenta una forma composta da linee disegnate tra più posizioni in GeoJSON. Per altre informazioni, vedere GeoJSON Polygon.Creare un nuovo file denominato Result.cs. Aggiungere un tipo di record denominato
Result
con queste due proprietà:Tipo Descrizione name string
Nome del risultato corrispondente distanceKilometers decimal
Distanza in chilometri public record Result( string name, decimal distanceKilometers );
Salvare i file Office.cs, Region.cse Result.cs.
Aprire di nuovo il file Program.cs.
Creare una nuovo
Polygon
in una variabile denominatamainCampusPolygon
.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), }) } );
Creare una nuova
Region
variabile denominatamainCampusRegion
usando il poligono, l'identificatore univoco1000
e il nomeMain Campus
.Region mainCampusRegion = new ("1000", "Main Campus", mainCampusPolygon);
Usare
Container.UpsertItemAsync
per aggiungere la regione al contenitore. Scrivere le informazioni della regione nella console.await container.UpsertItemAsync<Region>(mainCampusRegion); Console.WriteLine($"[UPSERT ITEM]\t{mainCampusRegion}");
Suggerimento
Questa guida usa upsert invece di insert in modo da poter eseguire lo script più volte senza causare un conflitto tra identificatori univoci. Per altre informazioni sulle operazioni upsert, vedere Creazione di elementi.
Creare una nuova
Point
variabile denominataheadquartersPoint
. Usare la variabile per creare una nuovaOffice
variabile denominataheadquartersOffice
usando il punto, l'identificatore univoco0001
e il nomeHeadquarters
.Point headquartersPoint = new (-122.12827, 47.63980); Office headquartersOffice = new ("0001", "Headquarters", headquartersPoint);
Creare un'altra variabile
Point
denominataresearchPoint
. Usare la variabile per creare un'altraOffice
variabile denominataresearchOffice
usando il punto corrispondente, l'identificatore univoco0002
e il nomeResearch and Development
.Point researchPoint = new (-96.84369, 46.81298); Office researchOffice = new ("0002", "Research and Development", researchPoint);
Creare un
TransactionalBatch
per eseguire l'upsert di entrambeOffice
le variabili come singola transazione. Quindi, scrivere entrambe le informazioni dell'ufficio nella console.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}");
Nota
Per altre informazioni sulle transazioni, vedere operazioni batch transazionali.
Salvare il file Program.cs.
Eseguire l'applicazione in un terminale usando
dotnet run
. Verificare che l'output dell'esecuzione dell'applicazione includa informazioni sui tre nuovi elementi creati.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 }
Eseguire query sui dati geospaziali usando la query NoSQL
I tipi nello Microsoft.Azure.Cosmos.Spatial
spazio dei nomi possono essere usati come input per una query con parametri NoSQL per usare funzioni predefinite come ST_DISTANCE
.
Aprire il file Program.cs.
Creare una nuova variabile
string
denominatanosql
con la query che viene usata in questa sezione per misurare la distanza tra i punti.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 ";
Suggerimento
Questa query inserisce la funzione geospaziale all'interno di una sottoquery per semplificare il processo di riutilizzo del valore già calcolato più volte nelle
SELECT
clausole eWHERE
.Creare una nuova
QueryDefinition
variabile denominataquery
usando lanosqlString
variabile come parametro. Usare quindi ilQueryDefinition.WithParameter
metodo Fluent più volte per aggiungere questi parametri alla query:Valore @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));
Creare un nuovo iteratore usando
Container.GetItemQueryIterator<>
, ilResult
tipo generico e laquery
variabile. Usare quindi una combinazione di un , mentre e per ogni ciclo per eseguire l’iterazione di tutti i risultati in ogni pagina di risultati. Immettere ogni risultato nella console.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}"); } }
Nota
Per altre informazioni sull'enumerazione dei risultati delle query, vedere elementi di query.
Salvare il file Program.cs.
Eseguire di nuovo l'applicazione in un terminale usando
dotnet run
. Verificare che ora l'output includa i risultati della query.dotnet run
[DISTANCE KM] Result { name = Headquarters, distanceKilometers = 3.34 } [DISTANCE KM] Result { name = Research and Development, distanceKilometers = 1907.43 }
Eseguire query sui dati geospaziali usando LINQ
La funzionalità LINQ to NoSQL in .NET SDK supporta l'inclusione di tipi geospaziali nelle espressioni di query. Inoltre, l'SDK comprende metodi di estensione che eseguono il mapping a funzioni predefinite equivalenti:
Metodo di estensione | Funzione predefinita |
---|---|
Distance() |
ST_DISTANCE |
Intersects() |
ST_INTERSECTS |
IsValid() |
ST_ISVALID |
IsValidDetailed() |
ST_ISVALIDDETAILED |
Within() |
ST_WITHIN |
Aprire il file Program.cs.
Recuperare l'elemento
Region
dal contenitore con un identificatore univoco di1000
e archiviarlo in una variabile denominataregion
.Region region = await container.ReadItemAsync<Region>("1000", new PartitionKey("business-region"));
Usare il
Container.GetItemLinqQueryable<>
metodo per ottenere una query LINQ e compilare la query LINQ in modo fluente seguendo questi tre passaggi:Usare il
Queryable.Where<>
metodo di estensione per filtrare solo gli elementi con uncategory
equivalente a"business-office"
.Usare
Queryable.Where<>
di nuovo per filtrare solo le posizioni all'interno dellaregion
proprietà dellalocation
variabile usandoGeometry.Within()
.Convertire l'espressione LINQ in un iteratore di feed usando
CosmosLinqExtensions.ToFeedIterator<>
.
var regionIterator = container.GetItemLinqQueryable<Office>() .Where(o => o.category == "business-office") .Where(o => o.location.Within(region.location)) .ToFeedIterator<Office>();
Importante
In questo esempio la proprietà della posizione dell'ufficio ha un puntoe la proprietà della posizione della regione ha un poligono.
ST_WITHIN
determina se il punto dell'ufficio si trova all'interno del poligono della regione.Usare una combinazione di un , mentre e per ogni ciclo per eseguire l’iterazione di tutti i risultati in ogni pagina di risultati. Immettere ogni risultato nella console.
while (regionIterator.HasMoreResults) { var response = await regionIterator.ReadNextAsync(); foreach (var office in response) { Console.WriteLine($"[IN REGION]\t{office}"); } }
Salvare il file Program.cs.
Eseguire l'applicazione un'ultima volta in un terminale usando
dotnet run
. Verificare che l'output include ora i risultati della seconda query basata su LINQ.dotnet run
[IN REGION] Office { id = 0001, name = Headquarters, location = Microsoft.Azure.Cosmos.Spatial.Point, category = business-office }
Pulire le risorse
Rimuovere il database dopo aver completato questa guida.
Aprire un terminale e creare una variabile shell per il nome dell'account e del gruppo di risorse.
# Variable for resource group name resourceGroupName="<name-of-your-resource-group>" # Variable for account name accountName="<name-of-your-account>"
Usare
az cosmosdb sql database delete
per rimuovere il database.az cosmosdb sql database delete \ --resource-group "<resource-group-name>" \ --account-name "<nosql-account-name>" \ --name "cosmicworks"