Cassandra中的结果分页(CQL)
我想知道如何使用Cassandra实现分页 假设我有一个博客。该博客每页最多列出10篇文章。要访问下一篇文章,用户必须单击分页菜单以访问第2页(文章11-20)、第3页(文章21-30)等 使用MySQL下的SQL,我可以执行以下操作:Cassandra中的结果分页(CQL),cassandra,cql,cql3,datastax-enterprise,Cassandra,Cql,Cql3,Datastax Enterprise,我想知道如何使用Cassandra实现分页 假设我有一个博客。该博客每页最多列出10篇文章。要访问下一篇文章,用户必须单击分页菜单以访问第2页(文章11-20)、第3页(文章21-30)等 使用MySQL下的SQL,我可以执行以下操作: SELECT * FROM posts LIMIT 20,10; LIMIT的第一个参数是从结果集开始的偏移量,第二个参数是要获取的行数。上面的示例返回从第20行开始的10行 如何在CQL中实现相同的效果 我在谷歌上找到了一些解决方案,但所有的解决方案都需要“
SELECT * FROM posts LIMIT 20,10;
LIMIT的第一个参数是从结果集开始的偏移量,第二个参数是要获取的行数。上面的示例返回从第20行开始的10行
如何在CQL中实现相同的效果
我在谷歌上找到了一些解决方案,但所有的解决方案都需要“上一次查询的最后结果”。“下一步”按钮可以分页到另一个10个结果集,但是如果我想从第1页跳到第5页怎么办?尝试使用CQL中的令牌函数: 另一个建议是,如果您使用DSE,solr支持深度分页:
如果您使用的是Cassandra 2.0+,则不需要使用代币 卡桑德拉2.0具有自动分页功能。 它不再使用令牌函数来创建分页,而是一个内置功能 现在,开发人员可以迭代整个结果集,而不必关心它的大小是否大于内存。当客户机代码迭代结果时,可以提取一些额外的行,而删除旧的行 在Java中,请注意SELECT语句返回所有行,并且检索的行数设置为100 我在这里展示了一个简单的语句,但是同样的代码可以用一个准备好的语句编写,再加上一个绑定语句。如果不需要,可以禁用自动分页。测试各种获取大小设置也很重要,因为您希望保持足够小的内存,但不要太小,以至于需要多次往返数据库。查看博客文章,了解分页如何在服务器端工作 语句stmt=newsimplestatement( “从原始天气数据中选择*” +“其中wsid='725474:99999'” +“年份=2005年,月份=6”); stmt.setFetchSize(24); ResultSet rs=session.execute(stmt); 迭代器iter=rs.Iterator(); 而(!rs.isFullyFetched()){ rs.fetchmoresults(); 行=iter.next(); 系统输出打印项次(行); }
手动分页 驱动程序公开PagingState对象,该对象表示获取最后一页时我们在结果集中的位置:
ResultSet resultSet = session.execute("your query");
// iterate the result set...
PagingState pagingState = resultSet.getExecutionInfo().getPagingState();
此对象可以序列化为字符串或字节数组:
String string = pagingState.toString();
byte[] bytes = pagingState.toBytes();
此序列化表单可以保存在某种形式的持久存储中,以便以后重用。稍后检索该值时,我们可以将其反序列化并在语句中重新注入:
PagingState pagingState = PagingState.fromString(string);
Statement st = new SimpleStatement("your query");
st.setPagingState(pagingState);
ResultSet rs = session.execute(st);
请注意,分页状态只能与完全相同的语句(相同的查询字符串、相同的参数)一起重用。此外,它是一个不透明的值,仅用于收集、存储和重复使用。如果您试图修改其内容或使用其他语句重用它,驱动程序将引发错误
Src:虽然计数在CQL中可用,但到目前为止,我还没有看到用于偏移部分的好解决方案
所以。。。我一直在考虑的一个解决方案是使用后台进程创建页面集
在某些表中,我会创建博客页面A作为对页面1、2、。。。10然后是博客B页的另一个条目,指向第11页到第20页,以此类推
换句话说,我将使用设置为页码的行键构建自己的索引。您仍然可以使其具有一定的灵活性,因为您可以让用户选择每页查看10、20或30个参考资料。例如,当设置为30时,将集1、2和3显示为A页,将集4、5、6显示为B页,以此类推。)
如果您有一个后端进程来处理所有这些,您可以在添加新页面和从博客中删除旧页面时更新列表。这个过程应该非常快(比如1分钟,对于1000000行,即使是这么慢…),然后你可以在几乎瞬间找到要显示在列表中的页面。(显然,如果你想让数千名用户每人发布数百页,那么这个数字可能会快速增长。)
更复杂的是,如果您想提供一个复杂的Where子句。默认情况下,博客会显示从最新到最旧的所有帖子的列表。您还可以提供带有标记Cassandra的帖子列表。也许你想颠倒顺序等等,这使得它很难,除非你有某种形式的高级方法来创建你的索引。在我这方面,我有一种类似C的语言,它可以查看和戳一行中的值,以(a)选择它们,如果选择(b)对它们进行排序。换句话说,在我这方面,我已经可以拥有像SQL中那样复杂的WHERE子句了。然而,我还没有把我的列表分成几页。下一步我想…如果您阅读此文档“使用分页状态令牌获得下一个结果”
我们可以使用“分页状态令牌”在应用程序级别进行分页。
所以PHP逻辑应该是这样的
<?php
$limit = 10;
$offset = 20;
$cluster = Cassandra::cluster()->withContactPoints('127.0.0.1')->build();
$session = $cluster->connect("simplex");
$statement = new Cassandra\SimpleStatement("SELECT * FROM paging_entries Limit ".($limit+$offset));
$result = $session->execute($statement, new Cassandra\ExecutionOptions(array('page_size' => $offset)));
// Now $result has all rows till "$offset" which we can skip and jump to next page to fetch "$limit" rows.
while ($result->pagingStateToken()) {
$result = $session->execute($statement, new Cassandra\ExecutionOptions($options = array('page_size' => $limit,'paging_state_token' => $result->pagingStateToken())));
foreach ($result as $row) {
printf("key: '%s' value: %d\n", $row['key'], $row['value']);
}
}
?>
为节点js(koa-js,marko-js)使用cassandra节点驱动程序:分页
问题
由于缺少跳过功能,我们需要解决这个问题。下面是节点应用程序手动分页的实现,以防任何人都能想到
- 简单用户列表代码
- 在下一页和上一页状态之间导航
- 易于复制
next
和previous
记录的页面状态(维护堆栈或任何最适合的数据结构)
解决方案2:使用limit循环遍历所有记录,并将所有可能的页面状态保存在变量中,并生成与其页面状态相对应的页面
在模型中使用此注释代码,我们可以获得页面的所有状态
//for the next flow
//if (result.nextPage) {
// Retrieve the following pages:
// the same row handler from above will be used
// result.nextPage();
//}
路由器功能 var userModel = require('/models/users');
public.get('/users', users);
public.post('/users', filterUsers);
var users = function* () {//get request
var data = {};
var pageState = { "next": "", "previous": "" };
try {
var userCount = yield userModel.Count();//count all users with basic count query
var currentPage = 1;
var pager = yield generatePaging(currentPage, userCount, pagingMaxLimit);
var userList = yield userModel.List(pager);
data.pageNumber = currentPage;
data.TotalPages = pager.TotalPages;
console.log('--------------what now--------------');
data.pageState_next = userList.pageStates.next;
data.pageState_previous = userList.pageStates.previous;
console.log("next ", data.pageState_next);
console.log("previous ", data.pageState_previous);
data.previousStates = null;
data.isPrevious = false;
if ((userCount / pagingMaxLimit) > 1) {
data.isNext = true;
}
data.userList = userList;
data.totalRecords = userCount;
console.log('--------------------userList--------------------', data.userList);
//pass to html template
}
catch (e) {
console.log("err ", e);
log.info("userList error : ", e);
}
this.body = this.stream('./views/userList.marko', data);
this.type = 'text/html';
};
//post filter and get list
var filterUsers = function* () {
console.log("<------------------Form Post Started----------------->");
var data = {};
var totalCount;
data.isPrevious = true;
data.isNext = true;
var form = this.request.body;
console.log("----------------formdata--------------------", form);
var currentPage = parseInt(form.hdpagenumber);//page number hidden in html
console.log("-------before current page------", currentPage);
var pageState = null;
try {
var statesArray = [];
if (form.hdallpageStates && form.hdallpageStates !== '') {
statesArray = form.hdallpageStates.split(',');
}
console.log(statesArray);
//develop stack to track paging states
if (form.hdpagestateRequest === 'next') {
console.log('--------------------------next---------------------');
currentPage = currentPage + 1;
statesArray.push(form.hdpageState_next);
pageState = form.hdpageState_next;
}
else if (form.hdpagestateRequest === 'previous') {
console.log('--------------------------pre---------------------');
currentPage = currentPage - 1;
var p_st = statesArray.length - 2;//second last index
console.log('this index of array to be removed ', p_st);
pageState = statesArray[p_st];
statesArray.splice(p_st, 1);
//pageState = statesArray.pop();
}
else if (form.hdispaging === 'false') {
currentPage = 1;
pageState = null;
statesArray = [];
}
data.previousStates = statesArray;
console.log("paging true");
totalCount = yield userModel.Count();
var pager = yield generatePaging(form.hdpagenumber, totalCount, pagingMaxLimit);
data.pageNumber = currentPage;
data.TotalPages = pager.TotalPages;
//filter function - not yet constructed
var searchUsers = yield userModel.searchList(pager, pageState);
data.usersList = searchUsers;
if (searchUsers.pageStates) {
data.pageStates = searchUsers.pageStates;
data.next = searchUsers.nextPage;
data.pageState_next = searchUsers.pageStates.next;
data.pageState_previous = searchUsers.pageStates.previous;
//show previous and next buttons accordingly
if (currentPage == 1 && pager.TotalPages > 1) {
data.isPrevious = false;
data.isNext = true;
}
else if (currentPage == 1 && pager.TotalPages <= 1) {
data.isPrevious = false;
data.isNext = false;
}
else if (currentPage >= pager.TotalPages) {
data.isPrevious = true;
data.isNext = false;
}
else {
data.isPrevious = true;
data.isNext = true;
}
}
else {
data.isPrevious = false;
data.isNext = false;
}
console.log("response ", searchUsers);
data.totalRecords = totalCount;
//pass to html template
}
catch (e) {
console.log("err ", e);
log.info("user list error : ", e);
}
console.log("<------------------Form Post Ended----------------->");
this.body = this.stream('./views/userList.marko', data);
this.type = 'text/html';
};
//Paging function
var generatePaging = function* (currentpage, count, pageSizeTemp) {
var paging = new Object();
var pagesize = pageSizeTemp;
var totalPages = 0;
var pageNo = currentpage == null ? null : currentpage;
var skip = pageNo == null ? 0 : parseInt(pageNo - 1) * pagesize;
var pageNumber = pageNo != null ? pageNo : 1;
totalPages = pagesize == null ? 0 : Math.ceil(count / pagesize);
paging.skip = skip;
paging.limit = pagesize;
paging.pageNumber = pageNumber;
paging.TotalPages = totalPages;
return paging;
};
var clientdb = require('../utils/cassandradb')();
var Users = function (options) {
//this.init();
_.assign(this, options);
};
Users.List = function* (limit) {//first time
var myresult; var res = [];
res.pageStates = { "next": "", "previous": "" };
const options = { prepare: true, fetchSize: limit };
console.log('----------did i appeared first?-----------');
yield new Promise(function (resolve, reject) {
clientdb.eachRow('SELECT * FROM users_lookup_history', [], options, function (n, row) {
console.log('----paging----rows');
res.push(row);
}, function (err, result) {
if (err) {
console.log("error ", err);
}
else {
res.pageStates.next = result.pageState;
res.nextPage = result.nextPage;//next page function
}
resolve(result);
});
}).catch(function (e) {
console.log("error ", e);
}); //promise ends
console.log('page state ', res.pageStates);
return res;
};
Users.searchList = function* (pager, pageState) {//paging filtering
console.log("|------------Query Started-------------|");
console.log("pageState if any ", pageState);
var res = [], myresult;
res.pageStates = { "next": "" };
var query = "SELECT * FROM users_lookup_history ";
var params = [];
console.log('current pageState ', pageState);
const options = { pageState: pageState, prepare: true, fetchSize: pager.limit };
console.log('----------------did i appeared first?------------------');
yield new Promise(function (resolve, reject) {
clientdb.eachRow(query, [], options, function (n, row) {
console.log('----Users paging----rows');
res.push(row);
}, function (err, result) {
if (err) {
console.log("error ", err);
}
else {
res.pageStates.next = result.pageState;
res.nextPage = result.nextPage;
}
//for the next flow
//if (result.nextPage) {
// Retrieve the following pages:
// the same row handler from above will be used
// result.nextPage();
//}
resolve(result);
});
}).catch(function (e) {
console.log("error ", e);
info.log('something');
}); //promise ends
console.log('page state ', pageState);
console.log("|------------Query Ended-------------|");
return res;
};
<div class="box-footer clearfix">
<ul class="pagination pagination-sm no-margin pull-left">
<if test="data.isPrevious == true">
<li><a class='submitform_previous' href="">Previous</a></li>
</if>
<if test="data.isNext == true">
<li><a class="submitform_next" href="">Next</a></li>
</if>
</ul>
<ul class="pagination pagination-sm no-margin pull-right">
<li>Total Records : $data.totalRecords</li>
<li> | Total Pages : $data.TotalPages</li>
<li> | Current Page : $data.pageNumber</li>
</ul>
</div>