2009-11-25 62 views
10

我知道如何通過使用NextToken使用SimpleDB數據向前翻頁。但是,如何處理前一頁呢?我在.NET上,但我不認爲這很重要。我對總體戰略更感興趣。如何使用simpledb進行分頁?

邁克卡爾弗的An Introduction to Amazon SimpleDB網絡研討會提到使用麪包屑,但他沒有在視頻中實現它們。

編輯:視頻提到了一個示例項目,它實現了向後分頁,但視頻結束之前可以顯示下載的URL。我發現的一個示例項目沒有處理分頁。

回答

11

當進入下一頁時,您可以通過只允許「下一頁」而不是任意分頁來簡化用例。您可以通過使用LIMIT子句中的SimpleDB做到這一點:

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

你已經知道如何處理的nextToken,但如果你使用這個戰術,你可以通過將下一個令牌的導航條支持「前一頁」 (例如在網絡會話中),並重新發出帶有前一個NextToken的查詢,而不是後續的查詢。

但是,在SimpleDB中處理任意分頁的一般情況與上一個和下一個一樣。在一般情況下,用戶可以點擊一個任意頁碼,如5,而不必訪問第4頁或第6頁。

你在SimpleDB中處理這個問題的方法是使用NextToken只需要WHERE子句成爲同樣工作正常。因此,不是通過按順序查詢每個頁面來拉下所有干預項目,通常可以分兩步進行。

  1. 發出您的查詢與限制值的所需頁面應該開始,並選擇計數(*),而不是你想要的實際屬性。
  2. 使用的nextToken從步驟一個使用所需的屬性和頁大小爲LIMIT

所以在僞碼以提取實際的頁數據:

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

其中%1和%2是字符串替換,「sdb.select()」是一個虛構的方法,它包含字符串替換代碼和SimpleDB調用。

是否可以在SimpleDB的兩次調用中完成此操作(如代碼所示)將取決於WHERE子句的複雜性和數據集的大小。上面的代碼被簡化了,如果查詢花費了超過5秒的時間運行,臨時結果可能已經返回了部分計數。你真的想把這條線放在一個循環中,直到達到合適的數量。爲了使代碼更逼真我把它方法中,擺脫了字符串替換的:

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

這是沒有錯誤處理的總體思路,並適用於任意的頁面,但不包括第1頁。

+1

感謝您的全面迴應! – royco 2009-12-04 00:55:02

+0

很好的回答,謝謝 – theosp 2010-11-10 07:34:41

+0

當我運行一個語句來計算一個限制,我沒有得到一個令牌在我的結果結束(即使當我循環令牌)我錯過了什麼? – 2011-08-02 21:44:56

1

我記得在一個棕色袋子的網絡研討會上,順便提到這個標記可以重新提交,你會得到相應的結果集。

我還沒有嘗試過它,這只是一個想法,但是如何建立一個令牌列表,因爲你是向前分頁?如果要返回,則只需向後遍歷列表並重新提交該標記(並選擇語句)。

+0

是的,這有效。謝謝。我想知道什麼是存儲麪包屑令牌列表的最佳方式。 – royco 2009-12-04 00:56:00

+0

我會認爲LinkedList 將是一個不錯的選擇。它是雙重鏈接的,所以你可以前後移動。 – Darryl 2009-12-07 01:40:23

0

我被困在獲取令牌 - 是否與RequestId一樣?

我使用的PHP SimpleDB庫似乎沒有返回它。 http://sourceforge.net/projects/php-sdb/

發現這個文檔 http://docs.amazonwebservices.com/AmazonSimpleDB/2009-04-15/DeveloperGuide/index.html?SDB_API_Select.html

這似乎表明,存在的nextToken元素,但在樣本響應,它顯示的requestId ...

想通了 - 我們的PHP LIB是的確可以從我們可以訪問的地方抽取出來。挖進圖書館並找到它。

0

我已經使用官方的SimpleDB API創建了上述Java抽樣樣本,可能這對任何人都有用。

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