Indexer et interroger des données d’emplacement GeoJSON dans Azure Cosmos DB for NoSQL

S’APPLIQUE À : NoSQL

Les données géospatiales dans Azure Cosmos DB for NoSQL vous permettent de stocker des informations d’emplacement et d’effectuer des requêtes courantes, notamment :

  • Rechercher si un emplacement se trouve dans une zone définie
  • Mesure de la distance entre deux emplacements
  • Déterminer si un chemin d’accès croise un emplacement ou une zone

Ce guide décrit le processus de création de données géospatiales, d’indexation des données, puis d’interrogation des données dans un conteneur.

Prérequis

Créer un conteneur et une stratégie d’indexation

Tous les conteneurs incluent une stratégie d’indexation par défaut qui indexera correctement les données géospatiales. Pour créer une stratégie d’indexation personnalisée, créez un compte et spécifiez un fichier JSON avec la configuration de la stratégie. Dans cette section, un index spatial personnalisé est utilisé pour un conteneur nouvellement créé.

  1. Ouvrez un terminal.

  2. Créez une variable shell pour le nom de votre compte et groupe de ressources 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>"
    
  3. Créez une base de données sous le nom cosmicworks à l’aide de 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. Créez un fichier JSON nommé index-policy.json et ajoutez l’objet JSON suivant au fichier.

    {
      "indexingMode": "consistent",
      "automatic": true,
      "includedPaths": [
        {
          "path": "/*"
        }
      ],
      "excludedPaths": [
        {
          "path": "/\"_etag\"/?"
        }
      ],
      "spatialIndexes": [
        {
          "path": "/location/*",
          "types": [
            "Point",
            "Polygon"
          ]
        }
      ]
    }
    
  5. Utilisez az cosmosdb sql container create pour créer un conteneur nommé locations avec un chemin de clé de partition de /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. Enfin, obtenez le point de terminaison de votre compte à l’aide de az cosmosdb show et d’une requête JMESPath.

    az cosmosdb show \
        --resource-group "<resource-group-name>" \
        --name "<nosql-account-name>" \
        --query "documentEndpoint"
    
  7. Enregistrez le point de terminaison du compte car vous en aurez besoin dans la section suivante.

Créer une application console SDK .NET

Le kit de développement logiciel (SDK) .NET pour Azure Cosmos DB for NoSQL fournit des classes pour les objets GeoJSON courants. Utilisez ce kit de développement logiciel (SDK) pour simplifier le processus d’ajout d’objets géographiques à votre conteneur.

  1. Ouvrez un terminal dans un répertoire vide.

  2. Créez une application .NET à l’aide de la commande dotnet new avec le modèle de console.

    dotnet new console
    
  3. Importez le package NuGet Microsoft.Azure.Cosmos à l’aide de la commande dotnet add package.

    dotnet add package Microsoft.Azure.Cosmos --version 3.*
    

    Avertissement

    Entity Framework ne contient actuellement pas de données spatiales dans Azure Cosmos DB for NoSQL. Utilisez l’un des SDK Azure Cosmos DB for NoSQL pour la prise en charge de GeoJSON fortement typé.

  4. Importez le package NuGet Azure.Identity.

    dotnet add package Azure.Identity --version 1.*
    
  5. Générez le projet avec la commande dotnet build.

    dotnet build
    
  6. Ouvrez l’environnement de développement intégré (IDE) de votre choix dans le même répertoire que votre application console .NET.

  7. Ouvrez le fichier Program.cs nouvellement créé et supprimez tout code existant. Ajoutez des directives d’utilisation pour les espaces de noms Microsoft.Azure.Cosmos, Microsoft.Azure.Cosmos.Linq et Microsoft.Azure.Cosmos.Spatial.

    using Microsoft.Azure.Cosmos;
    using Microsoft.Azure.Cosmos.Linq;
    using Microsoft.Azure.Cosmos.Spatial;
    
  8. Ajoutez une autre directive d’utilisation pour l’espace de noms Azure.Identity.

    using Azure.Identity;
    
  9. Créez une variable nommée credential de type DefaultAzureCredential.

    DefaultAzureCredential credential = new();
    
  10. Créez une variable de chaîne nommée endpoint avec le point de terminaison de votre compte Azure Cosmos DB pour NoSQL.

    string endpoint = "<nosql-account-endpoint>";
    
  11. Créez une instance de la classe CosmosClient en la transmettant en connectionString et en l’enveloppant dans une instruction using.

    using CosmosClient client = new (connectionString);
    
  12. Récupérez une référence au conteneur créé précédemment (cosmicworks/locations) dans le compte Azure Cosmos DB for NoSQL à l’aide de CosmosClient.GetDatabase puis de Database.GetContainer. Stockez le résultat dans une variable nommée container.

    var container = client.GetDatabase("cosmicworks").GetContainer("locations");
    
  13. Enregistrez le fichier Program.cs.

Ajouter des données géospatiales

Le kit de développement logiciel (SDK) .NET inclut plusieurs types dans l’espace de noms Microsoft.Azure.Cosmos.Spatial pour représenter les objets GeoJSON courants. Ces types simplifient le processus d’ajout de nouvelles informations sur l’emplacement aux éléments d’un conteneur.

  1. Créez un fichier appelé Office.cs. Dans le fichier, ajoutez une instruction using à Microsoft.Azure.Cosmos.Spatial, puis créez un type d’enregistrement Office avec ces propriétés :

    Type Description Valeur par défaut
    id string Identificateur unique
    name string Nom du bureau
    location Point Point géographique GeoJSON
    category string Valeur de clé de la partition business-office
    using Microsoft.Azure.Cosmos.Spatial;
    
    public record Office(
        string id,
        string name,
        Point location,
        string category = "business-office"
    );
    

    Notes

    Cet enregistrement inclut une propriété représentant Point une position spécifique dans GeoJSON. Pour plus d’informations, consultez Point GeoJSON.

  2. Créez un autre fichier nommé Region.cs. Ajoutez un autre type d’enregistrement nommé Region avec les propriétés suivantes :

    Type Description Valeur par défaut
    id string Identificateur unique
    name string Nom du bureau
    location Polygon Forme géographique GeoJSON
    category string Valeur de clé de la partition business-region
    using Microsoft.Azure.Cosmos.Spatial;
    
    public record Region(
        string id,
        string name,
        Polygon location,
        string category = "business-region"
    );
    

    Notes

    Cet enregistrement inclut une propriété Polygon représentant une forme composée de lignes dessinées entre plusieurs emplacements dans GeoJSON. Pour plus d’informations, consultez Polygone GeoJSON.

  3. Créez un autre fichier nommé Result.cs. Ajoutez un type d’enregistrement nommé Result avec ces deux propriétés :

    Type Description
    name string Nom du résultat mis en correspondance
    distanceKilometers decimal Distance en kilomètres
    public record Result(
        string name,
        decimal distanceKilometers
    );
    
  4. Enregistrez les fichiers Office.cs, Region.cs et Result.cs .

  5. Rouvrez le fichier Program.cs.

  6. Créez une Polygon dans une variable nommée 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. Créez une variable Region nommée mainCampusRegion à l’aide du polygone, de l’identificateur unique 1000 et du nom Main Campus.

    Region mainCampusRegion = new ("1000", "Main Campus", mainCampusPolygon);
    
  8. Utilisez Container.UpsertItemAsync pour ajouter la région au conteneur. Écrivez les informations de la région dans la console.

    await container.UpsertItemAsync<Region>(mainCampusRegion);
    Console.WriteLine($"[UPSERT ITEM]\t{mainCampusRegion}");
    

    Conseil

    Ce guide utilise upsert au lieu de insert afin que vous puissiez exécuter le script plusieurs fois sans provoquer de conflit entre les identificateurs uniques. Pour plus d’informations sur les opérations upsert, consultez Création d’éléments.

  9. Créer une variable Point nommée headquartersPoint. Utilisez cette variable pour créer une variable Office nommée headquartersOffice à l’aide du point, de l’identificateur 0001unique et du nom Headquarters.

    Point headquartersPoint = new (-122.12827, 47.63980);
    Office headquartersOffice = new ("0001", "Headquarters", headquartersPoint);
    
  10. Créez une autre variable Point nommée researchPoint. Utilisez cette variable pour créer une autre variable Office nommée researchOffice à l’aide du point correspondant, de l’identificateur 0002unique et du nom Research and Development.

    Point researchPoint = new (-96.84369, 46.81298);
    Office researchOffice = new ("0002", "Research and Development", researchPoint);
    
  11. Créez un TransactionalBatch pour faire un upsert des deux variables Office en tant que transaction unique. Ensuite, écrivez les informations des deux bureaux dans la 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}");
    

    Notes

    Pour plus d’informations sur les transactions, consultez Opérations de lot transactionnel.

  12. Enregistrez le fichier Program.cs.

  13. Exécutez l’application dans un terminal à l’aide de dotnet run. Notez que la sortie de l’exécution de l’application inclut des informations sur les trois éléments nouvellement créés.

    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 }
    

Interroger des données géospatiales à l’aide d’une requête NoSQL

Les types de l’espace de noms Microsoft.Azure.Cosmos.Spatial peuvent être utilisés comme entrées dans une requête paramétrable NoSQL pour utiliser des fonctions intégrées telles que ST_DISTANCE.

  1. Ouvrez le fichier Program.cs.

  2. Créer une variable string nommée nosql avec la requête est utilisée dans cette section pour mesurer la distance entre les points.

    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
    ";
    

    Conseil

    Cette requête place la fonction géospatiale dans une sous-requête pour simplifier le processus de réutilisation de la valeur déjà calculée plusieurs fois dans les clauses SELECT et WHERE.

  3. Créez une variable QueryDefinition nommée query en utilisant la variable nosqlString comme paramètre. Utilisez ensuite la méthode Fluent QueryDefinition.WithParameter plusieurs fois pour ajouter ces paramètres à la requête :

    Valeur
    @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. Créez un itérateur à l’aide de Container.GetItemQueryIterator<>, du type générique Result et de la variable query. Ensuite, utilisez la combinaison d’une boucle while et foreach pour itérer sur tous les résultats dans chaque page de résultats. Sortie de chaque résultat dans la 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}");
        }
    }
    

    Notes

    Pour plus d’informations sur l’énumération des résultats de requête, consultez les éléments de requête.

  5. Enregistrez le fichier Program.cs.

  6. Exécutez à nouveau l’application dans un terminal à l’aide de dotnet run. Notez que la sortie inclut désormais les résultats de la requête.

    dotnet run
    
    [DISTANCE KM]   Result { name = Headquarters, distanceKilometers = 3.34 }
    [DISTANCE KM]   Result { name = Research and Development, distanceKilometers = 1907.43 }
    

Interroger des données géospatiales à l’aide de LINQ

La fonctionnalité LINQ to NoSQL dans le kit de développement logiciel (SDK) .NET prend en charge l’inclusion de types géospatiaux dans les expressions de requête. Plus encore, le kit de développement logiciel (SDK) inclut des méthodes d’extension qui correspondent à des fonctions intégrées équivalentes :

Méthode d’extension Fonction intégrée
Distance() ST_DISTANCE
Intersects() ST_INTERSECTS
IsValid() ST_ISVALID
IsValidDetailed() ST_ISVALIDDETAILED
Within() ST_WITHIN
  1. Ouvrez le fichier Program.cs.

  2. Récupérez l’élément Region du conteneur avec un identificateur unique de 1000 et stockez-le dans une variable nommée region.

    Region region = await container.ReadItemAsync<Region>("1000", new PartitionKey("business-region"));
    
  3. Utilisez la méthode Container.GetItemLinqQueryable<> pour obtenir un LINQ requêtable, générez la requête LINQ couramment en effectuant ces trois actions :

    1. Utilisez la méthode d’extension Queryable.Where<> pour filtrer uniquement les éléments ayant un category équivalent à "business-office".

    2. Utilisez Queryable.Where<> à nouveau pour afficher uniquement les emplacements de la propriété location de la variable region à l’aide de Geometry.Within().

    3. Traduisez l’expression LINQ en itérateur de flux à l’aide de CosmosLinqExtensions.ToFeedIterator<>.

    var regionIterator = container.GetItemLinqQueryable<Office>()
        .Where(o => o.category == "business-office")
        .Where(o => o.location.Within(region.location))
        .ToFeedIterator<Office>();
    

    Important

    Dans cet exemple, la propriété d’emplacement du bureau a un point et la propriété d’emplacement de la région a un polygone. ST_WITHIN détermine si le point du bureau se trouve dans le polygone de la région.

  4. Utilisez la combinaison d’une boucle while et foreach pour itérer sur tous les résultats dans chaque page de résultats. Sortie de chaque résultat dans la console.

    while (regionIterator.HasMoreResults)
    {
        var response = await regionIterator.ReadNextAsync();
        foreach (var office in response)
        {
            Console.WriteLine($"[IN REGION]\t{office}");
        }
    }
    
  5. Enregistrez le fichier Program.cs.

  6. Exécutez l’application une dernière fois dans un terminal à l’aide de dotnet run. Notez que la sortie inclut désormais les résultats de la deuxième requête basé sur LINQ.

    dotnet run
    
    [IN REGION]     Office { id = 0001, name = Headquarters, location = Microsoft.Azure.Cosmos.Spatial.Point, category = business-office }
    

Nettoyer les ressources

Supprimez votre base de données après avoir terminé ce guide.

  1. Ouvrez un terminal et créez une variable shell pour le nom de votre compte et groupe de ressources.

    # Variable for resource group name
    resourceGroupName="<name-of-your-resource-group>"
    
    # Variable for account name
    accountName="<name-of-your-account>"
    
  2. Utilisez az cosmosdb sql database delete pour supprimer la base de données.

    az cosmosdb sql database delete \
        --resource-group "<resource-group-name>" \
        --account-name "<nosql-account-name>" \
        --name "cosmicworks"
    

Étapes suivantes