Udostępnij za pośrednictwem


Indeksowanie i wykonywanie zapytań dotyczących 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

  • Istniejące konto usługi Azure Cosmos DB for NoSQL.
    • Jeśli nie masz subskrypcji platformy Azure, wypróbuj bezpłatnie usługę Azure Cosmos DB dla noSQL.
    • Jeśli masz istniejącą subskrypcję platformy Azure, utwórz nowe konto usługi Azure Cosmos DB for NoSQL.
  • Najnowsza wersja platformy .NET.
  • Najnowsza wersja interfejsu wiersza polecenia platformy Azure.
    • Jeśli używasz instalacji lokalnej, zaloguj się do interfejsu az login wiersza polecenia platformy Azure przy użyciu polecenia .

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 "<resource-group-name>" \
        --account-name "<nosql-account-name>" \
        --name "cosmicworks" \
        --throughput 400
    
  4. Utwórz nowy plik JSON o nazwie index-policy.json i dodaj następujący obiekt JSON do pliku.

    {
      "indexingMode": "consistent",
      "automatic": true,
      "includedPaths": [
        {
          "path": "/*"
        }
      ],
      "excludedPaths": [
        {
          "path": "/\"_etag\"/?"
        }
      ],
      "spatialIndexes": [
        {
          "path": "/location/*",
          "types": [
            "Point",
            "Polygon"
          ]
        }
      ]
    }
    
  5. Użyj az cosmosdb sql container create polecenia , aby utworzyć nowy kontener o nazwie locations z ścieżką /regionklucza partycji .

    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. Na koniec pobierz punkt końcowy konta dla konta przy użyciu polecenia az cosmosdb show i zapytanie JMESPath.

    az cosmosdb show \
        --resource-group "<resource-group-name>" \
        --name "<nosql-account-name>" \
        --query "documentEndpoint"
    
  7. Zarejestruj punkt końcowy konta, ponieważ będzie to potrzebne w następnej sekcji.

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 Microsoft.Azure.Cosmos dotnet add package pakiet NuGet przy użyciu 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 pliku GeoJSON.

  4. Zaimportuj Azure.Identity pakiet NuGet.

    dotnet add package Azure.Identity --version 1.*
    
  5. Skompiluj projekt za dotnet build pomocą polecenia .

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

  7. 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;
    
  8. Dodaj kolejną dyrektywę Azure.Identity using dla przestrzeni nazw.

    using Azure.Identity;
    
  9. Utwórz nową zmienną o nazwie credential typu DefaultAzureCredential.

    DefaultAzureCredential credential = new();
    
  10. Utwórz zmienną ciągu o nazwie endpoint przy użyciu punktu końcowego konta usługi Azure Cosmos DB for NoSQL.

    string endpoint = "<nosql-account-endpoint>";
    
  11. Utwórz nowe wystąpienie klasy przekazującej CosmosClient connectionString i zawijając je w instrukcji using.

    using CosmosClient client = new (connectionString);
    
  12. 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");
    
  13. 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 rekordu Office z następującymi właściwościami:

    Type Opis Domyślna wartość
    id string Unikatowy identyfikator
    name string Nazwa biura
    location Point Punkt geograficzny GeoJSON
    kategoria 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 właściwość reprezentującą Point 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 z Region następującymi właściwościami:

    Type Opis Domyślna wartość
    id string Unikatowy identyfikator
    name string Nazwa biura
    location Polygon Kształt geograficzny w formacie GeoJSON
    kategoria 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.

  3. Utwórz kolejny nowy plik o nazwie Result.cs. Dodaj typ rekordu o nazwie o Result następujących dwóch właściwościach:

    Type opis
    name 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 Container.UpsertItemAsync polecenia , aby dodać region do kontenera. Zapisz informacje o regionie w konsoli programu .

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

    Napiwek

    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 wartość , TransactionalBatch aby upsert obie Office zmienne jako pojedyncza transakcja. Następnie zapisz informacje o obu biurach w konsoli programu .

    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ą służyć 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 jest używana w tej sekcji 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
    ";
    

    Napiwek

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

  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. Wyprowadź każdy wynik 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

Funkcje LINQ to NoSQL w zestawie SDK platformy .NET obsługują uwzględnienie typów geoprzestrzennych w wyrażeniach zapytań. 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 o odpowiedniku category "business-office".

    2. Użyj Queryable.Where<> ponownie, aby filtrować tylko do lokalizacji we właściwości zmiennej location region 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. Wyprowadź każdy wynik 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

Po ukończeniu tego przewodnika usuń bazę danych.

  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 polecenia az cosmosdb sql database delete , aby usunąć bazę danych.

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

Następne kroki