2009-11-25 12 views
10

So come andare avanti con i dati SimpleDB usando NextToken. Tuttavia, come si gestisce esattamente le pagine precedenti? Sono su .NET, ma non penso che importi. Sono più interessato alla strategia generale.Come eseguire il paging con simpledb?

Il webinar di Mike Culver An Introduction to Amazon SimpleDB indica che i breadcrumb vengono utilizzati, ma non li implementa nel video.

MODIFICA: il video menziona un progetto di esempio che implementa il paging all'indietro, ma il video termina prima che possa essere visualizzato l'URL per il download. L'unico progetto di esempio che ho trovato non si occupava del paging.

risposta

11

Quando si passa alla pagina successiva è possibile semplificare il caso d'uso consentendo solo una "pagina successiva" e una paginazione non arbitraria. È possibile farlo in SimpleDB utilizzando la clausola LIMIT:

SELECT title, summary, votecount FROM posts WHERE userid = '000022656' LIMIT 25 

Sapete già come gestire la nextToken, ma se si utilizza questa tattica, è possibile sostenere "pagina precedente" memorizzando il filo di Arianna di prossimi gettoni (ad esempio nella sessione Web) e ri-emettere la query con un NextToken precedente anziché successivo.

Tuttavia, il caso generale per la gestione dell'impaginazione arbitraria in SimpleDB è lo stesso per precedente e successivo. Nel caso generale, l'utente può fare clic su un numero di pagina arbitrario, ad esempio 5, senza aver mai visitato la pagina 4 o 6.

Si gestisce questo in SimpleDB utilizzando il fatto che NextToken richiede solo la clausola WHERE per essere il lo stesso per funzionare correttamente. Quindi, piuttosto che interrogare ogni pagina in sequenza tirando giù tutti gli elementi intermedi, di solito è possibile farlo in due passaggi.

  1. Emettere la query con un valore limite di dove deve essere avviata la pagina desiderata e SELECT count (*) invece degli attributi effettivi desiderati.
  2. Utilizza la nextToken dal punto uno per recuperare i dati della pagina effettivi utilizzando gli attributi desiderati e la dimensione di pagina come limite

Così in pseudo-codice:

int targetPage, pageSize; 
... 
int jumpLimit = pageSize * (targetPage - 1); 
String query = "SELECT %1 FROM posts WHERE userid = '000022656' LIMIT %2"; 
String output = "title, summary, votecount"; 
Result temp = sdb.select(query, "count(*)", jumpLimit); 
Result data = sdb.select(query, output, pageSize, temp.getToken()); 

Dove% 1 e% 2 sono sostituzioni di stringhe e "sdb.select()" è un metodo fittizio che include il codice di sostituzione delle stringhe insieme alla chiamata SimpleDB.

Se è possibile eseguire questa operazione in due chiamate a SimpleDB (come mostrato nel codice), dipenderà dalla complessità della clausola WHERE e dalla dimensione del set di dati. Il codice sopra riportato è semplificato in quanto il risultato temporaneo potrebbe aver restituito un conteggio parziale se la query impiegava più di 5 secondi per essere eseguita. Si vorrebbe davvero mettere quella linea in un ciclo finché non si raggiunge il conteggio corretto.Per rendere il codice un po 'più realistico lo metterò all'interno dei metodi e sbarazzarsi delle sostituzioni String:

private Result fetchPage(String query, int targetPage) 
{ 
    int pageSize = extractLimitValue(query); 
    int skipLimit = pageSize * (targetPage - 1); 
    String token = skipAhead(query, skipLimit); 
    return sdb.select(query, token); 
} 

private String skipAhead(String query, int skipLimit) 
{ 
    String tempQuery = replaceClause(query, "SELECT", "count(*)"); 
    int accumulatedCount = 0; 
    String token = ""; 
    do { 
     int tempLimit = skipLimit - accumulatedCount; 
     tempQuery = replaceClause(tempQuery , "LIMIT", tempLimit + ""); 
     Result tempResult = sdb.select(query, token); 
     token = tempResult.getToken(); 
     accumulatedCount += tempResult.getCount(); 
    } while (accumulatedCount < skipLimit); 
    return token; 
} 

private int extractLimitValue(String query) {...} 
private String replaceClause(String query, String clause, String value){...} 

Questa è l'idea generale senza gestione degli errori, e funziona per qualsiasi pagina definita, escludendo Pagina 1.

+1

Grazie per la tua risposta completa! – royco

+0

Ottima risposta, grazie – theosp

+0

quando eseguo una dichiarazione per contare fino a un limite, non ottengo un token alla fine dei miei risultati (anche quando eseguo il loop dei token) Mi manca qualcosa? –

1

Ricordo che in uno dei webinar della borsa marrone, è stato menzionato di passaggio che i token potrebbero essere nuovamente inviati e si otterrebbe il corrispondente risultato arretrato.

Non l'ho provato, ed è solo un'idea, ma che ne dici di costruire un elenco di token mentre stai pagando in avanti? Per tornare indietro, quindi, attraversa la lista all'indietro e invia nuovamente il token (e seleziona l'istruzione).

+0

Sì, questo funziona. Grazie. Mi chiedo quale sia il modo migliore per memorizzare l'elenco dei token breadcrumb. – royco

+0

Penso che LinkedList sarebbe una buona scelta. È doppiamente collegato, quindi puoi andare avanti e indietro. – Darryl

0

Sono bloccato per ottenere il token - è la stessa cosa di RequestId?

La libreria PHP SimpleDB che sto utilizzando non sembra restituirla. http://sourceforge.net/projects/php-sdb/

Trovato questa documentazione http://docs.amazonwebservices.com/AmazonSimpleDB/2009-04-15/DeveloperGuide/index.html?SDB_API_Select.html

che sembra indicare che ci sia un elemento nextToken, ma nella risposta del campione, mostra IDRichiesta ...

capito - il nostro lib PHP era in effetti astrarre il prossimo traliccio dal punto in cui l'abbiamo avuto accesso. Scavato nella biblioteca e trovato.

0

Ho creato una versione Java del campionamento proposto sopra con l'API SimpleDB ufficiale, forse questo è utile per chiunque.

private static Set<String> getSdbAttributes(AmazonSimpleDBClient client, 
      String domainName, int sampleSize) { 
     if (!client.listDomains().getDomainNames().contains(domainName)) { 
     throw new IllegalArgumentException("SimpleDB domain '" + domainName 
       + "' not accessible from given client instance"); 
    } 

    int domainCount = client.domainMetadata(
      new DomainMetadataRequest(domainName)).getItemCount(); 
    if (domainCount < sampleSize) { 
     throw new IllegalArgumentException("SimpleDB domain '" + domainName 
       + "' does not have enough entries for accurate sampling."); 
    } 

    int avgSkipCount = domainCount/sampleSize; 
    int processedCount = 0; 
    String nextToken = null; 
    Set<String> attributeNames = new HashSet<String>(); 
    Random r = new Random(); 
    do { 
     int nextSkipCount = r.nextInt(avgSkipCount * 2) + 1; 

     SelectResult countResponse = client.select(new SelectRequest(
       "select count(*) from `" + domainName + "` limit " 
         + nextSkipCount).withNextToken(nextToken)); 

     nextToken = countResponse.getNextToken(); 

     processedCount += Integer.parseInt(countResponse.getItems().get(0) 
       .getAttributes().get(0).getValue()); 

     SelectResult getResponse = client.select(new SelectRequest(
       "select * from `" + domainName + "` limit 1") 
       .withNextToken(nextToken)); 

     nextToken = getResponse.getNextToken(); 

     processedCount++; 

     if (getResponse.getItems().size() > 0) { 
      for (Attribute a : getResponse.getItems().get(0) 
        .getAttributes()) { 
       attributeNames.add(a.getName()); 
      } 
     } 
    } while (domainCount > processedCount); 
    return attributeNames; 
} 
Problemi correlati