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-lhe armazenar informações de localização e realizar consultas comuns, incluindo, mas não se limitando a:
- Determinar se uma localização está numa área definida
- Medir a distância entre duas localizações
- Determinar se um caminho se cruza com uma localização ou área
Este guia explica o processo de criação de dados geoespaciais, a indexação dos dados e, em seguida, a consulta dos dados num contentor.
Pré-requisitos
- Uma conta existente do Azure Cosmos DB para NoSQL.
- Se não tiver uma subscrição do Azure, experimente o Azure Cosmos DB para NoSQL gratuitamente.
- Se tiver uma subscrição do Azure existente, 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 estiver a utilizar uma instalação local, inicie sessão na CLI do Azure com o
az login
comando .
- Se estiver a utilizar uma instalação local, inicie sessão na CLI do Azure com o
Criar política de contentor e indexação
Todos os contentores incluem uma política de indexação predefinida que indexará com êxito dados geoespaciais. Para criar uma política de indexação personalizada, crie uma conta e especifique um ficheiro JSON com a configuração da política. Nesta secção, é utilizado um índice espacial personalizado para um contentor criado recentemente.
Abra um terminal.
Crie uma variável de shell para o nome da sua conta e grupo de recursos do Azure Cosmos DB para NoSQL.
# Variable for resource group name resourceGroupName="<name-of-your-resource-group>" # Variable for account name accountName="<name-of-your-account>"
Crie uma nova base de dados com o
az cosmosdb sql database create
nomecosmicworks
.az cosmosdb sql database create \ --resource-group $resourceGroupName \ --account-name $accountName \ --name "cosmicworks" \ --throughput 400
Crie um novo ficheiro JSON com o nome index-policy.json e adicione o seguinte objeto JSON ao ficheiro.
{ "indexingMode": "consistent", "automatic": true, "includedPaths": [ { "path": "/*" } ], "excludedPaths": [ { "path": "/\"_etag\"/?" } ], "spatialIndexes": [ { "path": "/location/*", "types": [ "Point", "Polygon" ] } ] }
Utilize
az cosmosdb sql container create
para criar um novo contentor com o nomelocations
com um caminho de chave de partição de/region
.az cosmosdb sql container create \ --resource-group $resourceGroupName \ --account-name $accountName \ --database-name "cosmicworks" \ --name "locations" \ --partition-key-path "/category" \ --idx @index-policy.json
Obtenha a cadeia de ligação primária para a conta com
az cosmosdb keys list
.az cosmosdb keys list \ --resource-group $resourceGroupName \ --name $accountName \ --type "connection-strings" \ --query "connectionStrings[?keyKind == \`Primary\`].connectionString" \ --output tsv
Dica
Para ver todas as cadeias de ligação possíveis para uma conta, utilize
az cosmosdb keys list --resource-group $resourceGroupName --name $accountName --type "connection-strings"
.Grave a cadeia de ligação. Irá utilizar esta credencial mais adiante neste guia.
Criar a aplicação de consola do SDK .NET
O SDK .NET para o Azure Cosmos DB para NoSQL fornece classes para objetos GeoJSON comuns. Utilize este SDK para simplificar o processo de adição de objetos geográficos ao contentor.
Abra um terminal num diretório vazio.
Crie uma nova aplicação .NET com o
dotnet new
comando com o modelo de consola .dotnet new console
Importe o pacote NuGet Microsoft.Azure.Cosmos com o
dotnet add package
comando .dotnet add package Microsoft.Azure.Cosmos --version 3.*
Aviso
O Entity Framework não tem atualmente dados espaciais no Azure Cosmos DB para NoSQL. Utilize um dos SDKs do Azure Cosmos DB para NoSQL para suporte GeoJSON fortemente digitado.
Crie o projeto com o
dotnet build
comando .dotnet build
Abra o ambiente de programador integrado (IDE) à sua escolha no mesmo diretório que a sua aplicação de consola .NET.
Abra o ficheiro Program.cs recentemente criado e elimine qualquer código existente. Adicione diretivas de utilização para os
Microsoft.Azure.Cosmos
espaços de nomes ,Microsoft.Azure.Cosmos.Linq
eMicrosoft.Azure.Cosmos.Spatial
.using Microsoft.Azure.Cosmos; using Microsoft.Azure.Cosmos.Linq; using Microsoft.Azure.Cosmos.Spatial;
Adicione uma variável de cadeia com o nome *
connectionString
com a cadeia de ligação que gravou anteriormente neste guia.string connectionString = "<your-account-connection-string>"
Crie uma nova instância da classe que passa
CosmosClient
connectionString
e encapsula-a numa instrução de utilização.using CosmosClient client = new (connectionString);
Obtenha uma referência ao contentor criado anteriormente (
cosmicworks/locations
) na conta do Azure Cosmos DB para NoSQL comCosmosClient.GetDatabase
e, em seguidaDatabase.GetContainer
, . Armazene o resultado numa 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
espaço de nomes para representar objetos GeoJSON comuns. Estes tipos simplificam o processo de adicionar novas informações de localização a itens num contentor.
Crie um novo ficheiro com o nome Office.cs. No ficheiro, adicione uma diretiva de utilização a
Microsoft.Azure.Cosmos.Spatial
e, em seguida, crie umOffice
tipo de registo com estas propriedades:Tipo Descrição Valor predefinido id string
Identificador exclusivo nome string
Nome do escritório localização Point
Ponto geográfico GeoJSON categoria 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
Este registo inclui uma
Point
propriedade que representa uma posição específica em GeoJSON. Para obter mais informações, veja Ponto GeoJSON.Crie outro novo ficheiro com o nome Region.cs. Adicione outro tipo de registo com o nome
Region
com estas propriedades:Tipo Descrição Valor predefinido id string
Identificador exclusivo nome string
Nome do escritório localização Polygon
Forma geográfica GeoJSON categoria 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 registo inclui uma
Polygon
propriedade que representa uma forma composta por linhas desenhadas entre várias localizações em GeoJSON. Para obter mais informações, veja GeoJSON Polygon.Crie outro novo ficheiro com o nome Result.cs. Adicione um tipo de registo com o nome
Result
com estas duas propriedades:Tipo Description nome string
Nome do resultado correspondente distanceKilometers decimal
Distância em quilómetros public record Result( string name, decimal distanceKilometers );
Guarde os ficheiros Office.cs, Region.cs e Result.cs .
Abra novamente o ficheiro Program.cs .
Crie um novo
Polygon
numa variável com o nomemainCampusPolygon
.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 com o nomemainCampusRegion
com o polígono, o identificador1000
exclusivo e o nomeMain Campus
.Region mainCampusRegion = new ("1000", "Main Campus", mainCampusPolygon);
Utilize
Container.UpsertItemAsync
para adicionar a região ao contentor. Escreva as informações da região na consola do .await container.UpsertItemAsync<Region>(mainCampusRegion); Console.WriteLine($"[UPSERT ITEM]\t{mainCampusRegion}");
Dica
Este guia utiliza upsert em vez de inserir para que possa executar o script várias vezes sem causar um conflito entre identificadores exclusivos. Para obter mais informações sobre operações upsert, veja Criar itens.
Crie uma nova
Point
variável com o nomeheadquartersPoint
. Utilize essa variável para criar uma novaOffice
variável chamadaheadquartersOffice
com 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 com o nomeresearchPoint
. Utilize essa variável para criar outraOffice
variável com o nomeresearchOffice
com 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 uma
TransactionalBatch
para upsert ambas asOffice
variáveis como uma única transação. Em seguida, escreva as informações de ambos os escritórios na consola do .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, veja Operações de lotes transacionais.
Guarde o ficheiro Program.cs.
Execute a aplicação num terminal com
dotnet run
. Observe que o resultado da execução da aplicação inclui informações sobre os três itens criados recentemente.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 com a consulta NoSQL
Os tipos no Microsoft.Azure.Cosmos.Spatial
espaço de nomes podem ser utilizados como entradas para uma consulta parametrizada NoSQL para utilizar funções incorporadas como ST_DISTANCE
.
Abra o ficheiro Program.cs .
Crie uma nova
string
variável com o nomenosql
com a consulta utilizada nesta secçã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 ";
Dica
Esta consulta coloca a função geoespacial numa 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 com o nomequery
com anosqlString
variável como um parâmetro. Em seguida, utilize oQueryDefinition.WithParameter
método fluente várias vezes para adicionar estes parâmetros à consulta:Valor @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 com
Container.GetItemQueryIterator<>
, oResult
tipo genérico e aquery
variável . Em seguida, utilize uma combinação de um ciclo de tempo e foreach para iterar todos os resultados em cada página de resultados. Exportar cada resultado para a consola.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 a enumeração dos resultados da consulta, veja itens de consulta.
Guarde o ficheiro Program.cs.
Execute a aplicação novamente num terminal com
dotnet run
. Observe que o resultado inclui agora 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 com LINQ
A funcionalidade LINQ para NoSQL no SDK .NET suporta a 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 incorporadas equivalentes:
Método de extensão | Função incorporada |
---|---|
Distance() |
ST_DISTANCE |
Intersects() |
ST_INTERSECTS |
IsValid() |
ST_ISVALID |
IsValidDetailed() |
ST_ISVALIDDETAILED |
Within() |
ST_WITHIN |
Abra o ficheiro Program.cs .
Obtenha o
Region
item do contentor com um identificador exclusivo de1000
e armazene-o numa variável chamadaregion
.Region region = await container.ReadItemAsync<Region>("1000", new PartitionKey("business-region"));
Utilize o
Container.GetItemLinqQueryable<>
método para obter uma consulta LINQ e compile a consulta LINQ fluentemente ao realizar estas três ações:Utilize o
Queryable.Where<>
método de extensão para filtrar apenas itens com umcategory
equivalente a"business-office"
.Utilize
Queryable.Where<>
novamente para filtrar apenas as localizações naregion
propriedade dalocation
variável comGeometry.Within()
.Traduza a expressão LINQ para um iterador de feed com
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 de localização do escritório tem um ponto e a propriedade de localização da região tem um polígono.
ST_WITHIN
está a determinar se o ponto do escritório está dentro do polígono da região.Utilize uma combinação de um ciclo de tempo e foreach para iterar todos os resultados em cada página de resultados. Exportar cada resultado para a consola.
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 a aplicação uma última vez num terminal com
dotnet run
. Observe que o resultado inclui agora 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 }
Limpar os recursos
Remova a base 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>"
Utilize
az cosmosdb sql database delete
para remover a base de dados.az cosmosdb sql database delete \ --resource-group $resourceGroupName \ --account-name $accountName \ --name "cosmicworks"