Partilhar via


Indexar e consultar dados de localização GeoJSON no Azure Cosmos DB para NoSQL

APLICA-SE A: NoSQL

Os dados geoespaciais no Azure Cosmos DB para NoSQL permitem armazenar informações de localização e executar consultas comuns, incluindo, entre outras:

  • Descobrir se um local está dentro de uma área definida
  • Medição da distância entre dois locais
  • Determinar se um caminho se cruza com um local ou área

Este guia percorre o processo de criação de dados geoespaciais, indexação dos dados e, em seguida, consulta dos dados em um contêiner.

Pré-requisitos

Criar política de contêiner e indexação

Todos os contêineres incluem uma política de indexação padrão que indexará com êxito os dados geoespaciais. Para criar uma política de indexação personalizada, crie uma conta e especifique um arquivo JSON com a configuração da política. Nesta seção, um índice espacial personalizado é usado para um contêiner recém-criado.

  1. Abra um terminal.

  2. Crie uma variável de shell para o nome da sua conta do Azure Cosmos DB para NoSQL e grupo de recursos.

    # Variable for resource group name
    resourceGroupName="<name-of-your-resource-group>"
    
    # Variable for account name
    accountName="<name-of-your-account>"
    
  3. Crie um novo banco de dados chamado 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. Crie um novo arquivo JSON chamado index-policy.json e adicione o seguinte objeto JSON ao arquivo.

    {
      "indexingMode": "consistent",
      "automatic": true,
      "includedPaths": [
        {
          "path": "/*"
        }
      ],
      "excludedPaths": [
        {
          "path": "/\"_etag\"/?"
        }
      ],
      "spatialIndexes": [
        {
          "path": "/location/*",
          "types": [
            "Point",
            "Polygon"
          ]
        }
      ]
    }
    
  5. Use az cosmosdb sql container create para criar um novo contêiner nomeado locations com um caminho de chave de partição 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. Finalmente, obtenha o ponto de extremidade da conta para sua conta usando az cosmosdb show e uma consulta JMESPath.

    az cosmosdb show \
        --resource-group "<resource-group-name>" \
        --name "<nosql-account-name>" \
        --query "documentEndpoint"
    
  7. Registre o ponto de extremidade da conta, pois você precisará disso na próxima seção.

Criar aplicativo de console do SDK do .NET

O SDK do .NET para Azure Cosmos DB para NoSQL fornece classes para objetos GeoJSON comuns. Use este SDK para simplificar o processo de adição de objetos geográficos ao seu contêiner.

  1. Abra um terminal em um diretório vazio.

  2. Crie um novo aplicativo .NET usando o dotnet new comando com o modelo de console .

    dotnet new console
    
  3. Importe o Microsoft.Azure.Cosmos pacote NuGet usando o dotnet add package comando.

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

    Aviso

    Atualmente, o Entity Framework não possui dados espaciais no Azure Cosmos DB para NoSQL. Use um dos SDKs do Azure Cosmos DB para NoSQL para suporte a GeoJSON fortemente tipado.

  4. Importe o Azure.Identity pacote NuGet.

    dotnet add package Azure.Identity --version 1.*
    
  5. Crie o projeto com o dotnet build comando.

    dotnet build
    
  6. Abra o ambiente de desenvolvedor integrado (IDE) de sua escolha no mesmo diretório que seu aplicativo de console .NET.

  7. Abra o arquivo Program.cs recém-criado e exclua qualquer código existente. Adicione usando diretivas para o Microsoft.Azure.Cosmos, Microsoft.Azure.Cosmos.LinqeMicrosoft.Azure.Cosmos.Spatial namespaces.

    using Microsoft.Azure.Cosmos;
    using Microsoft.Azure.Cosmos.Linq;
    using Microsoft.Azure.Cosmos.Spatial;
    
  8. Adicione outra diretiva using para o Azure.Identity namespace.

    using Azure.Identity;
    
  9. Crie uma nova variável chamada credential do tipo DefaultAzureCredential.

    DefaultAzureCredential credential = new();
    
  10. Crie uma variável de cadeia de caracteres nomeada endpoint com seu ponto de extremidade de conta do Azure Cosmos DB para NoSQL.

    string endpoint = "<nosql-account-endpoint>";
    
  11. Crie uma nova instância da classe passando e envolvendo-a CosmosClient em uma instrução using.connectionString

    using CosmosClient client = new (connectionString);
    
  12. Recupere uma referência ao contêiner criado anteriormente (cosmicworks/locations) na conta do Azure Cosmos DB para NoSQL usando CosmosClient.GetDatabase e, em seguida, Database.GetContainer. Armazene o resultado em uma variável chamada container.

    var container = client.GetDatabase("cosmicworks").GetContainer("locations");
    
  13. Guarde o ficheiro Program.cs.

Adicionar dados geoespaciais

O SDK .NET inclui vários tipos no Microsoft.Azure.Cosmos.Spatial namespace para representar objetos GeoJSON comuns. Esses tipos simplificam o processo de adição de novas informações de localização aos itens em um contêiner.

  1. Crie um novo arquivo chamado Office.cs. No arquivo, adicione uma diretiva using e Microsoft.Azure.Cosmos.Spatial crie um tipo de Office registro com estas propriedades:

    Tipo Description Default value
    id string Identificador exclusivo
    Designação string Nome do escritório
    localização Point Ponto geográfico GeoJSON
    category string Valor da chave de partição business-office
    using Microsoft.Azure.Cosmos.Spatial;
    
    public record Office(
        string id,
        string name,
        Point location,
        string category = "business-office"
    );
    

    Nota

    Esse registro inclui uma Point propriedade que representa uma posição específica em GeoJSON. Para obter mais informações, consulte GeoJSON Point.

  2. Crie outro novo arquivo chamado Region.cs. Adicione outro tipo de registro nomeado Region com estas propriedades:

    Tipo Description Default value
    id string Identificador exclusivo
    Designação string Nome do escritório
    localização Polygon Forma geográfica GeoJSON
    category string Valor da chave de partição business-region
    using Microsoft.Azure.Cosmos.Spatial;
    
    public record Region(
        string id,
        string name,
        Polygon location,
        string category = "business-region"
    );
    

    Nota

    Este registro inclui uma Polygon propriedade que representa uma forma composta por linhas desenhadas entre vários locais em GeoJSON. Para obter mais informações, consulte Polígono GeoJSON.

  3. Crie outro novo arquivo chamado Result.cs. Adicione um tipo de registro nomeado Result com estas duas propriedades:

    Tipo Description
    Designação string Nome do resultado correspondente
    distânciaQuilómetros decimal Distância em quilómetros
    public record Result(
        string name,
        decimal distanceKilometers
    );
    
  4. Salve os arquivos Office.cs, Region.cs e Result.cs .

  5. Abra o arquivo Program.cs novamente.

  6. Crie um novo Polygon em uma variável chamada 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. Crie uma nova Region variável nomeada mainCampusRegion usando o polígono, o identificador 1000exclusivo e o nome Main Campus.

    Region mainCampusRegion = new ("1000", "Main Campus", mainCampusPolygon);
    
  8. Use Container.UpsertItemAsync para adicionar a região ao contêiner. Escreva as informações da região no console.

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

    Gorjeta

    Este guia usa upsert em vez de insert para que você possa executar o script várias vezes sem causar um conflito entre identificadores exclusivos. Para obter mais informações sobre operações de upsert, consulte a criação de itens.

  9. Crie uma nova Point variável chamada headquartersPoint. Use essa variável para criar uma nova Office variável nomeada headquartersOffice usando o ponto, o identificador 0001exclusivo e o nome Headquarters.

    Point headquartersPoint = new (-122.12827, 47.63980);
    Office headquartersOffice = new ("0001", "Headquarters", headquartersPoint);
    
  10. Crie outra Point variável chamada researchPoint. Use essa variável para criar outra Office variável nomeada researchOffice usando o ponto correspondente, o identificador 0002exclusivo e o nome Research and Development.

    Point researchPoint = new (-96.84369, 46.81298);
    Office researchOffice = new ("0002", "Research and Development", researchPoint);
    
  11. Crie um TransactionalBatch para atualizar ambas as Office variáveis como uma única transação. Em seguida, escreva as informações de ambos os escritórios no 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

    Para obter mais informações sobre transações, consulte Operações em lote transacionais.

  12. Guarde o ficheiro Program.cs.

  13. Execute o aplicativo em um terminal usando dotnet runo . Observe que a saída da execução do aplicativo inclui informações sobre os três itens recém-criados.

    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 }
    

Consultar dados geoespaciais usando a consulta NoSQL

Os tipos no Microsoft.Azure.Cosmos.Spatial namespace podem ser usados como entradas para uma consulta parametrizada NoSQL para usar funções internas como ST_DISTANCE.

  1. Abra o arquivo Program.cs .

  2. Criar uma nova string variável nomeada nosql com a consulta é usada nesta seção para medir a distância entre pontos.

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

    Gorjeta

    Esta consulta coloca a função geoespacial dentro de uma subconsulta para simplificar o processo de reutilização do valor já calculado várias vezes nas SELECT cláusulas e WHERE .

  3. Crie uma nova QueryDefinition variável chamada query usando a nosqlString variável como parâmetro. Em seguida, use o QueryDefinition.WithParameter método fluent várias vezes para adicionar esses parâmetros à consulta:

    Value
    @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. Crie um novo iterador usando Container.GetItemQueryIterator<>, o Result tipo genérico e a query variável. Em seguida, use uma combinação de um loop while e foreach para iterar todos os resultados em cada página de resultados. Envie cada resultado para o 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

    Para obter mais informações sobre como enumerar resultados de consulta, consulte itens de consulta.

  5. Guarde o ficheiro Program.cs.

  6. Execute o aplicativo novamente em um terminal usando dotnet runo . Observe que a saída agora inclui os resultados da consulta.

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

Consultar dados geoespaciais usando o LINQ

A funcionalidade LINQ to NoSQL no SDK do .NET oferece suporte à inclusão de tipos geoespaciais nas expressões de consulta. Além disso, o SDK inclui métodos de extensão que mapeiam para funções internas equivalentes:

Método de extensão Função integrada
Distance() ST_DISTANCE
Intersects() ST_INTERSECTS
IsValid() ST_ISVALID
IsValidDetailed() ST_ISVALIDDETAILED
Within() ST_WITHIN
  1. Abra o arquivo Program.cs .

  2. Recupere o Region item do contêiner com um identificador exclusivo e 1000 armazene-o em uma variável chamada region.

    Region region = await container.ReadItemAsync<Region>("1000", new PartitionKey("business-region"));
    
  3. Use o Container.GetItemLinqQueryable<> método para obter um LINQ consultável e crie a consulta LINQ fluentemente executando estas três ações:

    1. Use o Queryable.Where<> método extension para filtrar somente itens com um category equivalente a "business-office".

    2. Use Queryable.Where<> novamente para filtrar apenas locais dentro da region propriedade da location variável usando Geometry.Within().

    3. Traduza a expressão LINQ para um iterador de 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

    Neste exemplo, a propriedade location do escritório tem um ponto e a propriedade location da região tem um polígono. ST_WITHIN é determinar se o ponto do escritório está dentro do polígono da região.

  4. Use uma combinação de loop while e foreach para iterar todos os resultados em cada página de resultados. Envie cada resultado para o console.

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

  6. Execute o aplicativo uma última vez em um terminal usando dotnet runo . Observe que a saída agora inclui os resultados da segunda consulta baseada em LINQ.

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

Clean up resources (Limpar recursos)

Remova o banco de dados depois de concluir este guia.

  1. Abra um terminal e crie uma variável de shell para o nome da sua conta e grupo de recursos.

    # Variable for resource group name
    resourceGroupName="<name-of-your-resource-group>"
    
    # Variable for account name
    accountName="<name-of-your-account>"
    
  2. Use az cosmosdb sql database delete para remover o banco de dados.

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

Próximos passos