Share via


在 Azure Cosmos DB for NoSQL 中為 GeoJSON 位置資料編製索引和加以查詢

適用於:NoSQL

Azure Cosmos DB for NoSQL 中的地理空間資料可讓您儲存位置資訊及執行一般查詢,包括 (但不限於):

  • 確認某個位置是否在定義的區域內
  • 測量兩個位置之間的距離
  • 判斷路徑是否與某個位置或區域相交

本指南會逐步解說建立地理空間資料、編製資料索引,然後查詢容器所含資料的程序。

必要條件

建立容器和編製原則索引

所有容器都包含可成功為地理空間資料編製索引的預設索引編製原則。 若要建立自訂的索引編製原則,請建立帳戶,並使用原則的設定指定 JSON 檔案。 本節會對新建立的容器使用自訂空間索引。

  1. 開啟終端機。

  2. 為您的 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>"
    
  3. 使用 az cosmosdb sql database create 建立名為 cosmicworks 的新資料庫。

    az cosmosdb sql database create \
        --resource-group $resourceGroupName \
        --account-name $accountName \
        --name "cosmicworks" \
        --throughput 400
    
  4. 建立名為 index-policy.json 的新 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 $resourceGroupName \
        --account-name $accountName \
        --database-name "cosmicworks" \
        --name "locations" \
        --partition-key-path "/category" \
        --idx @index-policy.json
    
  6. 使用 az cosmosdb keys list 擷取帳戶的主要連接字串。

    az cosmosdb keys list \
        --resource-group $resourceGroupName \
        --name $accountName \
        --type "connection-strings" \
        --query "connectionStrings[?keyKind == \`Primary\`].connectionString" \
        --output tsv
    

    提示

    若要檢視帳戶所有可能的連接字串,請使用 az cosmosdb keys list --resource-group $resourceGroupName --name $accountName --type "connection-strings"

  7. 記錄連接字串。 您稍後會在本指南中使用此認證。

建立 .NET SDK 主控台應用程式

Azure Cosmos DB for NoSQL 的 .NET SDK 提供通用 GeoJSON 物件的類別。 使用此 SDK 簡化將地理物件新增至容器的程序。

  1. 在空的目錄中開啟終端機。

  2. 使用 dotnet new 命令搭配主控台範本來建立新的 .NET 應用程式。

    dotnet new console
    
  3. 使用 dotnet add package 命令匯入 Microsoft.Azure.Cosmos NuGet 套件。

    dotnet add package Microsoft.Azure.Cosmos --version 3.*
    

    警告

    Entity Framework 目前不支援 Azure Cosmos DB for NoSQL 中的空間資料。 將其中一個 Azure Cosmos DB for NoSQL 用於強型別 GeoJSON 支援。

  4. 使用 dotnet build 命令建立專案。

    dotnet build
    
  5. 在與您的 .NET 主控台應用程式相同的目錄中,開啟您選擇的整合式開發人員環境 (IDE)。

  6. 開啟新建立的 Program.cs 檔案,並刪除任何現有的程式碼。 為 Microsoft.Azure.CosmosMicrosoft.Azure.Cosmos.LinqMicrosoft.Azure.Cosmos.Spatial 命名空間新增 using 指示詞

    using Microsoft.Azure.Cosmos;
    using Microsoft.Azure.Cosmos.Linq;
    using Microsoft.Azure.Cosmos.Spatial;
    
  7. 使用您先前在本指南中記錄的連接字串,新增名為 *connectionString 的字串變數。

    string connectionString = "<your-account-connection-string>"
    
  8. 傳入 connectionString 並將其包裝在 using 陳述式中,以建立 CosmosClient 類別的新執行個體。

    using CosmosClient client = new (connectionString);
    
  9. 依序使用 CosmosClient.GetDatabaseDatabase.GetContainer,在 Azure Cosmos DB for NoSQL 帳戶中擷取對先前建立的容器 (cosmicworks/locations) 的參考。 將結果儲存在名為 container 的變數中。

    var container = client.GetDatabase("cosmicworks").GetContainer("locations");
    
  10. 儲存 Program.cs 檔案。

新增地理空間資料

.NET SDK 在 Microsoft.Azure.Cosmos.Spatial 命名空間中包含了多個類型,以代表常見的 GeoJSON 物件。 這些類型可簡化將新的位置資訊新增至容器所含項目的程序。

  1. 建立名為 Office.cs 的新檔案。 在該檔案中,將 using 指示詞新增至 Microsoft.Azure.Cosmos.Spatial,然後使用下列屬性建立 Office記錄類型

    類型 描述 預設值
    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 點

  2. 建立名為 Region.cs 的另一個新檔案。 使用下列屬性新增名為 Region 的另一個記錄類型:

    類型 描述 預設值
    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 多邊形

  3. 建立名為 Result.cs 的另一個新檔案。 使用以下兩個屬性新增名為 Result 的記錄類型:

    類型 Description
    name string 相符結果的名稱
    distanceKilometers decimal 以公里為單位的距離
    public record Result(
        string name,
        decimal distanceKilometers
    );
    
  4. 儲存 Office.csRegion.csResult.cs 檔案。

  5. 再次開啟 Program.cs 檔案。

  6. 在名為 mainCampusPolygon 的變數中建立新的 Polygon

    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. 使用多邊形、唯一識別碼 1000 和名稱 Main Campus 新建名為 mainCampusRegionRegion 變數。

    Region mainCampusRegion = new ("1000", "Main Campus", mainCampusPolygon);
    
  8. 使用 Container.UpsertItemAsync 將區域新增至容器。 將區域的資訊寫入主控台。

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

    提示

    本指南使用 upsert 而不是 insert,因此您可以執行指令碼多次,而不會造成唯一識別碼之間的衝突。 如需 upsert 作業的詳細資訊,請參閱建立項目

  9. 新建名為 headquartersPointPoint 變數。 使用該變數新建名為 headquartersOfficeOffice 變數 (使用點、唯一識別碼 0001 和名稱 Headquarters)。

    Point headquartersPoint = new (-122.12827, 47.63980);
    Office headquartersOffice = new ("0001", "Headquarters", headquartersPoint);
    
  10. 建立另一個名為 researchPointPoint 變數。 使用該變數建立名為 researchOffice 的另一個 Office 變數 (使用對應點、唯一識別碼 0002 和名稱 Research and Development)。

    Point researchPoint = new (-96.84369, 46.81298);
    Office researchOffice = new ("0002", "Research and Development", researchPoint);
    
  11. 建立 TransactionalBatch 以將兩個 Office 變數 upsert 為單一交易。 然後,將這兩個辦公室的資訊都寫入主控台。

    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. 透過此節中使用的查詢新建名為 nosqlstring 變數,以測量點之間的距離。

    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
    ";
    

    提示

    此查詢會將地理空間函式放在子查詢內,以便在 SELECTWHERE 子句中多次重複使用已計算的值。

  3. 使用 nosqlString 變數作為參數,新建名為 queryQueryDefinition 變數。 然後,使用 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 變數建立新的迭代器。 然後,使用 whileforeach 迴圈的組合,逐一查看每個結果頁面中的所有結果。 將每個結果輸出至主控台。

    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 查詢地理空間資料

.NET SDK 中的 LINQ to NoSQL 功能支援在查詢運算式中包含地理空間類型。 此外,SDK 也包含對應至對等內建函式的擴充方法:

擴充方法 內建函式
Distance() ST_DISTANCE
Intersects() ST_INTERSECTS
IsValid() ST_ISVALID
IsValidDetailed() ST_ISVALIDDETAILED
Within() ST_WITHIN
  1. 開啟 Program.cs 檔案。

  2. 使用唯一識別碼 1000 從容器中擷取 Region 項目,並將其儲存在名為 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<> 並搭配 Geometry.Within(),僅篩選出 region 變數的 location 屬性內的位置。

    3. 使用 CosmosLinqExtensions.ToFeedIterator<> 將 LINQ 運算式轉譯為摘要迭代器。

    var regionIterator = container.GetItemLinqQueryable<Office>()
        .Where(o => o.category == "business-office")
        .Where(o => o.location.Within(region.location))
        .ToFeedIterator<Office>();
    

    重要

    在此範例中,辦公室的位置屬性有一個,而區域的位置屬性則有多邊形ST_WITHIN 正在確認辦公室的點是否在區域的多邊形內。

  4. 使用 whileforeach 迴圈的組合,逐一查看每個結果頁面中的所有結果。 將每個結果輸出至主控台。

    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 $resourceGroupName \
        --account-name $accountName \
        --name "cosmicworks"
    

下一步