Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.
Travailler avec des résultats paginés : scans et requêtes
Les scan batch méthodes query et de l'API DynamoDB Enhanced Client renvoient des réponses contenant une ou plusieurs pages. Une page contient un ou plusieurs éléments. Votre code peut traiter la réponse page par page ou traiter des éléments individuels.
Une réponse paginée renvoyée par le DynamoDbEnhancedClient client synchrone renvoie un PageIterableDynamoDbEnhancedAsyncClient renvoie un objet. PagePublisher
Cette section traite du traitement des résultats paginés et fournit des exemples d'utilisation du scan et de la requête APIs.
Analyser une table
La scan
Tout d'abord, nous explorons l'PageIterableinterface en examinant la scan méthode de la classe de mappage synchrone, DynamoDbTable
Utiliser l'API synchrone
L'exemple suivant montre la scan méthode qui utilise une expression
L'expression de filtrage affichée après la ligne de commentaire 2 limite les ProductCatalog articles renvoyés à ceux dont le prix est compris entre 8,00 et 80,00€ inclusivement.
Cet exemple exclut également les isbn valeurs en utilisant la attributesToProject méthode indiquée après la ligne de commentaire 1.
Après la ligne de commentaire 3pagedResults, l'PageIterableobjet est renvoyé par la scan méthode. La stream méthode de PageIterable renvoi d'un java.util.Stream
À partir de la ligne de commentaire 4, l'exemple montre deux variantes d'accès aux ProductCatalog éléments. La version située après la ligne de commentaire 4a parcourt chaque page et trie et enregistre les éléments de chaque page. La version située après la ligne de commentaire 4b ignore l'itération de la page et accède directement aux éléments.
L'PageIterableinterface offre plusieurs manières de traiter les résultats grâce à ses deux interfaces parentes : java.lang.IterableSdkIterableIterableapporte les forEach spliterator méthodes, et SdkIterable apporte la stream méthode. iterator
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()) ); }
Utiliser l'API asynchrone
La scan méthode asynchrone renvoie les résultats sous forme d'PagePublisherobjet. L'PagePublisherinterface comporte deux subscribe méthodes que vous pouvez utiliser pour traiter les pages de réponse. L'une des subscribe méthodes provient de l'interface org.reactivestreams.Publisher parent. Pour traiter les pages à l'aide de cette première option, transmettez une Subscriber instance à la subscribe méthode. Le premier exemple qui suit montre l'utilisation de la subscribe méthode.
La deuxième subscribe méthode provient de l'SdkPublishersubscribe accepte un ConsumerSubscriber. Cette variante de subscribe méthode est illustrée dans le deuxième exemple qui suit.
L'exemple suivant montre la version asynchrone de la scan méthode qui utilise la même expression de filtre que dans l'exemple précédent.
Après la ligne de commentaire 3, DynamoDbAsyncTable.scan renvoie un PagePublisher objet. Sur la ligne suivante, le code crée une instance de l'org.reactivestreams.SubscriberinterfaceProductCatalogSubscriber, qui s'abonne à la ligne de commentaire 4 PagePublisher après.
L'Subscriberobjet collecte les ProductCatalog éléments de chaque page de la onNext méthode après la ligne de commentaire 8 dans l'exemple ProductCatalogSubscriber de classe. Les éléments sont stockés dans la List variable privée et sont accessibles dans le code d'appel avec la ProductCatalogSubscriber.getSubscribedItems() méthode. Ceci est appelé après la ligne de commentaire 5.
Une fois la liste récupérée, le code trie tous les ProductCatalog articles par prix et enregistre chaque article.
La ProductCatalogSubscriber classe CountDownLatch
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; } }
L'exemple d'extrait de code suivant utilise la version de la PagePublisher.subscribe méthode qui accepte une ligne de commentaire Consumer 6 après. Le paramètre Java lambda consomme des pages, qui traitent ensuite chaque élément. Dans cet exemple, chaque page est traitée et les éléments de chaque page sont triés puis enregistrés.
// 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.
La items méthode qui consiste à PagePublisher déballer les instances du modèle afin que votre code puisse traiter les éléments directement. Cette approche est illustrée dans l'extrait suivant.
// 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.
Interroger une table
Vous pouvez utiliser le client DynamoDB amélioré pour interroger votre table et récupérer plusieurs éléments répondant à des critères spécifiques. La query()@DynamoDbPartitionKeyaide @DynamoDbSortKey des annotations facultatives définies dans votre classe de données.
La query() méthode nécessite une valeur de clé de partition et accepte éventuellement les conditions de clé de tri pour affiner davantage les résultats. Comme l'scanAPI, les requêtes renvoient a PageIterable pour les appels synchrones et a PagePublisher pour les appels asynchrones.
Queryexemples de méthodes
L'exemple de code de query() méthode qui suit utilise la MovieActor classe. La classe de données définit une clé primaire composite composée de l'movieattribut en tant que clé de partition et de l'actorattribut en tant que clé de tri.
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); } } }
Les exemples de code qui suivent portent sur les éléments suivants.
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'}
Le code suivant définit deux QueryConditional instances : keyEqual (après la ligne de commentaire 1) et sortGreaterThanOrEqualTo (après la ligne de commentaire 1a).
Éléments de requête par clé de partition
L'keyEqualinstance fait correspondre les éléments dont la valeur de clé de partition est de movie01.
Cet exemple définit également une expression de filtre après la ligne de commentaire 2 qui filtre tout élément dépourvu de actingschoolnamevaleur.
QueryEnhancedRequestCombine la condition clé et l'expression du filtre pour la requête.
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. );
Exemple — Sortie utilisant la keyEqual requête conditionnelle
Voici le résultat de l'exécution de la méthode. La sortie affiche les éléments dont movieName la valeur est movie01 et n'affiche aucun élément dont la valeur est actingSchoolName égale à. 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'}
Rechercher des éléments par clé de partition et clé de tri
sortGreaterThanOrEqualToQueryConditionalAffine une correspondance de clé de partition (movie01) en ajoutant une condition de clé de tri pour les valeurs supérieures ou égales à actor2.
QueryConditionalles méthodessort nécessitent qu'une valeur de clé de partition corresponde et affinent davantage la requête par une comparaison basée sur la valeur de la clé de tri. Sortdans le nom de la méthode ne signifie pas que les résultats sont triés, mais qu'une valeur de clé de tri sera utilisée pour la comparaison.
Dans l'extrait suivant, nous modifions la demande de requête affichée précédemment après la ligne de commentaire 3. Cet extrait remplace la requête conditionnelle « KeyEqual » par la requête conditionnelle « sortGreaterThan OrEqualTo » définie après la ligne de commentaire 1a. Le code suivant supprime également l'expression du filtre.
QueryEnhancedRequest tableQuery = QueryEnhancedRequest.builder() .queryConditional(sortGreaterThanOrEqualTo).build();
Exemple — Sortie utilisant la sortGreaterThanOrEqualTo requête conditionnelle
La sortie suivante affiche les résultats de la requête. La requête renvoie les éléments dont movieName la valeur est égale à movie01 et uniquement les éléments dont actorName la valeur est supérieure ou égale à actor2. Comme nous supprimons le filtre, la requête renvoie des éléments qui n'ont aucune valeur pour l'actingSchoolNameattribut.
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'}