CouchDB HTTP ドキュメント API の概要
名前付けとアドレス指定
CouchDB に格納されたドキュメントには DocID があります。DocID は、大文字と小文字の区別がある文字列による識別子で、ドキュメントを一意に識別します。一つのデータベース内で 2 つのドキュメントが同一の識別子を持つことはできません。同一の識別子を持つドキュメントは、同じドキュメントとみなされます。
http://localhost:5984/test/some_doc_id http://localhost:5984/test/another_doc_id http://localhost:5984/test/BA1F48C5418E4E68E5183D5BD1F06476
上の URL はそれぞれ、test というデータベース内の some_doc_id、another_doc_id、BA1F48C5418E4E68E5183D5BD1F06476 を指しています。
有効なドキュメント ID
DocID の中に / を持つことはできますが、このようなドキュメントを URL で指定する場合には、必ず / を %2F にエンコードしなければなりません。1 つの特別な例は _design/ ドキュメントで、_design の後では、/ も、/ の代わりの %2F も、どちらも受け付けます。ただし、好ましいのは / の方で、DocID のほかの部分では %2F にする必要があります。
Q:有効なドキュメント ID の規則を教えてください。例を見る限りでは、[a-zA-Z0-9_] しか使えないようですが? マルチバイト UTF-8 文字についてはどうなのでしょうか。_ 以外の英数字でない文字も使えますか?
A:ドキュメント ID に関する制限は、データベース・レベルではまだありません。ただし、URL の中でマルチバイト文字を使った時にどうなるかについて、私自身はテストしていません。「うまく動作する」可能性はありますが、マルチバイト文字のエスケープ/エンコード/デコード処理については、おそらく別の場所で行う必要があると思います。現時点では、何ら「特別なところのない」、有効な URI 文字のみを対象として考えています。
データベース名に厳格な制限があるのは、データベース名とファイルとのマッピングをシンプルにするためです。データベースは複数のオペレーティングシステムにまたがってレプリケートする必要があるので、ファイルの名前付けは最小公分母にする必要がありました。
JSON
CouchDB のドキュメントは、JSON オブジェクトです。(URL クエリー引数に ?full=true を含めた場合は、メタデータのリビジョン情報も含まれます。)
次に示すのは、ドキュメントの例です。
{
"_id":"some_doc_id",
"_rev":"D1C946B7",
"Subject":"I like Plankton",
"Author":"Rusty",
"PostedDate":"2006-08-15T17:30:12-04:00",
"Tags":["plankton", "baseball", "decisions"],
"Body":"I decided today that I don't like baseball. I like plankton."
}
ドキュメントはどんな JSON オブジェクトでも構いませんが、_ という接頭辞で始まる名前を持つトップレベルのフィールドはすべて、CouchDB 自身のために予約されています。たとえば、上の例の中の _id フィールドや _rev フィールドがこれに該当します。
もう一つ例を示しましょう。
{
"_id":"discussion_tables",
"_rev":"D1C946B7",
"Sunrise":true,
"Sunset":false,
"FullHours":[1,2,3,4,5,6,7,8,9,10],
"Activities": [
{"Name":"Football", "Duration":2, "DurationUnit":"Hours"},
{"Name":"Breakfast", "Duration":40, "DurationUnit":"Minutes", "Attendees":["Jan", "Damien", "Laura", "Gwendolyn", "Roseanna"]}
]
}
デフォルトでは、構造がフラットである点に注意してください。上の例の場合、Activities の属性は、ユーザーが指定した構造になっています。
HTTP 経由でのドキュメントの操作
GET
ドキュメントを取得するには、取得するドキュメントの URL に対して GET 操作を実行します。
GET /somedatabase/some_doc_id HTTP/1.0
サーバーからは次のようなレスポンスが返ってきます。
HTTP/1.1 200 OK
Date: Thu, 17 Aug 2006 05:39:28 +0000GMT
Content-Type: application/json
Connection: close
{
"_id":"some_doc_id",
"_rev":"946B7D1C",
"Subject":"I like Plankton",
"Author":"Rusty",
"PostedDate":"2006-08-15T17:30:12Z-04:00",
"Tags":["plankton", "baseball", "decisions"],
"Body":"I decided today that I don't like baseball. I like plankton."
}
以前のリビジョンへのアクセス
リビジョンについては、「ドキュメントのリビジョン」も参照してください。
上に示した例は、最新のリビジョンを取得する場合の例です。次の構文を使えば、特定のリビジョンを取得することができます。
GET /somedatabase/some_doc_id?rev=946B7D1C HTTP/1.0
ドキュメントにどのようなリビジョンがあるか調べるには、次のようにします。
GET /somedatabase/some_doc_id?revs=true HTTP/1.0
この操作を実行すると、ドキュメントの最新のリビジョンが返されますが、追加のフィールド _revisions には、利用可能なリビジョン ID のリストが値として含まれています。これらのリビジョンのすべてが必ずしも利用可能であるわけではない点に注意してください。 たとえば、古いリビジョンの内容がデータベースの圧縮によって削除されている場合もあれば、レプリケートされたリビジョンの場合には、別のデータベースにしか存在しないこともあります。
利用可能なドキュメントのリビジョンの詳細を取得するには、revs の代わりに revs_info パラメータを使います。この場合、JSON 形式の結果には、次の例に示すように、オブジェクトの配列である _revs_info プロパティが含まれています。
{
"_revs_info": [
{"rev": "123456", "status": "disk"},
{"rev": "234567", "status": "missing"},
{"rev": "345678", "status": "deleted"},
]
}
ここで、disk は当該リビジョンがディスクに格納されていて、まだ取得可能であることを示します。これ以外の値は、当該リビジョンの内容が取得できないことを示します。
PUT
新しくドキュメントを作成するには、POST 操作または PUT 操作を使用できます。名前の付いたドキュメントを PUT 操作で作成または更新するには、URL にドキュメントの場所を指定する必要があります。
次に示すのは、HTTP PUT 操作の例です。この操作を実行すると、CouchDB サーバーは新しいリビジョン ID を生成し、これをドキュメントと一緒に保存します。
PUT /somedatabase/some_doc_id HTTP/1.0
Content-Length: 245
Content-Type: application/json
{
"Subject":"I like Plankton",
"Author":"Rusty",
"PostedDate":"2006-08-15T17:30:12-04:00",
"Tags":["plankton", "baseball", "decisions"],
"Body":"I decided today that I don't like baseball. I like plankton."
}
サーバーからは次のようなレスポンスが返ってきます。
HTTP/1.1 201 Created
Date: Thu, 17 Aug 2006 05:39:28 +0000GMT
Content-Type: application/json
Connection: close
{"ok": true, "id": "some_doc_id", "rev": "946B7D1C"}
既存のドキュメントを更新する場合も、PUT リクエストを実行します。この場合、JSON ボディには _rev プロパティが含まれていなければならず、CouchDB はこの_rev プロパティによって、どのリビジョンを基に編集が行われたかを特定します。リビジョン番号が、データベースに現在格納されているドキュメントのリビジョンと一致しない場合には、409 Conflict エラーが返されます。
リビジョン番号がデータベース内にあるものと一致すると、新しいリビジョン番号が生成されてクライアントに返されます。
次に例を示します。
PUT /somedatabase/some_doc_id HTTP/1.0
Content-Length: 245
Content-Type: application/json
{
"_id":"some_doc_id",
"_rev":"946B7D1C",
"Subject":"I like Plankton",
"Author":"Rusty",
"PostedDate":"2006-08-15T17:30:12-04:00",
"Tags":["plankton", "baseball", "decisions"],
"Body":"I decided today that I don't like baseball. I like plankton."
}
ドキュメント some_doc_id のリビジョン 946B7D1C がデータベースに格納されていれば、サーバーからは次のようなレスポンスが返ってきます。
HTTP/1.1 201 Created
Date: Thu, 17 Aug 2006 05:39:28 +0000GMT
Content-Type: application/json
Connection: close
{"ok":true, "id":"some_doc_id", "rev":"2774761002"}
一方、更新時の衝突が発生した場合(データベースに格納されているのがドキュメント some_doc_id のリビジョン 946B7D1C でない場合)には、サーバーから次のようなレスポンスが返ってきます。
HTTP/1.1 409 Conflict
Date: Thu, 17 Aug 2006 05:39:28 +0000GMT
Content-Length: 33
Connection: close
{"error":"conflict","reason":"Document update conflict."}
クエリー・オプション batch=ok を使うと、高いスループットを得られますが、その代償として処理の保証のレベルは低くなります。このオプションを使って PUT (または、あとで説明するドキュメント POST) を実行すると、ドキュメントはすぐにはディスクには書き込まれません。代わりに、ドキュメントはユーザーごとにメモリ内に 1 秒ほど(またはメモリ内のドキュメント数が一定の値になるまで)保持されます。定められたしきい値を超えると、ドキュメントはディスクにコミットされます。CouchDB 側では、ドキュメントがディスクに書き込まれるのを待ってレスポンスを返す代わりに、ただちに HTTP 202 Accepted レスポンスを返します。
batch=ok オプションは、重要なデータの処理には適していませんが、更新された部分の一部がクラッシュによって失われてもそれほど影響の大きくないロギングなどのアプリケーションで使うのに適しています。バッチ内のドキュメントは、_ensure_full_commit API を使って手動でフラッシュすることもできます。
POST
POST 操作は、サーバーが生成した DocID を使ってドキュメントを新規作成するときに使用できます。名前の付いたドキュメントを作成するには、POST ではなく PUT メソッドを使ってください。なお、可能な場合には POST の使用は避けてください。プロキシやその他のネットワーク上の中間ホストが POST リクエストを再送することがあり、結果として重複ドキュメントが作成される可能性があるからです。クライアント・ソフトウェアが、生成された UUID の一意性をを保証できない場合には、/_uuids?count=100 に対して GET を実行し、以後の PUT リクエストで使用できるドキュメント ID のリストを取得してください。注意が必要なのは、/_uuids の呼び出しでは、既存のドキュメント ID のチェックは行われないという点です。既存のドキュメント ID と同じ ID を使ってドキュメントを保存しようとすると、衝突が検出されます。
次に示すのは、HTTP POST 操作の例です。この操作を実行すると、CouchDB サーバーは新しい DocID とリビジョン ID を作成し、これをドキュメントと一緒に保存します。
POST /somedatabase/ HTTP/1.0
Content-Length: 245
Content-Type: application/json
{
"Subject":"I like Plankton",
"Author":"Rusty",
"PostedDate":"2006-08-15T17:30:12-04:00",
"Tags":["plankton", "baseball", "decisions"],
"Body":"I decided today that I don't like baseball. I like plankton."
}
サーバーからは次のようなレスポンスが返ってきます。
HTTP/1.1 201 Created
Date: Thu, 17 Aug 2006 05:39:28 +0000GMT
Content-Type: application/json
Connection: close
{"ok":true, "id":"123BAC", "rev":"946B7D1C"}
DELETE
ドキュメントを削除するには、rev パラメータにドキュメントの最新のリビジョンを指定し、ドキュメントの場所に対して DELETE 操作を実行します。成功すると、削除スタブのリビジョン ID が返されます。
DELETE /somedatabase/some_doc?rev=1582603387 HTTP/1.0
もう一つの方法として、rev パラメータを etag ヘッダーフィールドの If-Match に指定することもできます。.
DELETE /somedatabase/some_doc HTTP/1.0 If-Match: "1582603387"
サーバーからは次のようなレスポンスが返ってきます。
HTTP/1.1 200 OK
Date: Thu, 17 Aug 2006 05:39:28 +0000GMT
Content-Type: application/json
Connection: close
{"ok":true,"rev":"2839830636"}
COPY
この操作は、非標準の HTTP 拡張であることに注意してください。
HTTP COPY リクエストを実行すると、ドキュメントをコピーできます。この操作を実行すれば、CouchDB からあらかじめドキュメントを取得していなくても、ドキュメントの内容(と添付ファイル)を、別のドキュメント ID を持つ新しいドキュメントに複製することができます。コピー先のドキュメント(ターゲット・ドキュメント)を指定するには、Destination ヘッダーを使います。
複数のデータベース間でドキュメントをコピーすることはできません。また、バルクコピー操作を実行することも(まだ)できません。
COPY /somedatabase/some_doc HTTP/1.1 Destination: some_other_doc
既存のドキュメントに上書きする場合には、ターゲット・ドキュメントのリビジョンを Destination ヘッダーの rev パラメータで指定する必要があります。
COPY /somedatabase/some_doc HTTP/1.1 Destination: some_other_doc?rev=rev_id
上のいずれの操作の場合も、レスポンスにはターゲット・ドキュメントのリビジョンが含まれています。
HTTP/1.1 201 Created
Server: CouchDB/0.9.0a730122-incubating (Erlang OTP/R12B)
Etag: "355068078"
Date: Mon, 05 Jan 2009 11:12:49 GMT
Content-Type: text/plain;charset=utf-8
Content-Length: 41
Cache-Control: must-revalidate
{"ok":true,"id":"some_other_doc","rev":"355068078"}
MOVE
バージョン 0.8 と 0.9 の間の CouchDB Trunk には半年足らずの間、非標準の MOVE メソッドが含まれていました。MOVE は、実質的に COPY & DELETE であり、CouchDB の側では、COPY から DELETE までの間のアトミック性を単一ノードまたは複数ノード上において合理的に保証することができないため、MOVE メソッドは CouchDB 0.9 のリリース前に削除されました。
バルクドキュメント
一度にまとめて複数のドキュメントを編集する方法の詳細については、「HTTP バルクドキュメント API」を参照してください。
すべてのドキュメント
データベース内のすべてのドキュメントの一覧を取得するには、特別な _all_docs URI を使用します。これは特別なビューなので、「HTTP ビュー API」の「クエリー・オプション」が適用されます。
GET somedatabase/_all_docs HTTP/1.0
この操作を実行すると、すべてのドキュメントの一覧と各ドキュメントのリビジョン ID が、DocID(大文字小文字を区別します)を基準に並べ替えられた形で返されます。
HTTP/1.1 200 OK
Date: Thu, 17 Aug 2006 05:39:28 +0000GMT
Content-Type: application/json
Connection: close
{
"total_rows": 3, "offset": 0, "rows": [
{"id": "doc1", "key": "doc1", "value": {"rev": "4324BB"}},
{"id": "doc2", "key": "doc2", "value": {"rev":"2441HF"}},
{"id": "doc3", "key": "doc3", "value": {"rev":"74EC24"}}
]
}
返される結果の並べ替えの順序を逆にするには、クエリー引数に descending=true を指定します。
実際に操作を実行すると、前回と同じで順序だけが逆になった結果が返されます。
HTTP/1.1 200 OK
Date: Thu, 17 Aug 2006 05:39:28 +0000GMT
Content-Type: application/json
Connection: close
{
"total_rows": 3, "offset": 0, "rows": [
{"id": "doc3", "key": "doc3", "value": {"_rev":"74EC24"}}
{"id": "doc2", "key": "doc2", "value": {"_rev":"2441HF"}},
{"id": "doc1", "key": "doc1", "value": {"_rev": "4324BB"}},
]
}
クエリー文字列パラメータ startkey、endkey、および limit を使うと、結果セットを限定することができます。たとえば、次の操作を実行したとします。
GET somedatabase/_all_docs?startkey="doc2"&limit=2 HTTP/1.0
この場合、次のような結果が返されます。
HTTP/1.1 200 OK
Date: Thu, 17 Aug 2006 05:39:28 +0000GMT
Content-Type: application/json
Connection: close
{
"total_rows": 3, "offset": 1, "rows": [
{"id": "doc2", "key": "doc2", "value": {"_rev":"2441HF"}},
{"id": "doc3", "key": "doc3", "value": {"_rev":"74EC24"}}
]
}
特定の範囲のドキュメントを対象にするには、endkey を使用します。
GET somedatabase/_all_docs?startkey="doc2"&endkey="doc3" HTTP/1.0
この操作を実行すると、doc2 と doc3 を含み、これらの間にあるキー、たとえば doc2-b や doc234 を取得できます。
上の 2 つのどちらの操作でも、descending を組み合わせて使用できます。
GET somedatabase/_all_docs?startkey="doc2"&limit=2&descending=true HTTP/1.0
次のような結果が返されます。
HTTP/1.1 200 OK
Date: Thu, 17 Aug 2006 05:39:28 +0000GMT
Content-Type: application/json
Connection: close
{
"total_rows": 3, "offset": 1, "rows": [
{"id": "doc3", "key": "doc3", "value": {"_rev":"74EC24"}}
{"id": "doc2", "key": "doc2", "value": {"_rev":"2441HF"}},
]
}
_all_docs に対するリクエスト include_docs=true を追加すると、メタデータだけでなく、ドキュメント自体も返されます。
all_docs_by_seq
この操作を実行すると、更新または削除されたすべてのドキュメントを、更新または削除操作が行われた順に表示することができます。
GET somedatabase/_all_docs_by_seq HTTP/1.0
次のような結果が返されます。
HTTP/1.1 200 OK
Date: Fri, 8 May 2009 11:07:02 +0000GMT
Content-Type: application/json
Connection: close
{
"total_rows": 4, "offset": 0, "rows": [
{"id": "doc1", "key": "1", "value": {"rev":"1-4124667444"}},
{"id": "doc2", "key": "2", "value": {"rev":"1-1815587255"}},
{"id": "doc3", "key": "3", "value": {"rev": "1-1750227892"}},
{"id": "doc4", "key": "4", "value": {"rev": "2-524044848", "deleted": true}}
]
}
_all_docs_by_seq では、startkey や include_docs など、すべてのビュー・パラメータを使用できます。ただし startkey は、このビューに適用された場合には排他的な動作をします。このおかげで、前回のクエリーで返された最後のドキュメントのシーケンス ID を startkey に指定するような使い方が可能になっています。startkey は排他的なので、同一のドキュメントが 2 回処理されることはありません。
添付ファイル
ドキュメントは、ちょうどメールのように添付ファイルを持つことができます。添付ファイルには 2 つの扱い方があります。一つはドキュメントにインラインで格納する方法で、この方法を最初に説明します。もう一つは、添付ファイルに対して独立した REST API を実行する方法で、この方法についてはこのページの下の方で説明します。
添付ファイル名についての注意事項:添付ファイルには / 文字を埋め込むことができ、これらの文字はエスケープされずに CouchDB に送信されます。この仕組みを使うと、ドキュメントの下に添付ファイルのサブツリーを作ることができます。DocID では、すべての / 文字を必ず %2F にエスケープしなければなりません。したがって、添付ファイル d/e/f.txt を持つ a/b/c というドキュメントがあった場合には、
http://couchdb/db/a%2fb%2fc/d/e/f.txt でこのドキュメントにアクセスできることになります。
インライン添付ファイル
作成された添付ファイルは、ドキュメントの特別な _attachments 属性に格納されます。このとき添付ファイルは JSON 構造にエンコードされ、この構造の中に、名前、content_type、それに添付ファイルを base64 でエンコードしたデータが保持されます。1 つのドキュメントは任意の数の添付ファイルを持つことができます。
ドキュメントを取得するときは、添付ファイルの実際のデータは含まれず、メタデータだけが返されます。実際のデータは、特別な URI を使って別に取得する必要があります。
1 つのリクエストでドキュメントの添付ファイルにアクセスする必要がある場合には、?attachments=true という URL パラメータを渡すことで、base64 にエンコードされた形で JSON 構造に含まれるデータを取得することができます。このリクエストを実行すると CouchDB に非常に重い負荷がかかるため、実際の処理内容をよく理解している場合以外は、この機能の使用はお勧めできません
。
次に示すのは、添付ファイルのあるドキュメントの作成例です。
{
"_id":"attachment_doc",
"_attachments":
{
"foo.txt":
{
"content_type":"text\/plain",
"data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
}
}
}
送信する base64 データは 1 行 で送信する必要がある点に注意してください。したがって、復帰文字や改行文字はあらかじめデータから削除しておく必要があります。
上のドキュメントを取得します。
GET /database/attachment_doc
CouchDB からは次のようなレスポンスが返ってきます。
{
"_id":"attachment_doc",
"_rev":1589456116,
"_attachments":
{
"foo.txt":
{
"stub":true,
"content_type":"text\/plain",
"length":29
}
}
}
"stub":true 属性は、添付ファイル全体を返したわけではないことを示している点に注意してください。また、length 属性が自動的に追加されている点にも注意してください。
添付ファイルに対してリクエストを実行します。
GET /database/attachment_doc/foo.txt
CouchDB からは次のようなレスポンスが返ってきます。
This is a base64 encoded text
自動的にデコードされています。
複数の添付ファイル
次に示すのは、添付ファイルのあるドキュメントの作成例です。
{
"_id":"attachment_doc",
"_attachments":
{
"foo.txt":
{
"content_type":"text\/plain",
"data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
},
"bar.txt":
{
"content_type":"text\/plain",
"data": "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
}
}
}
スタンドアロンの添付ファイル
注:この機能は、CouchDB のバージョン 0.9 で追加されました。それ以前のバージョンでは利用できません。
CouchDB では、実際のドキュメントには手を加えずに、添付ファイルを作成、変更、削除することができます。さらにこの場合、データを base64 でエンコードする必要もありません。CouchDB もクライアントも base64 の変換を行う必要がないため、リクエストの処理を大幅に高速化することができます。
Content-Type ヘッダーを使って MIME タイプを指定する必要があります。CouchDB は、要求があれば、指定された Content-Type で添付ファイルを返します。
添付ファイルを作成するには、次のようにします。
PUT somedatabase/document/attachment?rev=123 HTTP/1.0 Content-Length: 245 Content-Type: image/jpeg <JPEG data>
CouchDB からは次のようなレスポンスが返ってきます。
{"ok": true, "id": "document", "rev": "765B7D1C"}
この操作は、存在しないドキュメントに対しても実行できる点に注意してください。この場合、ドキュメントと添付ファイルが暗黙のうちに作成されます。また、この場合には リビジョン ID は指定できません。
添付ファイルを変更するには、次のようにします。
PUT somedatabase/document/attachment?rev=765B7D1C HTTP/1.0 Content-Length: 245 Content-Type: image/jpeg <JPEG data>
CouchDB からは次のようなレスポンスが返ってきます。
{"ok": true, "id": "document", "rev": "766FC88G"}
添付ファイルを削除するには、次のようにします。
DELETE somedatabase/document/attachment?rev=765B7D1C HTTP/1.0
CouchDB からは次のようなレスポンスが返ってきます。
{"ok":true,"id":"document","rev":"519558700"}
添付ファイルを取得するには、次のようにします。
GET somedatabase/document/attachment HTTP/1.0
CouchDB からは次のようなレスポンスが返ってきます。
Content-Type:image/jpeg <JPEG data>
ETag とキャッシュ
CouchDB は、ドキュメントのリクエストに対して ETag を送信します。ETag ヘッダーの内容は、ドキュメントのリビジョンを引用符で囲ったものです。
たとえば、次のような GET リクエストを実行したとします。
GET /database/123182719287
レスポンスのヘッダーは次のようになります。
cache-control: no-cache, pragma: no-cache expires: Tue, 13 Nov 2007 23:09:50 GMT transfer-encoding: chunked content-type: text/plain;charset=utf-8 etag: "615790463"
POST リクエストも、新規作成ドキュメントと更新ドキュメントのいずれについても ETag ヘッダーを返します。