2009-11-25 41 views
10

Tôi biết cách chuyển tiếp trang bằng dữ liệu SimpleDB bằng cách sử dụng NextToken. Tuy nhiên, làm thế nào chính xác một trong những xử lý các trang trước đó? Tôi đang trên .NET, nhưng tôi không nghĩ rằng vấn đề. Tôi quan tâm nhiều hơn đến chiến lược chung.Cách phân trang với simpledb?

An Introduction to Amazon SimpleDB hội thảo trên web của Mike Culver đề cập đến các mẩu bánh mì được sử dụng nhưng không triển khai chúng trong video.

EDIT: Video đề cập đến dự án mẫu triển khai phân trang ngược, nhưng video kết thúc trước khi URL có thể tải xuống có thể được hiển thị. Một dự án mẫu mà tôi tìm thấy đã không xử lý phân trang.

Trả lời

11

Khi truy cập trang tiếp theo, bạn có thể đơn giản hóa trường hợp sử dụng bằng cách chỉ cho phép "trang tiếp theo" và không phân trang tùy ý. Bạn có thể làm điều này trong SimpleDB bằng LIMIT khoản:

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

Bạn đã biết làm thế nào để xử lý các nextToken, nhưng nếu bạn sử dụng chiến thuật này, bạn có thể hỗ trợ "trang trước" bằng cách lưu trữ đường điều hướng các thẻ tiếp theo (ví dụ: trong phiên web) và cấp lại truy vấn bằng NextToken trước đó thay vì truy vấn tiếp theo.

Tuy nhiên, trường hợp chung để xử lý phân trang tùy ý trong SimpleDB là như nhau cho trước và sau. Trong trường hợp chung, người dùng có thể nhấp vào số trang tùy ý, như 5, mà không bao giờ truy cập trang 4 hoặc 6.

Bạn xử lý điều này trong SimpleDB bằng cách sử dụng thực tế là NextToken chỉ yêu cầu mệnh đề WHERE là cùng làm việc đúng cách. Vì vậy, thay vì truy vấn qua mỗi trang theo thứ tự kéo tất cả các mục can thiệp, bạn thường có thể thực hiện theo hai bước.

  1. Đưa ra truy vấn của bạn với giá trị giới hạn nơi trang mong muốn sẽ bắt đầu và chọn SELECT (*) thay vì thuộc tính thực tế bạn muốn.
  2. Sử dụng nextToken từ bước một để lấy dữ liệu trang thực tế bằng cách sử dụng các thuộc tính mong muốn và kích thước trang là LIMIT

Vì vậy, trong mã giả:

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

đâu% 1 và% 2 là String thay thế và "sdb.select()" là một phương pháp hư cấu bao gồm mã thay thế String cùng với cuộc gọi SimpleDB.

Có hay không bạn có thể thực hiện điều này trong hai cuộc gọi tới SimpleDB (như được hiển thị trong mã) sẽ phụ thuộc vào độ phức tạp của mệnh đề WHERE và kích thước của tập dữ liệu của bạn. Đoạn mã trên được đơn giản hóa trong đó kết quả tạm thời có thể đã trả về một phần đếm nếu truy vấn mất hơn 5 giây để chạy. Bạn thực sự muốn đặt dòng đó trong vòng lặp cho đến khi đạt được số lượng thích hợp.Để làm cho mã thực tế hơn một chút, tôi sẽ đặt nó trong các phương thức và loại bỏ các thay thế 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){...} 

Đây là ý tưởng chung mà không xử lý lỗi và hoạt động cho bất kỳ trang tùy ý nào, ngoại trừ trang 1.

+1

Cảm ơn bạn đã phản hồi kỹ lưỡng! – royco

+0

Câu trả lời tuyệt vời, cảm ơn – theosp

+0

khi tôi chạy một tuyên bố để đếm đến giới hạn, tôi không nhận được mã thông báo ở cuối kết quả của mình (ngay cả khi tôi lặp lại mã thông báo) Tôi có thiếu gì đó không? –

1

Tôi nhớ rằng trong một trong các hội thảo trên web túi màu nâu, nó được đề cập đến khi thông báo rằng các thẻ có thể được gửi lại và bạn sẽ nhận được kết quả tương ứng được đặt lại.

Tôi chưa thử nó, và nó chỉ là một ý tưởng, nhưng làm thế nào để xây dựng một danh sách các thẻ khi bạn đang phân trang về phía trước? Để quay trở lại, sau đó, chỉ cần duyệt qua danh sách ngược lại và gửi lại mã thông báo (và chọn câu lệnh).

+0

Có, tính năng này hoạt động. Cảm ơn. Tôi tự hỏi cách tốt nhất để lưu trữ danh sách các mã thông báo đường dẫn. – royco

+0

Tôi nghĩ rằng LinkedList sẽ là một lựa chọn tốt. Nó được liên kết gấp đôi, vì vậy bạn có thể di chuyển về phía trước và ngược. – Darryl

0

Tôi bị kẹt khi nhận mã thông báo - đó có phải là điều tương tự như RequestId không?

Thư viện PHP SimpleDB mà tôi đang sử dụng dường như không trả lại. http://sourceforge.net/projects/php-sdb/

Tìm thấy tài liệu này http://docs.amazonwebservices.com/AmazonSimpleDB/2009-04-15/DeveloperGuide/index.html?SDB_API_Select.html

mà dường như chỉ ra rằng có một yếu tố nextToken, nhưng trong phản ứng mẫu, nó cho thấy RequestId ...

đặn nó ra - PHP lib của chúng tôi là thực sự trừu tượng các nexttoken đi từ nơi chúng tôi đã truy cập vào nó. Đào vào thư viện và tìm thấy nó.

0

Tôi đã tạo phiên bản Java của mẫu được đề xuất ở trên với API SimpleDB chính thức, có thể điều này rất hữu ích cho bất kỳ ai.

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