Если вам приходится иметь дело с большим количеством документов при выполнении запросов к кластеру 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 Couchbasecb = Couchbase.connect(bucket='beer-sample')hasRow = TruerowPerPage = 5page = 0currentStartkey=""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 Couchbasecb = Couchbase.connect(bucket='beer-sample')hasRow = TruerowPerPage = 5page = 0currentStartkey=""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: