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를 사용하여 파티션 키 경로가 /regionlocations라는 새 컨테이너를 만듭니다.

    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. console 템플릿에서 dotnet new 명령을 사용하여 새 .NET 애플리케이션을 만듭니다.

    dotnet new console
    
  3. dotnet add package 명령을 사용하여 Microsoft.Azure.Cosmos NuGet 패키지를 가져옵니다.

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

    Warning

    Entity Framework는 현재 Azure Cosmos DB for NoSQL의 공간 데이터를 지원하지 않습니다. 강력한 형식의 GeoJSON 지원을 위해 Azure Cosmos DB for NoSQL SDK 중 하나를 사용합니다.

  4. dotnet build 명령을 사용하여 프로젝트를 빌드합니다.

    dotnet build
    
  5. .NET 콘솔 애플리케이션과 동일한 디렉터리에서 원하는 IDE(통합 개발자 환경)를 엽니다.

  6. 새로 만든 Program.cs 파일을 열고 기존 코드를 삭제합니다. Microsoft.Azure.Cosmos, Microsoft.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.GetDatabase, Database.GetContainer를 차례로 사용하여 Azure Cosmos DB for NoSQL 계정에서 이전에 만든 컨테이너(cosmicworks/locations)에 대한 참조를 검색합니다. container라는 변수에 결과를 저장합니다.

    var container = client.GetDatabase("cosmicworks").GetContainer("locations");
    
  10. Program.cs 파일을 저장합니다.

지리 공간적 데이터 추가

.NET SDK에는 공통 GeoJSON 개체를 나타내는 여러 형식이 Microsoft.Azure.Cosmos.Spatial 네임스페이스에 포함되어 있습니다. 이러한 형식은 새 위치 정보를 컨테이너의 항목에 추가하는 프로세스를 간소화합니다.

  1. Office.cs라는 새 파일을 만듭니다. 파일에서 using 지시문을 Microsoft.Azure.Cosmos.Spatial에 추가하고, 다음 속성을 사용하여 Office 레코드 형식을 만듭니다.

    Type 설명 기본값
    id string 고유 식별자
    이름 string 사무실 이름
    location Point GeoJSON 지리적 점
    category string 파티션 키 값 business-office
    using Microsoft.Azure.Cosmos.Spatial;
    
    public record Office(
        string id,
        string name,
        Point location,
        string category = "business-office"
    );
    

    참고 항목

    이 레코드에는 GeoJSON의 특정 위치를 나타내는 Point 속성이 포함됩니다. 자세한 내용은 GeoJSON 점을 참조하세요.

  2. Region.cs라는 또 다른 새 파일을 만듭니다. 다음 속성을 사용하여 Region이라는 다른 레코드 형식을 추가합니다.

    Type 설명 기본값
    id string 고유 식별자
    이름 string 사무실 이름
    location Polygon GeoJSON 지리적 도형
    category string 파티션 키 값 business-region
    using Microsoft.Azure.Cosmos.Spatial;
    
    public record Region(
        string id,
        string name,
        Polygon location,
        string category = "business-region"
    );
    

    참고 항목

    이 레코드에는 GeoJSON의 여러 위치 사이에 그려진 선으로 구성된 도형을 나타내는 Polygon 속성이 포함됩니다. 자세한 내용은 GeoJSON 다각형을 참조하세요.

  3. Result.cs라는 또 다른 새 파일을 만듭니다. 다음 두 속성을 사용하여 Result라는 레코드 형식을 추가합니다.

    Type Description
    이름 string 일치하는 결과의 이름
    distanceKilometers decimal 킬로미터 단위의 거리
    public record Result(
        string name,
        decimal distanceKilometers
    );
    
  4. Office.cs, Region.csResult.cs 파일을 저장합니다.

  5. Program.cs 파일을 다시 엽니다.

  6. PolygonmainCampusPolygon이라는 변수에 만듭니다.

    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 이름을 사용하여 mainCampusRegion이라는 새 Region 변수를 만듭니다.

    Region mainCampusRegion = new ("1000", "Main Campus", mainCampusPolygon);
    
  8. Container.UpsertItemAsync를 사용하여 지역을 컨테이너에 추가합니다. 지역 정보를 콘솔에 씁니다.

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

    이 가이드에서는 insert 대신 upsert를 사용하므로 고유 식별자 간의 충돌이 발생하지 않고 스크립트를 여러 번 실행할 수 있습니다. upsert 작업에 대한 자세한 내용은 항목 만들기를 참조하세요.

  9. headquartersPoint라는 새 Point 변수를 만듭니다. 이 변수에서 점, 0001 고유 식별자 및 Headquarters 이름을 사용하여 headquartersOffice라는 새 Office 변수를 만듭니다.

    Point headquartersPoint = new (-122.12827, 47.63980);
    Office headquartersOffice = new ("0001", "Headquarters", headquartersPoint);
    
  10. researchPoint라는 또 다른 Point 변수를 만듭니다. 해당 변수를 사용하여 대응 점, 0002 고유 식별자 및 Research and Development 이름을 사용하여 researchOffice라는 또 다른 Office 변수를 만듭니다.

    Point researchPoint = new (-96.84369, 46.81298);
    Office researchOffice = new ("0002", "Research and Development", researchPoint);
    
  11. Office 변수를 모두 단일 트랜잭션으로 upsert하는 TransactionalBatch를 만듭니다. 그런 다음, 두 사무실의 정보를 콘솔에 씁니다.

    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. 이 섹션에서 점 사이의 거리를 측정하는 데 사용되는 쿼리를 사용하여 nosql이라는 새 string 변수를 만듭니다.

    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 변수를 매개 변수로 사용하여 query라는 새 QueryDefinition 변수를 만듭니다. 그런 다음, QueryDefinition.WithParameter 흐름 메서드를 여러 번 사용하여 다음 매개 변수를 쿼리에 추가합니다.

    @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<> 확장 메서드를 사용하여 "business-office"에 해당하는 category가 있는 항목만 필터링합니다.

    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>();
    

    Important

    이 예제에서 사무실의 위치 속성에는 이 있고, 지역의 위치 속성에는 다각형이 있습니다. 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"
    

다음 단계