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 du client DynamoDB Enhanced API 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êteAPIs.
Analyser une table
La scan
Tout d'abord, nous explorons l'PageIterable
interface en examinant la scan
méthode de la classe de mappage synchrone, DynamoDbTable
Utiliser le synchrone API
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'PageIterable
objet 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'PageIterable
interface offre plusieurs manières de traiter les résultats grâce à ses deux interfaces parentes : java.lang.Iterable
SdkIterable
Iterable
apporte 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'asynchrone API
La scan
méthode asynchrone renvoie les résultats sous forme d'PagePublisher
objet. L'PagePublisher
interface 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 Consumer
Subscriber
. 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.Subscriber
interfaceProductCatalogSubscriber
, qui s'abonne à la ligne de commentaire 4 PagePublisher
après.
L'Subscriber
objet 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
La query()
DynamoDbTable
classe trouve des éléments en fonction des valeurs des clés primaires. L'@DynamoDbPartitionKey
annotation et l'@DynamoDbSortKey
annotation facultative sont utilisées pour définir la clé primaire de votre classe de données.
La query()
méthode nécessite une valeur de clé de partition qui trouve les éléments correspondant à la valeur fournie. Si votre table définit également une clé de tri, vous pouvez ajouter une valeur à cette clé à votre requête en tant que condition de comparaison supplémentaire pour affiner les résultats.
À l'exception du traitement des résultats, les versions synchrone et asynchrone de query()
fonctionnent de la même manière. Comme pour le scan
API, le query
API renvoie un PageIterable
pour un appel synchrone et un PagePublisher
pour un appel asynchrone. Nous avons discuté de l'utilisation de PageIterable
et PagePublisher
précédemment dans la section de numérisation.
Query
exemples 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'movie
attribut de la clé de partition et de l'actor
attribut de la clé de tri.
La classe indique également qu'elle utilise un index secondaire global nommé acting_award_year
. La clé primaire composite de l'index est composée de l'actingaward
attribut de la clé de partition et de l'attribut actingyear
de la clé de tri. Plus loin dans cette rubrique, lorsque nous montrerons comment créer et utiliser des index, nous ferons référence à l'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); } } }
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 QueryConditionalQueryConditionals
fonctionne avec des valeurs clés (la clé de partition seule ou en combinaison avec la clé de tri) et correspondent aux principales expressions conditionnelles du service DynamoDB. API Après la ligne de commentaire 1, l'exemple définit l'keyEqual
instance qui correspond aux éléments dont la valeur de partition est de movie01
.
Cet exemple définit également une expression de filtre qui filtre tout élément non actingschoolname
activé après la ligne de commentaire 2.
Après la ligne de commentaire 3, l'exemple montre l'QueryEnhancedRequestDynamoDbTable.query()
méthode. Cet objet combine la condition clé et le filtre SDK utilisés pour générer la demande auprès du service DynamoDB.
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. );
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'}
Dans la variante de demande de requête suivante présentée précédemment après la ligne de commentaire 3, le code remplace le keyEqual
QueryConditional
par sortGreaterThanOrEqualTo
QueryConditional
celui qui a été défini après la ligne de commentaire 1a. Le code suivant supprime également l'expression du filtre.
QueryEnhancedRequest tableQuery = QueryEnhancedRequest.builder() .queryConditional(sortGreaterThanOrEqualTo)
Comme cette table possède une clé primaire composite, toutes les QueryConditional
instances nécessitent une valeur de clé de partition. QueryConditional
les méthodes qui commencent par sort...
indiquent qu'une clé de tri est requise. Les résultats ne sont pas triés.
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. Le filtre ayant été supprimé, la requête renvoie des éléments qui n'ont aucune valeur pour l'actingSchoolName
attribut.
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'}