Indexación y consulta de datos de ubicación de GeoJSON en Azure Cosmos DB for NoSQL
SE APLICA A: NoSQL
Los datos geoespaciales de Azure Cosmos DB for NoSQL permiten almacenar información de la ubicación y realizar consultas comunes, entre las que se incluyen, entre otras:
- Averiguar si una ubicación está dentro de un área definida
- Medir la distancia entre dos ubicaciones
- Determinar si una ruta de acceso se interseca con una ubicación o un área
En esta guía se explica el proceso de creación de datos geoespaciales, la indexación de los datos y la consulta de los datos en un contenedor.
Requisitos previos
- Una cuenta existente de Azure Cosmos DB for NoSQL.
- Si no tiene una suscripción de Azure, pruebe Azure Cosmos DB for NoSQL gratis.
- Si tiene una suscripción de Azure existente, cree una cuenta de Azure Cosmos DB for NoSQL.
- Versión más reciente de .NET.
- Versión más reciente de la CLI de Azure.
- Si usa una instalación local, inicie sesión en la CLI de Azure mediante el comando
az login
.
- Si usa una instalación local, inicie sesión en la CLI de Azure mediante el comando
Creación de una directiva de contenedores e indexación
Todos los contenedores incluyen una directiva de indexación predeterminada que indexará correctamente los datos geoespaciales. Para crear una directiva de indexación personalizada, cree una cuenta y especifique un archivo JSON con la configuración de la directiva. En esta sección, se usa un índice espacial personalizado para un contenedor recién creado.
Abra un terminal.
Cree una variable de shell para el nombre de la cuenta y el grupo de recursos de 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>"
Cree una base de datos denominada
cosmicworks
medianteaz cosmosdb sql database create
.az cosmosdb sql database create \ --resource-group $resourceGroupName \ --account-name $accountName \ --name "cosmicworks" \ --throughput 400
Cree un nuevo archivo JSON denominado index-policy.json y agregue el siguiente objeto JSON al archivo.
{ "indexingMode": "consistent", "automatic": true, "includedPaths": [ { "path": "/*" } ], "excludedPaths": [ { "path": "/\"_etag\"/?" } ], "spatialIndexes": [ { "path": "/location/*", "types": [ "Point", "Polygon" ] } ] }
Use
az cosmosdb sql container create
para crear un contenedor denominadolocations
con una ruta de acceso de clave de partición 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
Recupere la cadena de conexión principal de la cuenta mediante
az cosmosdb keys list
.az cosmosdb keys list \ --resource-group $resourceGroupName \ --name $accountName \ --type "connection-strings" \ --query "connectionStrings[?keyKind == \`Primary\`].connectionString" \ --output tsv
Sugerencia
Para ver todas las cadenas de conexión posibles para una cuenta, use
az cosmosdb keys list --resource-group $resourceGroupName --name $accountName --type "connection-strings"
.Registre la cadena de conexión. Esta credencial se usa más adelante en esta guía.
Creación del SDK de una aplicación de consola de .NET
El SDK de .NET para Azure Cosmos DB for NoSQL proporciona clases para objetos GeoJSON comunes. Use este SDK para simplificar el proceso de agregar objetos geográficos al contenedor.
Abra un terminal en un directorio vacío.
Cree una nueva aplicación .NET mediante el comando
dotnet new
con la plantilla console.dotnet new console
Importe el paquete de NuGet Microsoft.Azure.Cosmos mediante el comando
dotnet add package
.dotnet add package Microsoft.Azure.Cosmos --version 3.*
Advertencia
Entity Framework no tiene actualmente datos espaciales en Azure Cosmos DB for NoSQL. Use uno de los SDK de Azure Cosmos DB for NoSQL para admitir GeoJSON fuertemente tipado.
Compile el proyecto con el comando
dotnet build
.dotnet build
Abra el entorno de desarrollador integrado (IDE) que prefiera en el mismo directorio que la aplicación de consola de .NET.
Abra el archivo Program.cs recién creado y elimine cualquier código existente. Agregue mediante directivas para los espacios de nombres
Microsoft.Azure.Cosmos
,Microsoft.Azure.Cosmos.Linq
yMicrosoft.Azure.Cosmos.Spatial
.using Microsoft.Azure.Cosmos; using Microsoft.Azure.Cosmos.Linq; using Microsoft.Azure.Cosmos.Spatial;
Agregue una variable de cadena denominada *
connectionString
con la cadena de conexión que registró anteriormente en esta guía.string connectionString = "<your-account-connection-string>"
Cree una nueva instancia de la clase
CosmosClient
que pasa enconnectionString
y encapsúlela en una instrucción using.using CosmosClient client = new (connectionString);
Recupere una referencia al contenedor creado anteriormente (
cosmicworks/locations
) en la cuenta de Azure Cosmos DB for NoSQL medianteCosmosClient.GetDatabase
y, a continuación,Database.GetContainer
. Almacene el resultado en una variable denominadacontainer
.var container = client.GetDatabase("cosmicworks").GetContainer("locations");
Guarde el archivo Program.cs.
Agregar datos geoespaciales
El SDK de .NET incluye varios tipos en el espacio de nombres Microsoft.Azure.Cosmos.Spatial
para representar objetos GeoJSON comunes. Estos tipos simplifican el proceso de agregar nueva información de ubicación a los elementos de un contenedor.
Cree un nuevo archivo llamado Office.cs. En el archivo, agregue una directiva using a
Microsoft.Azure.Cosmos.Spatial
y, a continuación, cree unOffice
tipo de registro con estas propiedades:Tipo Descripción Valor predeterminado id string
Identificador único name string
Nombre de la oficina ubicación Point
Punto geográfico GeoJSON category string
Valor de la clave de partición business-office
using Microsoft.Azure.Cosmos.Spatial; public record Office( string id, string name, Point location, string category = "business-office" );
Nota
Este registro incluye una propiedad
Point
que representa una posición específica en GeoJSON. Para obtener más información, consulte Punto GeoJSON.Cree otro archivo denominado Region.cs. Agregue otro tipo de registro denominado
Region
con estas propiedades:Tipo Descripción Valor predeterminado id string
Identificador único name string
Nombre de la oficina ubicación Polygon
Forma geográfica de GeoJSON category string
Valor de la clave de partición business-region
using Microsoft.Azure.Cosmos.Spatial; public record Region( string id, string name, Polygon location, string category = "business-region" );
Nota
Este registro incluye una propiedad
Polygon
que representa una forma compuesta de líneas dibujadas entre varias ubicaciones en GeoJSON. Para obtener más información, consulte Polígono GeoJSON.Cree otro archivo denominado Result.cs. Agregue un tipo de registro denominado
Result
con estas dos propiedades:Tipo Descripción name string
Nombre del resultado coincidente distanceKilometers decimal
Distancia en kilómetros public record Result( string name, decimal distanceKilometers );
Guarde los archivos Office.cs, Region.cs y Result.cs.
Abra de nuevo el archivo Program.cs.
Cree un nuevo
Polygon
en una variable denominadamainCampusPolygon
.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), }) } );
Cree una variable
Region
denominadamainCampusRegion
con el polígono, el identificador único1000
y el nombreMain Campus
.Region mainCampusRegion = new ("1000", "Main Campus", mainCampusPolygon);
Use
Container.UpsertItemAsync
para agregar la región al contenedor. Escriba la información de la región en la consola.await container.UpsertItemAsync<Region>(mainCampusRegion); Console.WriteLine($"[UPSERT ITEM]\t{mainCampusRegion}");
Sugerencia
En esta guía se usa upsert en lugar de insertar para que pueda ejecutar el script varias veces sin causar un conflicto entre los identificadores únicos. Para obtener más información sobre las operaciones upsert, consulte creación de elementos.
Crear una nueva variable
Point
llamadaheadquartersPoint
. Use esa variable para crear una variableOffice
denominadaheadquartersOffice
con el punto, el identificador único0001
y el nombreHeadquarters
.Point headquartersPoint = new (-122.12827, 47.63980); Office headquartersOffice = new ("0001", "Headquarters", headquartersPoint);
Cree otra variable
Point
denominadaresearchPoint
. Use esa variable para crear otra variableOffice
denominadaresearchOffice
con su correspondiente punto, el identificador único0002
y el nombreResearch and Development
.Point researchPoint = new (-96.84369, 46.81298); Office researchOffice = new ("0002", "Research and Development", researchPoint);
Cree un objeto
TransactionalBatch
para upsert ambas variablesOffice
como una sola transacción. A continuación, escriba la información de ambas oficinas en la consola.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 obtener más información sobre las transacciones, consulte Operaciones por lotes transaccionales.
Guarde el archivo Program.cs.
Ejecute la aplicación en un terminal mediante
dotnet run
. Observe que la salida de la ejecución de la aplicación incluye información sobre los tres elementos recién creados.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 }
Consulta de datos geoespaciales mediante una consulta NoSQL
Los tipos del espacio de nombres Microsoft.Azure.Cosmos.Spatial
se pueden usar como entradas para que una consulta con parámetros NoSQL use funciones integradas como ST_DISTANCE
.
Abra el archivo Program.cs.
Cree una variable
string
denominadanosql
con la consulta para medir la distancia entre puntos.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 ";
Sugerencia
Esta consulta coloca la función geoespacial dentro de una subconsulta para simplificar el proceso de reutilización del valor ya calculado varias veces en las cláusulas
SELECT
yWHERE
.Cree una variable
QueryDefinition
denominadaquery
con la variablenosqlString
como parámetro. A continuación, use el método fluentQueryDefinition.WithParameter
varias veces para agregar estos parámetros a la 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));
Cree un nuevo iterador mediante
Container.GetItemQueryIterator<>
, el tipo genéricoResult
y la variablequery
. A continuación, use una combinación de un bucle while y foreach para recorrer en iteración todos los resultados en cada página de resultados. Genere cada resultado en la 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 obtener más información sobre cómo enumerar los resultados de la consulta, consulte elementos de consulta.
Guarde el archivo Program.cs.
Vuelva a ejecutar la aplicación en un terminal mediante
dotnet run
. Observe que la salida ahora incluye los resultados de la consulta.dotnet run
[DISTANCE KM] Result { name = Headquarters, distanceKilometers = 3.34 } [DISTANCE KM] Result { name = Research and Development, distanceKilometers = 1907.43 }
Consulta de datos geoespaciales mediante LINQ
La funcionalidad LINQ a NoSQL del SDK de .NET admite la inclusión de tipos geoespaciales en las expresiones de consulta. Además, el SDK incluye métodos de extensión que se asignan a funciones integradas equivalentes:
Método de extensión | Función integrada |
---|---|
Distance() |
ST_DISTANCE |
Intersects() |
ST_INTERSECTS |
IsValid() |
ST_ISVALID |
IsValidDetailed() |
ST_ISVALIDDETAILED |
Within() |
ST_WITHIN |
Abra el archivo Program.cs.
Recupere el elemento
Region
del contenedor con un identificador único de1000
y almacénelo en una variable denominadaregion
.Region region = await container.ReadItemAsync<Region>("1000", new PartitionKey("business-region"));
Use el método
Container.GetItemLinqQueryable<>
para obtener una consulta LINQ y la compilación fluida de la consulta LINQ mediante la realización de estas tres acciones:Use el método de extensión
Queryable.Where<>
para filtrar solo los elementos con uncategory
equivalente a"business-office"
.Use
Queryable.Where<>
de nuevo para filtrar solo las ubicaciones dentro de la propiedadregion
de la variablelocation
medianteGeometry.Within()
.Traduzca la expresión LINQ a un iterador de fuente mediante
CosmosLinqExtensions.ToFeedIterator<>
.
var regionIterator = container.GetItemLinqQueryable<Office>() .Where(o => o.category == "business-office") .Where(o => o.location.Within(region.location)) .ToFeedIterator<Office>();
Importante
En este ejemplo, la propiedad de ubicación de la oficina tiene un punto y la propiedad de ubicación de la región tiene un polígono.
ST_WITHIN
determina si el punto de la oficina está dentro del polígono de la región.A continuación, use una combinación de un bucle while y foreach para recorrer en iteración todos los resultados en cada página de resultados. Genere cada resultado en la consola.
while (regionIterator.HasMoreResults) { var response = await regionIterator.ReadNextAsync(); foreach (var office in response) { Console.WriteLine($"[IN REGION]\t{office}"); } }
Guarde el archivo Program.cs.
Ejecute la aplicación una última vez en un terminal mediante
dotnet run
. Observe que la salida ahora incluye los resultados de la segunda consulta basada en LINQ.dotnet run
[IN REGION] Office { id = 0001, name = Headquarters, location = Microsoft.Azure.Cosmos.Spatial.Point, category = business-office }
Limpieza de recursos
Quite la base de datos después de completar esta guía.
Abra un terminal y cree una variable de shell para el nombre de la cuenta y el 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 eliminar la base de datos.az cosmosdb sql database delete \ --resource-group $resourceGroupName \ --account-name $accountName \ --name "cosmicworks"