Indeksowanie i wykonywanie zapytań względem danych lokalizacji GeoJSON w usłudze Azure Cosmos DB for NoSQL

DOTYCZY: NoSQL

Dane geoprzestrzenne w usłudze Azure Cosmos DB for NoSQL umożliwiają przechowywanie informacji o lokalizacji i wykonywanie typowych zapytań, w tym między innymi:

  • Znajdowanie, czy lokalizacja znajduje się w zdefiniowanym obszarze
  • Mierzenie odległości między dwiema lokalizacjami
  • Określanie, czy ścieżka przecina się z lokalizacją lub obszarem

W tym przewodniku przedstawiono proces tworzenia danych geoprzestrzennych, indeksowania danych, a następnie wykonywania zapytań dotyczących danych w kontenerze.

Wymagania wstępne

Tworzenie zasad kontenera i indeksowania

Wszystkie kontenery zawierają domyślne zasady indeksowania, które pomyślnie indeksują dane geoprzestrzenne. Aby utworzyć dostosowane zasady indeksowania, utwórz konto i określ plik JSON z konfiguracją zasad. W tej sekcji niestandardowy indeks przestrzenny jest używany dla nowo utworzonego kontenera.

  1. Otwórz terminal.

  2. Utwórz zmienną powłoki dla nazwy konta i grupy zasobów usługi 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. Utwórz nową bazę danych o nazwie cosmicworks przy użyciu polecenia az cosmosdb sql database create.

    az cosmosdb sql database create \
        --resource-group $resourceGroupName \
        --account-name $accountName \
        --name "cosmicworks" \
        --throughput 400
    
  4. Utwórz nowy plik JSON o nazwie index-policy.json i dodaj do pliku następujący obiekt JSON.

    {
      "indexingMode": "consistent",
      "automatic": true,
      "includedPaths": [
        {
          "path": "/*"
        }
      ],
      "excludedPaths": [
        {
          "path": "/\"_etag\"/?"
        }
      ],
      "spatialIndexes": [
        {
          "path": "/location/*",
          "types": [
            "Point",
            "Polygon"
          ]
        }
      ]
    }
    
  5. Użyj polecenia az cosmosdb sql container create , aby utworzyć nowy kontener o nazwie locations ze ścieżką klucza partycji ./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. Pobierz podstawowe parametry połączenia dla konta przy użyciu polecenia az cosmosdb keys list.

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

    Porada

    Aby wyświetlić wszystkie możliwe parametry połączenia dla konta, użyj polecenia az cosmosdb keys list --resource-group $resourceGroupName --name $accountName --type "connection-strings".

  7. Zapisz parametry połączenia. To poświadczenie będzie używane w dalszej części tego przewodnika.

Tworzenie aplikacji konsolowej zestawu SDK platformy .NET

Zestaw .NET SDK dla usługi Azure Cosmos DB for NoSQL udostępnia klasy dla typowych obiektów GeoJSON. Użyj tego zestawu SDK, aby usprawnić proces dodawania obiektów geograficznych do kontenera.

  1. Otwórz terminal w pustym katalogu.

  2. Utwórz nową aplikację .NET przy użyciu dotnet new polecenia z szablonem konsoli .

    dotnet new console
    
  3. Zaimportuj pakiet NuGet Microsoft.Azure.Cosmos przy użyciu dotnet add package polecenia .

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

    Ostrzeżenie

    Program Entity Framework nie obsługuje obecnie danych przestrzennych w usłudze Azure Cosmos DB for NoSQL. Użyj jednego z zestawów SDK usługi Azure Cosmos DB for NoSQL w celu obsługi silnie typizowanego formatu GeoJSON.

  4. Skompiluj projekt za dotnet build pomocą polecenia .

    dotnet build
    
  5. Otwórz wybrane zintegrowane środowisko deweloperskie (IDE) w tym samym katalogu co aplikacja konsolowa platformy .NET.

  6. Otwórz nowo utworzony plik Program.cs i usuń dowolny istniejący kod. Dodaj dyrektywy using dla Microsoft.Azure.Cosmosprzestrzeni nazw , Microsoft.Azure.Cosmos.LinqiMicrosoft.Azure.Cosmos.Spatial .

    using Microsoft.Azure.Cosmos;
    using Microsoft.Azure.Cosmos.Linq;
    using Microsoft.Azure.Cosmos.Spatial;
    
  7. Dodaj zmienną ciągu o nazwie *connectionString z parametrami połączenia zarejestrowanymi wcześniej w tym przewodniku.

    string connectionString = "<your-account-connection-string>"
    
  8. Utwórz nowe wystąpienie klasy przekazującej CosmosClientconnectionString i opakowujące ją w instrukcji using.

    using CosmosClient client = new (connectionString);
    
  9. Pobierz odwołanie do wcześniej utworzonego kontenera (cosmicworks/locations) na koncie usługi Azure Cosmos DB for NoSQL przy użyciu polecenia CosmosClient.GetDatabase , a następnie Database.GetContainer. Zapisz wynik w zmiennej o nazwie container.

    var container = client.GetDatabase("cosmicworks").GetContainer("locations");
    
  10. Zapisz plik Program.cs.

Dodawanie danych geoprzestrzennych

Zestaw .NET SDK zawiera wiele typów w Microsoft.Azure.Cosmos.Spatial przestrzeni nazw do reprezentowania typowych obiektów GeoJSON. Te typy usprawniają proces dodawania nowych informacji o lokalizacji do elementów w kontenerze.

  1. Utwórz nowy plik o nazwie Office.cs. W pliku dodaj dyrektywę using, a Microsoft.Azure.Cosmos.Spatial następnie utwórz typ rekorduOffice z następującymi właściwościami:

    Typ Opis Wartość domyślna
    id string Unikatowy identyfikator
    Nazwa string Nazwa biura
    Lokalizacji Point GeoJSON — punkt geograficzny
    Kategorii string Wartość klucza partycji business-office
    using Microsoft.Azure.Cosmos.Spatial;
    
    public record Office(
        string id,
        string name,
        Point location,
        string category = "business-office"
    );
    

    Uwaga

    Ten rekord zawiera Point właściwość reprezentującą określoną pozycję w formacie GeoJSON. Aby uzyskać więcej informacji, zobacz GeoJSON Point (Punkt GeoJSON).

  2. Utwórz kolejny nowy plik o nazwie Region.cs. Dodaj inny typ rekordu o nazwie Region z następującymi właściwościami:

    Typ Opis Wartość domyślna
    id string Unikatowy identyfikator
    Nazwa string Nazwa biura
    Lokalizacji Polygon Kształt geograficzny GeoJSON
    Kategorii string Wartość klucza partycji business-region
    using Microsoft.Azure.Cosmos.Spatial;
    
    public record Region(
        string id,
        string name,
        Polygon location,
        string category = "business-region"
    );
    

    Uwaga

    Ten rekord zawiera właściwość reprezentującą Polygon kształt składający się z linii rysowanych między wieloma lokalizacjami w formacie GeoJSON. Aby uzyskać więcej informacji, zobacz GeoJSON Polygon(GeoJSON Polygon).

  3. Utwórz kolejny nowy plik o nazwie Result.cs. Dodaj typ rekordu o nazwie Result z tymi dwiema właściwościami:

    Typ Opis
    Nazwa string Nazwa dopasowanego wyniku
    distanceKilometers decimal Odległość w kilometrach
    public record Result(
        string name,
        decimal distanceKilometers
    );
    
  4. Zapisz pliki Office.cs, Region.cs i Result.cs .

  5. Otwórz ponownie plik Program.cs .

  6. Utwórz nową Polygon w zmiennej o nazwie 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. Utwórz nową Region zmienną o nazwie mainCampusRegion przy użyciu wielokąta, unikatowego identyfikatora 1000i nazwy Main Campus.

    Region mainCampusRegion = new ("1000", "Main Campus", mainCampusPolygon);
    
  8. Użyj polecenia Container.UpsertItemAsync , aby dodać region do kontenera. Zapisz informacje o regionie w konsoli programu .

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

    Porada

    W tym przewodniku użyto operacji upsert zamiast wstawiania , aby można było uruchomić skrypt wielokrotnie bez powodowania konfliktu między unikatowymi identyfikatorami. Aby uzyskać więcej informacji na temat operacji upsert, zobacz tworzenie elementów.

  9. Utwórz nową Point zmienną o nazwie headquartersPoint. Użyj tej zmiennej, aby utworzyć nową Office zmienną o nazwie headquartersOffice przy użyciu punktu, unikatowego identyfikatora 0001i nazwy Headquarters.

    Point headquartersPoint = new (-122.12827, 47.63980);
    Office headquartersOffice = new ("0001", "Headquarters", headquartersPoint);
    
  10. Utwórz inną zmienną Point o nazwie researchPoint. Użyj tej zmiennej, aby utworzyć inną Office zmienną o nazwie researchOffice przy użyciu odpowiedniego punktu, unikatowego identyfikatora 0002i nazwy Research and Development.

    Point researchPoint = new (-96.84369, 46.81298);
    Office researchOffice = new ("0002", "Research and Development", researchPoint);
    
  11. Utwórz funkcję TransactionalBatch upsert obu Office zmiennych jako pojedynczej transakcji. Następnie zapisz informacje o obu biurach w konsoli.

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

    Uwaga

    Aby uzyskać więcej informacji na temat transakcji, zobacz operacje transakcyjne wsadowe.

  12. Zapisz plik Program.cs.

  13. Uruchom aplikację w terminalu przy użyciu polecenia dotnet run. Zwróć uwagę, że dane wyjściowe przebiegu aplikacji zawierają informacje o trzech nowo utworzonych elementach.

    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 }
    

Wykonywanie zapytań dotyczących danych geoprzestrzennych przy użyciu zapytania NoSQL

Typy w Microsoft.Azure.Cosmos.Spatial przestrzeni nazw mogą być używane jako dane wejściowe do zapytania sparametryzowanego NoSQL w celu używania wbudowanych funkcji, takich jak ST_DISTANCE.

  1. Otwórz plik Program.cs .

  2. Utwórz nową string zmienną o nazwie nosql z zapytaniem, która służy do mierzenia odległości między punktami.

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

    Porada

    To zapytanie umieszcza funkcję geoprzestrzenną w podquerii , aby uprościć proces ponownego użycia już obliczonej wartości wiele razy w SELECT klauzulach i WHERE .

  3. Utwórz nową QueryDefinition zmienną o nazwie query przy użyciu zmiennej nosqlString jako parametru. Następnie użyj QueryDefinition.WithParameter metody fluent wiele razy, aby dodać te parametry do zapytania:

    Wartość
    @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. Utwórz nowy iterator przy użyciu Container.GetItemQueryIterator<>typu Result ogólnego i zmiennej query . Następnie użyj kombinacji pętli while i foreach , aby iterować wszystkie wyniki na każdej stronie wyników. Dane wyjściowe każdego wyniku do konsoli.

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

    Uwaga

    Aby uzyskać więcej informacji na temat wyliczania wyników zapytania, zobacz elementy zapytania.

  5. Zapisz plik Program.cs.

  6. Uruchom ponownie aplikację w terminalu przy użyciu polecenia dotnet run. Zwróć uwagę, że dane wyjściowe zawierają teraz wyniki zapytania.

    dotnet run
    
    [DISTANCE KM]   Result { name = Headquarters, distanceKilometers = 3.34 }
    [DISTANCE KM]   Result { name = Research and Development, distanceKilometers = 1907.43 }
    

Wykonywanie zapytań dotyczących danych geoprzestrzennych przy użyciu LINQ

Funkcja LINQ to NoSQL w zestawie SDK platformy .NET obsługuje m.in. typy geoprzestrzenne w wyrażeniach zapytania. Ponadto zestaw SDK zawiera metody rozszerzeń mapowane na równoważne wbudowane funkcje:

Metoda rozszerzenia Wbudowana funkcja
Distance() ST_DISTANCE
Intersects() ST_INTERSECTS
IsValid() ST_ISVALID
IsValidDetailed() ST_ISVALIDDETAILED
Within() ST_WITHIN
  1. Otwórz plik Program.cs .

  2. Region Pobierz element z kontenera z unikatowym identyfikatorem 1000 i zapisz go w zmiennej o nazwie region.

    Region region = await container.ReadItemAsync<Region>("1000", new PartitionKey("business-region"));
    
  3. Container.GetItemLinqQueryable<> Użyj metody , aby uzyskać zapytanie LINQ i skompiluj zapytanie LINQ płynnie, wykonując następujące trzy akcje:

    1. Queryable.Where<> Użyj metody rozszerzenia, aby filtrować tylko elementy z równoważną wartością category"business-office".

    2. Użyj Queryable.Where<> ponownie, aby filtrować tylko lokalizacje we właściwości zmiennej locationregion przy użyciu polecenia Geometry.Within().

    3. Przetłumacz wyrażenie LINQ na iterator kanału informacyjnego przy użyciu polecenia CosmosLinqExtensions.ToFeedIterator<>.

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

    Ważne

    W tym przykładzie właściwość lokalizacji biura ma punkt, a właściwość lokalizacji regionu ma wielokąt. ST_WITHIN określa, czy punkt biura znajduje się w obrębie wielokąta regionu.

  4. Użyj kombinacji pętli while i foreach , aby iterować wszystkie wyniki na każdej stronie wyników. Dane wyjściowe każdego wyniku do konsoli.

    while (regionIterator.HasMoreResults)
    {
        var response = await regionIterator.ReadNextAsync();
        foreach (var office in response)
        {
            Console.WriteLine($"[IN REGION]\t{office}");
        }
    }
    
  5. Zapisz plik Program.cs.

  6. Uruchom aplikację po raz ostatni w terminalu przy użyciu polecenia dotnet run. Zwróć uwagę, że dane wyjściowe zawierają teraz wyniki drugiego zapytania opartego na linQ.

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

Czyszczenie zasobów

Usuń bazę danych po ukończeniu tego przewodnika.

  1. Otwórz terminal i utwórz zmienną powłoki dla nazwy konta i grupy zasobów.

    # Variable for resource group name
    resourceGroupName="<name-of-your-resource-group>"
    
    # Variable for account name
    accountName="<name-of-your-account>"
    
  2. Użyj az cosmosdb sql database delete polecenia , aby usunąć bazę danych.

    az cosmosdb sql database delete \
        --resource-group $resourceGroupName \
        --account-name $accountName \
        --name "cosmicworks"
    

Następne kroki