27

独自の制約を適用するための手法/提案はありますか?はい、一意のキーを作成できますが、キーとキーを変更することはできません。また、このアプローチは複雑な検証(個別の一意のログイン、個別の一意の電子メールなど)には適していません。

たとえば、アカウントには一意のログインと電子メールが必要です。このフィールドからキーを取得すると、不整合が発生します。

key1: "Account-john@example.net-john", { email: "john@example.net", login: "john"}
key2: "Account-mary@example.net-mary", { email: "mary@example.net", login: "mary"}

見栄えは良いですが:

key1: "Account-john@example.net-mary", { email: "john@example.net", login: "mary"}
key2: "Account-mary@example.net-mary", { email: "mary@example.net", login: "mary"}

おっと、ログインできるアカウントが2つあります: "mary"

4

4 に答える 4

26

コアアンサー

次のように、一意に保ちたいフィールドを持つドキュメントのPOST/PUTを構成します。

  1. ビューを作成します。map 関数では、キーとして一意にするフィールドを使用します。値は何もありません。reduce 関数を使用して、各キーのカウントを取得します。(パフォーマンスのための) 最善の方法は、組み込みの_count reduce 関数を使用することです。

  2. 新しいドキュメントをデータベースにPUT/POSTした直後に、返さidれた andrevを取得して / yourdb /_design/yourapp/_view/viewname?group=true&key=" value-of-your-unique-field-from-step-1 を取得します。 "。

  3. 最後のGETの結果が1以外のカウント値を返す場合は、重複を挿入しただけです。すぐに / yourdb / id-from-step-2 ?rev= rev-from-step-2 を削除します。

  4. リラックス


大まかな例

ユーザーアカウントを保存していて、電子メールアドレスが一意であることを確認したいが、(何らかの理由で) ドキュメント ID にしたくないとします。上記のように、電子メール アドレスの一意性をすばやく確認するためのビューを作成します。それをemailと呼びましょう。おそらくこれに似たマップ機能があります...

function(doc) {  
  if(doc.type === 'account') {
    emit(doc.email, 1);
  }
}

そしてreduce関数_countと同じように. 上記のように1を発行する場合も機能します。上記のようにドキュメントにフィールドを設定してチェックすることは単なる慣例ですが、役立つ場合もあります。保存しているのがユーザー アカウントだけの場合、おそらくそれは必要ありません。_sumtype

今、そのようにドキュメントを挿入しているとしましょう...

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、重複したメールを挿入した可能性があります。そのため、ドキュメントを削除し、典型的な SQL データベースの応答と同様に、Email Address must be Uniqueの行に沿ってエラーを返します。

削除は次のようになります...

DELETE /mydb/7c5c249481f311e3ad9ae13f952f2d55?rev=1-442a0ec9af691a6b1690379996ccaba6

短いディスカッション(気にする場合)

あなたが古き良き*SQLの背景から来ているなら、これは間違っていて奇妙に思えるでしょう. フリップアウトする前に、これらの点を考慮してください。一意性など、フィールドに制約があることは、schemaを意味します。CouchDB はスキーマレスです。*SQL から来るということは、考え方を変える必要があるということです。ACID とロックだけが方法ではありません。CouchDB には多くの柔軟性とパワーがあります。必要なものを得るためにそれをどのように使用するかは、ユース ケースの詳細と、従来のリレーショナル データベースの考え方の制限をどれだけうまく回避できるかによって異なります。

私が時々このように独自性を実装する理由は、それが私にとって非常に心地よく感じられるからです。CouchDB が更新の競合などを処理する方法について考えてみると、このアプローチは同じフローに従います。与えられたドキュメントを保存し、フィールドが一意かどうかを確認します。そうでない場合は、挿入したばかりのドキュメントをまだ保持している便利なハンドルで取得し、たとえば削除するなど、一意性の必要性を解決するために何かを行います。

上記の例では、ドキュメントを作成するにメール アドレスの一意性を確認したくなるかもしれません。POST気をつけて!!複数のことが起こっている場合、メールが存在するかどうかを確認した直後に、同じメールアドレスを持つ別のドキュメントがデータベースに挿入される可能性がありますが、! そうすると、重複したメールが残ります。POST

の前にメールアドレスのチェックも実行しても問題ありませんPOST。たとえば、ユーザーがフォームに入力していて、電子メール フィールドの値をデータベースに ajax できる場合、これは良い考えです。メールアドレスが存在することをユーザーに事前に警告したり、送信を防止したりできます。ただし、すべての場合において、ドキュメントを作成した後に常に一意であることも確認する必要があります。その後、必要に応じて対応します。UI 側から見ると、上記の手順は、一意性制約を利用して従来の *SQL データベースから取得した結果と何ら変わりはないように見えます。POST

何か問題が発生する可能性はありますか? はい。このことを考慮。joe@example.orgがまだデータベースに存在していないとします。2 つのドキュメントがほぼ同時にデータベースに保存されます。Doc APOSTed、次にDoc Bは ですが、 Doc APOSTedの一意性を確認する前に. したがって、 Doc Aの一意性チェックを行うと、joe@example.orgがデータベースに 2 回あることがわかります。そのため、削除して問題を報告します。しかし、 Doc Aを削除する前に、 Doc Bの一意性チェックも行われ、 joe@example.orgに対して同じカウント値が取得されるとしましょう。今は両方 POST POSTPOSTsたとえjoe@example.orgがもともとデータベースに存在しなかったとしても、拒否されます! つまり、値が一致する 2 つのドキュメントがほぼ同時にアプリに入力された場合、それらのドキュメントが互いを認識し、その値が既にデータベースにあると誤って判断する可能性があります。CouchDBには従来の RDB スタイルのロックPOSTsがないため、これを完全に防ぐことはできません。しかし、そのわずかな価格と引き換えに、マスター間のレプリケーションやその他の優れた機能を大量に手に入れることができます。買います!これが実装にとって大きな問題である場合は、何らかの再試行メカニズムを実装するなどして対処することができます。

最後に、CouchDB は本当にデータベースの宝石ですが、これだけを受け入れて防弾アプローチであると期待しないでください。実際には、特定のアプリケーションの詳細に大きく依存します。

于 2014-03-16T17:56:43.973 に答える
17

これは、CouchDB のあまり面白くない点の 1 つです。(ユーザーの例のように)変更可能な一意のフィールドを処理するために私が見つけた最良の方法は、一意の値をキーのコンポーネントとして使用して「ポインター」ドキュメントを作成し、それらを使用して一意の値を主張できるようにすることです。これを行う際の重要な部分は、プライマリ ドキュメントの予測可能なキーを用意し、一意のフィールド クレーム ドキュメントをプライマリ ドキュメントを保存する前に保存することです (キーの競合によってプライマリ ドキュメントを保存できなくなります)。

一意のユーザー名と一意の電子メールを持つユーザーの場合、主要なドキュメントは次のようになります。

user-1234: { username: "kurt", email: "kurt@localhost" }
user-9876: { username: "petunia", email: "pie@crust.com" }

一意のフィールド ポインターは次のようになります。

user-username-kurt: { primary_doc: "user-1234" }
user-email-kurt@localhost: { primary_doc: "user-1234" }
user-username-petunia: { primary_doc: "user-9876" }
user-email-pie@crust.com: { primary_doc: "user-9876" }

ユーザーを作成または更新するには、次の手順を実行します。

  1. ユーザードキュメントを準備し、必要に応じてキーを生成します
  2. 変更された一意のフィールドごとに「ポインター」ドキュメントを保存します
  3. これらのいずれかの保存に失敗した場合は、停止してエラーを修正してください
  4. プライマリ ユーザー ドキュメントを保存する

ステップ 3 は少し考えます。たとえば、変更されていないフィールドに対して一意の値を主張しようとは思わないでしょう。可能ですが、その値を既に所有しているユーザーの値を要求している場合を処理するために、追加のロジックを配置する必要があります。

ステップ 3 は、人々が古い主張された値を取得できるようにするのに適した場所です。たとえば、あるユーザーがユーザー名 kurt を「解放」した場合、その特定のドキュメントを更新して、新しいキーが使用されていないことを確認した後、新しいキーを指すようにできます。別の方法は、要求された一意の値が変更されたときにクリアすることです。どちらがより少ない作業になるかはわかりません。古い主張された値を残すことは、私にとって最も理にかなっています。

このソリューションの興味深い点は、作成されたポインター ドキュメントを使用する必要がないことです。ユーザー ドキュメントで通常どおりビューを作成し、それらを使用してメールまたはユーザー名でクエリを実行できます。

このセットアップでは、ユーザー キーのカスケードを気にせずにリレーションシップを行うこともできます。あなたのことはわかりませんが、私のユーザー ドキュメントは、システム内のほぼすべてのドキュメントから参照されています。ユーザーキーを変更すると、お尻に大きな苦痛が生じます。

于 2009-12-19T17:42:58.073 に答える
9

場合によります。マルチマスターでレプリケートされたケースを考えてみましょう。競合するエントリがそこに追加され、各マスター内で一貫性が保たれている可能性がありますが、レプリケートされると一貫性がなくなります。あなたはcouchdbサーバーを1つしか使用していないかもしれませんが、一般的に、マルチマスターのケースを想定して設計されており、複製されていない単一のサーバーでのみ正しく機能する機能は組み込まれていません。

単一サーバーのケースのみに関心がある場合は、ネットワーク サポートを使用してカウチ js を再構築し、validate_doc_update()関数で http クエリを実行して DB に対してクエリを実行し、電子メール アドレスが既に使用されているかどうかを確認し、次の場合は更新に失敗する可能性があります。それで。検証メカニズムの詳細については、こちらを確認してください。代わりに、すべての一意性を id フィールドに (直接またはハッシュを介して) 埋め込み、ユーザーがそれに影響する何かを変更した場合は、ドキュメントの移動に対処します。

于 2009-10-18T14:16:07.593 に答える