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

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.

  1. Aprire un terminale.

  2. 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>"
    
  3. Creare un nuovo database denominato cosmicworks usando az cosmosdb sql database create.

    az cosmosdb sql database create \
        --resource-group "<resource-group-name>" \
        --account-name "<nosql-account-name>" \
        --name "cosmicworks" \
        --throughput 400
    
  4. 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"
          ]
        }
      ]
    }
    
  5. Usare az cosmosdb sql container create per creare un nuovo contenitore denominato locations 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
    
  6. 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"
    
  7. 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.

  1. Aprire un terminale in una directory vuota.

  2. Creare una nuova applicazione .NET usando il dotnet new comando con il modello di console .

    dotnet new console
    
  3. Importare il Microsoft.Azure.Cosmos pacchetto NuGet usando il dotnet 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.

  4. Importare il Azure.Identity pacchetto NuGet.

    dotnet add package Azure.Identity --version 1.*
    
  5. Creare il progetto con il dotnet build comando.

    dotnet build
    
  6. Aprire l'ambiente di sviluppo integrato (IDE) preferito nella stessa directory dell'applicazione console .NET.

  7. 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.LinqeMicrosoft.Azure.Cosmos.Spatial.

    using Microsoft.Azure.Cosmos;
    using Microsoft.Azure.Cosmos.Linq;
    using Microsoft.Azure.Cosmos.Spatial;
    
  8. Aggiungere un'altra direttiva using per lo spazio dei Azure.Identity nomi .

    using Azure.Identity;
    
  9. Creare una nuova variabile denominata credential di tipo DefaultAzureCredential.

    DefaultAzureCredential credential = new();
    
  10. Creare una variabile stringa denominata endpoint con l'endpoint dell'account Azure Cosmos DB per NoSQL.

    string endpoint = "<nosql-account-endpoint>";
    
  11. Creare una nuova istanza della CosmosClient classe passandola connectionString ed eseguendo il wrapping in un'istruzione d’uso.

    using CosmosClient client = new (connectionString);
    
  12. Recuperare un riferimento al contenitore creato in precedenza (cosmicworks/locations) nell'account Azure Cosmos DB per NoSQL usando CosmosClient.GetDatabase e dopo Database.GetContainer. Archiviare il risultato in una variabile denominata container.

    var container = client.GetDatabase("cosmicworks").GetContainer("locations");
    
  13. 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.

  1. Creare un nuovo file denominato Office.cs. Nel file aggiungere una direttiva using a Microsoft.Azure.Cosmos.Spatial e quindi creare un Office 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.

  2. 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.

  3. 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
    );
    
  4. Salvare i file Office.cs, Region.cse Result.cs.

  5. Aprire di nuovo il file Program.cs.

  6. Creare una nuovo Polygon in una variabile denominata 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),
            })
        }
    );
    
  7. Creare una nuova Region variabile denominata mainCampusRegion usando il poligono, l'identificatore univoco 1000e il nome Main Campus.

    Region mainCampusRegion = new ("1000", "Main Campus", mainCampusPolygon);
    
  8. 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.

  9. Creare una nuova Point variabile denominata headquartersPoint. Usare la variabile per creare una nuova Office variabile denominata headquartersOffice usando il punto, l'identificatore univoco 0001e il nome Headquarters.

    Point headquartersPoint = new (-122.12827, 47.63980);
    Office headquartersOffice = new ("0001", "Headquarters", headquartersPoint);
    
  10. Creare un'altra variabile Point denominata researchPoint. Usare la variabile per creare un'altra Office variabile denominata researchOffice usando il punto corrispondente, l'identificatore univoco 0002e il nome Research and Development.

    Point researchPoint = new (-96.84369, 46.81298);
    Office researchOffice = new ("0002", "Research and Development", researchPoint);
    
  11. Creare un TransactionalBatch per eseguire l'upsert di entrambe Office 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.

  12. Salvare il file Program.cs.

  13. 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.

  1. Aprire il file Program.cs.

  2. Creare una nuova variabile string denominata nosql 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 e WHERE.

  3. Creare una nuova QueryDefinition variabile denominata query usando la nosqlString variabile come parametro. Usare quindi il QueryDefinition.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));
    
  4. Creare un nuovo iteratore usando Container.GetItemQueryIterator<>, il Result tipo generico e la query 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.

  5. Salvare il file Program.cs.

  6. 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
  1. Aprire il file Program.cs.

  2. Recuperare l'elemento Region dal contenitore con un identificatore univoco di 1000 e archiviarlo in una variabile denominata region.

    Region region = await container.ReadItemAsync<Region>("1000", new PartitionKey("business-region"));
    
  3. Usare il Container.GetItemLinqQueryable<> metodo per ottenere una query LINQ e compilare la query LINQ in modo fluente seguendo questi tre passaggi:

    1. Usare il Queryable.Where<> metodo di estensione per filtrare solo gli elementi con un category equivalente a "business-office".

    2. Usare Queryable.Where<> di nuovo per filtrare solo le posizioni all'interno della region proprietà della location variabile usando Geometry.Within().

    3. 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.

  4. 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}");
        }
    }
    
  5. Salvare il file Program.cs.

  6. 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.

  1. 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>"
    
  2. 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"
    

Passaggi successivi