我最近在闲暇时间与Couch DB做了大量的工作,非常喜欢使用它。我发现它比使用关系数据库更灵活,但它并非没有它的缺点。
一个很大的缺点是缺乏动态查询/视图生成......因此,您必须在规划和证明视图方面做大量工作,因为您无法将该逻辑放入应用程序代码中做SQL。
例如,我写了一个基于JSON文档模板的登录方案,看起来有点像这样:
{
"_id": "blah",
"type": "user",
"name": "Bob",
"email": "bob@theaquarium.com",
"password": "blah",
}
为了防止创建重复的帐户,我写了一个非常基本的视图来生成一个用户名列表来查找为键:
emit(doc.name, null)
这对我来说似乎相当有效。我认为这比拖出整个文档列表(甚至每个文档只有少量字段)更好。所以我做了完全相同的事情来生成电子邮件地址列表:
emit(doc.email, null)
你能看到我对这个问题的看法吗?
在关系数据库(使用SQL)中,只需对同一个表进行两次查询。这种技术(将视图等同于SQL查询的产品)在某种程度上是类似的吗?
然后是性能/效率问题......这两个观点真的应该只是一个吗?或者是使用具有键的Couch DB视图而没有相关值是有效的做法?考虑到上面的例子,这两个视图都会在登录方案之外使用...如果我需要生成用户名列表,我可以检索它们而不需要额外的开销。
您怎么看?
答案 0 :(得分:1)
首先,你肯定可以将视图逻辑放入你的应用程序代码中 - 你需要的只是一个适当的构建或部署系统,它从应用程序中提取视图并将它们添加到设计文档中。缺少的是能够动态生成新查询的能力。
您的emit(doc.field,null)
方法当然不奇怪或不寻常。实际上,它是“按字段查找文档”查询的常用模式,其中使用include_docs=true
提取文档。也没有必要将两个视图合并为一个,唯一与性能相关的决定是两个视图是否应该放在同一个设计文档中:当访问任何视图时,设计文档中的所有视图都会更新。
当然,您的方法实际上并不能保证电子邮件是唯一的,即使您的应用程序非常努力。想象一下两个客户端应用程序A和B的以下情况:
A: queries view, determines that `test@email.com` does not exist.
B: queries view, determines that `test@email.com` does not exist.
A: creates account with `test@email.com`
B: creates account with `test@email.com`
这种情况很少发生,但仍有可能。更好的方法是将使用电子邮件地址的文档作为密钥,因为对单个文档的访问是事务性的(使用相同的密钥创建两个文档是不可能的)。典型的例子:
{
_id: "test@email.com",
type: "email"
user: "000000001"
}
{
_id: "000000001",
type: "user",
email: "test@email.com",
firstname: "Test",
...
}
编辑:只有在尝试为给定电子邮件创建帐户的两个客户端可靠地尝试访问相同的文档时,预留模式才有效。如果您随机生成新标识符,则客户端A将创建并保留文档XXXX,而客户端B将创建并保留文档YYYY,最终您将获得具有相同电子邮件的两个不同文档。
同样,执行事务“检查是否存在,创建是否存在”操作的唯一方法是让所有客户端更改单个文档。