다음을 통해 공유


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 "<resource-group-name>" \
        --account-name "<nosql-account-name>" \
        --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 "<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. 다음 섹션에서 필요하므로 계정 엔드포인트를 기록합니다.

.NET SDK 콘솔 애플리케이션 만들기

Azure Cosmos DB for NoSQL용 .NET SDK는 일반적인 GeoJSON 개체에 대한 클래스를 제공합니다. 이 SDK를 사용하여 지리적 개체를 컨테이너에 추가하는 프로세스를 간소화합니다.

  1. 빈 디렉터리에서 터미널을 엽니다.

  2. console 템플릿에서 dotnet new 명령을 사용하여 새 .NET 애플리케이션을 만듭니다.

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

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

    Warning

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

  4. Azure.Identity NuGet 패키지를 가져옵니다.

    dotnet add package Azure.Identity --version 1.*
    
  5. dotnet build 명령을 사용하여 프로젝트를 빌드합니다.

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

  7. 새로 만든 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;
    
  8. 네임스페이스에 대한 다른 using 지시문을 Azure.Identity 추가합니다.

    using Azure.Identity;
    
  9. DefaultAzureCredential 형식의 credential라는 새 변수를 만듭니다.

    DefaultAzureCredential credential = new();
    
  10. NoSQL용 Azure Cosmos DB 계정 엔드포인트로 명명된 endpoint 문자열 변수를 만듭니다.

    string endpoint = "<nosql-account-endpoint>";
    
  11. connectionString을 전달하고 이를 using 문으로 래핑하는 CosmosClient 클래스의 새 인스턴스를 만듭니다.

    using CosmosClient client = new (connectionString);
    
  12. CosmosClient.GetDatabase, Database.GetContainer를 차례로 사용하여 Azure Cosmos DB for NoSQL 계정에서 이전에 만든 컨테이너(cosmicworks/locations)에 대한 참조를 검색합니다. container라는 변수에 결과를 저장합니다.

    var container = client.GetDatabase("cosmicworks").GetContainer("locations");
    
  13. 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 "<resource-group-name>" \
        --account-name "<nosql-account-name>" \
        --name "cosmicworks"
    

다음 단계