CouchDB:单个文档与“加入”文档在一起

时间:2011-11-24 10:37:45

标签: couchdb couchapp

我正在尝试为CouchApp(没有中间件)决定最佳方法。由于与我的想法有相似之处,我们假设我们有一个存储在CouchDB中的stackoverflow页面。从本质上讲,它包括最重要的实际问题,答案和纪念。这些基本上是三层。

有两种存储方式。在包含适当的数据JSON表示的单个文档中,或者将条目的每个部分存储在单独的文档中,稍后通过视图将它们组合在一起(类似于:http://www.cmlenz.net/archives/2007/10/couchdb-joins

现在,这两种方法都可能没问题,但从我目前的观点来看,两者都有很大的缺点。存储繁忙文档(预期通过多个用户进行许多更改)作为单元实体会导致冲突发生。如果用户A将他/她的更改存储到文档中,则用户B在完成键入他/她的更新后将收到冲突错误。我可以想象,通过在重试之前重新下载文档,可以在没有用户知识的情况下解决这个问题。

Multi User Update problem

但如果文件相当大怎么办?除了它们之外我会变得非常疲惫,这会在保存过程中造成相当大的延迟,特别是如果由于许多用户同时更新文档而导致重试过程多次发生。

我看到的另一个问题是编辑。应允许每个用户编辑他/她的贡献。现在,如果它们存储在一个文档中,则可能很难编写一个可靠的auth处理程序。

好的,现在让我们来看看多文档方法。问题,答案和评论将存储在他们自己的文档中。优点:只有文档的实际所有者才会导致冲突,这种情况不会经常发生。作为整体的相当小的元素,重新下载不会花费太多时间。此外,auth例程应该很容易实现。

现在这是不利的一面。单个文档非常易于查询和显示。周围有很多未分类的片段看起来像是一个混乱的东西,因为我没有真正得到实际视图给我100%准备使用包含整个项目的有序和结构化格式的JSON对象。

enter image description here

我希望我能够传达实际问题。我试着决定哪种解决方案更适合我,哪些问题更容易克服。我想第一个解决方案是在存储和查询方面更漂亮,但第二个解决方案是通过视图中更好的密钥管理可解决的更实用的解决方案(我还没完全按照密钥原则)。

非常感谢您的帮助:)

1 个答案:

答案 0 :(得分:8)

选择第二个选项。它比处理冲突容易得多。以下是一些示例文档,我可以如何构建数据:

{
   _id: 12345,
   type: 'question',
   slug: 'couchdb-single-document-vs-joining-documents-together',
   markdown: 'Im tryting to decide the best approach for a CouchApp (no middleware). Since there are similarities to...' ,
   user: 'roman-geber',
   date: 1322150148041,
   'jquery.couch.attachPrevRev' : true
}
{
   _id: 23456,
   type: 'answer'
   question: 12345,
   markdown: 'Go with your second option...',
   user : 'ryan-ramage',
   votes: 100,
   date: 1322151148041,
   'jquery.couch.attachPrevRev' : true
}
{
   _id: 45678,
   type: 'comment'
   question: 12345,
   answer: 23456,
   markdown : 'I really like what you have said, but...' ,
   user: 'somedude',
   date: 1322151158041, 
   'jquery.couch.attachPrevRev' : true
}

要存储每个版本的修订版,我会将旧版本作为附件存储在正在编辑的文档中。如果你使用jcery客户端来处理couchdb,你可以通过添加jquery.couch.attachPrevRev = true来免费获得它。见Versioning docs in CouchDB by jchris

创建一个这样的视图

fullQuestion : {
   map : function(doc) {
       if (doc.type == 'question') emit([doc._id, null, null], null);
       if (doc.type == 'answer')   emit([doc.question, doc._id, null], null);
       if (doc.type == 'comment')  emit([doc.question, doc.answer, doc._id], null) ;
   }
}

并像这样查询视图

http://localhost:5984/so/_design/app/_view/fullQuestion?startkey=['12345']&endkey=['12345',{},{}]&include_docs=true

(注意:我没有对此查询进行网址编码,但更具可读性)

这将为您提供构建页面所需问题的所有相关文档。唯一的问题是它们不会按日期排序。您可以在客户端对它们进行排序(使用javascript)。

编辑:以下是视图和查询的替代选项

根据您的域名,您了解一些事实。你知道在问题存在之前不存在答案,并且在答案存在之前对答案的评论不可能存在。因此,让我们创建一个视图,可以更快地创建显示页面,尊重事物的顺序:

fullQuestion : {
   map : function(doc) {
       if (doc.type == 'question') emit([doc._id, doc.date], null);
       if (doc.type == 'answer')   emit([doc.question, doc.date], null);
       if (doc.type == 'comment')  emit([doc.question, doc.date], null);
   }
 }

这会将所有相关文档保存在一起,并按日期排序。这是一个示例查询

http://localhost:5984/so/_design/app/_view/fullQuestion?startkey=['12345']&endkey=['12345',{}]&include_docs=true

这将取回您需要的所有文档,从最旧到最新订购​​。您现在可以压缩结果,知道父对象将在子对象之前,如下所示:

function addAnswer(doc) {
   $('.answers').append(answerTemplate(doc));
}

function addCommentToAnswer(doc) {
   $('#' + doc.answer).append(commentTemplate(doc));
}

$.each(results.rows, function(i, row) {
   if (row.doc.type == 'question') displyQuestionInfo(row.doc);
   if (row.doc.type == 'answer') addAnswer(row.doc);
   if (row.doc.type == 'comment') addCommentToAnswer(row.doc)
})

那么你就不必执行任何客户端排序。

希望这有帮助。