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
- Uma conta existente do Azure Cosmos DB para NoSQL.
- Se você não tiver uma assinatura do Azure, experimente o Azure Cosmos DB para NoSQL gratuitamente.
- Se você tiver uma assinatura existente do Azure, crie uma nova conta do Azure Cosmos DB para NoSQL.
- Versão mais recente do .NET.
- Versão mais recente da CLI do Azure.
- Se você estiver usando uma instalação local, entre na CLI do Azure usando o
az login
comando.
- Se você estiver usando uma instalação local, entre na CLI do Azure usando o
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.
Abra um terminal.
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>"
Crie um novo banco de dados chamado
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
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" ] } ] }
Use
az cosmosdb sql container create
para criar um novo contêiner nomeadolocations
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
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"
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.
Abra um terminal em um diretório vazio.
Crie um novo aplicativo .NET usando o
dotnet new
comando com o modelo de console .dotnet new console
Importe o
Microsoft.Azure.Cosmos
pacote NuGet usando odotnet 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.
Importe o
Azure.Identity
pacote NuGet.dotnet add package Azure.Identity --version 1.*
Crie o projeto com o
dotnet build
comando.dotnet build
Abra o ambiente de desenvolvedor integrado (IDE) de sua escolha no mesmo diretório que seu aplicativo de console .NET.
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.Linq
eMicrosoft.Azure.Cosmos.Spatial
namespaces.using Microsoft.Azure.Cosmos; using Microsoft.Azure.Cosmos.Linq; using Microsoft.Azure.Cosmos.Spatial;
Adicione outra diretiva using para o
Azure.Identity
namespace.using Azure.Identity;
Crie uma nova variável chamada
credential
do tipoDefaultAzureCredential
.DefaultAzureCredential credential = new();
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>";
Crie uma nova instância da classe passando e envolvendo-a
CosmosClient
em uma instrução using.connectionString
using CosmosClient client = new (connectionString);
Recupere uma referência ao contêiner criado anteriormente (
cosmicworks/locations
) na conta do Azure Cosmos DB para NoSQL usandoCosmosClient.GetDatabase
e, em seguida,Database.GetContainer
. Armazene o resultado em uma variável chamadacontainer
.var container = client.GetDatabase("cosmicworks").GetContainer("locations");
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.
Crie um novo arquivo chamado Office.cs. No arquivo, adicione uma diretiva using e
Microsoft.Azure.Cosmos.Spatial
crie um tipo deOffice
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.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" );
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 );
Salve os arquivos Office.cs, Region.cs e Result.cs .
Abra o arquivo Program.cs novamente.
Crie um novo
Polygon
em uma variável chamadamainCampusPolygon
.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), }) } );
Crie uma nova
Region
variável nomeadamainCampusRegion
usando o polígono, o identificador1000
exclusivo e o nomeMain Campus
.Region mainCampusRegion = new ("1000", "Main Campus", mainCampusPolygon);
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.
Crie uma nova
Point
variável chamadaheadquartersPoint
. Use essa variável para criar uma novaOffice
variável nomeadaheadquartersOffice
usando o ponto, o identificador0001
exclusivo e o nomeHeadquarters
.Point headquartersPoint = new (-122.12827, 47.63980); Office headquartersOffice = new ("0001", "Headquarters", headquartersPoint);
Crie outra
Point
variável chamadaresearchPoint
. Use essa variável para criar outraOffice
variável nomeadaresearchOffice
usando o ponto correspondente, o identificador0002
exclusivo e o nomeResearch and Development
.Point researchPoint = new (-96.84369, 46.81298); Office researchOffice = new ("0002", "Research and Development", researchPoint);
Crie um
TransactionalBatch
para atualizar ambas asOffice
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.
Guarde o ficheiro Program.cs.
Execute o aplicativo em um terminal usando
dotnet run
o . 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
.
Abra o arquivo Program.cs .
Criar uma nova
string
variável nomeadanosql
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 eWHERE
.Crie uma nova
QueryDefinition
variável chamadaquery
usando anosqlString
variável como parâmetro. Em seguida, use oQueryDefinition.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));
Crie um novo iterador usando
Container.GetItemQueryIterator<>
, oResult
tipo genérico e aquery
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.
Guarde o ficheiro Program.cs.
Execute o aplicativo novamente em um terminal usando
dotnet run
o . 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 |
Abra o arquivo Program.cs .
Recupere o
Region
item do contêiner com um identificador exclusivo e1000
armazene-o em uma variável chamadaregion
.Region region = await container.ReadItemAsync<Region>("1000", new PartitionKey("business-region"));
Use o
Container.GetItemLinqQueryable<>
método para obter um LINQ consultável e crie a consulta LINQ fluentemente executando estas três ações:Use o
Queryable.Where<>
método extension para filtrar somente itens com umcategory
equivalente a"business-office"
.Use
Queryable.Where<>
novamente para filtrar apenas locais dentro daregion
propriedade dalocation
variável usandoGeometry.Within()
.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.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}"); } }
Guarde o ficheiro Program.cs.
Execute o aplicativo uma última vez em um terminal usando
dotnet run
o . 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.
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>"
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"