Статьи

Нумерация страниц с Couchbase

Если вам приходится иметь дело с большим количеством документов при выполнении запросов к кластеру Couchbase, важно использовать разбиение на страницы для получения строк за страницей. Вы можете найти некоторую информацию в документации в главе « Разбивка на страницы », но я хочу перейти к более подробной информации и образцу кода в этой статье.

Для этого примера я начну с создания простого представления на основе набора данных образца пива, которое используется для поиска пивоваренного завода по стране:

1
2
3
4
5
function (doc, meta) {
  if (doc.type == "brewery" && doc.country){
   emit(doc.country);
  }
}

В этом представлении перечислены все пивоваренные заводы по странам, индекс выглядит так:

ID документа ключ Значение
Берсальеры Аргентина значение NULL
cervecera_jerome Аргентина значение NULL
brouwerij_nacional_balashi Аруба значение NULL
australian_brewing_corporation Австралия значение NULL
carlton_and_united_breweries Австралия значение NULL
coopers_brewery Австралия значение NULL
foster_s_australia_ltd Австралия значение NULL
gold_coast_brewery Австралия значение NULL
lion_nathan_australia_hunter_street Австралия значение NULL
little_creatures_brewery Австралия значение NULL
malt_shovel_brewery Австралия значение NULL
matilda_bay_brewing Австралия значение NULL
yellowstone_valley_brewing Соединенные Штаты значение NULL
yuengling_son_brewing Соединенные Штаты значение NULL
zea_rotisserie_and_brewery Соединенные Штаты значение NULL
fosters_tien_gang Вьетнам значение NULL
hue_brewery Вьетнам значение NULL

Итак, теперь вы хотите перемещаться по этому индексу с размером страницы 5 строк.

Использование параметров пропуска / ограничения

Самый упрощенный подход – использовать лимит и пропустить параметры, например:

Страница 1:? Limit = 5 & skip0

Страница 2:? Limit = 5 & skip = 5

Страница x:? Limit = 5 & skip (limit * (page-1))

Очевидно, что вы можете использовать любые другие параметры, необходимые для выполнения запросов диапазона или ключа (startkey / endkey, key, keys) и опции сортировки (по убыванию).

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

Пример кода в Python, который разбит на страницы с использованием этого представления:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
from couchbase import Couchbase
cb = Couchbase.connect(bucket='beer-sample')
 
hasRow = True
rowPerPage = 5
page = 0
currentStartkey=""
startDocId=""
 
while hasRow :
  hasRow = False
    skip = 0 if page == 0 else 1
    page = page + 1
    print "-- Page %s --" % (page)
    rows = cb.query("test", "by_country", limit=rowPerPage, skip=skip, startkey=currentStartkey, startkey_docid=startDocId)
    for row in rows:
        hasRow = True
        print "Country: \"%s\" \t Id: '%s'" % (row.key, row.docid)
        currentStartkey = row.key
        startDocId = row.docid
    print " -- -- -- -- \n"

Это приложение зацикливается на всех страницах до конца индекса.

Как я уже говорил ранее, это не лучший подход, поскольку система должна считывать все значения, пока не будет достигнут пропуск. В следующем примере показан лучший способ справиться с этим.

Использование параметров startkey / startkey_docid

Чтобы сделать эту нумерацию страниц более эффективной, можно использовать другой подход. Этот подход использует startkey и startkey_docid для выбора правильных документов.

  • Параметр startkey будет значением ключа, с которого должен начинаться чтение запроса (на основе последнего ключа «предыдущей страницы»).
  • Поскольку для ключа, например «Германия», у вас может быть один или несколько идентификаторов (документов), необходимо указать обработчику запросов Couchbase, с чего начать, для этого вам нужно использовать параметр startkey_docid и игнорировать этот идентификатор, так как он является последняя из предыдущей страницы.

Так что, если мы посмотрим на индекс и добавим номер строки, чтобы объяснить нумерацию страниц

Номер строки ID документа ключ Значение
Запрос на страницу 1
? Предел = 5
1 Берсальеры Аргентина значение NULL
2 cervecera_jerome Аргентина значение NULL
3 brouwerij_nacional_balashi Аруба значение NULL
4 australian_brewing_corporation Австралия значение NULL
5 carlton_and_united_breweries Австралия значение NULL
Запрос на страницу 2
? Предел = 5 & StartKey =»Австралия» и startkey_docid = carlton_and_united_breweries и пропустить = 1
6 coopers_brewery Австралия значение NULL
7 foster_s_australia_ltd Австралия значение NULL
8 gold_coast_brewery Австралия значение NULL
9 lion_nathan_australia_hunter_street Австралия значение NULL
10 little_creatures_brewery Австралия значение NULL
Запрос на страницу 3

? Предел = 5 & StartKey =»Австралия» и startkey_docid = little_creatures_brewery и пропустить = 1
11 malt_shovel_brewery Австралия значение NULL
12 matilda_bay_brewing Австралия значение NULL
yellowstone_valley_brewing Соединенные Штаты значение NULL
yuengling_son_brewing Соединенные Штаты значение NULL
zea_rotisserie_and_brewery Соединенные Штаты значение NULL
fosters_tien_gang Вьетнам значение NULL
hue_brewery Вьетнам значение NULL

Как видно из приведенных выше примеров, запрос использует стартовую клавишу, идентификатор документа и просто передает его, используя skip = 1.

Давайте теперь посмотрим на код приложения, еще раз в Python

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
from couchbase import Couchbase
cb = Couchbase.connect(bucket='beer-sample')
 
hasRow = True
rowPerPage = 5
page = 0
currentStartkey=""
startDocId=""
 
while hasRow :
    hasRow = False
    skip = 0 if page == 0 else 1
    page = page + 1
    print "-- Page %s --" % (page)
    rows = cb.query("test", "by_country", limit=rowPerPage, skip=skip, startkey=currentStartkey, startkey_docid=startDocId)
    for row in rows:
        hasRow = True
        print "Country: \"%s\" \t Id: '%s'" % (row.key, row.docid)
        currentStartkey = row.key
        startDocId = row.docid
    print " -- -- -- -- \n"

Это приложение зацикливается на всех страницах до конца индекса

Используя этот подход, приложение начинает читать индекс по определенному ключу (параметр startkey) и выполняет цикл только для необходимой записи в индексе. Это более эффективно, чем использование простого подхода с пропуском.

Представления с функцией уменьшения

Когда ваше представление использует функцию сокращения и группирование, невозможно использовать параметр startkey_docid, поскольку идентификатор документа недоступен, когда вы уменьшаете результат.

Поэтому, когда вы используете уменьшение, вы должны использовать параметры пропуска и ограничения.

Couchbase Java SDK Paginator

В предыдущих примерах я показал, как выполнять разбиение на страницы, используя различные параметры запроса. Java SDK предоставляет объект Paginator, чтобы помочь разработчикам справляться с нумерацией страниц. В следующем примере используется то же представление с API Paginator.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package com.couchbase.devday;
 
import com.couchbase.client.CouchbaseClient;
import com.couchbase.client.protocol.views.*;
import java.net.URI;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
 
public class JavaPaginatorSample {
 
public static void main(String[] args) {
 
    configure();
    System.out.println("--------------------------------------------------------------------------");
    System.out.println("\tCouchbase - Paginator");
    System.out.println("--------------------------------------------------------------------------");
 
    List<URI> uris = new LinkedList<URI>();
    uris.add(URI.create("http://127.0.0.1:8091/pools"));
 
    CouchbaseClient cb = null;
    try {
        cb = new CouchbaseClient(uris, "beer-sample", "");
        System.out.println("--------------------------------------------------------------------------");
        System.out.println("Breweries (by_name) with docs & JSON parsing");
        View view = cb.getView("test", "by_country");
        Query query = new Query();
        int docsPerPage = 5;
 
        Paginator paginatedQuery = cb.paginatedQuery(view, query, docsPerPage);
        int pageCount = 0;
        while(paginatedQuery.hasNext()) {
            pageCount++;
            System.out.println(" -- Page "+ pageCount +" -- ");
            ViewResponse response = paginatedQuery.next();
            for (ViewRow row : response) {
                System.out.println(row.getKey() + " : " + row.getId());
            }
            System.out.println(" -- -- -- ");
        }
         
        System.out.println("\n\n");
        cb.shutdown(10, TimeUnit.SECONDS);
    } catch (Exception e) {
        System.err.println("Error connecting to Couchbase: " + e.getMessage());
    }
}
 
 
 
private static void configure() {
 
    for(Handler h : Logger.getLogger("com.couchbase.client").getParent().getHandlers()) {
        if(h instanceof ConsoleHandler) {
            h.setLevel(Level.OFF);
        }
    }
    Properties systemProperties = System.getProperties();
    systemProperties.put("net.spy.log.LoggerImpl", "net.spy.memcached.compat.log.SunLogger");
    System.setProperties(systemProperties);
 
    Logger logger = Logger.getLogger("com.couchbase.client");
    logger.setLevel(Level.OFF);
    for(Handler h : logger.getParent().getHandlers()) {
        if(h instanceof ConsoleHandler){
            h.setLevel(Level.OFF);
        }
    }
}
 
}

Итак, как вы можете видеть, вы можете легко разбить на страницы результаты запроса с помощью Java Paginator.

  • В строке # 37 Paginator создается с использованием объектов представления и запроса и указывается размер страницы.
  • Тогда вам просто нужно использовать методы hasNext () и next () для навигации по результатам.

Java Paginator осознает тот факт, что они запрашивают уменьшение или нет, поэтому вы можете использовать его со всеми типами запросов – внутренне он будет переключаться между подходом пропуска / ограничения и подходом doc_id. Вы можете увидеть, как это делается в классе Paginator .

Обратите внимание, что если вы хотите сделать это в веб-приложении между запросами HTTP, вы должны сохранить объект Paginator в сеансе пользователя, поскольку текущий API сохраняет текущую страницу в ее состоянии.

Вывод

В этом посте вы узнали, как работать с нумерацией страниц в представлениях Couchbase; подвести итоги

  • Разбиение на страницы основано на некоторых конкретных параметрах, которые вы отправляете при выполнении запроса.
  • Разработчики Java могут использовать класс Paginator, который упрощает разбиение на страницы.

Я приглашаю вас взглянуть на новый Couchbase Query Language N1QL, который все еще находится в стадии разработки, который предоставит больше возможностей для разработчиков, включая разбиение на страницы, используя параметры LIMIT & OFFSET, например:

1
2
3
4
5
SELECT fname, age
    FROM tutorial
        WHERE age > 30
    LIMIT 2
    OFFSET 2

Если вы хотите узнать больше о N1QL:

Ссылка: нумерация страниц с Couchbase от нашего партнера JCG Тугдуала Граля в блоге Tug’s Blog .