【问题标题】:CouchDB - Top N documents for each groupCouchDB - 每个组的前 N ​​个文档
【发布时间】:2011-09-21 20:14:40
【问题描述】:

我目前正在评估 CouchDB,通过浏览我们将在我们的 web 项目中遇到的几个常见用例。

其中一个用例如下:

考虑一个包含(人为的例子)的系统:

  • 文章
  • 问题
  • 主题

文章和问题可以分配给多个主题。

一个主题有它自己的页面(想想http://www.quora.com 主题)。

是否有可能通过来自 couchdb 的 1 个查询来获得两者:

  • 关于主题 X 的最新 N 篇文章
  • 以及关于主题 X 的最新 N(或 M?)个问题

在更通用的术语中:我正在寻找一种按类型进行分组的方法(在这种情况下, type = 'article' 或 'question' )并为每个组返回给定特定条件的前 n 个文档排序(在这种情况下,排序是逆时间顺序)受限于特定的过滤器(在这种情况下是主题“X”)

根据我的阅读,从性能的角度来看,并行执行多个 couchdb 查询通常没什么大不了的,但我只是好奇这个(对于我们经常使用的)用例是否可以在一个请求中完成。

感谢您的任何见解

【问题讨论】:

    标签: architecture couchdb couchbase


    【解决方案1】:

    没有。

    CouchDB 视图是一维的。对于给定的主题,最近的文章和最近的问题是一个二维查询,因此不可能在一个 HTTP 请求中。

    关于解决方法的想法

    CouchDB 专为并发查询而设计,并鼓励并发查询。在生产中,我会同时从我的另一个答案中进行两个查询。 (在 Javascript 中,这很容易,但任何异步或线程编程语言都可以做到。)

    接收两个结果的响应时间将只是较长结果的响应时间(即首先完成的结果是“空闲”)。您甚至可以遍历 两个 响应的行,以在 O(1) 空间和 O(n) 时间中合并它们的时间线——还不错!

    CouchDB 唯一不能保证的是两个查询都代表完全相同的数据库状态的快照。您提到 Quora,这是现代数据库需求的完美示例。 理论上,您不知道这两个查询之间数据库状态发生了多少变化。通常,您不知道一种观点与另一种观点相比是否有意义。 在实践中,答案很明显:谁在乎? 仅以毫秒为间隔的查询实际上可以完美地结合在一起。这就是 CouchDB 非常适合 Web 应用程序的原因,尽管它的功能集受到严格限制。

    替代解决方案:GeoCouch

    GeoCouch 扩展实际上是一个通用的二维边界框查询引擎。此外,很明显,地理空间数据,例如,可以用于查询存储为timestamp x severity 2 空间的日志。但是它目前仍然是一个独立于 CouchDB 的项目,所以我不愿意将其称为“CouchDB 查询”。

    【讨论】:

    • 谢谢。我认为这不一定是性能问题是正确的,对吗?请求无论如何都会被缓存,等等。我想我只是过于热心地尝试将它们组合起来,有时需要多个请求并不是那么糟糕。
    • 注意,我在您发表评论之前扩展了我的答案,不确定您是否看到了。我认为多个请求肯定是有代价的:更复杂的客户端代码,更多的错误机会。一个愤世嫉俗的观点是,CouchDB 将繁重的工作外包给了应用程序开发人员。我的感觉是,就像有通用语言和特定领域语言一样,也有通用数据库和特定领域数据库。 CouchDB 的优势在于简单的 HTTP API 和复制。所以“成本”是值得的。
    • 感谢扩展:我确实错过了。很高兴听到 CouchDB 确实是为并发查询而设计的。我将采用并发路线(在 Java 和 JS 中)。 “陈旧”对我们来说无关紧要,所以那里没有问题。对于某些人来说,这确实是一个好主意,但我们不会走 GeoCouch 路线:如果我们将来还需要每个主题的图片、评论、cmets 等,该怎么办。我们需要一个 N 维通用边界框实现。不确定这是否会很快出现:).. 再次感谢。
    【解决方案2】:

    可以通过 CouchDB 的 1 个查询来获得两者。两个查询都使用 map/reduce 查询,尽管您不需要 reduce 函数。

    您需要视图行具有 [$type, $topic, $timestamp] 键对:

    ["article" , "money", "2011-09-21T20:50:29.819Z"]
    ["article" , "shoes", "2011-09-21T20:30:29.819Z"]
    ["article" , "shoes", "2011-09-21T20:50:29.819Z"]
    ["question", "grits", "2011-01-13T20:30:18.123Z"]
    ["question", "money", "2011-09-20T20:30:18.123Z"]
    

    这个函数可能会这样做:

    function(doc) {
        // _design/my_app/_view/topic_parts
        var key;
    
        if(doc.type && doc.parent_topic && doc.created_at) {
            // Looks good, emit it into the view.
            key = [doc.type, doc.parent_topic, doc.created_at];
            emit(key, doc);
        }
    }
    

    要查找最新的 N 行(无论是文章还是问题),您基本上需要按降序匹配 [$type, $topic, *] 的行。例如,对于主题 X 的最新 N 篇文章,就可以这样分解。 (注意null是CouchDB中的最小值,对象{}是最大的。)

    • descending=true 获取反向时间顺序。 (注意“降序”在概念上意味着沙发从“底部”扫描到“顶部”行。所以 startkey 和 endkey 是相反的。)
    • startkey=["articles","X",{}],所以这是关于X文章,从时间的尽头开始
    • endkey=["articles","X",null],这是相同的文章关于X以时间的开始结尾
    • limit=N,将结果缩减

    查询因此看起来像这样(如果需要,请记住对 URL 进行编码)。

    GET /db/_design/my_app/_view/topic_parts?descending=true&startkey=["articles","X",{}]&endkey=["articles","X",null]&limit=N
    

    【讨论】:

    • 看起来不错,但最终查询GET /db/_design/my_app/_view/topic_parts?descending=true&startkey=["articles","X",{}]&endkey=["articles","X",null]&limit=N 不是只显示前N 篇文章吗?而不是前 N 篇文章和前 N 个问题?
    • 澄清一下:你的回答是这样开始的:“CouchDB 的 1 个查询可以同时获得两者”。但最终您的查询将导致只给出“文章”而不是(文章和问题)。关于如何扩展您的回复以包含这些内容的任何提示?还是事后看来不可能?
    • 我读到您的问题的意思是您希望在一个查询中使用每个项目符号,而不是同时使用。请看我的第二个回答。
    猜你喜欢
    • 1970-01-01
    • 2010-12-25
    • 2020-05-31
    • 1970-01-01
    • 1970-01-01
    • 2023-02-23
    • 1970-01-01
    • 2020-08-25
    • 2015-07-29
    相关资源
    最近更新 更多