1、簡介
Apache CouchDB 是一個面向文檔的數據庫管理系統。它提供以 JSON 作為數據格式的 REST 接口來對其進行操作,並可以通過視圖來操縱文檔的組織和呈現。 CouchDB 是 Apache 基金會的頂級開源項目。
CouchDB里面有一個小問題,就是它的一些預定義的key
有時候有下划線,有時候又不能有下划線開頭,例如id``rev
這樣的。沒有固定下來,不知道是為何。
參考資料:
2、安裝
直接去官網下載對應的安裝包安裝即可。
安裝之后服務會自動啟動,監聽5984
(HTTP)和6984
(HTTPS)端口,可以直接在瀏覽器訪問http://127.0.0.1:5984/_utils地址,打開Web交互操作頁面。
在Your Account
頁面創建服務管理員用戶或修改用戶密碼。
2、HTTP接口簡單使用
CounchDB相關文檔可以查閱http://docs.couchdb.org
這里只簡單記錄一下我用到的幾個HTTP
接口。
CouchDB HTTP接口返回狀態碼簡述
這里只列出常用的狀態碼及其說明,特定的請求類型不同的狀態碼信息在相應的API參考文檔中有詳細說明。
狀態碼 | 狀態 | 說明 |
---|---|---|
200 | OK | 請求已經成功完成 |
201 | Created | 文檔創建成功 |
202 | Accepted | 請求已被接受,但相應的操作可能尚未完成。 這用於后台操作,例如數據庫壓縮 |
304 | Not Modified | 請求的其他內容尚未修改。這與ETag系統一起使用以識別返回的信息的版本 |
400 | Bad Request | 不良請求。該錯誤可能由於請求URL、Path或Header出錯。 提供的MD5值和content不匹配也會觸發此錯誤,因為這可能是消息內容被破壞 |
401 | Unauthorized | 使用提供的授權無法獲得所請求的項目,或未提供授權 |
403 | Forbidden | 禁止請求的項目或操作 |
404 | Not Found | 找不到請求的內容。 返回的content將相關信息,返回的JSON對象包含鍵 error 和reason (原因)。例如:{"error":"not_found","reason":"no_db_file"} |
405 | Method Not Allowed | 對請求的URL使用無效的HTTP請求類型發出了請求。 例如,您需要POST時請求PUT。此類型的錯誤也可能由無效的URL字符串觸發。 |
406 | Not Acceptable | 服務器不支持請求的內容類型 |
409 | Conflict | 請求導致更新沖突 |
412 | Precondition Failed | 來自客戶端的請求標頭和服務器的功能不匹配 |
413 | Request Entity Too Large | 請求實體太大 |
415 | Unsupported Media Type | 不支持的媒體類型 |
416 | Requested Range Not Satisfiable | 服務器無法滿足請求頭中指定的范圍 |
417 | Expectation Failed | 批量發送文檔時,批量加載操作失敗 |
201 | Internal Server Error | 請求無效,原因是提供的JSON無效,或者作為請求的一部分提供了無效信息 |
2.1、認證接口
認證的方式官網文檔中介紹的有三種,我只對前面兩種進行了操作。具體的可見http://docs.couchdb.org/en/latest/api/server/authn.html
要學習HTTP協議的認證相關內容,可以看Mozilla的文檔,全中文的。https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Authentication
2.1.1 Basic Authentication
基本身份驗證Basic authentication (RFC 2617)是一個比較快速方便的認證方式,對於需要認證的接口,每次都要發送用戶憑據。
這個認證方式具體的操作,就是在發送請求的時候,在HTTP頭
中添加Authorization
消息頭,其內容為Bascic 使用Basic64編碼的賬號:密碼
。
例如,賬號為user1
,密碼為abcd1234
的用戶,那么他發送的一個請求的示例如下:
curl命令請求
curl --request GET \
--url http://localhost:5984/db1/doc1 \
--header 'authorization: Basic bzphc2QxMjM='
}'
請求報文如下:
GET / HTTP/1.1
Accept: application/json
Authorization: Basic dXNlcjE6YWJjZDEyMzQ=
Host: localhost:5984
其中dXNlcjE6YWJjZDEyMzQ=
即為user1:abcd1234
的Base64編碼值。
2.1.2 Cookie Authentication
cookie身份驗證(RFC 2109),CouchDB會生成一個token
(令牌),客戶端可以使用該token
(令牌)來處理CouchDB的下一個請求。 token
(令牌)在超時前有效。 當CouchDB在后續請求中看到有效token
(令牌)時,它將通過此token
(令牌)對用戶進行身份驗證,而無需再次請求密碼。 默認情況下,cookie有效期為10分鍾,可以在配置頁面進行修改,也可以使cookie持久化。
具體操作過程如下:
1、獲取token
要獲取一個token(實際就是獲取響應消息頭中的Set-Cookie值),必須將用戶名和密碼發送到CouchDB服務器,這個通過_session
接口來實現。
_session接口說明
_session
是一個POST
請求接口,只需要設置請求消息頭Content-Type
參數,其值可以是application/x-www-form-urlencoded
或者application/json
。請求的content實體內容格式根據Content-Type
的值確定。
更多具體的參數請參考官方文檔。
curl命令如下(發送的是json格式的):
curl --request POST \
--url http://localhost:5984/_session \
--header 'content-type: application/json' \
--data '{
"name":"user1",
"password":"abcd1234"
}'
報文示例如下:
POST /_session HTTP/1.1
Accept: application/json
Content-Length: 24
Content-Type: application/x-www-form-urlencoded
Host: localhost:5984
name=root&password=relax
使用JSON格式的請求報文示例:
POST /_session HTTP/1.1
Accept: application/json
Content-Length: 37
Content-Type: application/json
Host: localhost:5984
{
"name": "root",
"password": "relax"
}
成功返回的響應內容示例如下:
HTTP/1.1 200 OK
Cache-Control: must-revalidate
Content-Length: 43
Content-Type: application/json
Date: Mon, 03 Dec 2012 01:23:14 GMT
Server: CouchDB (Erlang/OTP)
Set-Cookie: AuthSession=cm9vdDo1MEJCRkYwMjq0LO0ylOIwShrgt8y-UkhI-c6BGw; Version=1; Path=/; HttpOnly
{"ok":true,"name":"root","roles":["_admin"]}
返回的json對象中ok
的值表示操作狀態,name
的值是用戶名,relos
數組中是用戶的角色(一個用戶可以有多個角色)。
響應報文中的Set-Cookie消息頭的值,就是后面所有請求需要使用到的Cookie內容。
返回的狀態碼可能是以下三個
狀態碼 | 狀態 | 說明 |
---|---|---|
200 | OK | 成功通過身份驗證 |
302 | Found | 成功驗證后重定向 |
401 | Unathorized | 無法識別用戶名或密碼 |
2、使用token
通過上一步獲取到Cookie
值之后,就可以對一些需要認證的接口進行操作。簡單來說,就是在發送請求的時候添加Cookie
消息頭。
curl命令示例
curl --request GET \
--url http://localhost:5984/db1 \
--header 'cookie: AuthSession=cm9vdDo1MEJCRkYwMjq0LO0ylOIwShrgt8y-UkhI-c6BGw
# 或將上面的--header不要,改為使用下面的參數
# --cookie 'AuthSession=cm9vdDo1MEJCRkYwMjq0LO0ylOIwShrgt8y-UkhI-c6BGw'
3、刪除token
刪除一個token
即表示這個Cookie
不再使用了,也就是關閉這個會話。但因為CouchDB Cookie是無狀態的,所以此操作實際並不起什么作用。
刪除使用一個DELETE
請求,curl命令示例如下:
curl --request DELETE \
--url http://localhost:5984/_session \
--header 'cookie: AuthSession=cm9vdDo1MEJCRkYwMjq0LO0ylOIwShrgt8y-UkhI-c6BGw;
請求報文示例如下:
DELETE /_session HTTP/1.1
Accept: application/json
Cookie: AuthSession=cm9vdDo1MjA1NEVGMDo1QXNQkqC_0Qmgrk8Fw61_AzDeXw
Host: localhost:5984
響應報文示例如下:
HTTP/1.1 200 OK
Cache-Control: must-revalidate
Content-Length: 12
Content-Type: application/json
Date: Fri, 09 Aug 2013 20:30:12 GMT
Server: CouchDB (Erlang/OTP)
Set-Cookie: AuthSession=; Version=1; Path=/; HttpOnly
{
"ok": true
}
2.2 創建與刪除數據庫
創建數據庫就是向服務器發送一個特定的PUT
請求即可。
curl命令示例如下:
curl --request PUT \
--url http://127.0.0.1:5984/db1 \
--cookie 'AuthSession=cm9vdDo1MEJCRkYwMjq0LO0ylOIwShrgt8y-UkhI-c6BGw; Version=1; Path=/; HttpOnly'
這里的URL
中的路徑中的db1
就是數據庫名稱,這個名稱只能是小寫字母和數組,不能是別的。
如果已經存在或別的錯誤,返回的JSON中會有相關信息
{
"error": "file_exists",
"reason": "The database could not be created, the file already exists."
}
或數據庫名稱不合規則時候(__user1不符合規則)
{
"error": "illegal_database_name",
"reason": "Name: '__user1'. Only lowercase characters (a-z), digits (0-9), and any of the characters _, $, (, ), +, -, and / are allowed. Must begin with a letter."
}
刪除數據庫則向服務器發送一個DELETE
請求,參數與創建的一致。
curl命令示例如下:
curl --request DELETE \
--url http://127.0.0.1:5984/db1 \
--cookie 'AuthSession=cm9vdDo1MEJCRkYwMjq0LO0ylOIwShrgt8y-UkhI-c6BGw; Version=1; Path=/; HttpOnly'
不存在的時候也會返回相關的信息
{
"error": "not_found",
"reason": "Database does not exist."
}
2.3 插入、刪除、更新文檔(記錄)
2.3.1 插入文檔
插入文檔也使用PUT
請求,文檔必須插入到某個數據庫內。每個文檔都有一個_id
的主鍵,這個鍵的值不能以下划線開頭。如果沒有給定_id
則會自動生成一個,類似於f9ba72763355579163d36f31de002246
這樣的。
文檔的內容必須是一個合法的json格式。CouchDB中所有預定義的鍵值都是以下划線開頭的,所以所有的鍵都不能以下划線開頭。每個文檔都有一個版本號,創建的時候會自動添加_rev
字段表示。
curl插入單個文檔示例:
curl --request PUT \
--url http://localhost:5984/db1/%E4%B8%AD%E6%96%87 \
--header 'content-type: application/json'
--cookie 'AuthSession=bzo1QkJENkRCRToXLIOUaf3twhYIrs-eTTyTwJFB5w; Version=1; Path=/; HttpOnly' \
--data '
{
"字符串": "字符串值",
"數組": [
"值2",
2,
1.234
],
"對象":{
"key":"值"
}
}'
上面的路徑參數中的db1
就是先前創建的數據庫名,后面的%E4%B8%AD%E6%96%87
是鍵值
兩個字的URL編碼格式,也就是文檔的_id
值。
返回結果如下:
{
"ok": true,
"id": "鍵值",
"rev": "1-4fd4c8f7fe3d72bc579f6cfc45f6b0d4"
}
對於批量插入文檔,則需要使用_bulk_docs
命令來操作,這個命令需要使用POST
方法來請求。
批量插入的時候,將需要插入的多個文檔的內容,放入一個json數組中,每個文檔的_id
參數也必須明確指定。
示例如下:
curl插入多個文檔示例:
curl --request POST \
--url http://localhost:5984/db1/_bulk_docs \
--header 'content-type: application/json' \
--cookie AuthSession=bzo1QkJEOUM4QTr6P3NYSJprjQlgFYgeba6cwwBqJw \
--data '
{
"docs": [
{"_id":"s1","desc":"bobo","age":15},
{"_id":"s2","desc":"小紅","age":19},
{"_id":"s3","desc":"小明","age":22},
{"_id":"s4","desc":"ab","age":14}
]
}'
報文如下:
POST /db1/_bulk_docs HTTP/1.1
Host: localhost:5984
User-Agent: insomnia/6.0.2
Cookie: AuthSession=bzo1QkJENkRCRToXLIOUaf3twhYIrs-eTTyTwJFB5w
Content-Type: application/json
Accept: */*
Content-Length: 175
{
"docs": [
{"_id":"s1","desc":"bobo","age":15},
{"_id":"s2","desc":"小紅","age":19},
{"_id":"s3","desc":"小明","age":22},
{"_id":"s4","desc":"ab","age":14}
]
}
返回結果如下(數組中的每個對象,都和單個插入是一樣的):
[
{
"ok": true,
"id": "s1",
"rev": "1-5efc8527e2a56dc31e71abe36d365e38"
},
{
"ok": true,
"id": "s2",
"rev": "1-959ab718375f00a5b678ece00f68a1f7"
},
{
"ok": true,
"id": "s3",
"rev": "1-08ded6f70086c004a2b8ef6bb2439b88"
},
{
"ok": true,
"id": "s4",
"rev": "1-ccd6248a85ad565b2b12a3127c1b82d7"
}
]
2.3.2 刪除文檔
刪除文檔和刪除數據庫類似,也是使用DELETE
請求,使用URL
中的Path
部分指定數據庫和文檔_id
。
刪除的時候還必須指定文檔的版本,如果沒有指定版本,則會提示更新沖突。
curl刪除單個文檔示例:
curl --request DELETE \
--url 'http://localhost:5984/dbtest/s3?rev=1-08ded6f70086c004a2b8ef6bb2439b88' \
--cookie AuthSession=bzo1QkJEOUYxMjr2HOFipeXf2K3e3Qy8QmeKuHgTaw
返回結果示例:
{
"ok": true,
"id": "s3",
"rev": "2-eba8354ebc6964892c780f2d1435dd7f"
}
可以看到,刪除的結果中僅rev
值變化了。這時候如果再向數據庫中插入_id
為s3
的文檔,則其_rev
值將會變為3-xxxxx...xxx
,也就是版本號更新了。
對於刪除多個文檔,也是使用_bulk_docs
命令來操作。在POST提交的數據中,JSON對象中docs
數組下的每個對象都表示一個要刪除的文檔,必須含有文檔的_id
和_rev
參數,_deleted
參數用來指示操作為刪除操作。
curl刪除多個文檔示例:
curl --request POST \
--url http://127.0.0.1:5984/dbtest/_bulk_docs \
--header 'content-type: application/json' \
--cookie AuthSession=bzo1QkJEQTEzOTpmznvulsDVLy38QY1AemMA5LZXNQ \
--data '{
"docs": [
{
"_id": "s1",
"_rev": "5-64964a8e3910a7f17b8bd1b2942811bf",
"_deleted": true
},
{
"_id": "s2",
"_rev": "5-64964a8e3910a7f17b8bd1b2942811bf",
"_deleted": true
},
{
"_id": "s4",
"_rev": "3-e319f63912531859882a9495568fe7f4",
"_deleted": true
}
]
}'
返回結果如下:
[
{
"ok": true,
"id": "s1",
"rev": "6-266b9d0fda1846ccf8c37facbb2f03ba"
},
{
"id": "s2",
"error": "conflict",
"reason": "Document update conflict."
},
{
"ok": true,
"id": "s4",
"rev": "4-836b5d321c9eed62ffdbe124f21df167"
}
]
因為我把s2
的_rev
參數寫錯了,所以未能成功刪除s2
。
2.3.3 更新文檔
更新文檔和插入文件基本是一致的,只是更新文檔需要指定文檔的_rev
值,如果沒有指定則會返回更新沖突。
這里其實好理解可以這么認為CouchDB是沒有插入(刪除)操作的,當指定_id
的文檔不存在的時候,就會插入,否則就是更新。如果更新沒有指定_rev
參數或參數值不正確,則更新就不會成功,原因是Document update conflict
。
CouchDB不支持對JSON文檔進行部分更新,必須是全更新。也就是說不能添加或者刪除字段,也不能僅更新某些字段的值。更新文檔的時候_rev 參數必須是最新獲取的,因為任何修改操作都會引起
_rev`值變化。
curl更新單個文檔示例:
curl --request PUT \
--url http://localhost:5984/dbtest/doc4 \
--header 'content-type: application/json' \
--cookie 'AuthSession=bzo1QkJEN0ZFRToKbM2PpPtuAYMsfUoiml7T1NesyQ; Version=1; Path=/; HttpOnly' \
--data '
{
"_rev":"4-8cf3de6bbe77006418d2f74e570be270",
"地區": "北京北邊",
"新的鍵":"新的 值"
}'
返回和插入的返回時一樣的,這里就不寫了。
curl批量更新文檔示例:
curl --request POST \
--url http://localhost:5984/dbtest/_bulk_docs \
--header 'content-type: application/json' \
--cookie AuthSession=bzo1QkJEQTU4NDr5_i2OSSoponSuDz4rLu9fh4NHbw \
--data '
{
"docs": [
{"_id":"s1","_rev":"7-5efc8527e2a56dc31e71abe36d365e38","desc":"bobo2","age":56.32},
{"_id":"s2","_rev": "6-959ab718375f00a5b678ece00f68a1f7","desc":"小紅2","age":19.36}
]
}'
返回結果如下:
[
{
"ok": true,
"id": "s1",
"rev": "8-2951951ea0b4479fc1f2a0210dc83ebe"
},
{
"ok": true,
"id": "s2",
"rev": "7-50d7fd758615aa6aefbc6aa2ec86fd8b"
}
]
2.4 查詢記錄
查詢記錄可以查閱http://docs.couchdb.org/en/latest/api/database/find.html
這里就不再詳細寫具體的請求過程了。
2.4.1 _find查詢
_find
命令用來查詢摸個數據庫中的文檔,類似於MongoDB
的find
操作。
_find
命令也使用POST
方法請求,提交的數據是一個JSON
對象,格式介紹如下:
請求體JSON對象格式
字段 | 值類型 | 說明 |
---|---|---|
selector | object | 用於選擇文檔的標准JSON對象。 相關語法將在后面詳細敘述 |
limit | number | 接收返回的最大結果數。默認為25,可選 |
skip | number | 跳過前面的N個結果,可選 |
sort | object | 排序規則,可選 |
fields | array | 指定返回結果含有哪些字段的數組,省略則返回所有字段 |
use_index | string/array | 指示查詢使用的特定索引。 可以是 "<design_document>" 或 ["<design_document>", "<index_name>"] ,可選 |
r | number | 讀取結果的所需法定數量。(有副本的情況)默認為1,可選 |
bookmarks | string | 指定所需的結果頁面,用於分頁結果集。默認為null,可選 |
update | boolean | 是否在范圍結果之前更新索引。默認為true,可選 |
stable | boolean | 是否從stable 分片集返回視圖結果,可選 |
execution_stats | boolean | 是否在查詢響應中包含統計結果,默認false,可選 |
curl示例1,查詢所有age字段在5到20之間的文檔
curl --request POST \
--url http://localhost:5984/dbtest/_find \
--header 'content-type: application/json' \
--cookie AuthSession=bzo1QkJEQTU4NDr5_i2OSSoponSuDz4rLu9fh4NHbw \
--data '{
"selector": {
"age": {
"$gte": 5,
"$lte": 20
}
},
"fields": ["_id", "_rev", "age", "desc"],
"execution_stats": true
}'
返回結果如下:
{
"docs": [
{
"_id": "s2",
"_rev": "2-50d7fd758615aa6aefbc6aa2ec86fd8b",
"age": 19.36,
"desc": "小紅2"
},
{
"_id": "s4",
"_rev": "1-ccd6248a85ad565b2b12a3127c1b82d7",
"age": 14,
"desc": "ab"
}
],
"bookmark": "g1AAAAA0eJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYkXm4BkOGAyULEsAGrUDgc",
"execution_stats": {
"total_keys_examined": 0,
"total_docs_examined": 15,
"total_quorum_docs_examined": 0,
"results_returned": 2,
"execution_time_ms": 1.0
},
"warning": "no matching index found, create an index to optimize query time"
}
selector中可以使用的操作符如下:
具體的還是建議看官方文檔,這里也只是一個摘錄。
操作符 | 說明 | 示例 |
---|---|---|
$eq | 等於 | {"selector":{"desc":{"$qe":"s2"}}} |
$ne | 不等於 | {"selector":{"age":{"$ne":20}}} |
$gt | 大於 | {"selector":{"age":{"$gt":20}}} |
$gte | 大於等於 | {"selector":{"age":{"$gte":20}}} |
$lt | 小於 | {"selector":{"age":{"$lt":20}}} |
$lte | 小於等於 | {"selector":{"age":{"$lte":20}}} |
$regex | 正則表達式 | {"selector":{"address":{"$regex":"北京.*"}}} |
$and | 參數為數組,表示必須同時匹配 | {"selector":{"$and":[{"age":{"$lt":20}},{"address":{"$regex":"北京.*"}}]}} |
$or | 參數為數組,表示必須匹配其中一個 | {"selector":{"$or":[{"age":{"$lt":20}},{"address":{"$regex":"北京.*"}}]}} |
$not | 表示不匹配給定選擇器的結果 | {"selector":{$not:{"address":{"$regex":"北京.*"}}}} |
$nor | 參數為數組,表示所有選擇器都不匹配,則匹配 | {"selector":{"$nor":[{"age":{"$lt":20}},{"address":{"$regex":"北京.*"}}]}} |
$all | 這個只能用來查詢數組字段,如果文檔中該數組字段的元素包含了參數數組中全部元素,則匹配 | {"selector":{{"age":{"$all":[13,14]}} 這里我再次入庫了一批數據,age字段是一個數組,里面都是一些整數值。對於這個例子,如果某個文檔的age字段的數組元素中同時含有13、14這兩個值,則匹配上 |
$elemMatch | 匹配數組字段,符合至少一個條件的文檔 | {"selector":{"age":{"$elemMatch":{"$gt":5,"$lt":18}}}} 如果某個文檔的age字段的數組元素中,含有大於(eq)5或小於(lt)20的值,就匹配上 |
$allMatch | 匹配數組字段,符合全部條件的字段 | {"selector":{"age":{"$allMatch":{"$gt":5,"$lt":18}}}} 如果某個文檔的age字段的數組元素全部大於(gt)5且小於(lt)18,則匹配上 |
2.4.2 獲取記錄
可以使用_find
來查詢記錄,也可以直接獲取記錄的值。
下面的比較簡單,就簡單的記錄一下。
1、獲取單個記錄
使用GET
請求獲取,在URL中指定數據庫和文檔_id
。
curl示例:
curl --request GET \
--url http://localhost:5984/dbtest/s1 \
--cookie AuthSession=bzo1QkJEQTU4NDr5_i2OSSoponSuDz4rLu9fh4NHbw
2、獲取數據庫全部記錄列表
獲取全部記錄列表需要使用_all_docs
命令,這里也值演示使用GET
方法請求的結果,還可以使用POST
請求來操作,可以傳遞更多的參數來控制返回結果。
curl示例如下:
curl --request GET \
--url http://localhost:5984/dbtest/_all_docs \
--cookie AuthSession=bzo1QkJEQTU4NDr5_i2OSSoponSuDz4rLu9fh4NHbw
返回結果中只包含文檔的_id
和_rev
字段,如果需要獲取包含文檔所有字段的json,則需在URL后加上請求參數include_docs=true
示例如下:
{
"total_rows": 19,
"offset": 0,
"rows": [
{
"id": "doc1",
"key": "doc1",
"value": {
"rev": "1-c346e9395d9469f4a0359bbc07327a52"
}
},
.... 此處省略很多....
{
"id": "鍵值",
"key": "鍵值",
"value": {
"rev": "1-4fd4c8f7fe3d72bc579f6cfc45f6b0d4"
}
}
]
}
3、使用_bulk_get
命令來獲取多個記錄
此處可以參考官方文檔http://docs.couchdb.org/en/latest/api/database/bulk-api.html#db-bulk-get
這個使用和前面的批量刪除差不多,也是使用POST
方法請求,然后將請求參數放在content
中。
JSON參數對象中的docs
數組中的每一個元素是一個json對象,必須含有id
鍵,來指定獲取哪一個文檔記錄。可以含有rev
鍵值。
curl批量獲取記錄示例:
curl --request POST \
--url http://127.0.0.1:5984/dbtest/_bulk_get \
--header 'content-type: application/json' \
--cookie AuthSession=bzo1QkJEQTU4NDr5_i2OSSoponSuDz4rLu9fh4NHbw \
--data '{
"docs": [
{
"id": "s5",
"rev":"4-753875d51501a6b1883a9d62b4d33f91"
},
{
"id": "s6",
"rev": "1-46eeb3a2fb10de49fd7cbb3c74db5361"
},
{
"id": "s7"
}
]
}'
返回結果示例:
{
"results": [
{
"id": "s5",
"docs": [
{
"error": {
"id": "s5",
"rev": "4-753875d51501a6b1883a9d62b4d33f91",
"error": "not_found",
"reason": "missing"
}
}
]
},
{
"id": "s6",
"docs": [
{
"ok": {
"_id": "s6",
"_rev": "1-46eeb3a2fb10de49fd7cbb3c74db5361",
"desc": "bobo",
"age": [
15,
16
]
}
}
]
},
{
"id": "s7",
"docs": [
{
"ok": {
"_id": "s7",
"_rev": "1-9f2b329717a1d2a6d6193328312e5b83",
"desc": "小明",
"age": [
22,
23
]
}
}
]
}
]
}