Как сделать пейджинг с помощью simpleedb?

Я знаю, как пересылать страницы с данными SimpleDB, используя NextToken. Однако как точно обрабатывать предыдущие страницы? Я на .NET, но я не думаю, что это важно. Меня больше интересует общая стратегия.

Майк Калвер Введение в Amazon SimpleDB В веб-семинаре упоминается, что используются панировочные сухари, но он не реализует их в видео.

EDIT: видео упоминает пример проекта, который реализует обратный пейджинг, но видео заканчивается до того, как URL-адрес для загрузки может быть отображен. Один образец проекта, который я нашел, не касался пейджинга.

Ответ 1

При переходе на следующую страницу вы можете упростить вариант использования, разрешив только "следующую страницу", а не произвольный пейджинг. Вы можете сделать это в SimpleDB, используя предложение LIMIT:

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

Вы уже знаете, как обращаться с NextToken, но если вы используете эту тактику, вы можете поддержать "предыдущую страницу", сохранив тропку для следующих маркеров (например, в веб-сеансе) и повторно отправив запрос с предыдущей NextToken, а не следующий.

Однако общий случай обработки произвольной разбивки на страницы в SimpleDB одинаковый для предыдущего и следующего. В общем случае пользователь может щелкнуть по произвольному номеру страницы, например 5, без посещения страницы 4 или 6.

Вы обрабатываете это в SimpleDB, используя тот факт, что NextToken требует, чтобы условие WHERE было одинаковым для правильной работы. Поэтому вместо того, чтобы запрашивать каждую страницу в последовательности, отбрасывая все промежуточные элементы, вы обычно можете сделать это за два шага.

  • Задайте свой запрос с предельным значением, где должна начинаться требуемая страница, и SELECT count (*) вместо фактических атрибутов, которые вы хотите.
  • Используйте 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()" - это фиктивный метод, который включает в себя код замены String вместе с вызовом SimpleDB.

Можно ли выполнить это в двух вызовах SimpleDB (как показано в коде), будет зависеть от сложности вашего предложения WHERE и размера вашего набора данных. Вышеприведенный код упрощен тем, что результат temp может возвращать частичный счет, если запрос занял более 5 секунд для запуска. Вы действительно хотели бы поставить эту строку в цикле, пока не будет достигнут правильный счет. Чтобы сделать код более реалистичным, я поставлю его в методы и избавлюсь от подстановок 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){...}

Это общая идея без обработки ошибок и работает для любой произвольной страницы, исключая страницу 1.

Ответ 2

Я помню, что на одном из вебинаров с коричневой сумкой было упомянуто попутно, что токены могут быть повторно отправлены, и вы получите соответствующий результат.

Я не пробовал, и это всего лишь идея, но как насчет создания списка токенов, когда вы пейджинг вперед? Чтобы вернуться назад, просто перейдите в список назад и повторно подайте токен (и выберите оператор).

Ответ 3

Я застрял в получении токена - это то же самое, что и RequestId?

Библиотека PHP SimpleDB, которую я использую, кажется, не возвращает ее. http://sourceforge.net/projects/php-sdb/

Эта документация http://docs.amazonwebservices.com/AmazonSimpleDB/2009-04-15/DeveloperGuide/index.html?SDB_API_Select.html

который, как представляется, указывает, что существует следующий элемент Token, но в ответе образца он показывает RequestId...

Выяснилось, что наш PHP lib действительно абстрагировал nexttoken далеко от того, где у нас был доступ к нему. Вложил в библиотеку и нашел ее.

Ответ 4

Я создал Java-версию выборки, предложенную выше, с официальным SimpleDB API, возможно, это полезно для всех.

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