如何使用SimpleDB进行分页?

10

我知道如何使用NextToken在SimpleDB数据中向前翻页。但是,如何处理以前的页面呢?我正在.NET上工作,但我认为这并不重要。我更感兴趣的是一般策略。

Mike Culver的《Amazon SimpleDB简介》网络研讨会提到了面包屑导航的使用,但他在视频中没有实现它们。

编辑:视频提到了一个实现向后翻页的示例项目,但视频结束之前没有显示下载的URL。我找到的一个示例项目没有处理分页。

4个回答

11

当需要跳转到下一页时,您可以通过仅允许“下一页”而不是任意分页来简化使用情况。在SimpleDB中,您可以使用LIMIT子句来实现此目的:

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

你已经知道如何处理NextToken,但如果使用这种策略,可以通过存储下一个token的面包屑路径(例如在web会话中)并重新发出查询而不是后续查询来支持“上一页”。

然而,在SimpleDB中处理任意分页的一般情况与上一页和下一页相同。在一般情况下,用户可能会点击任意页面编号,例如5,而从未访问过第4或第6页。

您可以利用SimpleDB中NextToken只需要WHERE子句相同即可正常工作的特性来处理此问题。因此,您通常可以通过两个步骤完成操作,而不是按顺序查询每个页面并拉取所有介于它们之间的项目。

  1. 使用所需页面开始的限制值和SELECT count(*)而不是实际要求的属性来发出查询。
  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
感谢您的详细回复! - royco
当我运行一个计数到限制的语句时,我的结果末尾没有标记(即使我循环遍历标记)。我是不是漏了什么? - Chris Hayes
这是一个很好的答案,但它仅适用于向前分页,如果需要向后分页,该怎么做呢?比如说有多个页面的功能... - Abishek
晚了点,但无论如何...对于“返回”链接,您需要存储当前页面的查询,并在打开下一页时使用当前页面查询作为您的“返回”链接。 - Kluyg

1

我记得在其中一个“棕色袋午餐网络研讨会”中,曾经提到过可以重新提交令牌,然后您将获得相应的结果集。

我还没有尝试过,这只是一个想法,但是在向前分页时,如何构建令牌列表呢?那么,要返回,只需向后遍历列表并重新提交令牌(和选择语句)即可。


是的,这个可以。谢谢。我在想存储面包屑标记列表的最佳方法是什么。 - royco
我认为LinkedList<string>是一个不错的选择。它是双向链表,因此您可以向前和向后遍历。 - Darryl

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

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库确实将nexttoken抽象化到我们无法访问的地方。深入研究了该库并找到了它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接