CouchDB:单文档vs";加入;文件一起

CouchDB:单文档vs";加入;文件一起,couchdb,couchapp,Couchdb,Couchapp,我正在尝试确定CouchApp(无中间件)的最佳方法。由于这与我的想法有相似之处,让我们假设我们有一个存储在CouchDB中的stackoverflow页面。从本质上讲,它由实际的问题、答案和逗号组成。这基本上是三层 有两种存储方法。在包含数据的适当JSON表示的单个文档中,或者将条目的每个部分存储在单独的文档中,稍后通过视图将它们组合在一起(类似于此:) 现在,这两种方法都很好,但从我目前的观点来看,这两种方法都有巨大的缺点。将繁忙的文档(预期会通过多个用户进行许多更改)存储为单个实体会导致冲

我正在尝试确定CouchApp(无中间件)的最佳方法。由于这与我的想法有相似之处,让我们假设我们有一个存储在CouchDB中的stackoverflow页面。从本质上讲,它由实际的问题、答案和逗号组成。这基本上是三层

有两种存储方法。在包含数据的适当JSON表示的单个文档中,或者将条目的每个部分存储在单独的文档中,稍后通过视图将它们组合在一起(类似于此:)

现在,这两种方法都很好,但从我目前的观点来看,这两种方法都有巨大的缺点。将繁忙的文档(预期会通过多个用户进行许多更改)存储为单个实体会导致冲突的发生。如果用户A存储了他/她的文档更改,那么用户B在输入完更新后将收到冲突错误。我可以想象,在用户不知情的情况下,通过在重试之前重新下载文档来修复此问题是可能的

但如果文件太大怎么办?随着时间的推移,我会发现它们会变得越来越庞大,这会给保存过程带来相当明显的延迟,特别是当由于许多用户同时更新文档而导致重试过程必须多次发生时

我看到的另一个问题是编辑。应允许每个用户编辑其贡献。现在,如果它们存储在一个文档中,那么可能很难编写一个可靠的身份验证处理程序

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

现在是不利的一面。单个文档非常易于查询和显示。有很多未排序的代码片段似乎很混乱,因为我没有真正获得实际的视图来向我展示一个100%可使用的JSON对象,该对象以有序和结构化的格式包含整个项目

我希望我能把实际问题说出来。我试图决定哪种解决方案更适合我,哪种问题更容易克服。我认为第一种解决方案在存储和查询方面更漂亮,而第二种解决方案更实用,可以通过视图中更好的密钥管理来解决(我还不完全了解密钥的原理)


非常感谢您事先提供的帮助:)

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

{
   _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
}
为了存储每个版本的修订,我将旧版本作为附件存储在正在编辑的文档中。如果您使用用于couchdb的jquery客户端,则可以通过添加jquery.coach.attachPrevRev=true免费获得它。看

创建这样的视图

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
然后像这样查询视图

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
(注意:我没有对这个查询进行url编码,但它更具可读性)

这将为您获取构建页面所需的问题的所有相关文档。唯一的问题是它们不会按日期排序。您可以在客户端对它们进行排序(使用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)
})
因此,您不必执行任何客户端排序


希望这有帮助。

嗨,瑞安!非常感谢您详细易懂的回答。你告诉了我很多我还不知道的事情。我已经很快实现了第二个选项的第一个版本,但会根据您的输入进行调整。非常感谢!:)