Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.
Lavora con risultati impaginati: scansioni e interrogazioni
I scan
batch
metodi query
e del DynamoDB Enhanced API Client restituiscono risposte con una o più pagine. Una pagina contiene uno o più elementi. Il codice può elaborare la risposta per pagina o elaborare singoli elementi.
Una risposta impaginata restituita dal client sincrono restituisce un PageIterableDynamoDbEnhancedClient
client asincrono restituisce un oggetto. DynamoDbEnhancedAsyncClient
PagePublisher
Questa sezione esamina l'elaborazione dei risultati impaginati e fornisce esempi che utilizzano la scansione e l'interrogazione. APIs
Esegui la scansione di una tabella
Il scan
Innanzitutto, esploriamo l'PageIterable
interfaccia esaminando il scan
metodo della classe di mappatura sincrona,. DynamoDbTable
Usa il sincrono API
L'esempio seguente mostra il scan
metodo che utilizza un'espressione
L'espressione di filtro mostrata dopo la riga di commento 2 limita gli ProductCatalog
articoli che vengono restituiti a quelli con un valore di prezzo compreso tra 8,00 e 80,00 inclusi.
Questo esempio esclude anche isbn
i valori utilizzando il attributesToProject
metodo mostrato dopo la riga di commento 1.
Dopo la riga di commento 3, l'PageIterable
oggetto,pagedResults
, viene restituito dal scan
metodo. Il stream
metodo di PageIterable
restituisce un java.util.Stream
A partire dalla riga di commento 4, l'esempio mostra due varianti di accesso agli ProductCatalog
elementi. La versione successiva alla riga di commento 4a scorre attraverso ogni pagina e ordina e registra gli elementi su ogni pagina. La versione successiva alla riga di commento 4b salta l'iterazione della pagina e accede direttamente agli elementi.
L'PageIterable
interfaccia offre diversi modi per elaborare i risultati grazie alle sue due interfacce principali: e. java.lang.Iterable
SdkIterable
Iterable
porta i spliterator
metodiforEach
, iterator
e SdkIterable
porta il stream
metodo.
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()) ); }
Usa l'asincrono API
Il scan
metodo asincrono restituisce i risultati come oggetto. PagePublisher
L'PagePublisher
interfaccia dispone di due subscribe
metodi che è possibile utilizzare per elaborare le pagine di risposta. Un subscribe
metodo proviene dall'interfaccia org.reactivestreams.Publisher
principale. Per elaborare le pagine utilizzando questa prima opzione, passate un'Subscriber
istanza al subscribe
metodo. Il primo esempio che segue mostra l'uso del subscribe
metodo.
Il secondo subscribe
metodo proviene dall'SdkPublishersubscribe
accetta a Consumer
Subscriber
. Questa variazione di subscribe
metodo è illustrata nel secondo esempio che segue.
L'esempio seguente mostra la versione asincrona del scan
metodo che utilizza la stessa espressione di filtro mostrata nell'esempio precedente.
Dopo la riga di commento 3, DynamoDbAsyncTable.scan
restituisce un oggetto. PagePublisher
Nella riga successiva, il codice crea un'istanza dell'org.reactivestreams.Subscriber
interfacciaProductCatalogSubscriber
, che sottoscrive la riga 4 PagePublisher
dopo il commento.
L'Subscriber
oggetto raccoglie gli ProductCatalog
elementi da ogni pagina del onNext
metodo dopo la riga di commento 8 nell'esempio di ProductCatalogSubscriber
classe. Gli elementi sono memorizzati nella List
variabile privata e vi si accede nel codice di chiamata con il ProductCatalogSubscriber.getSubscribedItems()
metodo. Viene chiamato dopo la riga di commento 5.
Dopo aver recuperato l'elenco, il codice ordina tutti gli ProductCatalog
articoli in base al prezzo e registra ogni articolo.
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; } }
Il seguente esempio di frammento utilizza la versione del PagePublisher.subscribe
metodo che accetta una riga Consumer
successiva al commento 6. Il parametro Java lambda utilizza le pagine, che elaborano ulteriormente ogni elemento. In questo esempio, ogni pagina viene elaborata e gli elementi di ogni pagina vengono ordinati e quindi registrati.
// 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.
Il items
metodo di PagePublisher
decodifica le istanze del modello in modo che il codice possa elaborare direttamente gli elementi. Questo approccio è illustrato nel frammento seguente.
// 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.
Esecuzione di una query su una tabella
Il query()
DynamoDbTable
classe trova gli elementi in base ai valori della chiave primaria. L'@DynamoDbPartitionKey
annotazione e l'@DynamoDbSortKey
annotazione opzionale vengono utilizzate per definire la chiave primaria sulla classe di dati.
Il query()
metodo richiede un valore della chiave di partizione che trovi gli elementi che corrispondono al valore fornito. Se la tabella definisce anche una chiave di ordinamento, è possibile aggiungerne un valore alla query come condizione di confronto aggiuntiva per ottimizzare i risultati.
Fatta eccezione per l'elaborazione dei risultati, le versioni sincrona e asincrona di query()
funzionano allo stesso modo. Come nel caso di scan
API, query
API restituisce un PageIterable
per una chiamata sincrona e un per una chiamata asincrona. PagePublisher
Abbiamo discusso l'uso di PageIterable
e in PagePublisher
precedenza nella sezione di scansione.
Query
esempi di metodi
L'esempio di codice del query()
metodo che segue utilizza la MovieActor
classe. La classe di dati definisce una chiave primaria composita composta dall'movie
attributo per la chiave di partizione e dall'actor
attributo per la chiave di ordinamento.
La classe segnala inoltre che utilizza un indice secondario globale denominato acting_award_year
. La chiave primaria composita dell'indice è composta dall'actingaward
attributo per la chiave di partizione e dall'attributo actingyear
per la chiave di ordinamento. Più avanti in questo argomento, quando mostreremo come creare e utilizzare gli indici, faremo riferimento all'indice. acting_award_year
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); } } }
Gli esempi di codice che seguono eseguono una query sui seguenti elementi.
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'}
Il codice seguente definisce due QueryConditionalQueryConditionals
funzionano con i valori chiave, la sola chiave di partizione o in combinazione con la chiave di ordinamento, e corrispondono alle espressioni condizionali chiave del servizio DynamoDB. API Dopo la riga di commento 1, l'esempio definisce l'keyEqual
istanza che corrisponde agli elementi con un valore di partizione di. movie01
Questo esempio definisce anche un'espressione di filtro che filtra qualsiasi elemento che non è attivo dopo la actingschoolname
riga di commento 2.
Dopo la riga di commento 3, l'esempio mostra l'QueryEnhancedRequestDynamoDbTable.query()
metodo. Questo oggetto combina la condizione chiave e il filtro SDK utilizzati per generare la richiesta al servizio 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. );
Di seguito è riportato l'output dell'esecuzione del metodo. L'output mostra gli elementi con un movieName
valore di movie01 e non visualizza alcun elemento actingSchoolName
uguale a. 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'}
Nella seguente variante della richiesta di interrogazione mostrata in precedenza dopo la riga di commento 3, il codice sostituisce sortGreaterThanOrEqualTo
QueryConditional
quella definita dopo la riga di commento 1a. keyEqual
QueryConditional
Il codice seguente rimuove anche l'espressione del filtro.
QueryEnhancedRequest tableQuery = QueryEnhancedRequest.builder() .queryConditional(sortGreaterThanOrEqualTo)
Poiché questa tabella ha una chiave primaria composita, tutte le QueryConditional
istanze richiedono un valore di chiave di partizione. QueryConditional
i metodi che iniziano con sort...
indicano che è necessaria una chiave di ordinamento. I risultati non vengono ordinati.
L'output seguente mostra i risultati dell'interrogazione. La query restituisce gli elementi con un movieName
valore uguale a movie01 e solo gli elementi che hanno un actorName
valore maggiore o uguale a actor2. Poiché il filtro è stato rimosso, la query restituisce elementi che non hanno alcun valore per l'attributo. 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'}