2009-11-25 4 views
10

Je sais comment paginer avec des données SimpleDB en utilisant NextToken. Cependant, comment gère-t-on exactement les pages précédentes? Je suis sur .NET, mais je ne pense pas que cela compte. Je suis plus intéressé par la stratégie générale. Le webinaire An Introduction to Amazon SimpleDB de Mike Culver mentionne que les fils d'Ariane sont utilisés, mais il ne les met pas en œuvre dans la vidéo.Comment faire du paging avec simpledb?

EDIT: La vidéo mentionne un exemple de projet qui implémente la pagination arrière, mais la vidéo se termine avant que l'URL du téléchargement puisse être affichée. Le seul exemple de projet que j'ai trouvé ne traitait pas de pagination.

Répondre

11

Lorsque vous allez à la page suivante, vous pourrez peut-être simplifier le cas d'utilisation en n'autorisant qu'une "page suivante" et non une pagination arbitraire. Vous pouvez le faire en SimpleDB en utilisant la clause LIMIT:

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

Vous savez déjà comment gérer la NextToken, mais si vous utilisez cette tactique, vous pouvez soutenir « page précédente » en stockant la piste de navigation des jetons suivants (par exemple dans la session Web) et réémettre la requête avec un précédent NextToken plutôt qu'un suivant.

Toutefois, le cas général de la gestion de la pagination arbitraire dans SimpleDB est le même pour le précédent et le suivant. Dans le cas général, l'utilisateur peut cliquer sur un numéro de page arbitraire, comme 5, sans jamais avoir visité la page 4 ou 6.

Vous manipulez ceci dans SimpleDB en utilisant le fait que NextToken exige seulement que la clause WHERE soit la même pour fonctionner correctement. Donc, plutôt que d'interroger toutes les pages en séquence en tirant sur tous les éléments intermédiaires, vous pouvez généralement le faire en deux étapes.

  1. Émettez votre requête avec une valeur limite d'où la page souhaitée devrait commencer, et SELECT count (*) au lieu des attributs réels que vous voulez.
  2. Utilisation du NextToken de la première étape pour aller chercher les données de pages réelles en utilisant les attributs désirés et la taille de page en tant que LIMIT

Ainsi, dans le pseudo-code:

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()); 

où% 1 et% 2 sont des substitutions de chaînes et "sdb.select()" est une méthode fictive qui inclut le code de substitution de chaîne avec l'appel SimpleDB. Si vous pouvez accomplir cela en deux appels à SimpleDB (comme indiqué dans le code) dépendra de la complexité de votre clause WHERE et de la taille de votre ensemble de données. Le code ci-dessus est simplifié en ce que le résultat temporaire peut avoir retourné un compte partiel si la requête a duré plus de 5 secondes. Vous voudriez vraiment mettre cette ligne dans une boucle jusqu'à ce que le compte correct soit atteint.Pour rendre le code un peu plus réaliste que je vais le mettre dans les méthodes et se débarrasser des substitutions de chaîne:

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){...} 

Telle est l'idée générale sans gestion des erreurs, et fonctionne pour une page quelconque, à l'exclusion page 1.

+1

Merci pour votre réponse complète! – royco

+0

Bonne réponse, merci – theosp

+0

quand je lance une déclaration pour compter jusqu'à une limite, je n'obtiens pas de jeton à la fin de mes résultats (même quand je boucle des jetons) Ai-je raté quelque chose? –

1

Je me souviens que dans l'un des webinaires sur les sacs bruns, il a été mentionné en passant que les jetons pouvaient être soumis de nouveau et que le résultat correspondant serait rétabli.

Je ne l'ai pas essayé, et c'est juste une idée, mais que diriez-vous de construire une liste des jetons pendant que vous paginez en avant? Pour revenir en arrière, alors, parcourez simplement la liste en arrière et soumettez à nouveau le jeton (et sélectionnez l'instruction).

+0

Oui, cela fonctionne. Merci. Je me demande quelle est la meilleure façon de stocker la liste des jetons de fil d'Ariane. – royco

+0

Je pense que LinkedList serait un bon choix. Il est doublement lié, donc vous pouvez traverser en avant et en arrière. – Darryl

0

Je suis bloqué à l'obtention du jeton - est-ce la même chose que RequestId?

La bibliothèque PHP SimpleDB que j'utilise ne semble pas le renvoyer. http://sourceforge.net/projects/php-sdb/

trouvé cette documentation http://docs.amazonwebservices.com/AmazonSimpleDB/2009-04-15/DeveloperGuide/index.html?SDB_API_Select.html

ce qui semble indiquer qu'il ya un élément nextToken, mais dans la réponse de l'échantillon, il montre RequestId ...

figured it out - notre lib PHP était En effet, abstraction du nexttoken loin de l'endroit où nous avions accès. Creusé dans la bibliothèque et l'a trouvé.

0

J'ai créé une version Java de l'échantillonnage proposé ci-dessus avec l'API SimpleDB officielle, ce qui est peut-être utile pour tout le monde.

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; 
}