【问题标题】:Pagination in CouchDB using variable keys在 CouchDB 中使用变量键进行分页
【发布时间】:2014-03-20 01:11:46
【问题描述】:

这里有很多与使用 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 需要与_idstartkey_docid 中使用的文档上的键完全相同。

我要问的是,当实际的startkey 可能在您想要使用它时发生变化时,是否有人知道使用startkey_docid 进行分页的解决方法?我的应用程序是否应该只通过_id 查找文档并立即使用doc.votes 值,希望它在请求之间的几毫秒内没有改变?即使这样似乎也不是很可靠。

编辑:最终为了速度而切换到 Mongo,所以这个问题变得有点没有实际意义。

【问题讨论】:

    标签: couchdb


    【解决方案1】:

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

    1. 在您的文档中添加一些带有时间戳的评分历史记录。
    2. 像这样映射收视率和历史记录。
    3. 在您的应用中获取当前时间:start_time = Date.now() 并查询所有页面。
    4. 清理比最早的活动会话更早的历史记录。

    问题是,如果您发出[votes, date] 并尝试分页,您将永远不知道您必须获取多少文档才能获得每页所需的数量。总有一些旧版本你必须跳过,你将不得不从 DB 获取下一个版本。这就是为什么您可以考虑发出:[date, votes],始终读取视图两次 - 对于 start_time 和当前时间,然后合并和排序结果(如合并排序)。

    广告 1:

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

    广告 2:

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

    广告 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,它仍然会出现在历史记录中。

    广告 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。

    【讨论】:

    • 其实我不确定你的答案是否正确,但你做得很好,应该引起注意。
    【解决方案2】:

    我现在想不出任何简单解决方案,但有一些选择:

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

    【讨论】:

      【解决方案3】:

      我有点惊讶这个问题没有得到解答,因为 CouchDB Futon 的功能基本上是在您对 map 函数的结果进行分页时完成的。我打开 firebug 以查看在分页时 javascript 控制台中发生了什么,并看到对于每组分页结果,它都将 startkey 与 startkey_docid 一起传递。因此,尽管问题是如何在不包含 startkey 的情况下进行分页,但 CouchDB 指定 startkey 是必需的,并演示了它是如何工作的。 endkey没有指定,所以如果指定的startkey只有一个结果,那么下一组分页结果也会包含排序结果中与startkey不匹配的下一个key。

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

      希望能够在不指定键的情况下进行分页的用例场景有点奇怪。因此,假设下一个分页结果的起始 docid 确实将其关键值从 9 大幅更改为 3。我们还假设地图结果中只存在一个 docid 实例,即使它可能出现多次(我相信这就是需要指定 startkey 的原因)。当用户单击下一个按钮时,用户的分页结果现在将从查看 rank 9 移动到 rank 3。但是如果除了 startkey_docid 之外还包括 startkey,则分页结果将从头开始排名第 9 的结果比可能跳过大量结果更符合逻辑。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-04-27
        • 2020-01-23
        • 2016-08-28
        • 1970-01-01
        • 1970-01-01
        • 2010-09-23
        • 1970-01-01
        相关资源
        最近更新 更多