Arbeiten Sie mit paginierten Ergebnissen: Scans und Abfragen - AWS SDK for Java 2.x

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

Arbeiten Sie mit paginierten Ergebnissen: Scans und Abfragen

Die scan batch Methoden query und der DynamoDB Enhanced Client API geben Antworten mit einer oder mehreren Seiten zurück. Eine Seite enthält ein oder mehrere Elemente. Ihr Code kann die Antwort pro Seite oder einzelne Elemente verarbeiten.

Eine vom synchronen Client zurückgegebene paginierte Antwort gibt ein PageIterableObjekt zurück, wohingegen eine vom asynchronen DynamoDbEnhancedClient DynamoDbEnhancedAsyncClient Client zurückgegebene Antwort ein Objekt zurückgibt. PagePublisher

Dieser Abschnitt befasst sich mit der Verarbeitung paginierter Ergebnisse und enthält Beispiele für die Verwendung von Scan und Abfrage. APIs

Scannen einer Tabelle

Die scanMethode des SDK entspricht der gleichnamigen DynamoDB-Operation. Die DynamoDB Enhanced Client API bietet dieselben Optionen, verwendet jedoch ein vertrautes Objektmodell und übernimmt die Paginierung für Sie.

Zunächst untersuchen wir die PageIterable Schnittstelle, indem wir uns die scan Methode der synchronen Mapping-Klasse ansehen,. DynamoDbTable

Verwenden Sie die synchrone API

Das folgende Beispiel zeigt die scan Methode, die einen Ausdruck verwendet, um die zurückgegebenen Elemente zu filtern. Das ProductCatalogist das Modellobjekt, das zuvor gezeigt wurde.

Der nach Kommentarzeile 2 angezeigte Filterausdruck beschränkt die Anzahl der zurückgegebenen ProductCatalog Artikel auf Artikel mit einem Preiswert zwischen 8,00 und 80,00 (einschließlich).

In diesem Beispiel werden auch die isbn Werte ausgeschlossen, indem die attributesToProject Methode verwendet wird, die nach Kommentarzeile 1 gezeigt wird.

Nach Kommentarzeile 3 wird das PageIterable Objekt,pagedResults, von der scan Methode zurückgegeben. Die stream Methode von PageIterable gibt ein java.util.StreamObjekt zurück, mit dem Sie die Seiten bearbeiten können. In diesem Beispiel wird die Anzahl der Seiten gezählt und protokolliert.

Beginnend mit Kommentarzeile 4 zeigt das Beispiel zwei Varianten des Zugriffs auf die ProductCatalog Elemente. Die Version nach der Kommentarzeile 4a durchläuft jede Seite und sortiert und protokolliert die Elemente auf jeder Seite. Die Version nach der Kommentarzeile 4b überspringt die Seiteniteration und greift direkt auf die Elemente zu.

Die PageIterable Schnittstelle bietet aufgrund ihrer beiden übergeordneten Schnittstellen — und — mehrere Möglichkeiten zur Verarbeitung von Ergebnissen. java.lang.IterableSdkIterable Iterablebringt forEach die spliterator Methoden iterator und und SdkIterable bringt die stream Methode.

public static void scanSync(DynamoDbTable<ProductCatalog> productCatalog) { Map<String, AttributeValue> expressionValues = Map.of( ":min_value", numberValue(8.00), ":max_value", numberValue(80.00)); ScanEnhancedRequest request = ScanEnhancedRequest.builder() .consistentRead(true) // 1. the 'attributesToProject()' method allows you to specify which values you want returned. .attributesToProject("id", "title", "authors", "price") // 2. Filter expression limits the items returned that match the provided criteria. .filterExpression(Expression.builder() .expression("price >= :min_value AND price <= :max_value") .expressionValues(expressionValues) .build()) .build(); // 3. A PageIterable object is returned by the scan method. PageIterable<ProductCatalog> pagedResults = productCatalog.scan(request); logger.info("page count: {}", pagedResults.stream().count()); // 4. Log the returned ProductCatalog items using two variations. // 4a. This version sorts and logs the items of each page. pagedResults.stream().forEach(p -> p.items().stream() .sorted(Comparator.comparing(ProductCatalog::price)) .forEach( item -> logger.info(item.toString()) )); // 4b. This version sorts and logs all items for all pages. pagedResults.items().stream() .sorted(Comparator.comparing(ProductCatalog::price)) .forEach( item -> logger.info(item.toString()) ); }

Verwenden Sie die asynchrone API

Die asynchrone scan Methode gibt Ergebnisse als PagePublisher Objekt zurück. Die PagePublisher Schnittstelle verfügt über zwei subscribe Methoden, mit denen Sie Antwortseiten verarbeiten können. Eine subscribe Methode stammt von der org.reactivestreams.Publisher übergeordneten Schnittstelle. Um Seiten mit dieser ersten Option zu verarbeiten, übergeben Sie der subscribe Methode eine Subscriber Instanz. Das erste Beispiel, das folgt, zeigt die Verwendung der subscribe Methode.

Die zweite subscribe Methode stammt von der SdkPublisherSchnittstelle. Diese Version von subscribe akzeptiert Consumereher a als Subscriber a. Diese subscribe Methodenvariante wird im zweiten Beispiel gezeigt, das folgt.

Das folgende Beispiel zeigt die asynchrone Version der scan Methode, die denselben Filterausdruck wie im vorherigen Beispiel verwendet.

Gibt nach Kommentarzeile 3 ein PagePublisher Objekt DynamoDbAsyncTable.scan zurück. In der nächsten Zeile erstellt der Code eine Instanz der org.reactivestreams.Subscriber SchnittstelleProductCatalogSubscriber, die die vierte Zeile PagePublisher nach dem Kommentar abonniert.

Das Subscriber Objekt sammelt die ProductCatalog Elemente von jeder Seite in der onNext Methode nach der Kommentarzeile 8 im ProductCatalogSubscriber Klassenbeispiel. Die Elemente werden in der privaten List Variablen gespeichert und im aufrufenden Code mit der ProductCatalogSubscriber.getSubscribedItems() Methode aufgerufen. Dies wird nach Kommentarzeile 5 aufgerufen.

Nachdem die Liste abgerufen wurde, sortiert der Code alle ProductCatalog Artikel nach Preis und protokolliert jeden Artikel.

Die Klasse CountDownLatchin der ProductCatalogSubscriber Klasse blockiert den aufrufenden Thread, bis alle Elemente zur Liste hinzugefügt wurden, bevor sie nach Kommentarzeile 5 weitermacht.

public static void scanAsync(DynamoDbAsyncTable productCatalog) { ScanEnhancedRequest request = ScanEnhancedRequest.builder() .consistentRead(true) .attributesToProject("id", "title", "authors", "price") .filterExpression(Expression.builder() // 1. :min_value and :max_value are placeholders for the values provided by the map .expression("price >= :min_value AND price <= :max_value") // 2. Two values are needed for the expression and each is supplied as a map entry. .expressionValues( Map.of( ":min_value", numberValue(8.00), ":max_value", numberValue(400_000.00))) .build()) .build(); // 3. A PagePublisher object is returned by the scan method. PagePublisher<ProductCatalog> pagePublisher = productCatalog.scan(request); ProductCatalogSubscriber subscriber = new ProductCatalogSubscriber(); // 4. Subscribe the ProductCatalogSubscriber to the PagePublisher. pagePublisher.subscribe(subscriber); // 5. Retrieve all collected ProductCatalog items accumulated by the subscriber. subscriber.getSubscribedItems().stream() .sorted(Comparator.comparing(ProductCatalog::price)) .forEach(item -> logger.info(item.toString())); // 6. Use a Consumer to work through each page. pagePublisher.subscribe(page -> page .items().stream() .sorted(Comparator.comparing(ProductCatalog::price)) .forEach(item -> logger.info(item.toString()))) .join(); // If needed, blocks the subscribe() method thread until it is finished processing. // 7. Use a Consumer to work through each ProductCatalog item. pagePublisher.items() .subscribe(product -> logger.info(product.toString())) .exceptionally(failure -> { logger.error("ERROR - ", failure); return null; }) .join(); // If needed, blocks the subscribe() method thread until it is finished processing. }
private static class ProductCatalogSubscriber implements Subscriber<Page<ProductCatalog>> { private CountDownLatch latch = new CountDownLatch(1); private Subscription subscription; private List<ProductCatalog> itemsFromAllPages = new ArrayList<>(); @Override public void onSubscribe(Subscription sub) { subscription = sub; subscription.request(1L); try { latch.await(); // Called by main thread blocking it until latch is released. } catch (InterruptedException e) { throw new RuntimeException(e); } } @Override public void onNext(Page<ProductCatalog> productCatalogPage) { // 8. Collect all the ProductCatalog instances in the page, then ask the publisher for one more page. itemsFromAllPages.addAll(productCatalogPage.items()); subscription.request(1L); } @Override public void onError(Throwable throwable) { } @Override public void onComplete() { latch.countDown(); // Call by subscription thread; latch releases. } List<ProductCatalog> getSubscribedItems() { return this.itemsFromAllPages; } }

Das folgende Codefragmentbeispiel verwendet die Version der PagePublisher.subscribe Methode, die eine Eingabe Consumer nach der Kommentarzeile 6 akzeptiert. Der Java-Lambda-Parameter verbraucht Seiten, die jedes Element weiterverarbeiten. In diesem Beispiel wird jede Seite verarbeitet und die Elemente auf jeder Seite werden sortiert und anschließend protokolliert.

// 6. Use a Consumer to work through each page. pagePublisher.subscribe(page -> page .items().stream() .sorted(Comparator.comparing(ProductCatalog::price)) .forEach(item -> logger.info(item.toString()))) .join(); // If needed, blocks the subscribe() method thread until it is finished processing.

Die items Methode von PagePublisher entpackt die Modellinstanzen, sodass Ihr Code die Elemente direkt verarbeiten kann. Dieser Ansatz wird im folgenden Snippet gezeigt.

// 7. Use a Consumer to work through each ProductCatalog item. pagePublisher.items() .subscribe(product -> logger.info(product.toString())) .exceptionally(failure -> { logger.error("ERROR - ", failure); return null; }) .join(); // If needed, blocks the subscribe() method thread until it is finished processing.

Tabellen abfragen

Sie können den DynamoDB Enhanced Client verwenden, um Ihre Tabelle abzufragen und mehrere Elemente abzurufen, die bestimmten Kriterien entsprechen. Die query()Methode findet Elemente auf der Grundlage von Primärschlüsselwerten anhand der @DynamoDbPartitionKey und optionaler @DynamoDbSortKey Anmerkungen, die in Ihrer Datenklasse definiert sind.

Die query() Methode erfordert einen Partitionsschlüsselwert und akzeptiert optional Sortierschlüsselbedingungen, um die Ergebnisse weiter zu verfeinern. Wie die scan API geben Abfragen a PageIterable für synchrone Aufrufe und a PagePublisher für asynchrone Aufrufe zurück.

QueryBeispiele für Methoden

Das folgende query() Methodencodebeispiel verwendet die MovieActor Klasse. Die Datenklasse definiert einen zusammengesetzten Primärschlüssel, der aus dem movieAttribut als Partitionsschlüssel und dem actorAttribut als Sortierschlüssel besteht.

package org.example.tests.model; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbAttribute; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondarySortKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey; import java.util.Objects; @DynamoDbBean public class MovieActor implements Comparable<MovieActor> { private String movieName; private String actorName; private String actingAward; private Integer actingYear; private String actingSchoolName; @DynamoDbPartitionKey @DynamoDbAttribute("movie") public String getMovieName() { return movieName; } public void setMovieName(String movieName) { this.movieName = movieName; } @DynamoDbSortKey @DynamoDbAttribute("actor") public String getActorName() { return actorName; } public void setActorName(String actorName) { this.actorName = actorName; } @DynamoDbSecondaryPartitionKey(indexNames = "acting_award_year") @DynamoDbAttribute("actingaward") public String getActingAward() { return actingAward; } public void setActingAward(String actingAward) { this.actingAward = actingAward; } @DynamoDbSecondarySortKey(indexNames = {"acting_award_year", "movie_year"}) @DynamoDbAttribute("actingyear") public Integer getActingYear() { return actingYear; } public void setActingYear(Integer actingYear) { this.actingYear = actingYear; } @DynamoDbAttribute("actingschoolname") public String getActingSchoolName() { return actingSchoolName; } public void setActingSchoolName(String actingSchoolName) { this.actingSchoolName = actingSchoolName; } @Override public String toString() { final StringBuffer sb = new StringBuffer("MovieActor{"); sb.append("movieName='").append(movieName).append('\''); sb.append(", actorName='").append(actorName).append('\''); sb.append(", actingAward='").append(actingAward).append('\''); sb.append(", actingYear=").append(actingYear); sb.append(", actingSchoolName='").append(actingSchoolName).append('\''); sb.append('}'); return sb.toString(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MovieActor that = (MovieActor) o; return Objects.equals(movieName, that.movieName) && Objects.equals(actorName, that.actorName) && Objects.equals(actingAward, that.actingAward) && Objects.equals(actingYear, that.actingYear) && Objects.equals(actingSchoolName, that.actingSchoolName); } @Override public int hashCode() { return Objects.hash(movieName, actorName, actingAward, actingYear, actingSchoolName); } @Override public int compareTo(MovieActor o) { if (this.movieName.compareTo(o.movieName) != 0){ return this.movieName.compareTo(o.movieName); } else { return this.actorName.compareTo(o.actorName); } } }

In den folgenden Codebeispielen werden die folgenden Elemente abgefragt.

MovieActor{movieName='movie01', actorName='actor0', actingAward='actingaward0', actingYear=2001, actingSchoolName='null'} MovieActor{movieName='movie01', actorName='actor1', actingAward='actingaward1', actingYear=2001, actingSchoolName='actingschool1'} MovieActor{movieName='movie01', actorName='actor2', actingAward='actingaward2', actingYear=2001, actingSchoolName='actingschool2'} MovieActor{movieName='movie01', actorName='actor3', actingAward='actingaward3', actingYear=2001, actingSchoolName='null'} MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'} MovieActor{movieName='movie02', actorName='actor0', actingAward='actingaward0', actingYear=2002, actingSchoolName='null'} MovieActor{movieName='movie02', actorName='actor1', actingAward='actingaward1', actingYear=2002, actingSchoolName='actingschool1'} MovieActor{movieName='movie02', actorName='actor2', actingAward='actingaward2', actingYear=2002, actingSchoolName='actingschool2'} MovieActor{movieName='movie02', actorName='actor3', actingAward='actingaward3', actingYear=2002, actingSchoolName='null'} MovieActor{movieName='movie02', actorName='actor4', actingAward='actingaward4', actingYear=2002, actingSchoolName='actingschool4'} MovieActor{movieName='movie03', actorName='actor0', actingAward='actingaward0', actingYear=2003, actingSchoolName='null'} MovieActor{movieName='movie03', actorName='actor1', actingAward='actingaward1', actingYear=2003, actingSchoolName='actingschool1'} MovieActor{movieName='movie03', actorName='actor2', actingAward='actingaward2', actingYear=2003, actingSchoolName='actingschool2'} MovieActor{movieName='movie03', actorName='actor3', actingAward='actingaward3', actingYear=2003, actingSchoolName='null'} MovieActor{movieName='movie03', actorName='actor4', actingAward='actingaward4', actingYear=2003, actingSchoolName='actingschool4'}

Der folgende Code definiert zwei QueryConditional Instanzen: keyEqual (nach Kommentarzeile 1) und sortGreaterThanOrEqualTo (nach Kommentarzeile 1a).

Fragen Sie Elemente anhand des Partitionsschlüssels ab

Die keyEqual Instanz gleicht Elementen mit einem Partitionsschlüsselwert von ab movie01.

In diesem Beispiel wird auch ein Filterausdruck nach Kommentarzeile 2 definiert, der alle Elemente herausfiltert, die keinen actingschoolnameWert haben.

Der QueryEnhancedRequest kombiniert die Schlüsselbedingung und den Filterausdruck für die Abfrage.

public static void query(DynamoDbTable movieActorTable) { // 1. Define a QueryConditional instance to return items matching a partition value. QueryConditional keyEqual = QueryConditional.keyEqualTo(b -> b.partitionValue("movie01")); // 1a. Define a QueryConditional that adds a sort key criteria to the partition value criteria. QueryConditional sortGreaterThanOrEqualTo = QueryConditional.sortGreaterThanOrEqualTo(b -> b.partitionValue("movie01").sortValue("actor2")); // 2. Define a filter expression that filters out items whose attribute value is null. final Expression filterOutNoActingschoolname = Expression.builder().expression("attribute_exists(actingschoolname)").build(); // 3. Build the query request. QueryEnhancedRequest tableQuery = QueryEnhancedRequest.builder() .queryConditional(keyEqual) .filterExpression(filterOutNoActingschoolname) .build(); // 4. Perform the query using the "keyEqual" conditional and filter expression. PageIterable<MovieActor> pagedResults = movieActorTable.query(tableQuery); logger.info("page count: {}", pagedResults.stream().count()); // Log number of pages. pagedResults.items().stream() .sorted() .forEach( item -> logger.info(item.toString()) // Log the sorted list of items. );
Beispiel — Ausgabe unter Verwendung der keyEqual Abfragebedingung

Das Folgende ist die Ausgabe der Ausführung der Methode. In der Ausgabe werden Elemente mit dem movieName Wert movie01 und keine Elemente mit dem Wert actingSchoolName gleich angezeigt. null

2023-03-05 13:11:05 [main] INFO org.example.tests.QueryDemo:46 - page count: 1 2023-03-05 13:11:05 [main] INFO org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor1', actingAward='actingaward1', actingYear=2001, actingSchoolName='actingschool1'} 2023-03-05 13:11:05 [main] INFO org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor2', actingAward='actingaward2', actingYear=2001, actingSchoolName='actingschool2'} 2023-03-05 13:11:05 [main] INFO org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}

Fragen Sie Elemente nach Partitionsschlüssel und Sortierschlüssel ab

Der sortGreaterThanOrEqualTo QueryConditional verfeinert die Übereinstimmung mit den Partitionsschlüsseln (movie01), indem eine Sortierschlüsselbedingung für Werte hinzugefügt wird, die größer oder gleich actor2 sind.

QueryConditionalMethoden, die mit beginnen, sort erfordern, dass ein Partitionsschlüsselwert übereinstimmt und die Abfrage durch einen Vergleich auf der Grundlage des Sortierschlüsselwerts weiter verfeinert wird. Sortbedeutet im Methodennamen nicht, dass die Ergebnisse sortiert sind, sondern dass ein Sortierschlüsselwert für den Vergleich verwendet wird.

Im folgenden Snippet ändern wir die Abfrageanfrage, die zuvor nach Kommentarzeile 3 angezeigt wurde. Dieses Snippet ersetzt die Abfragebedingung „keyEqual“ durch die Abfragebedingung "sortGreaterThanOrEqualTo", die nach der Kommentarzeile 1a definiert wurde. Der folgende Code entfernt auch den Filterausdruck.

QueryEnhancedRequest tableQuery = QueryEnhancedRequest.builder() .queryConditional(sortGreaterThanOrEqualTo).build();
Beispiel — Ausgabe unter Verwendung der sortGreaterThanOrEqualTo Abfragebedingung

In der folgenden Ausgabe werden die Ergebnisse der Abfrage angezeigt. Die Abfrage gibt Elemente zurück, deren movieName Wert movie01 entspricht, und nur Elemente, deren actorName Wert größer oder gleich actor2 ist. Da wir den Filter entfernen, gibt die Abfrage Elemente zurück, die keinen Wert für das Attribut haben. actingSchoolName

2023-03-05 13:15:00 [main] INFO org.example.tests.QueryDemo:46 - page count: 1 2023-03-05 13:15:00 [main] INFO org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor2', actingAward='actingaward2', actingYear=2001, actingSchoolName='actingschool2'} 2023-03-05 13:15:00 [main] INFO org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor3', actingAward='actingaward3', actingYear=2001, actingSchoolName='null'} 2023-03-05 13:15:00 [main] INFO org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}