如果对Couchbase集群进行查询时必须处理大量文档,则使用分页来逐页获取行很重要。 您可以在“ 分页 ”一章的文档中找到一些信息,但是我想在本文中提供更多详细信息和示例代码。
在此示例中,我将基于啤酒样本数据集创建一个简单的视图,该视图用于按国家/地区查找啤酒厂:
function (doc, meta) {
if (doc.type == "brewery" && doc.country){
emit(doc.country);
}
}
该视图按国家列出所有啤酒厂,索引如下:
文件编号 | 键 | 值 |
---|---|---|
狂暴者 | 阿根廷 | 空值 |
cervecera_jerome | 阿根廷 | 空值 |
brouwerij_nacional_balashi | 阿鲁巴岛 | 空值 |
australian_brewing_corporation | 澳大利亚 | 空值 |
carlton_and_united_breweries | 澳大利亚 | 空值 |
库珀酿酒厂 | 澳大利亚 | 空值 |
寄养人_s_澳大利亚_公司 | 澳大利亚 | 空值 |
gold_coast_brewery | 澳大利亚 | 空值 |
lion_nathan_australia_hunter_street | 澳大利亚 | 空值 |
little_creatures_brewery | 澳大利亚 | 空值 |
麦芽铲子酿酒厂 | 澳大利亚 | 空值 |
matilda_bay_brewing | 澳大利亚 | 空值 |
… | … | … |
… | … | … |
… | … | … |
yellowstone_valley_brewing | 美国 | 空值 |
yuengling_son_brewing | 美国 | 空值 |
zea_rotisserie_and_brewery | 美国 | 空值 |
培养者 | 越南 | 空值 |
色相酿酒 | 越南 | 空值 |
因此,现在您要以5行的页面大小浏览此索引。
使用跳过/限制参数
最简单的方法是使用limit和skip参数,例如:
页面1:?limit = 5&skip0
页面2:?limit = 5&skip = 5
…
页面x:?limit = 5&skip(limit *(page-1))
很明显,您可以使用进行范围或键查询(startkey / endkey,键,键)和排序选项(降序)所需的任何其他参数。
这是简单但不是最有效的方法,因为查询引擎必须读取与查询匹配的所有行,直到达到跳过值。
python中的一些代码示例使用此视图进行分页:
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参数将是查询应开始读取的键的值(基于“上一页”的最后一个键)
- 由于对于例如“ Germany”的键,您可能具有一个或多个ID(文档),因此有必要对Couchbase查询引擎说从哪里开始,为此,您需要使用startkey_docid参数,并忽略此ID,因为它是前一页的最后一个。
因此,如果我们查看索引,并添加一个行号来说明分页
行数 | 文件编号 | 键 | 值 | |
---|---|---|---|---|
查询第1页 极限= 5 | ||||
1个 | 狂暴者 | 阿根廷 | 空值 | |
2 | cervecera_jerome | 阿根廷 | 空值 | |
3 | brouwerij_nacional_balashi | 阿鲁巴岛 | 空值 | |
4 | australian_brewing_corporation | 澳大利亚 | 空值 | |
5 | carlton_and_united_breweries | 澳大利亚 | 空值 | |
| ||||
6 | 库珀酿酒厂 | 澳大利亚 | 空值 | |
7 | 寄养人_s_澳大利亚_公司 | 澳大利亚 | 空值 | |
8 | gold_coast_brewery | 澳大利亚 | 空值 | |
9 | lion_nathan_australia_hunter_street | 澳大利亚 | 空值 | |
10 | little_creatures_brewery | 澳大利亚 | 空值 | |
查询第3页
| ||||
11 | 麦芽铲子酿酒厂 | 澳大利亚 | 空值 | |
12 | matilda_bay_brewing | 澳大利亚 | 空值 | |
… | … | … | ||
… | … | … | ||
… | … | … | ||
… | yellowstone_valley_brewing | 美国 | 空值 | |
… | yuengling_son_brewing | 美国 | 空值 | |
… | zea_rotisserie_and_brewery | 美国 | 空值 | |
… | 培养者 | 越南 | 空值 | |
… | 色相酿酒 | 越南 | 空值 |
因此,如您在上面的示例中看到的那样,查询使用startkey和文档ID,并使用skip = 1传递它。
现在,让我们再次使用Python查看应用程序代码
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参数)处读取索引,并且仅在索引中的必要条目上循环。 这比使用简单的跳过方法更有效。
具有减少功能的视图
当您的视图使用reduce函数进行分组时,无法使用参数startkey_docid,因为在缩小结果时文档的ID不可用。
因此,在使用reduce时,必须使用skip和limit参数。
Couchbase Java SDK分页器
在前面的示例中,我展示了如何使用各种查询参数进行分页。 Java SDK提供了一个Paginator对象,以帮助开发人员处理分页。 以下示例在Paginator API中使用相同的视图。
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请求之间的Web应用程序中执行此操作,则必须将Paginator对象保留在用户会话中,因为当前API会将当前页面保持在其状态。
结论
在此博客文章中,您学习了如何在Couchbase视图中处理分页。 总结
- 分页基于您在执行查询时发送的某些特定参数。
- Java开发人员可以使用简化分页的Paginator类。
我邀请您查看仍在开发中的新Couchbase查询语言N1QL,它将使用LIMIT&OFFSET参数为开发人员提供更多选项,包括分页,例如:
SELECT fname, age
FROM tutorial
WHERE age > 30
LIMIT 2
OFFSET 2
如果您想了解有关N1QL的更多信息:
翻译自: https://www.javacodegeeks.com/2013/10/pagination-with-couchbase.html