Индексирование и запрос данных расположения GeoJSON в Azure Cosmos DB для NoSQL

ОБЛАСТЬ ПРИМЕНЕНИЯ: NoSQL

Геопространственные данные в Azure Cosmos DB для NoSQL позволяют хранить сведения о расположении и выполнять общие запросы, включая не только следующие:

  • Поиск расположения в определенной области
  • Измерение расстояния между двумя расположениями
  • Определение того, пересекается ли путь с расположением или областью

В этом руководстве описывается процесс создания геопространственных данных, индексирования данных и последующего запроса данных в контейнере.

Необходимые компоненты

Создание политики контейнера и индексирования

Все контейнеры включают политику индексирования по умолчанию, которая успешно индексирует геопространственные данные. Чтобы создать настраиваемую политику индексирования, создайте учетную запись и укажите JSON-файл с конфигурацией политики. В этом разделе для только что созданного контейнера используется настраиваемый пространственный индекс.

  1. Откройте окно терминала.

  2. Создайте переменную оболочки для имени учетной записи Azure Cosmos DB для NoSQL и группы ресурсов.

    # Variable for resource group name
    resourceGroupName="<name-of-your-resource-group>"
    
    # Variable for account name
    accountName="<name-of-your-account>"
    
  3. Создайте новую базу данных с именем 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
    
  4. Создайте файл JSON с именем index-policy.json и добавьте следующий объект JSON в файл.

    {
      "indexingMode": "consistent",
      "automatic": true,
      "includedPaths": [
        {
          "path": "/*"
        }
      ],
      "excludedPaths": [
        {
          "path": "/\"_etag\"/?"
        }
      ],
      "spatialIndexes": [
        {
          "path": "/location/*",
          "types": [
            "Point",
            "Polygon"
          ]
        }
      ]
    }
    
  5. Используется 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
    
  6. Наконец, получите конечную точку учетной записи для учетной записи с помощью az cosmosdb show запроса JMESPath.

    az cosmosdb show \
        --resource-group "<resource-group-name>" \
        --name "<nosql-account-name>" \
        --query "documentEndpoint"
    
  7. Запишите конечную точку учетной записи, так как это потребуется в следующем разделе.

Создание консольного приложения пакета SDK для .NET

Пакет SDK для .NET для Azure Cosmos DB для NoSQL предоставляет классы для распространенных объектов GeoJSON. Используйте этот пакет SDK для упрощения процесса добавления географических объектов в контейнер.

  1. Откройте терминал в пустом каталоге.

  2. Создайте новое приложение .NET, используя команду dotnet new с шаблоном console.

    dotnet new console
    
  3. 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.

  4. Azure.Identity Импортируйте пакет NuGet.

    dotnet add package Azure.Identity --version 1.*
    
  5. Создайте проект с помощью команды dotnet build.

    dotnet build
    
  6. Откройте интегрированную среду разработки (IDE) в том же каталоге, что и консольное приложение .NET.

  7. Откройте созданный файл Program.cs и удалите существующий код. Добавьте директивы using для Microsoft.Azure.CosmosMicrosoft.Azure.Cosmos.Linqпространств имен иMicrosoft.Azure.Cosmos.Spatial пространств имен.

    using Microsoft.Azure.Cosmos;
    using Microsoft.Azure.Cosmos.Linq;
    using Microsoft.Azure.Cosmos.Spatial;
    
  8. Добавьте другую директиву Azure.Identity using для пространства имен.

    using Azure.Identity;
    
  9. Создайте новую переменную с именем credential типа DefaultAzureCredential.

    DefaultAzureCredential credential = new();
    
  10. Создайте строковую переменную с именем endpoint конечной точки учетной записи Azure Cosmos DB для NoSQL.

    string endpoint = "<nosql-account-endpoint>";
    
  11. Создайте новый экземпляр класса, передаваемого CosmosClient connectionString и упаковав его в инструкцию using.

    using CosmosClient client = new (connectionString);
    
  12. Получите ссылку на ранее созданный контейнер (cosmicworks/locations) в учетной записи Azure Cosmos DB для NoSQL с помощью CosmosClient.GetDatabase , а затем Database.GetContainer. Сохраните результат в переменной под названием container.

    var container = client.GetDatabase("cosmicworks").GetContainer("locations");
    
  13. Сохраните файл Program.cs.

Добавление геопространственных данных

Пакет SDK для .NET включает несколько типов в Microsoft.Azure.Cosmos.Spatial пространстве имен для представления общих объектов GeoJSON. Эти типы упрощают процесс добавления новых сведений о расположении в элементы в контейнере.

  1. Создайте файл с именем 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.

  2. Создайте другой файл с именем 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.

  3. Создайте другой файл с именем Result.cs. Добавьте тип Result записи с этими двумя свойствами:

    Тип Description
    name string Имя соответствующего результата
    distanceKilometers decimal Расстояние в километрах
    public record Result(
        string name,
        decimal distanceKilometers
    );
    
  4. Сохраните файлы Office.cs, Region.cs и Result.cs .

  5. Откройте опять файл Program.cs.

  6. Создайте новую 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),
            })
        }
    );
    
  7. Создайте новую Region переменную с именем mainCampusRegion многоугольника, уникального идентификатора 1000и имени Main Campus.

    Region mainCampusRegion = new ("1000", "Main Campus", mainCampusPolygon);
    
  8. Используйте Container.UpsertItemAsync для добавления региона в контейнер. Напишите сведения о регионе в консоль.

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

    Совет

    В этом руководстве используется upsert вместо вставки , чтобы можно было выполнять скрипт несколько раз, не вызывая конфликт между уникальными идентификаторами. Дополнительные сведения об операциях upsert см. в статье о создании элементов.

  9. Создайте новую Point переменную с именем headquartersPoint. Используйте ее для создания новой Office переменной с именем headquartersOffice точки, уникального идентификатора 0001и имени Headquarters.

    Point headquartersPoint = new (-122.12827, 47.63980);
    Office headquartersOffice = new ("0001", "Headquarters", headquartersPoint);
    
  10. Создайте другую Point переменную с именем researchPoint. Используйте ту переменную, чтобы создать другую Office переменную researchOffice с помощью соответствующей точки, уникального идентификатора 0002и имени Research and Development.

    Point researchPoint = new (-96.84369, 46.81298);
    Office researchOffice = new ("0002", "Research and Development", researchPoint);
    
  11. 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}");
    

    Примечание.

    Дополнительные сведения о транзакциях см. в разделе транзакций пакетных операций.

  12. Сохраните файл Program.cs.

  13. Запустите приложение в терминале с помощью 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.

  1. Откройте файл Program.cs.

  2. Создание новой переменной 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

  3. Создайте новую 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));
    
  4. Создайте итератор с помощью 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}");
        }
    }
    

    Примечание.

    Дополнительные сведения о перечислении результатов запроса см. в разделе "Элементы запроса".

  5. Сохраните файл Program.cs.

  6. Снова запустите приложение в терминале с помощью 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
  1. Откройте файл Program.cs.

  2. Region Извлеките элемент из контейнера с уникальным идентификатором 1000 и сохраните его в переменной с именемregion.

    Region region = await container.ReadItemAsync<Region>("1000", new PartitionKey("business-region"));
    
  3. Container.GetItemLinqQueryable<> Используйте метод для получения запрашиваемого кода LINQ и сборки запроса LINQ с помощью следующих трех действий:

    1. Queryable.Where<> Используйте метод расширения, чтобы отфильтровать только элементы с эквивалентом category "business-office".

    2. Используйте Queryable.Where<> еще раз, чтобы отфильтровать только расположения в свойстве location переменной region с помощьюGeometry.Within().

    3. Перевод выражения 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 определяет, находится ли точка офиса в многоугольнике региона.

  4. Используйте сочетание времени и цикла foreach , чтобы итерировать все результаты на каждой странице результатов. Выводит каждый результат в консоль.

    while (regionIterator.HasMoreResults)
    {
        var response = await regionIterator.ReadNextAsync();
        foreach (var office in response)
        {
            Console.WriteLine($"[IN REGION]\t{office}");
        }
    }
    
  5. Сохраните файл Program.cs.

  6. Запустите приложение в последний раз в терминале с помощью dotnet run. Обратите внимание, что выходные данные теперь включают результаты второго запроса на основе LINQ.

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

Очистка ресурсов

Удалите базу данных после завершения работы с этим руководством.

  1. Откройте терминал и создайте переменную оболочки для имени учетной записи и группы ресурсов.

    # Variable for resource group name
    resourceGroupName="<name-of-your-resource-group>"
    
    # Variable for account name
    accountName="<name-of-your-account>"
    
  2. Используется az cosmosdb sql database delete для удаления базы данных.

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

Следующие шаги