Индексирование и запрос данных расположения GeoJSON в Azure Cosmos DB для NoSQL
ОБЛАСТЬ ПРИМЕНЕНИЯ: NoSQL
Геопространственные данные в Azure Cosmos DB для NoSQL позволяют хранить сведения о расположении и выполнять общие запросы, включая не только следующие:
- Поиск расположения в определенной области
- Измерение расстояния между двумя расположениями
- Определение того, пересекается ли путь с расположением или областью
В этом руководстве описывается процесс создания геопространственных данных, индексирования данных и последующего запроса данных в контейнере.
Необходимые компоненты
- Существующая учетная запись Azure Cosmos DB для NoSQL.
- Если у вас нет подписки Azure, попробуйте использовать Azure Cosmos DB для NoSQL бесплатно.
- Если у вас есть подписка Azure, создайте новую учетную запись Azure Cosmos DB для NoSQL.
- Последняя версия .NET.
- Последняя версия Azure CLI.
- Если вы используете локальную установку, войдите в Azure CLI с помощью
az login
команды.
- Если вы используете локальную установку, войдите в Azure CLI с помощью
Создание политики контейнера и индексирования
Все контейнеры включают политику индексирования по умолчанию, которая успешно индексирует геопространственные данные. Чтобы создать настраиваемую политику индексирования, создайте учетную запись и укажите JSON-файл с конфигурацией политики. В этом разделе для только что созданного контейнера используется настраиваемый пространственный индекс.
Откройте окно терминала.
Создайте переменную оболочки для имени учетной записи Azure Cosmos DB для NoSQL и группы ресурсов.
# Variable for resource group name resourceGroupName="<name-of-your-resource-group>" # Variable for account name accountName="<name-of-your-account>"
Создайте новую базу данных с именем
cosmicworks
с помощьюaz cosmosdb sql database create
.az cosmosdb sql database create \ --resource-group "<resource-group-name>" \ --account-name "<nosql-account-name>" \ --name "cosmicworks" \ --throughput 400
Создайте файл JSON с именем index-policy.json и добавьте следующий объект JSON в файл.
{ "indexingMode": "consistent", "automatic": true, "includedPaths": [ { "path": "/*" } ], "excludedPaths": [ { "path": "/\"_etag\"/?" } ], "spatialIndexes": [ { "path": "/location/*", "types": [ "Point", "Polygon" ] } ] }
Используется
az cosmosdb sql container create
для создания контейнера с именемlocations
с путем ключа секции/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
Наконец, получите конечную точку учетной записи для учетной записи с помощью
az cosmosdb show
запроса JMESPath.az cosmosdb show \ --resource-group "<resource-group-name>" \ --name "<nosql-account-name>" \ --query "documentEndpoint"
Запишите конечную точку учетной записи, так как это потребуется в следующем разделе.
Создание консольного приложения пакета SDK для .NET
Пакет SDK для .NET для Azure Cosmos DB для NoSQL предоставляет классы для распространенных объектов GeoJSON. Используйте этот пакет SDK для упрощения процесса добавления географических объектов в контейнер.
Откройте терминал в пустом каталоге.
Создайте новое приложение .NET, используя команду
dotnet new
с шаблоном console.dotnet new console
Microsoft.Azure.Cosmos
Импортируйте пакет NuGet с помощьюdotnet add package
команды.dotnet add package Microsoft.Azure.Cosmos --version 3.*
Предупреждение
Entity Framework в настоящее время не выполняет пространственные данные в Azure Cosmos DB для NoSQL. Используйте один из пакетов SDK Azure Cosmos DB для NoSQL для строго типизированной поддержки GeoJSON.
Azure.Identity
Импортируйте пакет NuGet.dotnet add package Azure.Identity --version 1.*
Создайте проект с помощью команды
dotnet build
.dotnet build
Откройте интегрированную среду разработки (IDE) в том же каталоге, что и консольное приложение .NET.
Откройте созданный файл Program.cs и удалите существующий код. Добавьте директивы using для
Microsoft.Azure.Cosmos
Microsoft.Azure.Cosmos.Linq
пространств имен иMicrosoft.Azure.Cosmos.Spatial
пространств имен.using Microsoft.Azure.Cosmos; using Microsoft.Azure.Cosmos.Linq; using Microsoft.Azure.Cosmos.Spatial;
Добавьте другую директиву
Azure.Identity
using для пространства имен.using Azure.Identity;
Создайте новую переменную с именем
credential
типаDefaultAzureCredential
.DefaultAzureCredential credential = new();
Создайте строковую переменную с именем
endpoint
конечной точки учетной записи Azure Cosmos DB для NoSQL.string endpoint = "<nosql-account-endpoint>";
Создайте новый экземпляр класса, передаваемого
CosmosClient
connectionString
и упаковав его в инструкцию using.using CosmosClient client = new (connectionString);
Получите ссылку на ранее созданный контейнер (
cosmicworks/locations
) в учетной записи Azure Cosmos DB для NoSQL с помощьюCosmosClient.GetDatabase
, а затемDatabase.GetContainer
. Сохраните результат в переменной под названиемcontainer
.var container = client.GetDatabase("cosmicworks").GetContainer("locations");
Сохраните файл Program.cs.
Добавление геопространственных данных
Пакет SDK для .NET включает несколько типов в Microsoft.Azure.Cosmos.Spatial
пространстве имен для представления общих объектов GeoJSON. Эти типы упрощают процесс добавления новых сведений о расположении в элементы в контейнере.
Создайте файл с именем Office.cs. В файле добавьте директиву using и
Microsoft.Azure.Cosmos.Spatial
создайтеOffice
тип записи со следующими свойствами:Тип Описание Default value id string
Уникальный идентификатор name string
Имя офиса расположение Point
Географическая точка GeoJSON category string
Значение ключа раздела business-office
using Microsoft.Azure.Cosmos.Spatial; public record Office( string id, string name, Point location, string category = "business-office" );
Примечание.
Эта запись включает
Point
свойство, представляющее определенную позицию в GeoJSON. Дополнительные сведения см. в разделе GeoJSON Point.Создайте другой файл с именем Region.cs. Добавьте другой тип
Region
записи с именами этих свойств:Тип Описание Default value id string
Уникальный идентификатор name string
Имя офиса расположение Polygon
Географическая фигура GeoJSON category string
Значение ключа раздела business-region
using Microsoft.Azure.Cosmos.Spatial; public record Region( string id, string name, Polygon location, string category = "business-region" );
Примечание.
Эта запись включает
Polygon
свойство, представляющее фигуру, состоящую из линий, нарисованных между несколькими расположениями в GeoJSON. Дополнительные сведения см. в разделе GeoJSON Polygon.Создайте другой файл с именем Result.cs. Добавьте тип
Result
записи с этими двумя свойствами:Тип Description name string
Имя соответствующего результата distanceKilometers decimal
Расстояние в километрах public record Result( string name, decimal distanceKilometers );
Сохраните файлы Office.cs, Region.cs и Result.cs .
Откройте опять файл Program.cs.
Создайте новую
Polygon
переменную с именем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), }) } );
Создайте новую
Region
переменную с именемmainCampusRegion
многоугольника, уникального идентификатора1000
и имениMain Campus
.Region mainCampusRegion = new ("1000", "Main Campus", mainCampusPolygon);
Используйте
Container.UpsertItemAsync
для добавления региона в контейнер. Напишите сведения о регионе в консоль.await container.UpsertItemAsync<Region>(mainCampusRegion); Console.WriteLine($"[UPSERT ITEM]\t{mainCampusRegion}");
Совет
В этом руководстве используется upsert вместо вставки , чтобы можно было выполнять скрипт несколько раз, не вызывая конфликт между уникальными идентификаторами. Дополнительные сведения об операциях upsert см. в статье о создании элементов.
Создайте новую
Point
переменную с именемheadquartersPoint
. Используйте ее для создания новойOffice
переменной с именемheadquartersOffice
точки, уникального идентификатора0001
и имениHeadquarters
.Point headquartersPoint = new (-122.12827, 47.63980); Office headquartersOffice = new ("0001", "Headquarters", headquartersPoint);
Создайте другую
Point
переменную с именемresearchPoint
. Используйте ту переменную, чтобы создать другуюOffice
переменнуюresearchOffice
с помощью соответствующей точки, уникального идентификатора0002
и имениResearch and Development
.Point researchPoint = new (-96.84369, 46.81298); Office researchOffice = new ("0002", "Research and Development", researchPoint);
TransactionalBatch
Создайте переменные для upsert обоихOffice
переменных в виде одной транзакции. Затем напишите сведения о обоих офисах в консоль.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}");
Примечание.
Дополнительные сведения о транзакциях см. в разделе транзакций пакетных операций.
Сохраните файл Program.cs.
Запустите приложение в терминале с помощью
dotnet run
. Обратите внимание, что выходные данные запуска приложения содержат сведения о трех только что созданных элементах.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 }
Запрос геопространственных данных с помощью запроса NoSQL
Типы в Microsoft.Azure.Cosmos.Spatial
пространстве имен можно использовать в качестве входных данных для параметризованного запроса NoSQL для использования встроенных функций, таких как ST_DISTANCE
.
Откройте файл Program.cs.
Создание новой переменной
string
с именемnosql
запроса используется в этом разделе для измерения расстояния между точками.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 ";
Совет
Этот запрос помещает геопространственные функции в вложенный запрос, чтобы упростить процесс повторного использования уже вычисляемого значения несколько раз в
SELECT
предложениях.WHERE
Создайте новую
QueryDefinition
переменную с именемquery
с помощью переменнойnosqlString
в качестве параметра. Затем несколько раз используйтеQueryDefinition.WithParameter
метод fluent, чтобы добавить эти параметры в запрос:Значение @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));
Создайте итератор с помощью
Container.GetItemQueryIterator<>
универсальногоResult
типа и переменнойquery
. Затем используйте сочетание некоторого времени и цикла foreach , чтобы итерировать все результаты на каждой странице результатов. Выводит каждый результат в консоль.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}"); } }
Примечание.
Дополнительные сведения о перечислении результатов запроса см. в разделе "Элементы запроса".
Сохраните файл Program.cs.
Снова запустите приложение в терминале с помощью
dotnet run
. Обратите внимание, что выходные данные теперь включают результаты запроса.dotnet run
[DISTANCE KM] Result { name = Headquarters, distanceKilometers = 3.34 } [DISTANCE KM] Result { name = Research and Development, distanceKilometers = 1907.43 }
Запрос геопространственных данных с помощью LINQ
Функции LINQ to NoSQL в пакете SDK для .NET поддерживают включение геопространственных типов в выражения запроса. Кроме того, пакет SDK включает методы расширения, которые сопоставляют с эквивалентными встроенными функциями:
Метод расширения | Встроенная функция |
---|---|
Distance() |
ST_DISTANCE |
Intersects() |
ST_INTERSECTS |
IsValid() |
ST_ISVALID |
IsValidDetailed() |
ST_ISVALIDDETAILED |
Within() |
ST_WITHIN |
Откройте файл Program.cs.
Region
Извлеките элемент из контейнера с уникальным идентификатором1000
и сохраните его в переменной с именемregion
.Region region = await container.ReadItemAsync<Region>("1000", new PartitionKey("business-region"));
Container.GetItemLinqQueryable<>
Используйте метод для получения запрашиваемого кода LINQ и сборки запроса LINQ с помощью следующих трех действий:Queryable.Where<>
Используйте метод расширения, чтобы отфильтровать только элементы с эквивалентомcategory
"business-office"
.Используйте
Queryable.Where<>
еще раз, чтобы отфильтровать только расположения в свойствеlocation
переменнойregion
с помощьюGeometry.Within()
.Перевод выражения LINQ в итератор веб-канала с помощью
CosmosLinqExtensions.ToFeedIterator<>
.
var regionIterator = container.GetItemLinqQueryable<Office>() .Where(o => o.category == "business-office") .Where(o => o.location.Within(region.location)) .ToFeedIterator<Office>();
Внимание
В этом примере свойство расположения офиса имеет точку, а свойство расположения региона имеет многоугольник.
ST_WITHIN
определяет, находится ли точка офиса в многоугольнике региона.Используйте сочетание времени и цикла foreach , чтобы итерировать все результаты на каждой странице результатов. Выводит каждый результат в консоль.
while (regionIterator.HasMoreResults) { var response = await regionIterator.ReadNextAsync(); foreach (var office in response) { Console.WriteLine($"[IN REGION]\t{office}"); } }
Сохраните файл Program.cs.
Запустите приложение в последний раз в терминале с помощью
dotnet run
. Обратите внимание, что выходные данные теперь включают результаты второго запроса на основе LINQ.dotnet run
[IN REGION] Office { id = 0001, name = Headquarters, location = Microsoft.Azure.Cosmos.Spatial.Point, category = business-office }
Очистка ресурсов
Удалите базу данных после завершения работы с этим руководством.
Откройте терминал и создайте переменную оболочки для имени учетной записи и группы ресурсов.
# Variable for resource group name resourceGroupName="<name-of-your-resource-group>" # Variable for account name accountName="<name-of-your-account>"
Используется
az cosmosdb sql database delete
для удаления базы данных.az cosmosdb sql database delete \ --resource-group "<resource-group-name>" \ --account-name "<nosql-account-name>" \ --name "cosmicworks"