使用可变键在CouchDB中进行分页

时间:2013-01-04 23:22:18

标签: couchdb

这里有很多关于使用CouchDB进行分页的问题,但没有一个问题非常符合我的想法。

基本上,我有一个按投票数排名的结果集,我想按降序翻页。

以下是map供参考。

function(doc) {
  emit(doc.votes);
}

现在,问题。我发现startkey_docid不能自行运行。您必须将其与startkey结合使用。问题是,对于查询,我不使用startkey参数(我不打算限制结果,只是得到最多 - >最少)。我想我可以使用startkey = {{doc.votes}}& startkey_docid = {{doc._id}}代替,但是当有人点击“下一页”时,文档的投票数可能会改变链接。

解决这个问题的方法似乎很明显:只需设置startkey=99999999以便它将返回数据库中的所有文档,我可以使用startkey_docid从我们上次停止的那个开始。奇怪的是,当我这样做时,startkey_docid停止工作,只是允许所有结果再次返回。显然,startkey需要与_id中使用startkey_docid的文档上的密钥完全相同。

我要问的是,当您想要使用实际的startkey_docid时,是否有人知道使用startkey进行寻呼的解决方法?我的应用程序是否应该按_id查找文档并立即使用doc.votes值,希望它在请求之间的几毫秒内没有更改?即使这似乎也不太可靠。

编辑:结束切换到Mongo的速度,所以这个问题结果有点没有实际意义。

3 个答案:

答案 0 :(得分:1)

我从来没有做过这样的事情,但我想我知道怎么做。您可以做的是拍摄评级的快照并在每个页面中引用它。您可能希望您的视图不占用太多空间,因此您不应该在拍摄快照后使用未更改的投票来映射文档的单独副本。因此,您可以执行以下操作:

  1. 在文档中添加一些带时间戳的评分记录。
  2. 映射评分和历史记录。
  3. 在您的应用中获取当前时间:start_time = Date.now()并查询所有页面。
  4. 清除旧历史记录,然后清除最旧的活动会话。
  5. 问题在于,如果您发出[votes, date]并尝试分页,您将永远不知道需要获取多少文档才能获得每页所需的数量。总有一些旧的版本你必须跳过,你将从DB获得下一个。这就是为什么你可以考虑发出:[date, votes],总是两次读取视图 - start_time和当前时间,并合并和排序结果(如合并排序)。

    Ad.1:

    { ...,
      votes: 12,
      history: [
        {date: 1357390271342, votes: 10},
        {date: 1357390294682, votes: 11}
      ]
    }
    

    Ad.2:

    function (doc) {
      emit([{}, doc.votes], null);
      doc.history && doc.history.forEach(function(h) {
        emit([h.date, h.votes], null);
      });
    }
    

    Ad.3:

    ?startkey=[start_time, votes]&limit=items_per_page_plus1
    ?startkey=[{}, votes]&limit=items_per_page_plus1
    

    合并列表,在应用中按votes排序(在列表函数中)。 如果您在使用start_docid时遇到问题,则可以发出[date, votes, id]并明确查询ID。即使此特定文档更改了其votes,它仍将在历史记录中可用。

    Ad.4: 如果您发出[date, votes],那么您可以获得过时的历史记录宽度:?startkey=[0]&endkey=[oldest_active_session_time]&inclusive_end=false并使用update handler更新它们:

    function(doc, req) {
      if (!doc || !doc.history) return [null, 'Error'];
      var history = new Array();
      var oldest = +(req.query.date);
      doc.history.forEach(function(h) {
        if (h.date >= oldest)
          history.push(h);
      });
      doc.history = history;
      return [doc, 'OK'];
    }
    

    注意:我没有对它进行过测试,因此预计不会在没有修改的情况下运行:)

    据我所知,CouchDB使用b-tree阴影进行更新,原则上应该可以访问视图的旧版本。我没有参与CouchDB设计,所以这只是猜测而且似乎没有任何(记录的)API。

答案 1 :(得分:0)

我现在无法找出任何简单的解决方案,但有以下选项:

  • 不常将您的排序列表复制到小型专用数据库中,因此它会比stale = ok
  • 更陈旧
  • 以一种您可以通过一些更稳定的数据排序的方式修改您的架构。查看CouchDb指南中的银行/分类帐示例:http://guide.couchdb.org/draft/recipes.html#banking。例如,尝试记录每次投票并每小时减少一次投票。作为奖励,您将获得历史/趋势:)

答案 2 :(得分:0)

我很惊讶这个问题一直没有得到解答,因为CouchDB Futon的功能基本上是在你对地图功能的结果进行分页时这样做的。我打开了一个firebug来查看javascript控制台中发生的事情,因为我分页并看到每个分页结果集合都传递了startkey和startkey_docid。因此,虽然问题是如何在不包含startkey的情况下进行分页,但CouchDB指定需要启动键并演示它如何工作。未指定endkey,因此如果指定的启动键只有一个结果,则下一组分页结果还将包含与启动键不匹配的排序结果的下一个键。

所以为了澄清一点,这个问题的答案是,当你分页并跟踪startkey_docid时,你还需要捕获同一文档的开始键,它将成为下一组结果的开始。当您调用分页结果时,请使用捕获的startkey和startkey_docid作为couchdb所需。保持endkey关闭,以便结果将继续到排序结果的下一个键。

想要能够在不指定密钥的情况下进行分页的用例场景有点奇怪。因此,让我们说下一个分页结果的开始docid确实将它的关键值从9变为3。我们还假设只存在一个docid实例。映射结果,即使它可能会出现多次(我相信这是为什么需要指定startkey)。当用户点击下一个按钮时,用户的分页结果现在已从查看等级9移至等级3.但如果除了startkey_docid之外还包括启动键,则分页结果将仅启动所有在排名9的开头,结果是比可能跳过一大堆结果更合乎逻辑的进展。