Lavora con risultati impaginati: scansioni e interrogazioni - AWS SDK for Java 2.x

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 PageIterableoggetto, mentre una risposta restituita dal DynamoDbEnhancedClient 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 scanmetodo SDK's corrisponde all'operazione DynamoDB con lo stesso nome. Il DynamoDB Enhanced API Client offre le stesse opzioni ma utilizza un modello a oggetti familiare e gestisce l'impaginazione al posto tuo.

Innanzitutto, esploriamo l'PageIterableinterfaccia 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 per filtrare gli elementi restituiti. ProductCatalogÈ l'oggetto modello che è stato mostrato in precedenza.

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'PageIterableoggetto,pagedResults, viene restituito dal scan metodo. Il stream metodo di PageIterable restituisce un java.util.Streamoggetto, che è possibile utilizzare per elaborare le pagine. In questo esempio, il numero di pagine viene contato e registrato.

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'PageIterableinterfaccia offre diversi modi per elaborare i risultati grazie alle sue due interfacce principali: e. java.lang.IterableSdkIterable Iterableporta 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'PagePublisherinterfaccia 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'Subscriberistanza al subscribe metodo. Il primo esempio che segue mostra l'uso del subscribe metodo.

Il secondo subscribe metodo proviene dall'SdkPublisherinterfaccia. Questa versione di subscribe accetta a Consumeranziché aSubscriber. 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.SubscriberinterfacciaProductCatalogSubscriber, che sottoscrive la riga 4 PagePublisher dopo il commento.

L'Subscriberoggetto 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 CountDownLatchin the blocca il thread chiamante fino a quando tutti gli elementi non sono stati aggiunti all'elenco prima di continuare dopo la riga di commento 5.

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()metodo della DynamoDbTable classe trova gli elementi in base ai valori della chiave primaria. L'@DynamoDbPartitionKeyannotazione e l'@DynamoDbSortKeyannotazione 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 scanAPI, 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.

Queryesempi 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'movieattributo per la chiave di partizione e dall'actorattributo 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'actingawardattributo per la chiave di partizione e dall'attributo actingyearper 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 QueryConditionalistanze. QueryConditionalsfunzionano 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'keyEqualistanza 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 actingschoolnameriga di commento 2.

Dopo la riga di commento 3, l'esempio mostra l'QueryEnhancedRequestistanza che il codice passa al DynamoDbTable.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. QueryConditionali 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'}