Если вам приходится иметь дело с большим количеством документов при выполнении запросов к кластеру 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 | |
|
||||
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
|
||||
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>(); 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: