CouchDB 简单HTTP接口使用说明

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对象包含键errorreason(原因)。例如:
{"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编码值。

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值变化了。这时候如果再向数据库中插入_ids3的文档,则其_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命令用来查询摸个数据库中的文档,类似于MongoDBfind操作。
_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
						]
					}
				}
			]
		}
	]
}
posted @ 2018-10-10 16:55  乌合之众  阅读(7347)  评论(0编辑  收藏  举报
clear