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 PageIterableDynamoDbEnhancedClient
DynamoDbEnhancedAsyncClient
Client zurückgegebene Antwort ein Objekt zurückgibt. PagePublisher
Dieser Abschnitt befasst sich mit der Verarbeitung paginierter Ergebnisse und enthält Beispiele, in denen die Scan- und Abfrage-APIs verwendet werden.
Scannen einer Tabelle
Die scan
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
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.Stream
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.Iterable
SdkIterable
Iterable
bringt 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 SdkPublishersubscribe
akzeptiert Consumer
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 CountDownLatchProductCatalogSubscriber
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 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.
Abfragen einer Tabelle
Die query()
DynamoDbTable
Klasse findet Elemente auf der Grundlage von Primärschlüsselwerten. Die @DynamoDbPartitionKey
Anmerkung und die optionale @DynamoDbSortKey
Anmerkung werden verwendet, um den Primärschlüssel für Ihre Datenklasse zu definieren.
Die query()
Methode erfordert einen Partitionsschlüsselwert, der Elemente findet, die dem angegebenen Wert entsprechen. Wenn Ihre Tabelle auch einen Sortierschlüssel definiert, können Sie Ihrer Abfrage einen Wert für diesen als zusätzliche Vergleichsbedingung hinzufügen, um die Ergebnisse zu optimieren.
Mit Ausnahme der Verarbeitung der Ergebnisse query()
funktionieren die synchronen und asynchronen Versionen von genauso. Wie bei der scan
API gibt die query
API a PageIterable
für einen synchronen Aufruf und a für einen asynchronen Aufruf zurück. PagePublisher
Wir haben die Verwendung von PageIterable
und PagePublisher
bereits im Abschnitt „Scannen“ besprochen.
Query
Beispiele für Methoden
Das folgende query()
Methodencodebeispiel verwendet die MovieActor
Klasse. Die Datenklasse definiert einen zusammengesetzten Primärschlüssel, der aus dem movie
Attribut für den Partitionsschlüssel und dem actor
Attribut für den Sortierschlüssel besteht.
Die Klasse signalisiert auch, dass sie einen globalen sekundären Index mit dem Namen verwendet acting_award_year
. Der zusammengesetzte Primärschlüssel des Indexes besteht aus dem actingaward
Attribut für den Partitionsschlüssel und dem actingyear
für den Sortierschlüssel. Wenn wir später in diesem Thema zeigen, wie Indizes erstellt und verwendet werden, beziehen wir uns auf den acting_award_year
Index.
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 QueryConditionalQueryConditionals
arbeiten mit Schlüsselwerten — entweder dem Partitionsschlüssel allein oder in Kombination mit dem Sortierschlüssel — und entsprechen den bedingten Schlüsselausdrücken der DynamoDB-Dienst-API. Nach der ersten Kommentarzeile definiert das Beispiel die keyEqual
Instanz, die Elementen mit dem Partitionswert von entspricht. movie01
In diesem Beispiel wird auch ein Filterausdruck definiert, der alle Elemente herausfiltert, für die nach Kommentarzeile 2 kein actingschoolname
On angegeben ist.
Nach Kommentarzeile 3 zeigt das Beispiel die QueryEnhancedRequestDynamoDbTable.query()
Methode übergibt. Dieses Objekt kombiniert die Schlüsselbedingung und den Filter, die das SDK verwendet, um die Anforderung an den DynamoDB-Dienst zu generieren.
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. 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. );
Das Folgende ist das Ergebnis 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'}
In der folgenden Variante der Abfrageanforderung, die zuvor nach Kommentarzeile 3 angezeigt wurde, ersetzt der Code die Variante durch die keyEqual
QueryConditional
Variante sortGreaterThanOrEqualTo
QueryConditional
, die nach der Kommentarzeile 1a definiert wurde. Der folgende Code entfernt auch den Filterausdruck.
QueryEnhancedRequest tableQuery = QueryEnhancedRequest.builder() .queryConditional(sortGreaterThanOrEqualTo)
Da diese Tabelle einen zusammengesetzten Primärschlüssel hat, benötigen alle QueryConditional
Instanzen einen Partitionsschlüsselwert. QueryConditional
Methoden, die mit beginnen, sort...
geben an, dass ein Sortierschlüssel erforderlich ist. Die Ergebnisse sind nicht sortiert.
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 der Filter entfernt wurde, 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'}