2009-11-25 20 views
10

Sé cómo reenviar páginas con datos SimpleDB utilizando NextToken. Sin embargo, ¿cómo maneja uno las páginas anteriores? Estoy en .NET, pero no creo que eso importe. Estoy más interesado en la estrategia general.¿Cómo hacer paginación con simpledb?

El seminario de Mike Culver An Introduction to Amazon SimpleDB menciona que se utilizan migas de pan, pero no las implementa en el video.

EDITAR: El video menciona un proyecto de muestra que implementa la paginación hacia atrás, pero el video termina antes de que se muestre la URL para la descarga. El proyecto de una muestra que encontré no se ocupó de la búsqueda.

Respuesta

11

Al pasar a la página siguiente, puede simplificar el caso de uso permitiendo solo una "página siguiente" y no paginación arbitraria. Puede hacer esto en SimpleDB mediante el uso de la cláusula LIMIT:

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

Usted ya sabe cómo manejar la nextToken, pero si se utiliza esta táctica, puede apoyar "página anterior" mediante el almacenamiento de la ruta de navegación de los siguientes fichas (por ejemplo, en la sesión web) y volver a emitir la consulta con un NextToken anterior en lugar de uno posterior.

Sin embargo, el caso general para manejar la paginación arbitraria en SimpleDB es el mismo para el anterior y el siguiente. En el caso general, el usuario puede hacer clic en un número de página arbitrario, como 5, sin haber visitado la página 4 o 6.

Puede manejar esto en SimpleDB utilizando el hecho de que NextToken solo requiere que la cláusula WHERE sea la lo mismo para trabajar correctamente. Entonces, en lugar de consultar cada página en secuencia, tirando de todos los elementos intermedios, generalmente puede hacerlo en dos pasos.

  1. Emita su consulta con un valor límite de donde debe comenzar la página deseada, y SELECCIONE la cuenta (*) en lugar de los atributos reales que desea.
  2. Usa el nextToken desde el paso uno a buscar a los datos de la página real utilizando los atributos deseados y el tamaño de página como el límite

Así que en pseudo código:

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

donde% 1 y% 2 son sustituciones de cadenas y "sdb.select()" es un método ficticio que incluye el código de sustitución de cadenas junto con la llamada SimpleDB.

Si puede o no lograr esto en dos llamadas a SimpleDB (como se muestra en el código) dependerá de la complejidad de su cláusula WHERE y del tamaño de su conjunto de datos. El código anterior se simplifica porque el resultado temporal puede haber devuelto un recuento parcial si la consulta tardó más de 5 segundos en ejecutarse. Realmente querrás poner esa línea en un bucle hasta que se llegue al recuento adecuado.Para que el código un poco más realista lo voy a poner dentro de los métodos y deshacerse de las sustituciones de Cuerda:

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

Esta es la idea general y sin control de errores, y funciona para cualquier página cualquiera, con exclusión de la página 1.

+1

¡Gracias por su minuciosa respuesta! – royco

+0

Excelente respuesta, gracias – theosp

+0

cuando ejecuto una declaración para contar hasta el límite, no obtengo un token al final de mis resultados (incluso cuando paso por tokens) ¿Me falta algo? –

1

Recuerdo que en uno de los webinars de la cartera marrón, se mencionaba de paso que los tokens podían reenviarse y que se obtenía el resultado correspondiente.

No lo he probado, y es solo una idea, pero ¿qué tal crear una lista de tokens a medida que avance? Para volver atrás, simplemente, recorra la lista al revés y vuelva a enviar el token (y seleccione la instrucción).

+0

Sí, esto funciona. Gracias. Me pregunto cuál es la mejor manera de almacenar la lista de tokens de migas de pan. – royco

+0

Creo que LinkedList sería una buena opción. Está doblemente vinculado, por lo que puede atravesar hacia adelante y hacia atrás. – Darryl

0

Estoy atascado en obtener el token - ¿Es eso lo mismo que RequestId?

La biblioteca PHP SimpleDB que estoy usando parece no devolverla. http://sourceforge.net/projects/php-sdb/

Encontrado esta documentación http://docs.amazonwebservices.com/AmazonSimpleDB/2009-04-15/DeveloperGuide/index.html?SDB_API_Select.html

lo que parece indicar que hay un elemento de nextToken, pero en la respuesta de muestra, se muestra RequestID ...

lo descubrió - lib nuestra era PHP de hecho, abstraemos el nexttoken de donde teníamos acceso. Excavó en la biblioteca y lo encontró.

0

He creado una versión de Java del muestreo propuesto anteriormente con la API oficial de SimpleDB, quizás esto sea útil para cualquiera.

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