核心答案
为具有要保持唯一的字段的文档构建POST/PUT ,如下所示:
创建一个视图。在map 函数中,使用要强制唯一的字段作为键。价值可以什么都不是。使用reduce 函数获取每个键的计数。最好的方法(性能)是使用内置的_count减少功能。
在您将新文档PUT/POST到数据库后,立即获取返回的和id
GET / yourdb/_design/yourapp/_view/viewname?group=true&key=" value-of-your-unique-field-from-step-1 ”。rev
如果最后一个GET的结果为您提供了1以外的计数值,那么您只是插入了一个副本。立即删除/yourdb/ id-from-step-2 ?rev= rev-from-step-2。
放松。
粗略的例子
假设您正在存储用户帐户,并且您想确保电子邮件地址是唯一的,但您不想将其设为文档 ID(无论出于何种原因)。如上所述,构建一个视图以快速检查电子邮件地址的唯一性。让我们称之为电子邮件。它将具有可能类似于此的地图功能...
function(doc) {
if(doc.type === 'account') {
emit(doc.email, 1);
}
}
就像reduce 函数_count
一样。如果你发出一个像上面的1也可以。如上所示,在您的文档上拥有并检查一个字段只是一种约定,但我发现它有时很有帮助。如果您存储的只是用户帐户,那么您可能不需要它。_sum
type
现在让我们说我们正在插入一个像这样的文档......
POST /mydb/
{
"name": "Joe",
"email": "joe@example.org"
}
CouchDB 会以类似...
{
ok: true,
id: 7c5c249481f311e3ad9ae13f952f2d55,
rev: 1-442a0ec9af691a6b1690379996ccaba6
}
检查我们现在在数据库中是否有多个 joe@example.org ...
GET /mydb/_design/myapp/_view/emails/?group=true&key="joe@example.org"
CouchDB 会以类似...
{
rows: [
{
key: "joe@example.org",
value: 1
}
]
}
如果value
不是1
该回复中的任何内容,您可能只是插入了重复的电子邮件。因此,删除文档并返回错误,如Email Address must be Unique,类似于典型的 SQL 数据库响应,或任何您想要的。
删除会是这样的......
DELETE /mydb/7c5c249481f311e3ad9ae13f952f2d55?rev=1-442a0ec9af691a6b1690379996ccaba6
简短讨论(如果你关心的话)
如果您来自一个良好的旧 *SQL 背景,这将看起来是错误的和奇怪的。在你翻出来之前考虑这些点。对字段有约束,例如唯一性或其他任何内容,都意味着模式。CouchDB 是无模式的。来自*SQL 意味着您将不得不改变您的思维方式。ACID 和锁不是唯一的方法。CouchDB 具有很大的灵活性和强大的功能。你如何使用它来获得你想要的东西将取决于你的用例的细节以及你能在多大程度上摆脱传统关系数据库思维的限制。
我有时以这种方式实现唯一性的原因是因为它对我来说非常Couchy。如果您考虑 CouchDB 处理更新冲突等的方式,这种方法遵循相同的流程。我们存储给定的文档,然后检查我们的字段是否是唯一的。如果没有,请抓住我们刚刚插入的那个文档,我们仍然拥有它的方便句柄,并做一些事情来解决对唯一性的需求,例如删除它。
在上面的示例中,您可能会在查看文档之前检查电子邮件地址的唯一性。POST
当心!!如果发生了多件事情,则可能在您检查电子邮件是否存在之后,但在您执行POST
! 这会给您留下一封重复的电子邮件。
在您之前检查电子邮件地址也没有错POST
。例如,如果您的用户正在填写表单并且您可以将电子邮件字段值 ajax 到数据库,这是一个好主意。您可以预先警告用户该电子邮件地址存在或阻止提交等。但是,在所有情况下,您还应该始终检查文档后的唯一性。POST
然后根据需要做出反应。从 UI 端的角度来看,上述步骤看起来与传统 *SQL 数据库在唯一性约束上的结果没有什么不同。
会出什么问题吗?是的。考虑一下。假设数据库中不存在joe@example.org 。两个文档几乎同时进入以保存到数据库中。Doc A是POSTed
,然后Doc B是POSTed
,但在我们能够检查Doc A POST
的唯一性之前。所以现在当我们对Doc A POST
进行唯一性检查时,我们看到joe@example.org在数据库中出现了两次。因此,我们将删除它并报告问题。但是让我们说在我们可以删除Doc A之前, Doc B的唯一性检查也会发生,为joe@example.org获得相同的计数值。现在两者POSTs
将被拒绝,即使joe@example.org最初实际上不在数据库中!换句话说,如果两个具有匹配值的文档几乎同时进入您的应用程序,他们可能会看到彼此POSTs
并错误地得出他们携带的值已经在数据库中的结论!我们无法真正阻止这种情况,因为 CouchDB 中没有传统的 RDB 样式锁定。但是为了换取这么小的价格,我们得到了主主复制和大量其他很酷的东西。我要买它!如果这对您的实现来说是一个大问题,您可以尝试通过实现某种重试机制等来解决它。
最后,CouchDB 确实是数据库的瑰宝,但不要仅仅接受它并期望它是一种防弹的方法。很多事情实际上取决于您的特定应用程序的细节。