如何使用 simpledb 进行分页?

发布于 2024-08-12 00:44:32 字数 346 浏览 5 评论 0原文

我知道如何使用 NextToken 向前翻页 SimpleDB 数据。然而,究竟如何处理前一页呢?我在 .NET 上,但我认为这并不重要。我对总体策略更感兴趣。

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

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

I know how to page forward with SimpleDB data by using NextToken. However, how exactly does one handle previous pages? I'm on .NET, but I don't think that matters. I'm more interested in the general strategy.

Mike Culver's An Introduction to Amazon SimpleDB webinar mentions that breadcrumbs are used, but he doesn't implement them in the video.

EDIT: The video mentions a sample project which implements backwards paging, but the video ends before the URL for the download can be displayed. The one sample project I found didn't deal with paging.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

捎一片雪花 2024-08-19 00:44:32

当转到下一页时,您可以通过仅允许“下一页”而不是任意分页来简化用例。您可以在 SimpleDB 中使用 LIMIT 子句来做到这一点:

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

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

然而,在 SimpleDB 中处理任意分页的一般情况对于上一个和下一个是相同的。在一般情况下,用户可以单击任意页码,例如 5,而无需访问第 4 或 6 页。

您可以在 SimpleDB 中处理此问题,因为 NextToken 只需要 WHERE 子句相同即可正常工作。因此,您通常可以分两步完成,而不是按顺序查询每个页面并下拉所有中间项。

  1. 使用所需页面开始位置的限制值发出查询,并选择 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 页。

When going to the next page you may be able to simplify the use case by only allowing a "next page" and not arbitrary paging. You can do this in SimpleDB by using the LIMIT clause:

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

You already know how to handle the NextToken, but if you use this tactic, you can support "previous page" by storing the breadcrumb trail of next tokens (e.g. in the web session) and re-issuing the query with a previous NextToken rather than a subsequent one.

However, the general case for handling arbitrary pagination in SimpleDB is the same for previous and next. In the general case, the user may click on an arbitrary page number, like 5, without ever having visited page 4 or 6.

You handle this in SimpleDB by using the fact that NextToken only requires the WHERE clause to be the same to work properly. So rather than querying through every page in sequence pulling down all the intervening items, you can usually do it in two steps.

  1. Issue your query with a limit value of where the desired page should start, and SELECT count(*) instead of the actual attributes you want.
  2. Use the NextToken from step one to fetch the actual page data using the desired attributes and the page size as the LIMIT

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

Where %1 and %2 are String substitutions and "sdb.select()" is a fictitious method that includes the String substitution code along with the SimpleDB call.

Whether or not you can accomplish this in two calls to SimpleDB (as shown in the code) will depend on the complexity of your WHERE clause and the size of your data set. The above code is simplified in that the temp result may have returned a partial count if the query took more than 5 seconds to run. You would really want to put that line in a loop until the proper count is reached. To make the code a little more realistic I'll put it within methods and get rid of the String substitutions:

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

This is the general idea without error handling, and works for any arbitrary page, excluding page 1.

海未深 2024-08-19 00:44:32

我记得在一次棕色包网络研讨会中,顺便提到可以重新提交令牌,并且您会得到相应的结果集。

我还没有尝试过,这只是一个想法,但是当您向前翻页时构建一个标记列表怎么样?然后,要返回,只需向后遍历列表并重新提交令牌(和 select 语句)。

I recall that in one of the brown bag webinars, it was mentioned in passing that the tokens could be resubmitted and you'd get the corresponding result set back.

I haven't tried it, and it is just an idea, but how about building a list of the tokens as you are paging forward? To go back, then, just traverse the list backwards and resubmit the token (and select statement).

长伴 2024-08-19 00:44:32

我一直在获取令牌 - 这与 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 确实将 nexttoken 从我们可以访问的地方抽象出来。翻遍图书馆,找到了。

i'm stuck at getting the token - is that the same thing as RequestId?

The PHP SimpleDB library that i'm using doesn't seem to return it.
http://sourceforge.net/projects/php-sdb/

Found this documentation
http://docs.amazonwebservices.com/AmazonSimpleDB/2009-04-15/DeveloperGuide/index.html?SDB_API_Select.html

which seems to indicate that there is a nextToken element, but in the sample response, it shows RequestId...

Figured it out - our PHP lib was indeed abstracting the nexttoken away from where we had access to it. Dug into the library and found it.

埋葬我深情 2024-08-19 00:44:32

我已经使用官方 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;
}

I have created a Java version of the sampling proposed above with the official SimpleDB API, maybe this is useful for anybody.

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文