【问题标题】:Sorting CouchDB Views By Value按值对 CouchDB 视图进行排序
【发布时间】:2010-05-12 10:00:52
【问题描述】:

我正在测试 CouchDB,看看它如何处理记录一些搜索结果。我想做的是生成一个视图,我可以在其中从结果中生成最热门的查询。目前我有这样的事情:

示例文档部分

{
  "query": "+dangerous +dogs",
  "hits": "123"
}

地图功能 (不完全是我需要/想要的,但足以进行测试)

function(doc) {
  if (doc.query) {
    var split = doc.query.split(" ");
    for (var i in split) {
      emit(split[i], 1);
    }
  }
}

归约函数

function (key, values, rereduce) {
  return sum(values);
}

现在,这将为我提供一种格式的结果,其中查询词是键,右侧是该词的计数,这很棒。但我希望它按值排序,而不是按键排序。听上去,这在 CouchDB 中是不可能的。

那么,有没有人知道如何查看我有查询词的有序版本及其相关计数的视图?我对 CouchDB 很陌生,我只是想不出我将如何编写所需的函数。

【问题讨论】:

    标签: sorting couchdb


    【解决方案1】:

    确实没有简单的答案。但是有几种模式。

    1. http://wiki.apache.org/couchdb/View_Snippets#Retrieve_the_top_N_tags。我个人不喜欢这样,因为他们承认这是一个脆弱的解决方案,而且代码看起来并不轻松。

    2. Avi 的答案,就是在你的应用程序中进行内存排序。

    3. couchdb-lucene 似乎每个人最终都会发现自己需要它!

    4. 我喜欢 Chris 在 Avi 的引文中所说的话。放松。在 CouchDB 中,数据库是轻量级的,擅长为您提供独特的数据视角。这些天来,嗡嗡声都是关于过滤复制的,这都是关于切出数据子集以放入单独的数据库中。

      无论如何,基础很简单。您从视图输出中获取您的.rows,然后将其插入到单独的数据库中,该数据库只是在计数上发出键控。另一个技巧是编写一个非常简单的_list 函数。列出将原始沙发输出“渲染”为不同格式。你的_list 函数应该输出

      { "docs":
          [ {..view row1...},
            {..view row2...},
            {..etc...}
          ]
      }
      

      这将完全按照_bulk_docs API 要求的方式格式化视图输出。现在您可以将 curl 直接导入另一个 curl:

      curl host:5984/db/_design/myapp/_list/bulkdocs_formatter/query_popularity \
       | curl -X POST host:5984/popularity_sorter/_design/myapp/_view/by_count
      
    5. 事实上,如果您的列表函数可以处理所有文档,您可以让它自己对它们进行排序,然后将它们排序返回给客户端。

    【讨论】:

    • 很好的答案,非常感谢 JHS。有很多选择供我研究。
    • 不客气。如果不清楚,我会说第一选择=在客户端排序;第二选择 = 使用替代视图(AVI 更简洁地识别了这两个视图!)
    • 我很欣赏这种努力,但这几乎是一种关闭。我能得到什么回报?
    • 您能否扩展第三个选项:couchdb-lucene。我不清楚如何使用全文索引来快速排名标签(或任何其他类似的缩减视图结果)。
    • 链接 Retrieve_the_top_N_tags 似乎已损坏。请您描述一下这种方法吗?
    【解决方案2】:

    CouchDB 用户邮件列表中的 came up 和主要开发人员之一 Chris Anderson 写道:

    这是一个常见的请求,但 CouchDB 不直接支持 视图——为此,您需要将 group-reduce 查询复制到 另一个数据库,并构建一个按值排序的视图。

    这是我们为支持动态范围查询和 增量索引。

    我最近也需要这样做,我最终在我的应用层中这样做了。这在 JavaScript 中很容易做到:

    db.view('mydesigndoc', 'myview', {'group':true}, function(err, data) {
    
        if (err) throw new Error(JSON.stringify(err));
    
        data.rows.sort(function(a, b) {
            return a.value - b.value;
        });
    
        data.rows.reverse(); // optional, depending on your needs
    
        // do something with the data…
    });
    

    此示例在Node.js 中运行并使用node-couchdb,但它可以很容易地适应在浏览器或其他JavaScript 环境中运行。当然,这个概念可以移植到任何编程语言/环境。

    HTH!

    【讨论】:

    • 我扩展了专用数据库以进行排序,但应用程序中的排序可能在大多数情况下都有效。如果这些是搜索词,那么对于 OP 来说可能不是,但仍然.. :)
    • 感谢 Avi 的精彩回答。我会试试看。
    • 您只需将排序功能更改为return b.value - a.value即可避免data.rows.reverse()
    【解决方案3】:

    这是一个老问题,但我觉得它仍然值得一个体面的答案(我花了至少 20 分钟来寻找正确的答案......)

    我不赞成此处答案中的其他建议,并认为它们不令人满意。特别是我不喜欢对应用层中的行进行排序的建议,因为它不能很好地扩展并且不能处理需要限制数据库中的结果集的情况。

    this thread 建议了我遇到的更好的方法,它假定如果您需要对查询中的值进行排序,您应该将它们添加到键集中,然后使用范围查询键 - 指定所需的键和放宽值范围。例如,如果您的密钥由国家、州和城市组成:

    emit([doc.address.country,doc.address.state, doc.address.city], doc);
    

    然后您只查询国家/地区并免费对其余关键组件进行排序:

    startkey=["US"]&endkey=["US",{}] 
    

    如果您还需要颠倒顺序 - 请注意,简单地定义 descending: true 是不够的。您实际上需要颠倒开始和结束键的顺序,即:

    startkey=["US",{}]&endkey=["US"]
    

    在这个伟大的source 上查看更多参考。

    【讨论】:

    • 好主意。让我快速行动。感觉这是关于如何在视图中排序的唯一真正答案。
    • 这是一个很好的答案,非常适合我。
    【解决方案4】:

    我不确定您返回的结果是 1,但我很肯定这应该可以解决问题:

    emit([doc.hits, split[i]], 1);

    rules of sorting 在文档中定义。

    【讨论】:

    • 我不应该把 hits 参数放在那里。这有点像红鲱鱼。我现在所拥有的东西会产生这样的东西: Key: +dangerous Value: 25 。其中 25 表示 25 人输入了包含文本“+dangerous”的查询。代码来自此处的 CouchDB wiki:wiki.apache.org/couchdb/View_Snippets#Retrieve_the_top_N_tags
    【解决方案5】:

    根据 Avi 的回答,我想出了这个 Couchdb 列表函数,它可以满足我的需求,它只是最流行事件的报告(key=event name, value=attendees)。

    ddoc.lists.eventPopularity = function(req, res) { start({ headers : { "Content-type" : "text/plain" } }); 变量数据 = [] 而(行= getRow()){ 数据.推送(行); } data.sort(函数(a, b){ 返回 a.value - b.value; })。逆转(); for(我在数据中){ 发送(数据[i].value + ': ' + data[i].key + "\n"); } }

    供参考,下面是对应的视图函数:

    ddoc.views.eventPopularity = { 地图:功能(文档){ 如果(doc.type == '用户'){ for(我在 doc.events 中){ 发射(doc.events[i].event_name,1); } } }, 减少:'_count' }

    以及列表函数的输出(截图):

    165:设计驱动的创新:设计师如何促进对话 165:您的客户是人群还是社区? 164:社交媒体流言终结者 163:不要害怕创造力!任何事情都可能发生 159:代理商需要像软件公司一样思考吗? 158:客户体验:未来趋势与洞察 156:意外作家:适合所有人的出色网络副本 155:为什么一切都很棒,但没有人快乐

    【讨论】:

      【解决方案6】:

      我认为上面的每个解决方案都会破坏 couchdb 的性能。我对这个数据库很陌生。据我所知,couchdb 视图会在查询之前准备好结果。看来我们需要手动准备结果。例如,每个搜索词都将驻留在具有命中计数的数据库中。当有人搜索时,将查找其搜索词并增加点击数。当我们想查看搜索词的流行度时,它会发出 (hitcount, searchterm) 对。

      【讨论】:

        【解决方案7】:

        链接 Retrieve_the_top_N_tags 似乎已损坏,但我找到了另一个解决方案 here

        引用编写该解决方案的开发人员:

        我不会在 map 步骤中返回由标签键入的结果,而是发出每个标签的每次出现。然后在 reduce 步骤中,我会使用哈希计算按标签分组的聚合值,将其转换为数组,对其进行排序,然后选择前 3 个。

        如 cmets 所述,唯一的问题是长尾:

        问题是您必须小心获得的标签数量;如果结果大于 500 字节,您将有 couchdb 抱怨它,因为“减少必须有效减少”。不过,3 或 6 甚至 20 个标签应该不成问题。

        它对我来说非常有效,检查链接以查看代码!

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-01-25
        • 1970-01-01
        • 2013-04-04
        • 2012-12-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-27
        相关资源
        最近更新 更多