CouchDB返回One to First of Many

时间:2013-12-19 04:03:57

标签: view couchdb

我有一个包含任务的Couch数据库。这些任务可以分别有多个注释。我想要一个视图,它返回所有任务,每个任务只有一个最新的评论。

将其视为获取所有博客帖子以及每篇帖子的最新评论。

这是可能的吗?

1 个答案:

答案 0 :(得分:0)

哦,你有选择!最有效的将在很大程度上取决于您的用例。

与客户端过滤分开

在这种方法中,客户端获取所有信息并决定要显示的内容。您可以从显示最新评论开始,如果用户想要查看更多评论,它将是即时的 - 没有更多请求,因为您已经收到了数据!

评论示例:

{
    "_id": "89f34369f0a50685bcb0682103003b2d",
    "_rev": "1-f64ffb6e374b8055b2fcdaa040f26a4f",
    "taskId": "89f34369f0a50685bcb0682103002037",
    "text": "use the tire pressure in the driver's side door jamb",
    "type": "comment",
    "timestamp": 300
}

示例任务:

{
    "_id": "89f34369f0a50685bcb0682103002037",
    "_rev": "1-6249079866249429dc35ba113d4772de",
    "type": "task",
    "text": "check tire pressure"
}

查看以向您提供任务和评论:

{
    "_id": "_design/mydesign",
    "_rev": "1-3ac9fa693c8b20f9598a4c667009ed45",
    "views": {
        "tasks_with_most_recent_comment": {
            "map": "function(doc) { if(doc.type === 'task') { emit([doc._id, 0], doc); } else if (doc.type === 'comment') { emit([doc.taskId, 1], doc); } }"
        }
    }
}

在结果中,所有评论都有key[1] === 0,而所有评论都有key[1] === 1

单独和最近嵌入

如果我有一个阅读量大的应用程序,收到很多评论,我会考虑将评论建模为一个单独的文档,并将最新的评论嵌入到任务中。

  • PRO:现在每项任务都有显示所需的所有信息
  • CON:客户现在需要确保每条评论都作为评论保留并更新任务

评论示例:

{
    "_id": "89f34369f0a50685bcb0682103003b2d",
    "_rev": "1-f64ffb6e374b8055b2fcdaa040f26a4f",
    "taskId": "89f34369f0a50685bcb0682103002037",
    "text": "use the tire pressure in the driver's side door jamb",
    "type": "comment",
    "timestamp": 300
}

示例任务:

{
    "_id": "89f34369f0a50685bcb0682103002037",
    "_rev": "1-6249079866249429dc35ba113d4772de",
    "type": "task",
    "text": "check tire pressure",
    "mostRecentComment": {
        "_id": "89f34369f0a50685bcb0682103003b2d",
        "_rev": "1-f64ffb6e374b8055b2fcdaa040f26a4f",
        "taskId": "89f34369f0a50685bcb0682103002037",
        "text": "use the tire pressure in the driver's side door jamb",
        "type": "comment",
        "timestamp": 300
    }
}

在这种方法中,获取最新的注释是在写入时处理的,因此检索它的查询非常简单。

嵌入的每个评论

如果评论很少,也许将它们全部嵌入到这样的任务中是有意义的:

{
    "_id": "89f34369f0a50685bcb0682103000640",
    "_rev": "1-5fb40b1d24bdbacbbb652112dfb3e3e5",
    "text": "pick up milk",
    "comments": [
        { "timestamp": 100000000, "text": "first"  },
        { "timestamp": 900000000, "text": "last"   },
        { "timestamp": 800000000, "text": "middle" }
    ]
}

只提取最新的合理方法是按时间戳按降序排序doc.comments,然后抓住第一个。

我们不能这样做,因为views cannot update documents;他们必须是无副作用的。因此,虽然简单地用比较器调用doc.comments.sort是很诱人的,但是会尝试sort in place,改变comments数组,并抛出错误:

Log :: function raised exception (new TypeError("doc.comments.sort(function (a, b) {return b.timestamp - a.timestamp;}) is read-only", "undefined", 1)) with doc._id 89f34369f0a50685bcb0682103000640

相反,我们可以调用slice来为我们提供数组的浅表副本。

var recentComments = doc.comments.slice()    // new array we're free to modify
    .sort(function(a, b) {                   // sort in place
        return b.timestamp - a.timestamp;    // descending order by timestamp
    }).slice(0,1);                           // take only the first result as an array

所以现在我们有一个只有最新评论的评论数组。我们已经知道我们无法重新分配它,所以让我们构建另一个对象来提供我们的emit函数。请注意,新对象应包含您关注的所有字段。

emit(doc._id, {text: doc.text, comments: recentComments});

所有这些让我们最终了解:

{
    "_id": "_design/mydesign",
    "_rev": "1-be95f2e2a699bb7182184dbc0729d3ce",
    "views": {
        "task_with_most_recent_comment": {
            "map": "function(doc){ emit(doc._id, {text: doc.text, comments: doc.comments.slice().sort(function(a, b) { return b.timestamp - a.timestamp; }).slice(0,1) }); }}"
        }
    }
}