@ -0,0 +1,23 @@ | |||
.eunit | |||
*.o | |||
*.beam | |||
*.plt | |||
erl_crash.dump | |||
.concrete/DEV_MODE | |||
ebin | |||
# rebar 2.x | |||
.rebar | |||
rel/example_project | |||
ebin/*.beam | |||
deps | |||
# rebar 3 | |||
.rebar3 | |||
_build/ | |||
_checkouts/ | |||
rebar.lock | |||
# idea | |||
.idea | |||
*.iml |
@ -0,0 +1,21 @@ | |||
MIT License | |||
Copyright (c) 2019 AICells | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE. |
@ -0,0 +1,41 @@ | |||
# erlArango | |||
arangodb erlang driver | |||
erlang otp21.2+ arangodb 3.6.2 3.7 | |||
# Feature | |||
Efficient, fast and easy to use. | |||
1. To make this driver as efficient as possible, customizations encapsulate an HTTP1.1 client(agHttpCli) with connection pooling. | |||
Comparisons between packaged agHttpCli and similar HTTP client tests are available:[Address](https://github.com/SisMaker/httpc_bench) | |||
2. This driver can use connection pooling or simply establish multiple connections in a single process (non-connection pooling mode) for various data operations. | |||
Synchronous and asynchronous operations are supported when using connection pooling, | |||
and you need to save the requestId extra if you want to use asynchronous operations Waiting for the received data to return, | |||
the API encapsulated by the current driver all USES synchronous operation, and can be modified if asynchronous operation is needed. | |||
Only synchronous operations are supported for single-process operations. | |||
In single-process mode, compared with connection pooling mode, data replication between processes can be reduced once. | |||
For operation of large amount of data, database connection can be established separately in data management process instead of connection pooling. | |||
3. The connection pooling mode and connectionless pool mode API interface ensures the identity, does not need to be treated differently, | |||
and is easy to understand and change between connection pooling mode and connectionless pool mode. | |||
# Batch requests are not supported | |||
https://www.arangodb.com/docs/stable/http/batch-request.html | |||
# compile | |||
rebar get-deps; rebar compile or rebar3 compile | |||
Note: If you build Jiffy on The Windows platform, you will need to set up an additional compilation environment. [See jiffy for details](https://github.com/SisMaker/erlUtils/tree/master/src/docs) | |||
# how to use | |||
rebar: erl -pa ./ebin -pa ./deps/jiffy/ebin or | |||
revar3: rebar3 shell | |||
Non-connection pooling mode | |||
Make a connection first | |||
{ok, Socket} = agHttpCli:connect([]). %% Use default Settings | |||
%% Then you can then call various apis using Socket as the first argument | |||
agMgrDb:curDbInfo(Socket). | |||
Connection pooling mode | |||
application:ensure_all_started(erlArango). %% start app | |||
agHttpCli:startPool(poolName, [], []). %% start pool | |||
%% Then you can then invoke various apis using poolName as the first argument | |||
agMgrDb:curDbInfo(poolName). | |||
@ -0,0 +1,286 @@ | |||
Client / Server Communication (VST 1.1) | |||
======================================= | |||
Version 1.1.0 of 23 November 2016 | |||
HTTP | |||
---- | |||
Use VelocyPack as body. Content-Type is `"application/vpack"` | |||
Binary Protocol | |||
--------------- | |||
This is not a request / response protocol. It is symmetric (in | |||
principle). Messages can be sent back and forth, pipelined, multiplexed, | |||
uni-directional or bi-directional. | |||
It is possible that a message generates | |||
- no response | |||
- exactly one response | |||
- multiple responses | |||
The VelocyStream does **not** impose or specify or require one of the | |||
above behaviors. The application must define a behavior in general or on | |||
a per-request basis, see below. The server and client must then | |||
implement that behavior. | |||
### Message vs. Chunk | |||
The consumer (client or server) will deal with messages. A message | |||
consists of one or more VelocyPacks (or in some cases of certain parts | |||
of binary data). How many VelocyPacks are part of a message is | |||
completely application dependent, see below for ArangoDB. | |||
It is possible that the messages are very large. As messages can be | |||
multiplexed over one connection, large messages need to be split into | |||
chunks. The sender/receiver class will accept a vector of VelocyPacks, | |||
split them into chunks, send these chunks over the wire, assemble these | |||
chunks, generates a vector of VelocyPacks and passes this to the | |||
consumer. | |||
### Chunks | |||
In order to allow reassemble chunks, each package is prefixed by a small | |||
header. A chunk is always at least 24 bytes long. The byte order is | |||
ALWAYS little endian. The format of a chunk is the following, regardless | |||
on whether it is the first in a message or a subsequent one: | |||
| name | type | description | | |||
| --------------- | ------------------------- | --- | | |||
| length | uint32\_t | total length in bytes of the current chunk, including this header | | |||
| chunkX | uint32\_t | chunk/isFirstChunk (upper 31bits/lowest bit), details see below | | |||
| messageId | uint64\_t | a unique identifier, it is the responsibility of the sender to generate such an identifier (zero is reserved for not set ID) | | |||
| messageLength | uint64\_t | the total size of the message. | | |||
| Binary data | binary data blob | size b1 | | |||
Clarification: "chunk" and "isFirstChunk" are combined into an unsigned | |||
32bit value. Therefore it will be encoded as | |||
uint32_t chunkX | |||
and extracted as | |||
chunk = chunkX >> 1 | |||
isFirstChunk = chunkX & 0x1 | |||
For the first chunk of a message, the low bit of the second uint32\_t is | |||
set, for all subsequent ones it is reset. In the first chunk of a | |||
message, the number "chunk" is the total number of chunks in the | |||
message, in all subsequent chunks, the number "chunk" is the current | |||
number of this chunk. | |||
The total size of the data package is (24 + b1) bytes. This number is | |||
stored in the length field. If one needs to send messages larger than | |||
UINT32\_MAX, then these messages must be chunked. In general it is a | |||
good idea to restrict the maximal size to a few megabytes. | |||
**Notes:** | |||
When sending a (small) message, it is important (for performance reasons) | |||
to ensure that only one TCP | |||
packet is sent. For example, by using sendmmsg under Linux | |||
([*https://blog.cloudflare.com/how-to-receive-a-million-packets/*](https://blog.cloudflare.com/how-to-receive-a-million-packets/)) | |||
Implementors should nevertheless be aware that in TCP/IP one cannot | |||
enforce this and so implementations must always be aware that some part | |||
of the network stack can split packets and the payload might arrive in | |||
multiple parts! | |||
ArangoDB | |||
======== | |||
### Request / Response | |||
For an ArangoDB client, the request is of the following format, the | |||
array is a VelocyPack array: | |||
[ | |||
/* 0 - version: */ 1, // [int] | |||
/* 1 - type: */ 1, // [int] 1=Req, 2=Res,.. | |||
/* 2 - database: */ "test", // [string] | |||
/* 3 - requestType: */ 1, // [int] 0=Delete, ... | |||
/* 4 - request: */ "/_api/collection", // [string\] | |||
/* 5 - parameter: */ { force: true }, // [[string]->[string]] | |||
/* 6 - meta: */ { x-arangodb: true } // [[string]->[string]] | |||
] | |||
Body (binary data) | |||
If database is missing (entry is `null`), then "\_system" is assumed. | |||
`type`: | |||
1 = Request | |||
2 = Response (final response for this message id) | |||
3 = Response (but at least one more response will follow) | |||
1000 = Authentication | |||
`requestType`: | |||
0 = DELETE | |||
1 = GET | |||
2 = POST | |||
3 = PUT | |||
4 = HEAD (not used in VPP) | |||
5 = PATCH | |||
6 = OPTIONS (not used in VPP) | |||
For example: | |||
The HTTP request | |||
http://localhost:8529/_db/test/_admin/echo?a=1&b=2&c[]=1&c[]=3 | |||
With header: | |||
X-ArangoDB-Async: true | |||
is equivalent to | |||
[ | |||
1, // version | |||
1, // type | |||
"test", // database | |||
1, // requestType GET | |||
"/_admin/echo", // request path | |||
{ // parameters | |||
a: 1, | |||
b: 2, | |||
c: [ 1, 3 ] | |||
}, | |||
{ // meta | |||
x-arangodb-async: true | |||
} | |||
] | |||
The request is a message beginning with one VelocyPack. This VelocyPack | |||
always contains the header fields, parameters and request path. If the | |||
meta field does not contain a content type, then the default | |||
`"application/vpack"` is assumed and the body will be one or multiple | |||
VelocyPack object. | |||
The response will be | |||
[ | |||
1, // 0 - version | |||
2 or 3, // 1 - type | |||
400, // 2 - responseCode | |||
{ etag: "1234" } // 3 - meta: [[str]->[str]] | |||
] | |||
Body (binary data) | |||
Request can be pipelined or mixed. The responses are mapped using the | |||
"messageId" in the header. It is the responsibility of the **sender** to | |||
generate suitable "messageId" values. | |||
The default content-type is `"application/vpack"`. | |||
### Authentication | |||
A connection can be authenticated with the following message: | |||
[ | |||
1, // version | |||
1000, // type | |||
"plain", // encryption | |||
"admin", // user | |||
"plaintext", // password | |||
] | |||
or | |||
[ | |||
1, // version | |||
1000, // type | |||
"jwt", // encryption | |||
"abcd..." // token | |||
] | |||
The response is | |||
{ "error": false } | |||
if successful or | |||
{ | |||
"error": true, | |||
"errorMessage": "MESSAGE", | |||
"errorCode": CODE | |||
} | |||
if not successful, and in this case the connection is closed by the server. | |||
One can acquire a JWT token in the same way as with HTTP using the | |||
open, unauthenticated route `/_open/auth` with the same semantics as | |||
in the HTTP version. In this way, the complete authentication can be | |||
done in a single session via JWT. | |||
### Content-Type and Accept | |||
In general the content-type will be VPP, that is the body is an object | |||
stored as VelocyPack. | |||
Sometimes it is necessary to respond with unstructured data, like text, | |||
css or html. The body will be a VelocyPack object containing just a | |||
binary attribute and the content-type will be set accordingly. | |||
The rules are as follows. | |||
#### Http | |||
Request: Content-Type | |||
- `"application/json"`: the body contains the JSON string representation | |||
- `"application/vpack"`: the body contains a velocy pack | |||
There are some handler that allow lists of JSON (seperared by newline). | |||
In this case we also allow multiple velocy packs without any separator. | |||
Request: Accept | |||
- `"application/json"`: send a JSON string representation in the body, | |||
if possible | |||
- `"application/vpack"`: send velocy pack in the body, if possible | |||
If the request asked for `"application/json"` or `"application/vpack"` and | |||
the handler produces something else (i.e. `"application/html"`), then the | |||
accept is ignored. | |||
If the request asked `"application/json"` and the handler produces | |||
`"application/vpack"`, then the VPACK is converted into JSON. | |||
If the request asked `"application/vpack"` and the handler produces | |||
"application/json", then the JSON is converted into VPACK. | |||
#### VPP | |||
Similar to HTTP with the exception: the "Accept" header is not supported | |||
and `"application/json"` will always be converted into | |||
"application/vpack". This means that the body contains one or more | |||
velocy-packs. In general it will contain one - notable exception being | |||
the import. | |||
If the handler produces something else (i.e. `"application/html"`), then | |||
The body will be a binary blob (instead of a velocy-pack) and the | |||
content-type will be set accordingly. | |||
The first bytes sent after a connection (the "client" side - even if the | |||
program is bi-directional, there is a server listening to a port and a | |||
client connecting to a port) are | |||
VST/1.1\r\n\r\n | |||
(11 Bytes) | |||
@ -0,0 +1,188 @@ | |||
Client / Server Communication (VST 1.1) | |||
======================================= | |||
Version 1.1.0 of 23 November 2016 | |||
# HTTP | |||
使用VelocyPack作为身体。内容类型为"application/vpack" | |||
Binary Protocol | |||
--------------- | |||
这不是请求/响应协议。它是对称的(原则上)。消息可以来回发送,流水线,多路复用,单向或双向。 | |||
可能会生成一条消息 | |||
没有反应 | |||
只是一个回应 | |||
多重回应 | |||
该VelocyStream并没有强加或指定或要求上述行为之一。 | |||
应用程序必须在一般情况下或根据每个请求定义行为, | |||
请参见下文。然后,服务器和客户端必须实现该行为。 | |||
### Message vs. Chunk | |||
使用者(客户端或服务器)将处理消息。一条消息包含一个或多个VelocyPack(在某些情况下是二进制数据的某些部分)。 | |||
消息中有多少VelocyPacks完全取决于应用程序,请参阅下文了解ArangoDB。 | |||
消息可能很大。由于可以通过一个连接对消息进行多路复用,因此需要将大消息拆分为多个块。 | |||
发送者/接收者类将接受一个VelocyPacks向量,将其分割成块,通过导线发送这些块,组装这些块, | |||
生成一个VelocyPacks向量并将其传递给消费者。 | |||
### Chunks | |||
为了允许重组块,每个程序包都以一个 header 作为前缀。块始终至少为24个字节长。字节顺序始终是小端。 | |||
块的格式如下,无论它是消息中的第一个消息还是随后的消息: | |||
名称 类型 描述 | |||
length uint32_t 当前块(包括此标头)的总长度(以字节为单位) | |||
chunkX uint32_t chunk / isFirstChunk(高31位/最低位),详细信息请参见下文 | |||
messageId uint64_t 唯一标识符,发送方有责任生成这样的标识符(zero is reserved for not set ID) | |||
messageLength uint64_t the total size of the message. | |||
Binary data binary data blob size b1 | | |||
声明:"chunk" and "isFirstChunk" are combined into an unsigned 32bit value. | |||
Therefore it will be encoded as | |||
uint32_t chunkX and extracted as | |||
chunk = chunkX >> 1 | |||
isFirstChunk = chunkX & 0x1 | |||
对于消息的第一块,设置第二个uint32_t的低位,对于所有后续消息,将其复位。 | |||
在消息的第一个块中,数字 chunk 是消息中的块总数,在所有后续块中,数字 chunk 是该块的当前number。 | |||
数据包的总大小为(24 + b1)字节。此数字存储在length字段中。 | |||
如果需要发送大于UINT32_MAX的消息,则必须将这些消息分块。通常,最好将最大大小限制为几兆字节。 | |||
### **Notes:** | |||
发送(小)消息时,(出于性能原因)确保仅发送一个TCP数据包非常重要。 | |||
例如,通过在Linux下使用sendmmsg | |||
([https://blog.cloudflare.com/how-to-receive-a-million-packets](https://blog.cloudflare.com/how-to-receive-a-million-packets/)) | |||
但是,实现者应该意识到,在TCP / IP中不能强制执行此操作,因此实现者必须始终意识到, | |||
网络堆栈的某些部分可以拆分数据包,并且有效负载可能分成多个部分! | |||
ArangoDB | |||
======== | |||
### Request / Response | |||
对于ArangoDB客户端,请求的格式如下,该数组是VelocyPack数组: | |||
[ | |||
/* 0 - version: */ 1, // [int] | |||
/* 1 - type: */ 1, // [int] 1=Req, 2=Res,.. | |||
/* 2 - database: */ "test", // [string] | |||
/* 3 - requestType: */ 1, // [int] 0=Delete, ... | |||
/* 4 - request: */ "/_api/collection", // [string\] | |||
/* 5 - parameter: */ { force: true }, // [[string]->[string]] http的请求参数列表 | |||
/* 6 - meta: */ { x-arangodb: true } // [[string]->[string]] http的header | |||
] | |||
Body (binary data) | |||
如果数据库未设置(entry is `null`),则数据库为“ _system”。 | |||
#### type: | |||
1 = Request | |||
2 = Response (final response for this message id) | |||
3 = Response (but at least one more response will follow) | |||
1000 = Authentication | |||
#### requestType: | |||
0 = DELETE | |||
1 = GET | |||
2 = POST | |||
3 = PUT | |||
4 = HEAD (not used in VPP) | |||
5 = PATCH | |||
6 = OPTIONS (not used in VPP) | |||
### For example: | |||
HTTP请求 | |||
http://localhost:8529/_db/test/_admin/echo?a=1&b=2&c[]=1&c[]=3 | |||
With header:: | |||
X-ArangoDB-Async: true | |||
is equivalent to | |||
[ | |||
1, // version | |||
1, // type | |||
"test", // database | |||
1, // requestType GET | |||
"/_admin/echo", // request path | |||
{ // parameters | |||
a: 1, | |||
b: 2, | |||
c: [ 1, 3 ] | |||
}, | |||
{ // meta | |||
x-arangodb-async: true | |||
} | |||
] | |||
该请求是一条以VelocyPack开头的消息。这个VelocyPack始终包含标题字段,参数和请求路径。 | |||
如果meta字段不包含内容类型,则采用默认值 "application/vpack",并且正文将是一个或多个VelocyPack对象。 | |||
#### The response will be | |||
[ | |||
1, // 0 - version | |||
2 or 3, // 1 - type | |||
400, // 2 - responseCode | |||
{ etag: "1234" } // 3 - meta: [[str]->[str]] | |||
] | |||
#### Body (binary data) | |||
请求可以通过管道传输或混合。使用标头中的“ messageId”映射响应。发送者有责任生成合适的“ messageId”值。 | |||
默认内容类型为"application/vpack"。 | |||
### Authentication | |||
A connection can be authenticated with the following message: | |||
[ | |||
1, // version | |||
1000, // type | |||
"plain", // encryption | |||
"admin", // user | |||
"plaintext", // password | |||
] | |||
or | |||
[ | |||
1, // version | |||
1000, // type | |||
"jwt", // encryption | |||
"abcd..." // token | |||
] | |||
The response is | |||
{ "error": false } | |||
if successful or | |||
{ | |||
"error": true, | |||
"errorMessage": "MESSAGE", | |||
"errorCode": CODE | |||
} | |||
如果不成功,则在这种情况下服务器将关闭连接。可以使用/_open/auth与HTTP版本相同的语义,使用未经身份验证的开放式路由,以与HTTP相同的方式获取JWT令牌。这样,可以通过JWT在单个会话中完成完整的身份验证。 | |||
### Content-Type and Accept | |||
通常,内容类型将是VPP,即主体是存储为VelocyPack的对象。 | |||
有时有必要使用非结构化数据(例如文本,css或html)进行响应。主体将是一个仅包含二进制属性的VelocyPack对象,并将相应地设置content-type。 | |||
规则如下。 | |||
#### Http | |||
Request: Content-Type | |||
"application/json"`: the body contains the JSON string representation | |||
`"application/vpack"`: the body contains a velocy pack | |||
有一些处理程序允许JSON列表(由换行符分隔)。在这种情况下,我们还允许不使用任何分隔符的多个速度包。 | |||
Request: Accept | |||
"application/json":如果可能,在正文中发送JSON字符串表示形式 | |||
"application/vpack":如果可能的话,在体内发送速度包 | |||
如果请求是"application/json"或"application/vpack"处理程序产生了其他请求(即"application/html"),那么将忽略接受。 | |||
如果请求被请求"application/json"并且处理程序产生 "application/vpack",则VPACK将转换为JSON。 | |||
如果请求被请求"application/vpack",并且处理程序生成“ application / json”,则JSON将转换为VPACK。 | |||
#### VPP | |||
与HTTP相似,不同之处在于:不支持“ Accept”标头,并且"application/json"始终将其转换为“ application / vpack”。这意味着主体包含一个或多个速度包。通常,它将包含一个-值得注意的例外是导入。 | |||
如果处理程序产生了其他东西(即"application/html"),则主体将是一个二进制blob(而不是一个velocy-pack),并且将相应地设置content-type。 | |||
连接后发送的第一个字节(“客户端”端-即使程序是双向的,也有服务器在监听端口,而客户端在连接端口) | |||
VST/1.1\r\n\r\n | |||
(11 Bytes) |
@ -0,0 +1,202 @@ | |||
%% agency 管理进程的名称 | |||
-define(agAgencyPoolMgr, agAgencyPoolMgr). | |||
%% beam cache 模块名 | |||
-define(agBeamPool, agBeamPool). | |||
-define(agBeamAgency, agBeamAgency). | |||
%% 默认选项定义 | |||
-define(DEFAULT_BASE_URL, <<"http://127.0.0.1:8529">>). | |||
-define(DEFAULT_DBNAME, <<"_system">>). | |||
-define(DEFAULT_USER, <<"root">>). | |||
-define(DEFAULT_PASSWORD, <<"156736">>). | |||
-define(DEFAULT_BACKLOG_SIZE, 1024). | |||
-define(DEFAULT_CONNECT_TIMEOUT, 5000). | |||
-define(DEFAULT_POOL_SIZE, 16). | |||
-define(DEFAULT_IS_RECONNECT, true). | |||
-define(DEFAULT_RECONNECT_MIN, 500). | |||
-define(DEFAULT_RECONNECT_MAX, 120000). | |||
-define(DEFAULT_TIMEOUT, infinity). | |||
-define(DEFAULT_PID, self()). | |||
-define(DEFAULT_SOCKET_OPTS, [binary, {active, true}, {nodelay, true}, {delay_send, true}, {keepalive, true}, {recbuf, 1048576}, {send_timeout, 5000}, {send_timeout_close, true}]). | |||
-define(GET_FROM_LIST(Key, List), agMiscUtils:getListValue(Key, List, undefined)). | |||
-define(GET_FROM_LIST(Key, List, Default), agMiscUtils:getListValue(Key, List, Default)). | |||
-define(WARN(Tag, Format, Data), agMiscUtils:warnMsg(Tag, Format, Data)). | |||
-define(miDoNetConnect, miDoNetConnect). | |||
-record(miRequest, { | |||
method :: method() | |||
, path :: path() | |||
, headers :: headers() | |||
, body :: body() | |||
, requestId :: tuple() | |||
, fromPid :: pid() | |||
, overTime = infinity :: timeout() | |||
, isSystem = false :: boolean() | |||
}). | |||
-record(miRequestRet, { | |||
requestId :: requestId(), | |||
reply :: term() | |||
}). | |||
-record(reconnectState, { | |||
min :: non_neg_integer(), | |||
max :: non_neg_integer() | infinity, | |||
current :: non_neg_integer() | undefined | |||
}). | |||
-record(srvState, { | |||
poolName :: poolName(), | |||
serverName :: serverName(), | |||
userPassWord :: binary(), | |||
host :: binary(), | |||
dbName :: binary(), | |||
rn :: binary:cp(), | |||
rnrn :: binary:cp(), | |||
reconnectState :: undefined | reconnectState(), | |||
socket :: undefined | ssl:sslsocket(), | |||
timerRef :: undefined | reference() | |||
}). | |||
-record(recvState, { | |||
stage = header :: header | body | done, %% 一个请求收到http(底层是tcp)回复可能会有多个包 最多分三个阶接收 | |||
contentLength :: undefined | non_neg_integer() | chunked, | |||
statusCode :: undefined | 100..505, | |||
headers :: undefined | [binary()], | |||
buffer = <<>> :: binary(), | |||
body = <<>> :: binary() | |||
}). | |||
-record(cliState, { | |||
isHeadMethod = false :: boolean(), %% 是否是<<"HEAD">>请求方法 | |||
%method = undefined :: undefined | method(), | |||
requestsIns = [] :: list(), | |||
requestsOuts = [] :: list(), | |||
backlogNum = 0 :: integer(), | |||
backlogSize = 0 :: integer(), | |||
status = leisure :: waiting | leisure, | |||
curInfo = undefined :: tuple(), | |||
recvState = undefined :: recvState() | undefined | |||
}). | |||
-record(dbOpts, { | |||
host :: host(), | |||
port :: 0..65535, | |||
hostname :: hostName(), | |||
dbName :: binary(), | |||
protocol :: protocol(), | |||
poolSize :: poolSize(), | |||
userPassword :: binary(), | |||
socketOpts :: socketOpts() | |||
}). | |||
-record(agencyOpts, { | |||
reconnect :: boolean(), | |||
backlogSize :: backlogSize(), | |||
reconnectTimeMin :: pos_integer(), | |||
reconnectTimeMax :: pos_integer() | |||
}). | |||
-type miRequest() :: #miRequest{}. | |||
-type miRequestRet() :: #miRequestRet{}. | |||
-type recvState() :: #recvState{}. | |||
-type srvState() :: #srvState{}. | |||
-type cliState() :: #cliState{}. | |||
-type reconnectState() :: #reconnectState{}. | |||
-type poolName() :: atom(). | |||
-type poolNameOrSocket() :: atom() | socket(). | |||
-type serverName() :: atom(). | |||
-type protocol() :: ssl | tcp. | |||
-type method() :: binary(). | |||
-type headers() :: [{iodata(), iodata()}]. | |||
-type body() :: iodata() | undefined. | |||
-type path() :: binary(). | |||
-type host() :: binary(). | |||
-type hostName() :: string(). | |||
-type poolSize() :: pos_integer(). | |||
-type backlogSize() :: pos_integer() | infinity. | |||
-type requestId() :: {serverName(), reference()}. | |||
-type socket() :: inet:socket() | ssl:sslsocket(). | |||
-type socketOpts() :: [gen_tcp:connect_option() | ssl:tls_client_option()]. | |||
-type error() :: {error, term()}. | |||
-type dbCfg() :: | |||
{baseUrl, binary()} | | |||
{dbName, binary()} | | |||
{user, binary()} | | |||
{password, binary()} | | |||
{poolSize, poolSize()} | | |||
{socketOpts, socketOpts()}. | |||
-type agencyCfg() :: | |||
{reconnect, boolean()} | | |||
{backlogSize, backlogSize()} | | |||
{reconnectTimeMin, pos_integer()} | | |||
{reconnectTimeMax, pos_integer()}. | |||
-type dbCfgs() :: [dbCfg()]. | |||
-type dbOpts() :: #dbOpts{}. | |||
-type agencyCfgs() :: [agencyCfg()]. | |||
-type agencyOpts() :: #agencyOpts{}. | |||
%% http header 头 | |||
%% -type header() :: | |||
%% 'Cache-Control' | | |||
%% 'Connection' | | |||
%% 'Date' | | |||
%% 'Pragma'| | |||
%% 'Transfer-Encoding' | | |||
%% 'Upgrade' | | |||
%% 'Via' | | |||
%% 'Accept' | | |||
%% 'Accept-Charset'| | |||
%% 'Accept-Encoding' | | |||
%% 'Accept-Language' | | |||
%% 'Authorization' | | |||
%% 'From' | | |||
%% 'Host' | | |||
%% 'If-Modified-Since' | | |||
%% 'If-Match' | | |||
%% 'If-None-Match' | | |||
%% 'If-Range'| | |||
%% 'If-Unmodified-Since' | | |||
%% 'Max-Forwards' | | |||
%% 'Proxy-Authorization' | | |||
%% 'Range'| | |||
%% 'Referer' | | |||
%% 'User-Agent' | | |||
%% 'Age' | | |||
%% 'Location' | | |||
%% 'Proxy-Authenticate'| | |||
%% 'Public' | | |||
%% 'Retry-After' | | |||
%% 'Server' | | |||
%% 'Vary' | | |||
%% 'Warning'| | |||
%% 'Www-Authenticate' | | |||
%% 'Allow' | | |||
%% 'Content-Base' | | |||
%% 'Content-Encoding'| | |||
%% 'Content-Language' | | |||
%% 'Content-Length' | | |||
%% 'Content-Location'| | |||
%% 'Content-Md5' | | |||
%% 'Content-Range' | | |||
%% 'Content-Type' | | |||
%% 'Etag'| | |||
%% 'Expires' | | |||
%% 'Last-Modified' | | |||
%% 'Accept-Ranges' | | |||
%% 'Set-Cookie'| | |||
%% 'Set-Cookie2' | | |||
%% 'X-Forwarded-For' | | |||
%% 'Cookie' | | |||
%% 'Keep-Alive' | | |||
%% 'Proxy-Connection' | | |||
%% binary() | | |||
%% string(). | |||
@ -0,0 +1,6 @@ | |||
-define(AgGet, <<"GET ">>). | |||
-define(AgPut, <<"PUT ">>). | |||
-define(AgPost, <<"POST ">>). | |||
-define(AgHead, <<"HEAD ">>). | |||
-define(AgPatch, <<"PATCH ">>). | |||
-define(AgDelete, <<"DELETE ">>). |
@ -0,0 +1,8 @@ | |||
{erl_opts, [{i, "include"}]}. | |||
{edoc_opts, [{preprocess, true}]}. | |||
{deps, [ | |||
{eVPack, {git, "http://192.168.0.88:53000/SisMaker/eVPack.git", {branch, master}}}, | |||
{jiffy, {git, "https://github.com/davisp/jiffy.git", {tag, "1.0.5"}}} | |||
%% {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "v3.0.0"}}} | |||
]}. | |||
@ -0,0 +1,360 @@ | |||
-module(agAdminMonitor). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address:https://www.arangodb.com/docs/stable/http/administration-and-monitoring.html | |||
% 从服务器Permalink读取全局日志 | |||
% 返回服务器日志 | |||
% GET /_admin/log | |||
% 查询参数 | |||
% upto (可选):返回所有日志条目多达日志级别高达。请注意,upto必须为: | |||
% fatal 或0 | |||
% error或1 | |||
% warning或2 | |||
% info或3 | |||
% debug 或4 默认值为info。 | |||
% level (可选):返回日志级别的所有日志条目级别。请注意,查询参数 upto和level是互斥的。 | |||
% start(可选):返回所有日志条目,以使其日志条目标识符(lid值)大于或等于start。 | |||
% size(可选):将结果限制为最大大小的日志条目。 | |||
% offset(可选):开始返回日志条目,跳过第一个偏移日志条目。偏移量 和大小可用于分页。 | |||
% search (可选):仅返回包含search中指定的文本的日志条目。 | |||
% sort(可选):根据日志条目的盖值对日志条目进行升序(如果sort为asc)或降序(如果sort为desc)。请注意,盖子会 按时间顺序排列。默认值为asc。 | |||
% 从服务器的全局日志中返回致命,错误,警告或信息日志消息。结果是具有以下属性的JSON对象: | |||
% HTTP 200 | |||
% lid:日志条目标识符的列表。每个日志消息都由其@LIT {lid}唯一标识,并且标识符按升序排列。 | |||
% level:所有日志条目的日志级别列表。 | |||
% timestamp:所有日志条目的时间戳列表,自1970-01-01开始以秒为单位。 | |||
% text:所有日志条目的文本列表 | |||
% topic:所有日志条目的主题列表 | |||
% totalAmount:分页前的日志条目总数。 | |||
% 400:如果为up或level指定了无效值,则返回。 | |||
% 500:如果服务器由于内存不足错误而无法生成结果,则返回。 | |||
getAdminLog(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/log">>, [], undefined). | |||
getAdminLog(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_admin/log", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 返回当前的日志级别设置 | |||
% GET /_admin/log/level | |||
% 返回服务器的当前日志级别设置。结果是一个JSON对象,其日志主题为对象键,日志级别为对象值。 | |||
% 返回码 | |||
% 200:如果请求有效,则返回 | |||
% 500:如果服务器由于内存不足错误而无法生成结果,则返回。 | |||
getAdminLogLevel(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/log/level">>, [], undefined). | |||
% 修改当前的日志级别设置 | |||
% PUT /_admin/log/level | |||
% 具有以下属性的JSON对象是必需的: | |||
% agency:可能的日志级别之一。 | |||
% agencycomm:可能的日志级别之一。 | |||
% authentication:可能的日志级别之一。 | |||
% 授权:可能的日志级别之一。 | |||
% cache:可能的日志级别之一。 | |||
% cluster:可能的日志级别之一。 | |||
% collector:可能的日志级别之一。 | |||
% 通信:可能的日志级别之一。 | |||
% 压缩器:可能的日志级别之一。 | |||
% config:可能的日志级别之一。 | |||
% datafiles:可能的日志级别之一。 | |||
% 开发:可能的日志级别之一。 | |||
% 引擎:可能的日志级别之一。 | |||
% general:可能的日志级别之一。 | |||
% graphs:可能的日志级别之一。 | |||
% 心跳:可能的日志级别之一。 | |||
% 内存:可能的日志级别之一。 | |||
% mmap:可能的日志级别之一。 | |||
% 性能:可能的日志级别之一。 | |||
% pregel:可能的对数水平之一。 | |||
% 查询:可能的日志级别之一。 | |||
% 复制:可能的日志级别之一。 | |||
% 请求:可能的日志级别之一。 | |||
% rocksdb:可能的日志级别之一。 | |||
% ssl:可能的日志级别之一。 | |||
% startup:可能的日志级别之一。 | |||
% 监视:可能的日志级别之一。 | |||
% syscall:可能的日志级别之一。 | |||
% 线程:可能的日志级别之一。 | |||
% trx:可能的日志级别之一。 | |||
% v8:可能的日志级别之一。 | |||
% views:可能的日志级别之一。 | |||
% ldap:可能的日志级别之一。 | |||
% audit-authentication:可能的日志级别之一。 | |||
% audit-authorization:可能的日志级别之一。 | |||
% audit-database:可能的日志级别之一。 | |||
% audit-collection:可能的日志级别之一。 | |||
% audit-view:可能的日志级别之一。 | |||
% audit-document:可能的日志级别之一。 | |||
% audit-service:可能的日志级别之一。 | |||
% 修改并返回服务器的当前日志级别设置。请求主体必须是JSON对象,日志主题为对象键,日志级别为对象值。 | |||
% 结果是一个JSON对象,其中调整后的日志主题为对象键,调整后的日志级别为对象值。 | |||
% 它可以通过仅将日志级别指定为字符串而不设置json来设置所有功能的日志级别。 | |||
% 可能的日志级别为: | |||
% 致命-这是没有办法的。此消息后,ArangoDB将关闭。 | |||
% 错误-这是一个错误。您应该调查并修复它。它可能会损害您的生产。 | |||
% 警告-这可能是严重的应用程序明智的选择,但我们不知道。 | |||
% 信息-发生了什么事,请注意,但没有发生任何戏剧事件。 | |||
% 调试-输出调试消息 | |||
% 跟踪-跟踪-准备要淹没的日志-请勿在生产中使用。 | |||
% 返回码 | |||
% 200:如果请求有效,则返回 | |||
% 400:当请求正文包含无效JSON时返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果服务器由于内存不足错误而无法生成结果,则返回。 | |||
modifyAdminLogLevel(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_admin/log/level">>, [], BodyStr). | |||
% 返回统计信息 | |||
% GET /_admin/statistics | |||
% 返回统计信息。返回的对象包含根据_admin / statistics-description返回的描述分组在一起的 统计数字。例如, 要从组系统访问图userTime,您首先选择描述存储在系统中的组的子对象,然后在该子对象中,userTime的值存储在同名属性中。 | |||
% 如果是分发,则返回的对象包含以count为单位的总计 数和以counts为单位的分发列表。各个值的总和(或全部)中被返回总和。 | |||
% 事务统计信息显示本地已启动,已提交和已中止的事务,以及为查询的服务器完成的中间提交。对于RocksDB存储引擎,中间提交计数将仅采用非零值。协调器几乎不会在其本地数据库中进行任何本地事务,因此,群集事务(在协调器上启动的事务需要DBServer在事务提交到群集范围之前完成)才被添加到其本地统计信息中。这意味着您将在单个服务器上看到的统计信息大致是在使用单个协调器查询该协调器的群集设置中所期望的统计信息。区别在于群集事务没有中间提交的概念,也不会增加价值。 | |||
% HTTP 200统计信息已成功返回。 | |||
% error:布尔值标志,指示是否发生错误(在这种情况下为false) | |||
% code:HTTP状态码-在这种情况下为200 | |||
% time:当前服务器时间戳 | |||
% errorMessage:描述性错误消息 | |||
% enabled:如果服务器启用了统计模块,则为true。如果没有,不要期望任何值。 | |||
% 系统:从系统收集的有关此过程的指标;可能取决于主机操作系统 | |||
% minorPageFaults:pagefaults | |||
% majorPageFaults:pagefaults | |||
% userTime:服务器进程使用的用户CPU时间 | |||
% systemTime:服务器进程使用的系统CPU时间 | |||
% numberOfThreads:服务器中的线程数 | |||
% residentSize:流程的RSS | |||
% residentSizePercent:进程的RSS,以%为单位 | |||
% virtualSize:进程的VSS | |||
% client:有关连接的客户端及其资源使用情况的信息 | |||
% sum:所有计数的汇总值 | |||
% count:汇总的值数 | |||
% counts:包含值的数组 | |||
% connectionTime:总连接时间 | |||
% totalTime:系统时间 | |||
% requestTime:请求时间 | |||
% queueTime:请求排队等待处理的时间 | |||
% ioTime:IO时间 | |||
% bytesSent:发送给客户端的字节数 | |||
% bytesReceived:从客户端收到的字节数 | |||
% httpConnections:打开的http连接数 | |||
% http:动词的请求数 | |||
% requestsTotal:http请求总数 | |||
% requestsAsync:异步http请求的总数 | |||
% RequestsGet:使用GET动词的请求数 | |||
% requestHead:使用HEAD动词的请求数 | |||
% requestPost:使用POST动词的请求数 | |||
% requestsPut:使用PUT动词的请求数 | |||
% requestsPatch:使用PATCH动词的请求数 | |||
% requestsDelete:使用DELETE动词的请求数 | |||
% requestsOptions:使用OPTIONS动词的请求数 | |||
% requestOther:没有使用以上识别的动词的任何请求 | |||
% 服务器:服务器的统计信息 | |||
% 正常运行时间:服务器启动和运行的时间 | |||
% physicalMemory:服务器上的可用物理内存 | |||
% Transactions:交易统计 | |||
% 已开始:已开始交易的数量 | |||
% 已提交:已提交交易的数量 | |||
% 已中止:已中止交易的数量 | |||
% middleCommits:完成的中间提交数 | |||
% v8Context:有关V8 JavaScript上下文的统计信息 | |||
% 可用:当前生成的V8上下文的数量 | |||
% busy:当前活动的V8上下文的数量 | |||
% dirty:先前使用的上下文数量,现在应该在重新使用之前对其进行垃圾回收 | |||
% free:可以免费使用的V8上下文的数量 | |||
% max:我们可以通过--javascript.v8-contexts配置生成的V8上下文总数 | |||
% 内存:V8内存/垃圾回收水印列表;每次运行垃圾回收时都要刷新;将当时使用的最小/最大内存保留10秒钟 | |||
% contextId:这组内存统计信息来自的上下文的ID | |||
% tMax:10秒间隔开始的时间戳 | |||
% countOfTimes:这10秒内垃圾回收运行了多少次 | |||
% heapMax:所有垃圾收集的高水位标记在10秒内运行 | |||
% heapMin:这10秒内运行的所有垃圾回收的低水印 | |||
% 线程:有关服务器工作线程的统计信息(不包括特定于V8或jemalloc的线程和系统线程) | |||
% scheduler-threads:产生的工作线程数 | |||
% 进行中:当前繁忙的工作线程数 | |||
% 排队:排队等待工作线程可用的作业数 | |||
getAdminProps(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/statistics">>, [], undefined). | |||
% 统计数据说明 | |||
% 获取统计信息的描述性信息 | |||
% GET /_admin/statistics-description | |||
% 返回/ _admin / statistics返回的统计信息的描述。返回的对象在属性组中包含一组统计信息组,在属性图中包含一组统计信息 图。 | |||
% 统计组由 | |||
% group:组的标识符。 | |||
% name:组的名称。 | |||
% description:组的描述。 | |||
% 统计数字由 | |||
% group:此图所属的组的标识符。 | |||
% 标识符:图形的标识符。它在组中是唯一的。 | |||
% name:图形名称。 | |||
% description:对图形的描述。 | |||
% 类型:当前,累计或分配。 | |||
% cuts:分布向量。 | |||
% 单位:测量数字的单位。 | |||
% HTTP 200描述已成功返回。 | |||
% 组:统计组 | |||
% group:组的标识符。 | |||
% name:组的名称。 | |||
% description:组的描述。 | |||
% 数字:统计数字 | |||
% group:此图所属的组的标识符。 | |||
% 标识符:图形的标识符。它在组中是唯一的。 | |||
% name:图形名称。 | |||
% description:对图形的描述。 | |||
% 类型:当前,累计或分配。 | |||
% cuts:分布向量。 | |||
% 单位:测量数字的单位。 | |||
% code:HTTP状态码 | |||
% 错误:错误,在这种情况下为false | |||
getAdminStatisticsDesc(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/statistics-description">>, [], undefined). | |||
% TLS 永久链接 | |||
% 返回TLS数据的摘要 | |||
% 返回此服务器的TLS数据(服务器密钥,客户端身份验证CA) | |||
% GET /_admin/server/tls | |||
% 返回TLS数据的摘要。JSON响应将包含result具有以下组件的字段 : | |||
% keyfile:有关密钥文件的信息。 | |||
% clientCA:有关用于客户端证书验证的CA的信息。 | |||
% 如果使用服务器名称指示(SNI),并且为不同的服务器名称配置了多个密钥文件,那么将存在一个附加属性SNI,该属性为每个配置的服务器名称包含有关该服务器名称的密钥文件的相应信息。 | |||
% | |||
% 在所有情况下,该属性的值都将是一个JSON对象,该对象具有以下属性的子集(无论适当): | |||
% SHA256:该值是一个带有整个输入文件的SHA256的字符串。 | |||
% certificates:值是一个JSON数组,文件链中包含公共证书。 | |||
% privateKeySHA256:如果存在私钥(keyfile 但没有私钥clientCA),则此字段存在并且包含带有私钥SHA256的JSON字符串。 | |||
% 这是一个公共API,因此它不要求身份验证。 | |||
% 返回码 | |||
% 200:如果一切正常,此API将返回HTTP 200 | |||
getAdminTLS(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/server/tls">>, [], undefined). | |||
% 触发TLS数据的重新加载并返回摘要永久链接 | |||
% 触发此服务器的TLS数据(服务器密钥,客户端身份验证CA)的重新加载,并以摘要形式返回新数据。 | |||
% POST /_admin/server/tls | |||
% 此API调用触发所有TLS数据的重新加载,然后返回摘要。JSON响应与相应的GET请求完全相同(请参见此处)。 | |||
% 这是受保护的API,只能以超级用户权限执行。 | |||
% 返回码 | |||
% 200:如果一切正常,此API将返回HTTP 200 | |||
% 403:如果未使用超级用户权限调用此API,它将返回HTTP 403 FORBIDDEN。 | |||
triggerAdminTLS(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/server/tls">>, [], undefined). | |||
% 返回当前实例指标 | |||
% GET /_admin/metrics | |||
% 以Prometheus格式返回实例的当前指标。返回的文档收集所有实例指标,这些指标在任何给定时间进行测量,并将其公开以供Prometheus收集。 | |||
% 该文档包含不同的度量标准和度量标准组,具体取决于查询实例的角色。所有导出的指标都使用arangodb_或rocksdb_字符串发布,以将其与其他收集的数据区分开。 | |||
% 然后需要将API添加到Prometheus配置文件中进行收集。 | |||
% 返回码 | |||
% 200:指标已成功返回。 | |||
% 404:可以使用--server.export-metrics-api false 服务器中的设置禁用指标API 。在这种情况下,调用结果表明找不到该API。 | |||
getAdminMetrics(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/metrics">>, [], undefined). | |||
% 集群 | |||
% 服务器返回是否是在只读模式 | |||
% 返回此服务器的模式(只读或默认) | |||
% GET /_admin/server/mode | |||
% 关于服务器的返回模式信息。json响应将包含一个mode值为readonly或的字段default。在只读服务器中,所有写入操作将失败,错误代码为1004(ERROR_READ_ONLY)。创建或删除数据库和集合也将失败,并显示错误代码11(ERROR_FORBIDDEN)。 | |||
% 这是一个公共API,因此它不要求身份验证。 | |||
% 返回码 | |||
% 200:如果一切正常,此API将返回HTTP 200 | |||
getAdminServerMode(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/server/mode">>, [], undefined). | |||
% 返回集群永久链接中服务器的ID | |||
% 了解服务器的内部ID | |||
% GET /_admin/server/id | |||
% 返回集群中服务器的ID。如果服务器未在集群模式下运行,则请求将失败。 | |||
% 返回码 | |||
% 200:当服务器以群集模式运行时返回。 | |||
% 500:当服务器未在群集模式下运行时返回。 | |||
getAdminServerId(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/server/id">>, [], undefined). | |||
% 返回集群中服务器的角色 | |||
% GET /_admin/server/role | |||
% 返回集群中服务器的角色。该角色在结果的role属性中返回。角色的可能返回值是: | |||
% SINGLE:服务器是没有集群的独立服务器 | |||
% 协调器:服务器是集群中的协调器 | |||
% PRIMARY:服务器是集群中的DBServer | |||
% 次要的:不再使用此角色 | |||
% 代理:服务器是集群中的代理节点 | |||
% UNDEFINED:在集群中,如果无法确定服务器角色,则返回UNDEFINED。 | |||
% 在所有情况下均返回HTTP 200。 | |||
% 错误:始终为假 | |||
% code:HTTP状态码,始终为200 | |||
% errorNum:服务器错误号 | |||
% 作用:之一[ SINGLE,协调员,PRIMARY,SECONDARY,AGENT,UNDEFINED ] | |||
getAdminServerRole(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/server/role">>, [], undefined). | |||
% 返回服务器是否可用 | |||
% GET /_admin/server/availability | |||
% 返回有关服务器的可用性信息。 | |||
% 这是一个公共API,因此它不要求身份验证。它仅在服务器监视的上下文中使用。 | |||
% 返回码 | |||
% 200:如果服务器已启动并且正在运行并且可用于任意操作,并且未设置为只读模式,并且在活动故障转移设置的情况下当前不是关注者,则此API将返回HTTP 200。 | |||
% 503:如果服务器在启动或关闭过程中,设置为只读模式或当前在活动故障转移设置中为关注者,则将返回HTTP 503。 | |||
getAdminServerAvailability(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/server/availability">>, [], undefined). | |||
% DBserver 永久链接的查询统计信息 | |||
% 允许查询集群中数据库服务器的统计信息 | |||
% GET /_admin/clusterStatistics | |||
% 查询参数 | |||
% DBserver(必填):查询给定DBserver的统计信息 | |||
% 返回码 | |||
% 200: | |||
% 400:数据库服务器的ID | |||
% 403: | |||
getAdminClusterProps(PoolNameOrSocket, DBserver) -> | |||
Path = <<"/_admin/clusterStatistics?DBserver=", (agMiscUtils:toBinary(DBserver))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 查询集群的运行状况以监视Permalink | |||
% 返回由监督(机构)评估的集群的运行状况 | |||
% GET /_admin/cluster/health | |||
% 查询群集的运行状况以进行监视。该响应是一个JSON对象,包含标准code,error,errorNum,和errorMessage字段适当。特定于端点的字段如下: | |||
% ClusterId:标识集群的UUID字符串 | |||
% Health:一个对象,该对象包含群集中每个节点的描述性子对象。 | |||
% <nodeID>:中的每个条目Health将由节点ID键入,并包含以下属性: | |||
% Endpoint:代表服务器网络端点的字符串。 | |||
% Role:服务器扮演的角色。可能的值是"AGENT","COORDINATOR"和"DBSERVER"。 | |||
% CanBeDeleted:布尔值,表示是否可以安全地从群集中删除节点。 | |||
% Version:该节点使用的ArangoDB的版本字符串。 | |||
% Engine:该节点使用的存储引擎。 | |||
% Status:一个字符串,指示由监督(机构)评估的节点的运行状况。对于协调器和dbservers节点运行状况,应将其视为真实的主要来源。如果节点正常响应请求,则为"GOOD"。如果错过了一个心跳,那就是"BAD"。如果在缺少心跳约15秒钟后通过监督宣布它失败,则会对其进行标记"FAILED"。 | |||
% 此外,它还将具有以下属性: | |||
% 协调器和数据库服务器 | |||
% SyncStatus:节点上次报告的同步状态。该值主要用于确定的值Status。可能的值包括"UNKNOWN","UNDEFINED","STARTUP","STOPPING","STOPPED","SERVING","SHUTDOWN"。 | |||
% LastAckedTime:ISO 8601时间戳记,指定接收到的最后一个心跳。 | |||
% ShortName:代表服务器的简称的字符串,例如"Coordinator0001"。 | |||
% Timestamp:ISO 8601时间戳记,指定接收到的最后一个心跳。(已弃用) | |||
% Host:可选字符串,指定主机(如果已知)。 | |||
% 仅协调员 | |||
% AdvertisedEndpoint:表示已播报端点的字符串(如果已设置)。(例如,外部IP地址或负载平衡器,可选) | |||
% 代理商 | |||
% Leader:此节点视为领导者的代理的ID。 | |||
% Leading:此代理程序是否是领导者(true)或不是(false)。 | |||
% LastAckedTime:自上次以来的时间(acked以秒为单位)。 | |||
% 返回码 | |||
% 200: | |||
getAdminClusterHealth(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/cluster/health">>, [], undefined). | |||
% 重新加载路由表。 | |||
% POST /_admin/routing/reload | |||
% 从集合路由中重新加载路由信息。 | |||
% 返回码 | |||
% 200:路由信息重新加载成功 | |||
reloadAdminRouting(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/routing/reload">>, [], undefined). |
@ -0,0 +1,82 @@ | |||
-module(agAnalyzers). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address:https://www.arangodb.com/docs/stable/http/analyzers.html | |||
% 分析仪的HTTP接口 | |||
% 可通过/_api/analyzer端点访问用于管理ArangoSearch Analyzer的RESTful API 。 | |||
% 有关简介以及可用的类型,属性和功能,请参见分析器的描述。 | |||
% 根据提供的定义创建一个新的分析器 | |||
% POST /_api/analyzer | |||
% 具有以下属性的JSON对象是必需的: | |||
% name:分析器名称。 | |||
% type:分析器类型。 | |||
% properties:用于配置指定分析器类型的属性。 | |||
% features:在分析器生成的字段上设置的一组功能。默认值为空数组。 | |||
% 根据提供的配置创建一个新的分析器。 | |||
% 返回码 | |||
% 200:名称和定义匹配的分析器已存在。 | |||
% 201:成功创建了新的分析器定义。 | |||
% 400:缺少一个或多个必需参数,或者一个或多个参数无效。 | |||
% 403:用户无权使用此配置创建和分析器。 | |||
newAnalyzer(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/analyzer">>, [], BodyStr). | |||
% 返回分析器定义 | |||
% GET /_api/analyzer/{analyzer-name} | |||
% 路径参数 | |||
% Analyzer-name(必填):要检索的分析器的名称。 | |||
% 检索指定分析器名称的完整定义。结果对象包含以下属性: | |||
% name:分析器名称 | |||
% type:分析仪类型 | |||
% properties:用于配置指定类型的属性 | |||
% features:在分析器生成的字段上设置的功能集 | |||
% 返回码 | |||
% 200:分析器定义已成功检索。 | |||
% 404:不存在这种分析器配置。 | |||
getAnalyzer(PoolNameOrSocket, AnalyzerName) -> | |||
Path = <<"/_api/analyzer/", AnalyzerName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 返回可用的分析器定义列表 | |||
% GET /_api/analyzer | |||
% 检索所有分析器定义的数组。结果数组包含具有以下属性的对象: | |||
% name:分析器名称 | |||
% type:分析仪类型 | |||
% properties:用于配置指定类型的属性 | |||
% features:在分析器生成的字段上设置的功能集 | |||
% 返回码 | |||
% 200:分析器定义已成功检索。 | |||
getAnalyzerList(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/analyzer">>, [], undefined). | |||
% 删除分析仪配置 | |||
% DELETE /_api/analyzer/{analyzer-name} | |||
% 路径参数 | |||
% Analyzer-name(必填):要删除的分析器的名称。 | |||
% 查询参数 | |||
% force (可选):即使正在使用分析仪配置,也应将其删除。默认值为false。 | |||
% 删除由analyzer-name标识的Analyzer配置。 | |||
% 如果成功删除了分析器定义,将返回具有以下属性的对象: | |||
% 错误:假 | |||
% name:删除的分析器的名称 | |||
% 返回码 | |||
% 200:分析仪配置已成功删除。 | |||
% 400:未提供分析器名称,或其他请求参数无效。 | |||
% 403:用户无权删除此分析器配置。 | |||
% 404:不存在这种分析器配置。 | |||
% 409:指定的分析器配置仍在使用中,并且省略了强制或 指定了错误。。 | |||
delAnalyzer(PoolNameOrSocket, AnalyzerName) -> | |||
Path = <<"/_api/analyzer/", AnalyzerName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
delAnalyzer(PoolNameOrSocket, AnalyzerName, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/analyzer/", AnalyzerName/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). |
@ -0,0 +1,426 @@ | |||
-module(agAqls). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address: | |||
% AQL Query Cursors: | |||
% https://www.arangodb.com/docs/stable/http/aql-query-cursor.html | |||
% AQL Query: | |||
% https://www.arangodb.com/docs/stable/http/aql-query-cursor.html | |||
% AQL Query Results Cache: | |||
% https://www.arangodb.com/docs/stable/http/aql-query-cursor.html | |||
% AQL User Functions Management: | |||
% https://www.arangodb.com/docs/stable/http/aql-query-cursor.html | |||
% 该模块汇总封装上面所有AQL操作 | |||
% AQL查询游标的HTTP接口 | |||
% 这是ArangoDB查询的HTTP接口的简介。AQL的结果和简单查询作为游标返回,以便批量处理服务器与客户端之间的通信。每次调用将返回一批文档,并指示当前批是否为最终批。根据查询的不同,结果集中的文档总数可能会或可能不会事先知道。为了释放服务器资源,客户端应在不再需要游标时将其删除。 | |||
% 要执行查询,需要通过HTTP POST请求将查询详细信息从客户端传送到服务器。 | |||
% 检索查询结果 | |||
% 选择查询在服务器上即时执行,结果集将返回给客户端。 | |||
% 客户端可以通过两种方式从服务器获取结果集: | |||
% 单次返回 | |||
% 使用游标 | |||
% 单次返回 | |||
% 服务器将在一次往返中仅将一定数量的结果文档传送回客户端。客户端可以通过在发出查询时设置batchSize属性来控制此数字。 | |||
% 如果完整的结果可以一次性传递给客户端,则客户端不需要发出任何其他请求。客户端可以通过检查结果集的hasMore属性来检查是否已检索到完整的结果集。如果将其设置为false,则客户端已从服务器获取了完整的结果集。在这种情况下,将不会创建服务器端游标。 | |||
% 使用游标 | |||
% 如果结果集中包含的文档数量超出单次往返传输的文档数量(即通过batchSize属性设置的数量),则服务器将返回前几个文档并创建一个临时游标。游标标识符也将返回给客户端。服务器会将光标标识符放在响应对象的id属性中。此外, 响应对象的hasMore属性将设置为true。这表明客户端还有其他结果要从服务器获取。 | |||
% 通过HTTP访问游标 | |||
% 创建光标 | |||
% 创建一个游标并返回第一个结果 | |||
% POST /_api/cursor | |||
% 描述查询和查询参数的JSON对象。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% query:包含要执行的查询字符串 | |||
% count:指示是否应在结果的“ count”属性中返回结果集中的文档数。计算“ count”属性将来可能会对某些查询产生性能影响,因此默认情况下此选项处于关闭状态,并且仅在请求时返回“ count”。 | |||
% batchSize:一次往返从服务器传输到客户端的最大结果文档数。如果未设置此属性,则将使用服务器控制的默认值。甲BATCHSIZE的值 0是不允许的。 | |||
% ttl:光标的生存时间(以秒为单位)。在指定的时间后,游标将自动在服务器上删除。这对于确保客户端不完全获取的游标的垃圾回收很有用。如果未设置,则将使用服务器定义的值(默认值:30秒)。 | |||
% cache:用于确定是否应使用AQL查询结果缓存的标志。如果设置为false,那么将跳过查询的任何查询缓存。如果设置为真,这将导致被检查的查询缓存为查询,如果查询缓存模式是上还是需求。 | |||
% memoryLimit:允许查询使用的最大内存数(以字节为单位)。如果设置,则查询将分配过多的内存而失败,并显示错误“超出资源限制”。值为0表示没有内存限制。 | |||
% bindVars:表示绑定参数的键/值对。 | |||
% options:键/值对象,带有用于查询的其他选项。 | |||
% FULLCOUNT:如果设置为真,并在查询中包含LIMIT子句,那么结果将有一个额外的与子属性属性统计 和FULLCOUNT,{ ... , "extra": { "stats": { "fullCount": 123 } } }。该FULLCOUNT属性将包含的文档数量的结果应用于在查询的最后顶层限制之前。它可用于计算符合特定过滤条件的文档数量,但一次只能返回其中的一部分。因此,它类似于MySQL的SQL_CALC_FOUND_ROWS暗示。请注意,设置该选项将禁用一些LIMIT优化,并可能导致处理更多文档,从而使查询运行时间更长。请注意,仅当查询具有顶级LIMIT子句并且在查询中实际使用LIMIT子句时,fullCount属性才可能出现在结果中。 | |||
% maxPlans:限制AQL查询优化器创建的最大计划数。 | |||
% maxWarningCount:限制查询将返回的最大警告数。默认情况下,查询将返回的警告数限制为10,但是可以通过设置此属性来增加或减少该警告数。 | |||
% failOnWarning:设置为true时,查询将引发异常并中止而不产生警告。在开发过程中应使用此选项,以尽早发现潜在问题。当该属性设置为false时,警告将不会传播到异常,并将与查询结果一起返回。还有一个服务器配置选项,--query.fail-on-warning用于设置failOnWarning的默认值,因此不需要在每个查询级别上进行设置。 | |||
% stream:指定true,查询将以流方式执行。查询结果不存储在服务器上,而是动态计算的。注意:只要查询游标存在,长时间运行的查询就需要保持收集锁。设置为false时,查询将立即全部执行。在这种情况下,查询结果要么立即返回(如果结果集足够小),要么存储在arangod实例上,并且可以通过游标API进行访问(相对于ttl)。建议仅在短期运行的查询上使用此选项,或者不使用排他锁(MMFiles上的写锁)。请注意查询选项cache,count并且fullCount不适用于流查询。此外,查询统计信息,警告和概要分析数据仅在查询完成之后才可用。默认值为false | |||
% Optimizer:与查询优化器有关的选项。 | |||
% rules:可以在此属性中放入要包括的或要排除的优化器规则的列表,告诉优化器包括或排除特定的规则。要禁用规则,请在其名称前面加上-,以启用规则,并在其前面加上+。还有一个伪规则all,它匹配所有优化程序规则。-all禁用所有规则。 | |||
% profile:如果设置为true或1,那么如果未从查询缓存提供查询结果,则将在Extra Return属性的子属性配置文件中返回其他查询概要信息。设置为2时,查询将在Extra Return属性的子属性stats.nodes中包含每个查询计划节点的执行统计信息。此外,查询计划在子属性extra.plan中返回。 | |||
% satelliteSyncWait:此Enterprise Edition参数允许配置DB-Server将有多长时间使查询中涉及的Satellite集合同步。默认值为60.0(秒)。达到最大时间后,查询将停止。 | |||
% maxRuntime:查询必须在给定的运行时内执行,否则将被终止。该值以秒为单位指定。默认值为0.0(无超时)。 | |||
% maxTransactionSize:事务大小限制(以字节为单位)。仅受RocksDB存储引擎的尊敬。 | |||
% middleCommitSize:最大操作总数,之后将自动执行中间提交。仅受RocksDB存储引擎的尊敬。 | |||
% middleCommitCount:操作之后自动执行中间提交的最大操作数。仅受RocksDB存储引擎的尊敬。 | |||
% skipInaccessibleCollections:AQL查询(尤其是图遍历)将用户没有访问权限的集合视为这些集合为空。您的查询将正常执行,而不是返回禁止的访问错误。这旨在帮助某些用例:一个图包含多个集合,并且不同的用户在该图上执行AQL查询。现在,您可以通过更改用户对集合的访问权限来自然地限制可访问的结果。此功能仅在企业版中可用。 | |||
% | |||
% 查询详细信息包括查询字符串以及可选的查询选项和绑定参数。这些值需要在POST请求的主体中以JSON表示形式传递。 | |||
% 如果结果集可以由服务器创建,则返回HTTP 201。 | |||
% error:一个标志,指示发生错误(在这种情况下为false) | |||
% code:HTTP状态码 | |||
% result:结果文档数组(如果查询没有结果,则可能为空) | |||
% hasMore:一个布尔值指示符,指示服务器上的游标是否还有更多结果可用 | |||
% count:可用结果文档总数(仅当查询是在设置了count属性的情况下执行的) | |||
% id:在服务器上创建的临时光标的ID(可选,请参见上文) | |||
% extra:一个可选的JSON对象,其统计信息子属性中包含有关查询结果的额外信息。对于数据修改查询, extra.stats子属性将包含已修改的文档数和由于错误而无法修改的文档数(如果指定了ignoreErrors查询选项) | |||
% cached:一个布尔型标志,指示是否从查询缓存提供查询结果。如果从查询缓存提供查询结果,则额外的 return属性将不包含任何stats子属性,也不会包含任何配置文件子属性。 | |||
% | |||
% 如果JSON格式不正确或请求中缺少查询规范,则返回HTTP 400。 | |||
% 如果JSON格式不正确或请求中缺少查询规范,则服务器将使用HTTP 400进行响应。 | |||
% 响应的主体将包含带有其他错误详细信息的JSON对象。该对象具有以下属性: | |||
% error:布尔值标志,指示发生错误(在这种情况下为true) | |||
% code:HTTP状态码 | |||
% errorNum:服务器错误号 | |||
% errorMessage:描述性错误消息 | |||
% 如果查询规范已完成,服务器将处理查询。如果在查询处理期间发生错误,则服务器将使用HTTP 400进行响应。同样,响应的正文将包含有关错误的详细信息。 | |||
% 一个查询错误的列表可以在这里找到。 | |||
% 404:如果查询中访问了不存在的集合,服务器将以HTTP 404进行响应。 | |||
% 405:如果使用了不受支持的HTTP方法,则服务器将以HTTP 405进行响应。 | |||
newCursor(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/cursor">>, [], BodyStr). | |||
% 从现有游标返回下一个结果 | |||
% PUT /_api/cursor/{cursor-identifier} | |||
% 路径参数 | |||
% cursor-identifier(必填):光标的名称 | |||
% 如果游标仍然存在,则返回具有以下属性的对象: | |||
% id:光标标识符 | |||
% 结果:当前批次的文档列表 | |||
% hasMore:如果这是最后一批,则为false | |||
% count:如果存在,元素总数 | |||
% 请注意,即使hasMore返回true,下一次调用仍可能不返回任何文档。但是,如果hasMore为false,则光标将被耗尽。一旦hasMore属性的值为 false,客户端就可以停止。 | |||
% 返回码 | |||
% 200:如果成功,服务器将以HTTP 200响应。 | |||
% 400:如果省略了光标标识符,则服务器将使用HTTP 404进行响应。 | |||
% 404:如果找不到具有指定标识符的游标,则服务器将使用HTTP 404进行响应。 | |||
nextCursor(PoolNameOrSocket, CursorId) -> | |||
Path = <<"/_api/cursor/", (agMiscUtils:toBinary(CursorId))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined). | |||
% 删除光标永 | |||
% DELETE /_api/cursor/{cursor-identifier} | |||
% 路径参数 | |||
% cursor-identifier(必填):光标的ID | |||
% 删除游标并释放与其关联的资源。 | |||
% 当客户端从服务器上检索所有文档时,游标将在服务器上自动销毁。客户端还可以使用HTTP DELETE请求在任何较早的时间显式销毁游标。游标ID必须作为URL的一部分包含在内。 | |||
% 注意:在服务器控制的特定超时后,服务器还将自动销毁废弃的游标,以避免资源泄漏。 | |||
% 返回码 | |||
% 202:如果服务器知道游标,则返回。 | |||
% 404:如果服务器不知道游标,则返回404。如果在销毁游标后使用了游标,也将返回该值。 | |||
delCursor(PoolNameOrSocket, CursorId) -> | |||
Path = <<"/_api/cursor/", (agMiscUtils:toBinary(CursorId))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
% AQL查询的HTTP接口 | |||
% | |||
% 解释和解析查询 | |||
% ArangoDB有一个HTTP接口,用于语法验证AQL查询。此外,它提供了一个HTTP接口来检索任何有效AQL查询的执行计划。 | |||
% 这两个功能实际上并不执行提供的AQL查询,而只是检查它并返回有关它的元信息。 | |||
% 解释一个AQL查询并返回有关它的信息 | |||
% POST /_api/explain | |||
% 描述查询和查询参数的JSON对象。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% query:您要解释的查询;如果查询引用了任何绑定变量,则这些变量也必须在属性bindVars中传递。可以在options属性中传递查询的其他选项。 | |||
% bindVars:表示绑定参数的键/值对。 | |||
% options:查询选项 | |||
% allPlans:如果设置为true,将返回所有可能的执行计划。默认值为false,这意味着将仅返回最佳计划。 | |||
% maxNumberOfPlans:允许优化程序生成的可选计划最大数量。将此属性设置为较低的值可以限制优化器的工作量。 | |||
% Optimizer:与查询优化器有关的选项。 | |||
% rules:可以在此属性中放入要包括的或要排除的优化器规则的列表,告诉优化器包括或排除特定的规则。要禁用规则,请在其名称前面加上-,以启用规则,并在其前面加上+。还有一个伪规则all,它匹配所有优化程序规则。-all禁用所有规则。 | |||
% | |||
% 为了说明如何在服务器上执行AQL查询,可以通过HTTP POST请求将查询字符串发送到服务器。然后,服务器将验证查询并为其创建执行计划。将返回执行计划,但不会执行查询。 | |||
% 服务器返回的执行计划可用于估计查询的可能性能。尽管实际性能将取决于许多不同的因素,但是执行计划通常可以对服务器实际运行查询所需的工作量提供一些粗略的估计。 | |||
% 默认情况下,解释操作将返回查询优化器选择的最佳计划。最佳计划是总估计成本最低的计划。该计划将在响应对象的属性计划中返回。如果在请求中指定了allPlans选项,则结果将包含优化器创建的所有计划。然后将在属性计划中返回计划。 | |||
% 结果还将包含一个属性warnings,它是在优化或执行计划创建期间发生的一系列警告。此外,结果中还包含stats属性以及一些优化程序统计信息。如果allPlans设置为false,则结果将包含可缓存的属性 ,该属性指示如果使用了查询结果缓存,则是否可以将查询结果缓存在服务器上。该缓存时属性不存在allPlans 设置为真。 | |||
% 结果中的每个计划都是一个具有以下属性的JSON对象: | |||
% nodes:计划执行节点的数组。可在此处找到可用节点类型的数组 | |||
% estimatedCost:计划的总估计费用。如果有多个计划,优化器将选择总成本最低的计划。 | |||
% collections:查询中使用的一组collections | |||
% rules:优化程序应用的规则数组。可在此处找到可用规则的概述 | |||
% variables:查询中使用的变量数组(注意:这可能包含优化器创建的内部变量) | |||
% 返回码 | |||
% 200:如果查询有效,则服务器将使用HTTP 200进行响应,并在响应的plan属性中返回最佳执行计划。如果在请求中设置了选项allPlans,则将在allPlans属性中返回一系列计划。 | |||
% 400:如果请求格式错误或查询包含解析错误,服务器将以HTTP 400响应。响应的正文将包含嵌入在JSON对象中的错误详细信息。如果查询引用任何变量,则忽略绑定变量也会导致HTTP 400错误。 | |||
% 404:如果查询中访问了不存在的集合,服务器将以HTTP 404进行响应。 | |||
explainQuery(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/explain">>, [], BodyStr). | |||
% 解析一个AQL查询并返回有关它的信息 | |||
% POST /_api/query | |||
% 具有以下属性的JSON对象是必需的: | |||
% query:要在不执行查询字符串的情况下对其进行验证,可以通过HTTP POST请求将查询字符串传递到服务器。 | |||
% 该端点仅用于查询验证。要实际查询数据库,请参阅/api/cursor。 | |||
% 返回码 | |||
% 200:如果查询有效,服务器将使用HTTP 200进行响应,并在响应的bindVars属性中返回在查询中找到的绑定参数的名称(如果有)。它还将在collections属性中返回查询中使用的collections的数组。如果查询可以成功解析,则返回的JSON 的ast属性将包含查询的抽象语法树表示形式。ast的格式在将来的ArangoDB版本中可能会发生变化,但是可以用来检查ArangoDB如何解释给定查询。请注意,将在不对其应用任何优化的情况下返回抽象语法树。 | |||
% 400:如果请求格式错误或查询包含解析错误,服务器将以HTTP 400响应。响应的正文将包含嵌入在JSON对象中的错误详细信息。 | |||
parseQuery(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/query">>, [], BodyStr). | |||
% 查询跟踪固定链接 | |||
% ArangoDB具有HTTP接口,用于检索当前正在执行的AQL查询列表和慢速AQL查询列表。为了有意义地使用这些API,需要在执行HTTP请求的数据库中启用查询跟踪。 | |||
% 返回AQL查询跟踪的配置 | |||
% GET /_api/query/properties | |||
% 返回当前查询跟踪配置。该配置是具有以下属性的JSON对象: | |||
% enabled:如果设置为true,那么将跟踪查询。如果设置为 false,则不会跟踪查询或慢速查询。 | |||
% trackSlowQueries:如果设置为true,则如果慢查询的运行时间超过了slowQueryThreshold中设置的值,则将在慢查询列表中跟踪慢查询 。为了跟踪慢查询, 还必须将enabled属性设置为true。 | |||
% trackBindVars:如果设置为true,那么将跟踪查询中使用的绑定变量。 | |||
% maxSlowQueries:保留在慢速查询列表中的最大慢速查询数。如果慢速查询列表已满,则在发生其他慢速查询时,其中最早的条目将被丢弃。 | |||
% slowQueryThreshold:用于将查询视为慢查询的阈值。当启用慢查询跟踪时,运行时大于或等于此阈值的查询将被放入慢查询列表中。slowQueryThreshold的值以秒为单位指定。 | |||
% maxQueryStringLength:保留在查询列表中的最大查询字符串长度。查询字符串可以有任意长度,如果使用非常长的查询字符串,则可以使用此属性来节省内存。该值以字节为单位指定。 | |||
% 返回码 | |||
% 200:如果成功检索到属性,则返回。 | |||
% 400:如果请求格式错误,服务器将以HTTP 400进行响应, | |||
getQueryProps(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/query/properties">>, [], undefined). | |||
% 更改AQL查询跟踪的配置 | |||
% PUT /_api/query/properties | |||
% 具有以下属性的JSON对象是必需的: | |||
% enabled:如果设置为true,那么将跟踪查询。如果设置为 false,则不会跟踪查询或慢速查询。 | |||
% trackSlowQueries:如果设置为true,则如果慢查询的运行时间超过了slowQueryThreshold中设置的值,则将在慢查询列表中跟踪慢查询 。为了跟踪慢查询, 还必须将enabled属性设置为true。 | |||
% trackBindVars:如果设置为true,那么将与查询一起跟踪查询中使用的绑定变量。 | |||
% maxSlowQueries:保留在慢速查询列表中的最大慢速查询数。如果慢速查询列表已满,则在发生其他慢速查询时,其中最早的条目将被丢弃。 | |||
% slowQueryThreshold:用于将查询视为慢速的阈值。当启用慢查询跟踪时,运行时大于或等于此阈值的查询将被放入慢查询列表中。slowQueryThreshold的值以秒为单位指定。 | |||
% maxQueryStringLength:要保留在查询列表中的最大查询字符串长度。查询字符串可以有任意长度,如果使用非常长的查询字符串,则可以使用此属性来节省内存。该值以字节为单位指定。 | |||
% 这些属性需要在HTTP请求主体的属性属性中传递。属性必须是JSON对象。 | |||
% 更改属性后,将在HTTP响应中返回当前属性集。 | |||
% 返回码 | |||
% 200:如果属性更改成功,则返回。 | |||
% 400:如果请求格式错误,服务器将以HTTP 400进行响应, | |||
changeQueryProps(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/query/properties">>, [], BodyStr). | |||
% 返回当前正在运行的AQL查询的列表 | |||
% GET /_api/query/current | |||
% 返回一个数组,其中包含所选数据库中当前正在运行的AQL查询。每个查询都是一个具有以下属性的JSON对象: | |||
% id:查询的ID | |||
% query:查询字符串(可能被截断) | |||
% bindVars:查询使用的绑定参数值 | |||
% started:查询开始的日期和时间 | |||
% runTime:查询的运行时间,直到查询到查询列表为止 | |||
% state:查询的当前执行状态(以字符串形式) | |||
% stream:查询是否使用流游标 | |||
% 返回码 | |||
% 200:可以成功检索查询列表时返回。 | |||
% 400:如果请求格式错误,服务器将以HTTP 400进行响应, | |||
currentQuery(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/query/current">>, [], undefined). | |||
% 返回运行缓慢的AQL查询的列表 | |||
% GET /_api/query/slow | |||
% 返回一个数组,其中包含已完成并超过所选数据库中慢速查询阈值的最后一个AQL查询。可以通过设置查询跟踪属性来控制列表中的最大查询数量maxSlowQueries。可以通过设置查询跟踪属性来调整 将查询视为慢速查询的阈值slowQueryThreshold。 | |||
% 每个查询都是一个具有以下属性的JSON对象: | |||
% id:查询的ID | |||
% query:查询字符串(可能被截断) | |||
% bindVars:查询使用的绑定参数值 | |||
% started:查询开始的日期和时间 | |||
% runTime:查询的总运行时间 | |||
% state:查询的当前执行状态(对于慢速查询列表将始终“完成”) | |||
% stream:查询是否使用流游标 | |||
% 返回码 | |||
% 200:可以成功检索查询列表时返回。 | |||
% 400:如果请求格式错误,服务器将以HTTP 400进行响应, | |||
getSlowQuery(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/query/slow">>, [], undefined). | |||
% 清除慢速AQL查询列表 | |||
% DELETE /_api/query/slow | |||
% 清除慢速AQL查询列表 | |||
% 返回码 | |||
% 200:成功清除查询列表后,服务器将以HTTP 200响应。 | |||
% 400:如果请求格式错误,服务器将使用HTTP 400进行响应。 | |||
delSlowQuery(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, <<"/_api/query/slow">>, [], undefined). | |||
% 杀死查询永久链接 | |||
% 运行中的AQL查询也可以在服务器上终止。ArangoDB通过HTTP接口提供了终止功能。要终止正在运行的查询,必须指定其ID(在当前正在运行的查询列表中为该查询返回的ID)。然后将设置查询的kill标志,并且查询到达取消点后将中止查询。 | |||
% 杀死一个AQL查询 | |||
% DELETE /_api/query/{query-id} | |||
% 路径参数 | |||
% query-id(必填):查询的ID。 | |||
% 终止正在运行的查询。查询将在下一个取消点终止。 | |||
% 返回码 | |||
% 200:当执行终止请求并设置查询的终止标志时,查询仍在运行时,服务器将以HTTP 200响应。 | |||
% 400:如果请求格式错误,服务器将使用HTTP 400进行响应。 | |||
% 404:当找不到指定ID的查询时,服务器将以HTTP 404响应。 | |||
killQuery(PoolNameOrSocket, QueryId) -> | |||
Path = <<"/_api/query/", (agMiscUtils:toBinary(QueryId))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
% AQL查询结果缓存的HTTP接口 | |||
% 本节介绍用于控制AQL查询结果缓存的API方法。 | |||
% 返回AQL查询结果缓存中存储结果的列表 | |||
% GET /_api/query-cache/entries | |||
% 返回一个数组,其中包含当前存储在所选数据库的查询结果缓存中的AQL查询结果。每个结果都是一个具有以下属性的JSON对象: | |||
% hash:查询结果的哈希值 | |||
% query:查询字符串 | |||
% bindVars:查询的绑定参数。仅当服务器启动时启用了对绑定变量的跟踪时,才显示此属性 | |||
% size:查询结果和绑定参数的大小,以字节为单位 | |||
% results:查询结果中的文档/行数 | |||
% started:查询存储在缓存中的日期和时间 | |||
% hits:从缓存中提供结果的次数(对于仅存储在缓存中但以后再也不会访问的查询,可以为 0) | |||
% runTime:查询的运行时间 | |||
% dataSources:查询所使用的集合/视图的数组 | |||
% 返回码 | |||
% 200:可以成功检索结果列表时返回。 | |||
% 400:如果请求格式错误,服务器将以HTTP 400进行响应, | |||
getQueryCaches(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/query-cache/entries">>, [], undefined). | |||
% 清除AQL查询结果缓存中的所有结果 | |||
% DELETE /_api/query-cache | |||
% 清除当前数据库的查询结果缓存 | |||
% 返回码 | |||
% 200:成功清除缓存后,服务器将以HTTP 200响应。 | |||
% 400:如果请求格式错误,服务器将使用HTTP 400进行响应。 | |||
clearQueryCaches(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, <<"/_api/query-cache">>, [], undefined). | |||
% 返回AQL查询结果缓存的全局配置 | |||
% GET /_api/query-cache/properties | |||
% 返回全局AQL查询结果缓存配置。该配置是具有以下属性的JSON对象: | |||
% mode:AQL查询结果缓存运行的模式。该模式是下列值之一:off,on或demand。 | |||
% maxResults:每个特定于数据库的缓存将存储的最大查询结果数。 | |||
% maxResultsSize:每个数据库特定的缓存将存储的查询结果的最大累积大小。 | |||
% maxEntrySize:每个特定于数据库的缓存将存储的查询的最大单个结果大小。 | |||
% includeSystem:是否将涉及系统集合的查询结果存储在查询结果缓存中。 | |||
% 返回码 | |||
% 200:如果可以成功检索属性,则返回。 | |||
% 400:如果请求格式错误,服务器将以HTTP 400进行响应, | |||
getQCacheProps(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/query-cache/properties">>, [], undefined). | |||
% 全局调整AQL查询结果缓存属性 | |||
% PUT /_api/query-cache/properties | |||
% 具有以下属性的JSON对象是必需的: | |||
% mode:AQL查询缓存应以哪种模式运行。可能的值是off,on或demand。 | |||
% maxResults:每个特定于数据库的缓存将存储的最大查询结果数。 | |||
% maxResultsSize:每个数据库特定的缓存将存储的查询结果的最大累积大小。 | |||
% maxEntrySize:每个数据库特定的缓存将存储的查询结果的最大单个大小。 | |||
% includeSystem:是否存储涉及系统集合的查询结果。 | |||
% 更改属性后,将在HTTP响应中返回当前属性集。 | |||
% 注意:更改属性可能会使缓存中的所有结果无效。AQL查询缓存的全局属性。这些属性需要在HTTP请求主体的属性属性中传递。属性必须是具有以下属性的JSON对象: | |||
% 返回码 | |||
% 200:如果属性更改成功,则返回。 | |||
% 400:如果请求格式错误,服务器将以HTTP 400进行响应, | |||
changeQCacheProps(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/query-cache/properties">>, [], BodyStr). | |||
% AQL用户功能管理固定链接 | |||
% 这是用于管理AQL用户功能的ArangoDB HTTP接口的简介。AQL用户功能是一种使用用户定义的JavaScript代码扩展ArangoDB查询语言(AQL)功能的方法。 | |||
% 有关AQL用户功能及其含义的概述,请参阅“ 扩展AQL”一章。 | |||
% HTTP接口提供用于添加,删除和列出以前注册的AQL用户功能的API。 | |||
% 通过此接口管理的所有用户功能将存储在系统集合_aqlfunctions中。此集合中的文档不应直接访问,而只能通过专用接口访问。 | |||
% 创建一个新的AQL用户功能 | |||
% POST /_api/aqlfunction | |||
% 具有以下属性的JSON对象是必需的: | |||
% name:用户函数的标准名称。 | |||
% code:函数主体的字符串表示形式。 | |||
% isDeterministic:一个可选的布尔值,用于指示函数结果是否完全确定(函数返回值仅取决于输入值,并且对于具有相同输入的重复调用,返回值相同)。该isDeterministic属性是当前未使用但对于优化可以在以后使用。 | |||
% 如果成功,则返回HTTP 200。如果该功能无效等,则将返回包含详细错误消息的HTTP 400。 | |||
% HTTP 200如果功能已经存在并被调用所取代,则服务器将使用HTTP 200进行响应。 | |||
% error:布尔值标志,指示是否发生错误(在这种情况下为false) | |||
% code:HTTP状态码 | |||
% isNewlyCreated:布尔值标志,指示是否新创建了函数(在这种情况下为false) | |||
% HTTP 201如果服务器可以注册该功能,则服务器将使用HTTP 201进行响应 。 | |||
% error:布尔值标志,指示是否发生错误(在这种情况下为false) | |||
% code:HTTP状态码 | |||
% isNewlyCreated:布尔值标志,指示是否新创建了函数(在这种情况下为true) | |||
% HTTP 400如果JSON格式不正确或请求中缺少必需数据,则服务器将使用HTTP 400进行响应。 | |||
% error:布尔值标志,指示是否发生错误(在这种情况下为true) | |||
% code:HTTP状态码 | |||
% errorNum:服务器错误号 | |||
% errorMessage:描述性错误消息 | |||
newUserFun(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/aqlfunction">>, [], BodyStr). | |||
% 删除现有的AQL用户功能 | |||
% DELETE /_api/aqlfunction/{name} | |||
% 路径参数 | |||
% name(必填):AQL用户功能的名称。 | |||
% 查询参数 | |||
% group(可选): - 真:中提供的功能的名称的名称被视为一个命名空间前缀,并在指定的命名空间的所有功能。将被删除。如果没有匹配的字符串,返回的删除函数数可能变为0。 | |||
% false:中提供的函数名的名称必须是完全限定,包括任何命名空间。如果没有一个与名称匹配,则返回HTTP 404。 | |||
% 删除由name标识的现有AQL用户功能或功能组。 | |||
% HTTP 200如果服务器可以删除该功能,则服务器将使用HTTP 200进行响应 。 | |||
% error:布尔值标志,指示是否发生错误(在这种情况下为false) | |||
% code:HTTP状态码 | |||
% DeleteCount:删除的用户功能的数量,总是1在group设置为false时。设置为true>= 0时的任何数字group | |||
% HTTP 400如果用户功能名称格式错误,则服务器将使用HTTP 400进行响应。 | |||
% error:布尔值标志,指示是否发生错误(在这种情况下为true) | |||
% code:HTTP状态码 | |||
% errorNum:服务器错误号 | |||
% errorMessage:描述性错误消息 | |||
% HTTP 404如果指定的用户用户功能不存在,则服务器将使用HTTP 404进行响应。 | |||
% error:布尔值标志,指示是否发生错误(在这种情况下为true) | |||
% code:HTTP状态码 | |||
% errorNum:服务器错误号 | |||
% errorMessage:描述性错误消息 | |||
delUserFun(PoolNameOrSocket, UserFunName) -> | |||
Path = <<"/_api/aqlfunction/", (agMiscUtils:toBinary(UserFunName))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
delUserFun(PoolNameOrSocket, UserFunName, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/aqlfunction/", (agMiscUtils:toBinary(UserFunName))/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
% 返回注册的AQL用户功能 | |||
% GET /_api/aqlfunction | |||
% 查询参数 | |||
% namespace(可选):从result下的命名空间namespace返回所有已注册的AQL用户函数。 | |||
% 返回所有已注册的AQL用户功能。 | |||
% 该调用将返回一个带有状态代码的JSON数组,以及在result下找到的所有用户函数。 | |||
% HTTP 200成功HTTP 200返回。 | |||
% error:布尔值标志,指示是否发生错误(在这种情况下为false) | |||
% code:HTTP状态码 | |||
% result:所有函数,或与命名空间参数匹配的函数 | |||
% name:用户功能的标准名称 | |||
% code:函数体的字符串表示形式 | |||
% isDeterministic:一个可选的布尔值,用于指示函数结果是否完全确定(函数返回值仅取决于输入值,并且对于具有相同输入的重复调用,返回值相同)。该isDeterministic属性是当前未使用但对于优化可以在以后使用。 | |||
% HTTP 400如果用户功能名称格式错误,则服务器将使用HTTP 400进行响应。 | |||
% error:布尔值标志,指示是否发生错误(在这种情况下为true) | |||
% code:HTTP状态码 | |||
% errorNum:服务器错误号 | |||
% errorMessage:描述性错误消息 | |||
getUserFuns(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/aqlfunction">>, [], undefined). | |||
getUserFuns(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/aqlfunction", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
@ -0,0 +1,140 @@ | |||
-module(agAsyncResultHandling). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address:https://www.arangodb.com/docs/stable/http/async-results-management.html | |||
% 用于异步结果管理的HTTP接口 | |||
% 请求执行 | |||
% ArangoDB提供了执行客户端请求的各种方法。客户可以根据其吞吐量,控制流和耐用性要求,在每个请求级别上选择适当的方法。 | |||
% | |||
% 阻止执行 | |||
% ArangoDB是多线程服务器,允许同时处理多个客户端请求。通信处理和实际工作可以由多个工作线程并行执行。 | |||
% | |||
% 尽管多个客户端可以连接并并行将其请求发送到ArangoDB,但是客户端可能需要等待其请求被处理。 | |||
% | |||
% 默认情况下,服务器将完全处理传入的请求,然后将结果返回给客户端。客户端必须等待服务器的响应,然后才能通过连接发送其他请求。对于单线程或非事件驱动的客户端,等待完整的服务器响应可能不是最佳的。 | |||
% | |||
% 此外,请注意,即使客户端关闭了HTTP连接,在服务器上运行的请求仍将继续,直到完成为止,然后注意客户端不再侦听。因此,关闭连接无助于中止长时间运行的查询!有关详细信息,请参见下面的“ 异步执行”和“结果检索” 以及HttpJobPutCancel下的内容。 | |||
% | |||
% 忘记并 | |||
% 为了缓解客户端阻止问题,自版本1.4开始使用ArangoDB。提供了一种非阻塞请求的通用机制:如果客户端在请求中添加HTTP标头x-arango-async:true,则ArangoDB会将请求放入内存任务队列中,并向HTTP请求返回HTTP 202(接受)响应客户立即。服务器将异步执行队列中的任务,从而将客户端请求与实际工作分离。 | |||
% | |||
% 与客户端等待服务器响应相比,这可以提供更高的吞吐量。缺点是发送到客户端的响应始终是相同的(通用HTTP 202),并且客户端此时无法基于实际操作的结果做出决定。实际上,在通用响应到达客户端时,该操作甚至可能尚未执行。因此,客户不能依赖其请求已被成功处理。 | |||
% | |||
% 服务器上的异步任务队列不会保留,这意味着如果发生崩溃,队列中尚未处理的任务将丢失。但是,客户端将不知道它们是否已被处理。 | |||
% | |||
% 因此,当客户具有严格的持久性要求或依靠发送操作的结果采取进一步措施时,客户不应发送额外的标头。 | |||
% | |||
% 排队任务的最大数量由启动选项 --server.maximal-queue-size确定。如果已排队的任务数量超过此数量,则服务器将拒绝该请求,并显示HTTP 500错误。 | |||
% | |||
% 最后,请注意,无法取消这种火灾并忘记工作,因为稍后您将无法识别它。如果您需要取消请求,请使用“ 异步执行”以及更高版本的“结果检索” 和下面的HttpJobPutCancel。 | |||
% | |||
% 异步执行和以后的结果检索 | |||
% 通过将HTTP标头x-arango-async:存储添加到请求中,客户端可以指示ArangoDB服务器如上所述异步执行操作,还可以将操作结果存储在内存中以供以后检索。服务器将在HTTP响应标头x-arango-async-id中返回作业ID。客户端可以将此ID与/ _api / job上的HTTP API结合使用,这在本手册中有详细说明。 | |||
% | |||
% 客户可以通过异步作业API询问ArangoDB服务器,哪些结果准备好检索,哪些没有准备好。客户端还可以通过将原始返回的作业ID传递给异步作业API,以获取已执行的异步作业的原始结果。然后,服务器将返回作业结果,就好像作业已正常执行一样。此外,客户端可以通过其作业ID取消运行异步作业,请参见HttpJobPutCancel。 | |||
% | |||
% ArangoDB将保留通过x-arango-async:存储 头启动的所有作业结果。仅当客户端明确要求服务器提供特定结果时,才会从服务器中删除结果。 | |||
% | |||
% 异步作业API还提供了用于垃圾回收的方法,客户端可以使用这些方法来摆脱“旧的”未获取的结果。客户应定期调用此方法,因为ArangoDB不会人为地限制尚未获取的结果的数量。 | |||
% | |||
% 因此,客户有责任仅存储所需的尽可能多的结果,并尽快获取可用的结果,或至少不时清理未获取的结果。 | |||
% | |||
% 作业队列和结果仅保存在服务器上的内存中,因此在崩溃时它们将丢失。 | |||
% | |||
% 取消异步作业 | |||
% 如上所述,可以使用其作业ID取消异步运行的作业。如HttpJobPutCancel中所述,这是通过PUT请求 完成的。 | |||
% | |||
% 但是,要对幕后发生的事情进行一些解释。首先,一个正在运行的异步查询可以在内部由C ++代码或JavaScript代码执行。例如,CRUD操作直接在C ++中执行,而AQL查询和事务由JavaScript代码执行。作业取消仅适用于JavaScript代码,因为所使用的机制只是在JavaScript线程中触发不可捕获的异常,而该异常将在C ++级别上捕获,从而导致作业的取消。以后将无法检索到任何结果,因为有关该请求的所有数据都将被丢弃。 | |||
% | |||
% 如果取消在集群的协调器上运行的作业(共享),则仅停止在协调器上运行的代码,集群中可能残留了已经分配给DB-Server的任务,目前无法执行也要取消它们 | |||
% | |||
% 异步执行和身份验证 | |||
% 如果请求需要身份验证,则在排队之前运行身份验证过程。仅当请求有效凭据且身份验证成功时,该请求才会排队。如果请求不包含有效的凭据,则不会将其排队,但会以与“常规”非排队请求相同的方式立即被拒绝。 | |||
% 获取作业结果并将其从队列中删除 | |||
% PUT /_api/job/{job-id} | |||
% 路径参数 | |||
% job-id(必填):异步作业ID。 | |||
% 返回由job-id标识的异步作业的结果。如果服务器上存在异步作业结果,则该结果将从结果列表中删除。这意味着可以为每个job-id调用一次此方法。该方法将返回原始作业结果的标头和正文,以及附加的HTTP标头x-arango-async-job-id。如果存在此标头,则找到作业,并且响应中包含原始作业的结果。如果标题不存在,则找不到作业,并且响应中包含来自作业管理器的状态信息。 | |||
% 返回码 | |||
% 204:如果通过job-id请求的作业仍在待处理(或尚未完成)的作业队列中,则返回。在这种情况下,不会返回x-arango-async-id HTTP标头。 | |||
% 400:如果在请求中未指定作业ID,则返回。在这种情况下,不会返回x-arango-async-id HTTP标头。 | |||
% 404:如果找不到或已经从作业结果列表中删除或提取了作业,则返回404。在这种情况下,不会返回x-arango-async-id HTTP标头。 | |||
getAsyncJobRet(PoolNameOrSocket, JodId) -> | |||
Path = <<"/_api/job/", (agMiscUtils:toBinary(JodId))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined). | |||
% 取消异步作业 | |||
% PUT /_api/job/{job-id}/cancel | |||
% 路径参数 | |||
% job-id(必填):异步作业ID。 | |||
% 取消由作业ID标识的当前正在运行的作业。请注意,实际取消正在运行的异步作业仍可能需要一些时间。 | |||
% 返回码 | |||
% 200:取消已启动。 | |||
% 400:如果在请求中未指定作业ID,则返回。在这种情况下,不会返回x-arango-async-id HTTP标头。 | |||
% 404:如果找不到或已经从作业结果列表中删除或提取了作业,则返回404。在这种情况下,不会返回x-arango-async-id HTTP标头。 | |||
cancelAsyncJob(PoolNameOrSocket, JodId) -> | |||
Path = <<"/_api/job/", (agMiscUtils:toBinary(JodId))/binary, "/cancel">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined). | |||
% 删除异步作业结果 | |||
% DELETE /_api/job/{type}#by-type | |||
% 路径参数 | |||
% type(必填):要删除的作业类型。类型可以是: | |||
% all:删除所有作业结果。当前正在执行或排队的异步作业不会被此调用停止。 | |||
% expired:删除过期的结果。要确定结果的到期状态,请传递戳记查询参数。stamp必须是UNIX时间戳,所有在较低时间戳创建的异步作业结果都将被删除。 | |||
% an actual job-id:在这种情况下,调用将删除指定的异步工作的结果。如果作业当前正在执行或排队,则不会中止。 | |||
% 查询参数 | |||
% stamp(可选):UNIX时间戳记,用于指定类型过期时的过期阈值。 | |||
% 删除所有作业结果,过期的作业结果或特定作业的结果。客户可以使用此方法对工作结果进行最终的垃圾收集。 | |||
% 返回码 | |||
% 200:如果删除操作成功执行,则返回。如果未删除任何结果,还将返回此代码。 | |||
% 400:如果未指定type或值无效,则返回。 | |||
% 404:如果type为job-id,但未找到具有指定id的异步作业,则返回404。 | |||
delAsyncJobRet(PoolNameOrSocket, TypeOrJodId) -> | |||
Path = <<"/_api/job/", (agMiscUtils:toBinary(TypeOrJodId))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
delAsyncJobRet(PoolNameOrSocket, TypeOrJodId, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/job/", (agMiscUtils:toBinary(TypeOrJodId))/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
% 返回特定作业的状态 | |||
% GET /_api/job/{job-id} | |||
% 路径参数 | |||
% job-id(必填):异步作业ID。 | |||
% 返回指定作业的处理状态。可以通过查看响应的HTTP响应代码来确定处理状态。 | |||
% 返回码 | |||
% 200:如果已经执行了通过job-id请求的作业,并且已准备好获取其结果,则返回200。 | |||
% 204:如果通过job-id请求的作业仍在待处理(或尚未完成)的作业队列中,则返回。 | |||
% 404:如果找不到或已经从作业结果列表中删除或提取了作业,则返回404。 | |||
getAsyncJobStatus(PoolNameOrSocket, JodId) -> | |||
Path = <<"/_api/job/", (agMiscUtils:toBinary(JodId))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 返回具有特定状态的工作结果ID | |||
% GET /_api/job/{type}#by-type | |||
% 路径参数 | |||
% type(必填):要返回的作业类型。类型可以是 done 的或 pending 的。将类型设置为done将使该方法返回可以获取其结果的异步作业的ID。将类型设置为pending将返回尚未完成的异步作业的ID。 | |||
% 查询参数 | |||
% count(可选):每次调用返回的最大ID数。如果未指定,将使用服务器定义的最大值。 | |||
% 返回具有特定状态(已完成或未决)的异步作业的ID列表。客户端可以使用该列表获取作业系统状态的概述,并在以后检索完成的作业结果。 | |||
% 返回码 | |||
% 200:如果列表可以成功编译,则返回。注意:该列表可能为空。 | |||
% 400:如果未指定type或值无效,则返回。 | |||
getAsyncJobList(PoolNameOrSocket, Type) -> | |||
Path = <<"/_api/job/", (agMiscUtils:toBinary(Type))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
getAsyncJobList(PoolNameOrSocket, Type, Count) -> | |||
Path = <<"/_api/job/", (agMiscUtils:toBinary(Type))/binary, "?count=", (agMiscUtils:toBinary(Count))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
@ -0,0 +1,233 @@ | |||
-module(agBulkImportExport). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address:https://www.arangodb.com/docs/stable/http/bulk-imports.html | |||
% 批量导入的HTTP接口 | |||
% ArangoDB提供了一个HTTP接口,可以一次将多个文档导入一个集合中。这称为批量导入。 | |||
% 上传的数据必须以JSON格式提供。有两种导入数据的机制: | |||
% 自包含的JSON文档:在这种情况下,每个文档都包含所有属性名称和值。在上传的文档中,属性名称可能完全不同 | |||
% 属性名称加上文档数据:在这种情况下,第一个数组必须包含后面文档的属性名称。以下数组仅包含属性值。属性值将按位置映射到属性名称。 | |||
% 两种输入机制的端点地址均为/ _api / import。必须使用HTTP POST请求将数据发送到此URL。要导入的数据必须包含在POST请求的正文中。 | |||
% 该集合的查询参数必须用于指定目标集合导入。将数据导入到不存在的集合中将产生错误。 | |||
% 可以将waitForSync查询参数设置为true,以仅在所有文档都已同步到磁盘后才返回导入。 | |||
% 如果任何上传的文档无效并且无法导入,可以将complete查询参数设置为true,以使整个导入失败。在这种情况下,即使在导入结束时发生故障,导入操作也不会导入任何文档。 | |||
% 如果complete具有除true以外的其他值,则将导入有效文档,而拒绝无效文档,这意味着可能仅导入了一些上载文档。 | |||
% 可以将details查询参数设置为true,以使导入API返回有关无法导入的文档的详细信息。如果details为true,则结果还将包含一个details属性,该属性是详细错误消息的数组。如果将详细信息设置为false或省略,则不会返回任何详细信息。 | |||
% 从JSON编码的列表中导入文档 | |||
% POST /_api/import#document | |||
% 查询参数 | |||
% collection (必填):集合名称。 | |||
% fromPrefix(可选):_from属性中值的可选前缀。如果指定,该值将自动添加到每个_from输入值之前。这样就可以仅指定的键_from。 | |||
% toPrefix(可选):_to属性中值的可选前缀。如果指定,该值将自动添加到每个_to输入值之前。这样就可以仅指定的键_to。 | |||
% overwrite (可选):如果此参数的值为true或yes,则将在导入之前删除集合中的所有数据。请注意,任何现有的索引定义都将保留。 | |||
% waitForSync(可选):等待文档同步到磁盘后再返回。 | |||
% onDuplicate(可选):控制在违反唯一键约束的情况下执行的操作。可能的值为: | |||
% error:由于违反唯一键约束,因此不会导入当前文档。这是默认设置。 | |||
% update:这将使用请求中指定的数据更新数据库中的现有文档。请求中不存在的现有文档的属性将被保留。 | |||
% replace:这将用请求中指定的数据替换数据库中的现有文档。 | |||
% ignore:这不会更新现有文档,而只是忽略由唯一键约束冲突引起的错误。 | |||
% 请注意,仅当请求中的导入文档包含_key属性时,update,replace和ignore才起作用。由于次要唯一键约束冲突,更新和 替换也可能失败。 | |||
% | |||
% complete (可选):如果设置为true或yes,则如果发生任何错误,将使整个导入失败。否则,即使无法导入某些文档,导入也将继续。 | |||
% details(可选):如果设置为true或yes,结果将包括一个属性,details 其中包含有关无法导入的文档的详细信息。 | |||
% | |||
% 请求正文(字符串) | |||
% 主体必须由JSON编码的属性值数组组成,每个文档一行。请求的第一行必须是JSON编码的属性名称数组。这些属性名称用于后续各行中的数据。 | |||
% 在由标识的集合中创建文档collection-name。请求正文的第一行必须包含一个JSON编码的属性名称数组。请求正文中的以下所有行都必须包含JSON编码的属性值数组。每行都被解释为一个单独的文档,并且指定的值将映射到在第一标题行中指定的属性名称的数组。 | |||
% | |||
% 响应是具有以下属性的JSON对象: | |||
% created:导入的文件数。 | |||
% errors:由于错误而未导入的文档数。 | |||
% empty:在输入中找到的空行数(类型documents或只能包含大于零的值auto)。 | |||
% updated:更新/替换的文档数(如果onDuplicate 设置为update或replace)。 | |||
% ignored:失败但被忽略的插入操作数(如果 onDuplicate设置为ignore)。 | |||
% details:如果查询参数details设置为true,则结果将包含一个details属性,该属性是一个数组,其中包含有关无法插入哪些文档的更多详细信息。 | |||
% | |||
% 返回码 | |||
% 201:如果可以成功导入所有文档,则返回。 | |||
% 400:如果type包含无效值,未collection指定no ,文档编码错误或请求格式错误,则返回。 | |||
% 404:如果collection或导入边的_from或_to属性引用未知集合,则返回。 | |||
% 409:如果导入会触发唯一键冲突,complete则返回,并将 其设置为true。 | |||
% 500:如果服务器无法为没有用户定义密钥的文档自动生成文档密钥(密钥错误),则返回500。 | |||
docImport(PoolNameOrSocket, ListOfList, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/import", QueryBinary/binary>>, | |||
BodyStr = <<<<(jiffy:encode(OneList))/binary, "\n">> || OneList <- ListOfList>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
% 从JSON导入文档 | |||
% POST /_api/import#json | |||
% 查询参数 | |||
% type (必填):确定如何解释请求的正文。type可以具有以下值: | |||
% documents:使用此类型时,请求正文中的每一行都应为单独的JSON编码的文档。请求主体中的多个JSON对象需要用换行符分隔。 | |||
% list:使用此类型时,请求主体必须包含要导入的单个对象的单个JSON编码数组。 | |||
% auto:如果设置,它将自动确定主体类型( documents或list)。 | |||
% collection (必填):集合名称。 | |||
% fromPrefix(可选):_from属性中值的可选前缀。如果指定,该值将自动添加到每个_from输入值之前。这样就可以仅指定的键_from。 | |||
% toPrefix(可选):_to属性中值的可选前缀。如果指定,该值将自动添加到每个_to输入值之前。这样就可以仅指定的键_to。 | |||
% overwrite (可选):如果此参数的值为true或yes,则将在导入之前删除集合中的所有数据。请注意,任何现有的索引定义都将保留。 | |||
% waitForSync(可选):等待文档同步到磁盘后再返回。 | |||
% onDuplicate(可选):控制在违反唯一键约束的情况下执行的操作。可能的值为: | |||
% error:由于违反唯一键约束,因此不会导入当前文档。这是默认设置。 | |||
% update:这将使用请求中指定的数据更新数据库中的现有文档。请求中不存在的现有文档的属性将被保留。 | |||
% replace:这将用请求中指定的数据替换数据库中的现有文档。 | |||
% ignore:这不会更新现有文档,而只是忽略由唯一键约束冲突引起的错误。 | |||
% 请注意,仅当请求中的导入文档包含_key属性时,update,replace和ignore才起作用。由于次要唯一键约束冲突,更新和 替换也可能失败。 | |||
% complete (可选):如果设置为true或yes,则如果发生任何错误,将使整个导入失败。否则,即使无法导入某些文档,导入也将继续。 | |||
% details(可选):如果设置为true或yes,结果将包括一个属性,details 其中包含有关无法导入的文档的详细信息。 | |||
% 请求正文(字符串) | |||
% 主体必须是JSON编码的对象数组,或者是包含多个以换行符分隔的JSON对象的字符串。 | |||
% 在由标识的集合中创建文档collection-name。文档的JSON表示形式必须作为POST请求的主体传递。请求主体可以由多行组成,每行都是一个独立的JSON对象,也可以是包含子对象的JSON数组。 | |||
% | |||
% 响应是具有以下属性的JSON对象: | |||
% created:导入的文件数。 | |||
% errors:由于错误而未导入的文档数。 | |||
% empty:在输入中找到的空行数(类型documents或只能包含大于零的值auto)。 | |||
% updated:更新/替换的文档数(如果onDuplicate 设置为update或replace)。 | |||
% ignored:失败但被忽略的插入操作数(如果 onDuplicate设置为ignore)。 | |||
% details:如果查询参数details设置为true,则结果将包含一个details属性,该属性是一个数组,其中包含有关无法插入哪些文档的更多详细信息。 | |||
% 返回码 | |||
% 201:如果可以成功导入所有文档,则返回。 | |||
% 400:如果type包含无效值,未collection指定no ,文档编码错误或请求格式错误,则返回。 | |||
% 404:如果collection或导入边的_from或_to属性引用未知集合,则返回。 | |||
% 409:如果导入会触发唯一键冲突,complete则返回,并将 其设置为true。 | |||
% 500:如果服务器无法为没有用户定义密钥的文档自动生成文档密钥(密钥错误),则返回500。 | |||
jsonImport(PoolNameOrSocket, MapDataList, QueryPars) -> | |||
case lists:keyfind(type, 1, QueryPars) of | |||
{type, list} -> | |||
BodyStr = jiffy:encode(MapDataList); | |||
{type, documents} -> | |||
BodyStr = <<<<(jiffy:encode(OneList))/binary, "\n">> || OneList <- MapDataList>>; | |||
_ -> | |||
BodyStr = MapDataList | |||
end, | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/import", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
% 说明-------> | |||
% 导入自包含的JSON文档 | |||
% 此导入方法允许上传自包含的JSON文档。这些文档必须上传到HTTP POST请求的正文中。正文的每一行都将被解释为一个独立文档。正文中的空行是允许的,但将被跳过。使用此格式,文档将逐行导入。 | |||
% | |||
% 输入数据示例:{“ _key”:“ key1”,……} {“ _key”:“ key2”,……}… | |||
% | |||
% 要使用此方法,应将类型查询参数设置为documents。 | |||
% | |||
% 还可以上传嵌入到JSON数组中的自包含JSON文档。数组中的每个元素都将被视为文档并导入。 | |||
% | |||
% 这种情况下的示例输入数据: | |||
% | |||
% [ | |||
% { "_key": "key1", ... }, | |||
% { "_key": "key2", ... }, | |||
% ... | |||
% ] | |||
% 此格式不需要每个文档都位于单独的行上,并且JSON数据中允许使用任何空格。它可以用于将JSON格式的结果数组(例如,从arangosh中)导入回ArangoDB中。使用此格式需要ArangoDB解析整个数组,并在导入期间将其保留在内存中。这可能比逐行处理要消耗更多的资源。 | |||
% | |||
% 要使用此方法,类型查询参数应设置为array。 | |||
% | |||
% 将类型查询参数设置为auto将使服务器自动检测数据是按行的JSON文档(类型=文档)还是JSON数组(类型=数组)。 | |||
% | |||
% 例子 | |||
% | |||
% curl --data-binary @- -X POST --dump - "http://localhost:8529/_api/import?type=documents&collection=test" | |||
% { "name" : "test", "gender" : "male", "age" : 39 } | |||
% { "type" : "bird", "name" : "robin" } | |||
% | |||
% HTTP/1.1 201 Created | |||
% Server: ArangoDB | |||
% Connection: Keep-Alive | |||
% Content-type: application/json; charset=utf-8 | |||
% | |||
% {"error":false,"created":2,"empty":0,"errors":0} | |||
% 如果一切顺利,服务器将以HTTP 201响应。导入的文档数将在响应的创建的属性中返回。如果任何文档被跳过或格式错误,将在errors属性中返回该文档。响应中还将有一个空属性,其中包含值为0。 | |||
% | |||
% 如果在请求中将details参数设置为true,则响应还将包含一个属性details,该属性是有关导入期间在服务器端发生的错误的详细信息的数组。如果没有发生错误,则此数组可能为空。 | |||
% 导入标题和值 | |||
% 使用这种类型的导入时,要与实际文档值数据分开指定要导入的文档的属性名称。HTTP POST请求正文的第一行必须是JSON数组,其中包含后面文档的属性名称。以下各行被解释为文档数据。每个文档必须是值的JSON数组。此数据部分中不需要或不允许属性名称。 | |||
% 例子 | |||
% curl --data-binary @- -X POST --dump - "http://localhost:8529/_api/import?collection=test" | |||
% [ "firstName", "lastName", "age", "gender" ] | |||
% [ "Joe", "Public", 42, "male" ] | |||
% [ "Jane", "Doe", 31, "female" ] | |||
% | |||
% HTTP/1.1 201 Created | |||
% Server: ArangoDB | |||
% Connection: Keep-Alive | |||
% Content-type: application/json; charset=utf-8 | |||
% | |||
% {"error":false,"created":2,"empty":0,"errors":0} | |||
% 如果一切顺利,服务器将再次以HTTP 201响应。导入的文档数将在响应的创建的属性中返回。如果任何文档被跳过或格式错误,将在errors属性中返回该文档。输入文件中的空行数将在empty属性中返回。 | |||
% | |||
% 如果在请求中将details参数设置为true,则响应还将包含一个属性details,该属性是有关导入期间在服务器端发生的错误的详细信息的数组。如果没有发生错误,则此数组可能为空。 | |||
% | |||
% 导入到边缘集合 | |||
% 请注意,将文档导入 边缘集合时,必须强制所有导入的文档都包含_from和_to属性,并且这些属性必须包含对现有集合的引用。 | |||
% 批处理请求的HTTP接口 | |||
% 客户端通常在单独的HTTP请求中向ArangoDB发送单独的操作。这是直接且简单的,但是具有以下缺点:如果连续发出许多小请求,则网络开销可能会很大。 | |||
% 为了缓解此问题,ArangoDB提供了一个批处理请求API,客户端可以使用该API批量向ArangoDB发送多个操作。当客户端必须以较小的正文/有效负载发送许多HTTP请求并且各个请求的结果彼此不依赖时,此方法特别有用。 | |||
% 客户端可以通过向URL / _api / batch处理程序发出多部分HTTP POST请求来使用ArangoDB的批处理API 。如果Content-type为multipart / form-data,则处理程序将接受请求并指定边界字符串。然后,ArangoDB将使用此边界将批处理请求分解为各个部分。这也意味着边界字符串本身不能包含在任何部分中。当ArangoDB将多部分请求分为其各个部分时,它将按顺序处理所有部分,就好像它是一个独立的请求一样。处理完所有零件后,ArangoDB将生成一个多部分HTTP响应,其中每个零件操作结果都包含一个零件。例如,如果您发送包含5个部分的多部分请求,则ArangoDB还将发送包含5个部分的多部分响应。 | |||
% 服务器希望每个零件消息均以以下“头”开头: | |||
% Content-type: application/x-arango-batchpart | |||
% 您可以选择指定Content-Id “标头”以唯一标识每个零件消息。如果已指定,服务器将在其响应中返回Content-Id。否则,服务器将不会发回Content-Id“标头”。服务器将不会验证Content-Id的唯一性。在强制性Content-type和可选Content-Id标头之后,必须紧跟两个Windows换行符(即\ r \ n \ r \ n)。该结构的任何偏差都可能导致零件被拒绝或错误解释。零件请求有效载荷(格式为常规HTTP请求)必须直接遵循两个Windows换行符。 | |||
% 请注意,从 技术上来说,文字Content-type:application / x-arango-batchpart是MIME部分的标头,而HTTP请求(包括其标头)是MIME部分的正文部分。 | |||
% 实际的零件请求应以通常的HTTP方法,被调用的URL和HTTP协议版本开头,后跟任意的HTTP标头。它的主体应遵循通常的\ r \ n \ r \ n 文字。因此,部分请求是常规的HTTP请求,仅嵌入在多部分消息中。 | |||
% 以下示例将发送具有3个单独文档创建操作的批处理。在此示例中使用的边界是 XXXsubpartXXX。 | |||
% ******************************************************************* | |||
% 批处理请求的HTTP接口 改功能不予实现支持 | |||
% ******************************************************************* | |||
% 使用光标导出集合中的所有文档 | |||
% POST /_api/export | |||
% 查询参数 | |||
% collection (必填):要导出的集合的名称。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% flush:如果设置为true,则将在导出之前执行WAL刷新操作。刷新操作将开始将文档从WAL复制到集合的数据文件中。刷新后还会有最长为flushWait秒的额外等待时间,以使WAL收集器也可以更改调整后的文档元数据以指向数据文件。默认值为false(即不刷新),因此导出中可能缺少集合中最新插入或更新的文档。 | |||
% flushWait:刷新操作后的最大等待时间(以秒为单位)。默认值为10。仅当flush设置为true时,此选项才有效。 | |||
% count:布尔值标志,指示是否应在结果的“ count”属性中返回结果集中的文档数(可选)。计算“ count”属性将来可能会对性能产生影响,因此默认情况下将关闭此选项,并且仅在请求时才返回“ count”。 | |||
% batchSize:一次往返(从服务器到客户端)要传输的最大结果文档数(可选)。如果未设置此属性,则将使用服务器控制的默认值。 | |||
% limit:可选的限制值,确定要包含在光标中的最大文档数。省略limit属性或将其设置为0将导致不使用任何限制。如果使用限制,则不确定集合中的哪些文档将包含在导出中,哪些文档将排除在外。这是因为集合中没有自然的文档顺序。 | |||
% ttl:光标的可选生存时间(以秒为单位)。在指定的时间后,游标将自动在服务器上删除。这对于确保客户端不完全获取的游标的垃圾回收很有用。如果未设置,将使用服务器定义的值。 | |||
% restrict:包含以下属性的对象,返回结果文档时将包含或排除这些属性名称。默认情况下,不指定 限制将返回每个文档的所有属性。 | |||
% type:必须根据要使用的类型设置为 include or exclude | |||
% fields:包含要包含或排除的属性名称的数组。包含或排除属性名称的匹配将仅在顶层完成。目前不支持指定嵌套属性的名称。 | |||
% 对此方法的调用将创建一个游标,其中包含指定集合中的所有文档。与其他数据生成API相比,导出API生成的内部数据结构更轻便,因此这是从集合中检索所有文档的首选方法。 | |||
% 以与/_api/cursorREST API中相似的方式返回文档。如果集合的所有文档都适合第一批,则不会创建任何游标,并且结果对象的hasMore属性将设置为 false。如果不是所有文档都适合第一批文档,则结果对象的hasMore属性将设置为true,并且结果的id属性将包含游标id。 | |||
% 未指定文件退回的顺序。 | |||
% | |||
% 默认情况下,将仅返回集合中存储在集合数据文件中的那些文档。运行导出时在预写日志(WAL)中存在的文档将不会导出。 | |||
% 为了也导出这些文档,调用者可以在调用导出API或设置flush属性之前发出WAL刷新请求。设置冲洗 选项将在导出之前触发WAL冲洗,以便将文档从WAL复制到集合数据文件。 | |||
% 如果服务器可以创建结果集,则服务器将使用HTTP 201进行响应 。响应的主体将包含带有结果集的JSON对象。 | |||
% 返回的JSON对象具有以下属性: | |||
% error:布尔值标志,指示发生错误( 在这种情况下为false) | |||
% code:HTTP状态码 | |||
% result:结果文档数组(如果集合为空,则可能为空) | |||
% hasMore:一个布尔指示器,指示服务器上的光标是否还有更多结果可用 | |||
% count:可用结果文档总数(仅当查询是在设置了count属性的情况下执行的) | |||
% id:在服务器上创建的临时光标的ID(可选,请参见上文) | |||
% 如果JSON格式不正确或请求中缺少查询规范,则服务器将使用HTTP 400进行响应。 | |||
% 响应的主体将包含带有其他错误详细信息的JSON对象。该对象具有以下属性: | |||
% error:布尔值标志,指示发生错误(在这种情况下为true) | |||
% code:HTTP状态码 | |||
% errorNum:服务器错误号 | |||
% errorMessage:描述性错误消息 | |||
% 客户端应该始终尽早删除导出游标结果,因为缠结的导出游标会阻止基础集合被压缩或卸载。默认情况下,未使用的游标将在服务器定义的空闲时间后自动删除,并且客户端可以通过设置ttl值来调整此空闲时间。 | |||
% 注意:群集协调器当前不支持此API。 | |||
% 返回码 | |||
% 201:如果服务器可以创建结果集,则返回。 | |||
% 400:如果JSON表示格式错误或请求中缺少查询规范,则返回。 | |||
% 404:如果查询中访问了不存在的集合,服务器将以HTTP 404进行响应。 | |||
% 405:如果使用了不受支持的HTTP方法,则服务器将以HTTP 405进行响应。 | |||
% 501:如果在群集协调器上调用此API,则服务器将使用HTTP 501进行响应。 | |||
docExport(PoolNameOrSocket, CollName, MapData) -> | |||
Path = <<"/_api/export?collection=", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). |
@ -0,0 +1,107 @@ | |||
-module(agCluster). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
%% doc_address:https://www.arangodb.com/docs/stable/http/cluster.html | |||
% 集群的HTTP接口 | |||
% 本章介绍了ArangoDB群集的REST API。 | |||
% 服务器ID | |||
% 服务器角色 | |||
% 集群统计 | |||
% 集群健康 | |||
% 集群维护 | |||
% 机构 | |||
% 如何修复与坏簇distributeShardsLike集在描述修理章节。 | |||
% 了解服务器的内部ID | |||
% GET /_admin/server/id | |||
% 返回集群中服务器的ID。如果服务器未在集群模式下运行,则请求将失败。 | |||
% 返回码 | |||
% 200:当服务器以群集模式运行时返回。 | |||
% 500:当服务器未在群集模式下运行时返回。 | |||
serverId(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/server/id">>, [], undefined). | |||
% 返回集群中服务器的角色 | |||
% GET /_admin/server/role | |||
% 返回集群中服务器的角色。该角色在结果的role属性中返回。角色的可能返回值是: | |||
% SINGLE:服务器是没有集群的独立服务器 | |||
% COORDINATOR:服务器是集群中的协调器 | |||
% PRIMARY:服务器是集群中的DB-Server | |||
% SECONDARY:不再使用此角色 | |||
% AGENT:服务器是集群中的代理节点 | |||
% UNDEFINED:在集群中,如果无法确定服务器角色,则返回UNDEFINED。 | |||
% 在所有情况下均返回HTTP 200。 | |||
% error:始终为假 | |||
% code:HTTP状态码,始终为200 | |||
% errorNum:服务器错误号 | |||
% role:之一[ SINGLE,COORDINATOR,PRIMARY,SECONDARY,AGENT,UNDEFINED ] | |||
serverRole(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/server/role">>, [], undefined). | |||
% 数据库服务器的查询统计信息 | |||
% 允许查询集群中DB-Server的统计信息 | |||
% GET /_admin/clusterStatistics | |||
% 查询参数 | |||
% DBserver(必填):查询给定DB-Server的统计信息 | |||
% 返回码 | |||
% 200: | |||
% 400:数据库服务器的ID | |||
% 403: | |||
clusterStats(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/clusterStatistics", QueryBinary/binary>>, [], undefined). | |||
% 返回监督(机构)评估的集群的运行状况 | |||
% GET /_admin/cluster/health | |||
% 查询群集的运行状况以进行监视。该响应是一个JSON对象,包含标准code,error,errorNum,和errorMessage字段适当。特定于端点的字段如下: | |||
% ClusterId:标识集群的UUID字符串 | |||
% Health:一个对象,该对象包含群集中每个节点的描述性子对象。 | |||
% <nodeID>:中的每个条目Health将由节点ID键入,并包含以下属性: | |||
% Endpoint:代表服务器网络端点的字符串。 | |||
% Role:服务器扮演的角色。可能的值是"AGENT","COORDINATOR"和"DBSERVER"。 | |||
% CanBeDeleted:布尔值,表示是否可以安全地从群集中删除节点。 | |||
% Version:该节点使用的ArangoDB的版本字符串。 | |||
% Engine:该节点使用的存储引擎。 | |||
% Status:指示监督(机构)评估的节点运行状况的字符串。对于协调器和DB-Servers节点运行状况,应将其视为真实的主要来源。如果节点正常响应请求,则为"GOOD"。如果错过了一个心跳,那就是"BAD"。如果在缺少心跳约15秒钟后通过监督宣布它失败,则会对其进行标记"FAILED"。 | |||
% 此外,它还将具有以下属性: | |||
% 协调器和数据库服务器 | |||
% SyncStatus:节点上次报告的同步状态。该值主要用于确定的值Status。可能的值包括"UNKNOWN","UNDEFINED","STARTUP","STOPPING","STOPPED","SERVING","SHUTDOWN"。 | |||
% LastAckedTime:ISO 8601时间戳记,指定接收到的最后一个心跳。 | |||
% ShortName:代表服务器的简称的字符串,例如"Coordinator0001"。 | |||
% Timestamp:ISO 8601时间戳记,指定接收到的最后一个心跳。(已弃用) | |||
% Host:可选字符串,指定主机(如果已知)。 | |||
% 仅协调员 | |||
% AdvertisedEndpoint:表示已播报端点的字符串(如果已设置)。(例如,外部IP地址或负载平衡器,可选) | |||
% 代理商 | |||
% Leader:此节点视为领导者的代理的ID。 | |||
% Leading:此座席是否为领导者(true)或不是(false)。 | |||
% LastAckedTime:自上次以来的时间(acked以秒为单位)。 | |||
% 返回码 | |||
% 200: | |||
clusterHealth(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/cluster/health">>, [], undefined). | |||
% 启用或禁用集群监督(代理)维护模式 | |||
% PUT /_admin/cluster/maintenance | |||
% 通过此API,您可以临时启用监督维护模式。请注意,启用维护模式后,不会进行任何类型的自动故障转移。该集群监控会自动重新激活自身60分钟禁用后。 | |||
% 要启用维护模式,请求正文必须包含字符串"on"。要禁用它,请发送字符串"off"(请注意,该字符串 必须为小写并包括引号)。 | |||
% 返回码 | |||
% 200: | |||
% 400: | |||
% 501: | |||
% 504: | |||
setClusterMaintenance(PoolNameOrSocket, OnOrOff) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_admin/cluster/maintenance">>, [], OnOrOff). | |||
%%%%%%%%%%%%%%% Agency ?????????????????????????? | |||
@ -0,0 +1,482 @@ | |||
-module(agCollections). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address:https://www.arangodb.com/docs/stable/http/collection.html | |||
% 集合的HTTP接口 | |||
% 这是ArangoDB集合的HTTP接口的简介。 | |||
% | |||
% 收藏 | |||
% 集合由文档组成。它由其集合标识符唯一 标识。它也有一个唯一的名称,客户端应该使用它来标识和访问它。集合可以重命名。这将更改集合名称,但不会更改集合标识符。集合具有创建集合时用户指定的类型。当前有两种类型:文档和边。默认类型是document。 | |||
% | |||
% 集合标识符 | |||
% 集合标识符使您可以引用数据库中的集合。它是一个字符串值,在数据库中是唯一的。直到包括ArangoDB 1.1为止,集合标识符一直是客户端访问集合的主要手段。从ArangoDB 1.2开始,客户端应改为使用集合的唯一名称访问集合,而不是其标识符。ArangoDB当前使用64位无符号整数值在内部维护集合ID。当将集合ID返回给客户端时,ArangoDB会将它们放入字符串中,以确保不支持不支持大整数的客户端不截取集合ID。客户端在本地存储或使用它们时,应将ArangoDB返回的集合ID视为不透明字符串。 | |||
% | |||
% 注意:集合ID已返回为整数,包括ArangoDB 1.1在内 | |||
% | |||
% 集合名称 | |||
% 集合名称标识数据库中的集合。它是一个字符串,在数据库中是唯一的。与集合标识符不同,它由集合的创建者提供。集合名称必须仅由字母,数字和_(下划线)和-(破折号)字符组成。有关有效集合名称的更多信息,请参考ArangoDB中的命名约定。 | |||
% | |||
% 密钥生成器 | |||
% ArangoDB允许为每个集合使用密钥生成器。如果用户未指定键生成器,则其目的是为文件的_key属性自动生成值。默认情况下,ArangoDB将使用传统的密钥生成器。传统的密钥生成器将自动生成数字不断增加的字符串形式的密钥值。它使用的增量值是不确定的。 | |||
% | |||
% 相反,自动递增密钥生成器将自动生成确定性密钥值。创建集合时,可以定义起始值和增量值。默认起始值为0,默认增量为1,这意味着它将默认创建的键值为: | |||
% | |||
% 1,2,3,4,5,… | |||
% | |||
% 使用自动增量键生成器并以5增量创建集合时,生成的键将为: | |||
% | |||
% 1,6,11,16,21,… | |||
% | |||
% 自动增量值会增加,并在每次插入文档时分发。即使插入失败,也不会回滚自动增量值。这意味着,如果插入失败,则在分配的自动增量值序列中可能存在间隙。 | |||
% | |||
% 文档的基本操作(创建,读取,更新,删除)被映射到标准HTTP方法(POST,GET,PUT,DELETE)。 | |||
% | |||
% 集合地址 | |||
% ArangoDB中的所有集合都具有唯一的标识符和唯一的名称。ArangoDB在内部使用集合的唯一标识符来查找集合。但是,此标识符由ArangoDB管理,并且用户无法对其进行控制。为了允许用户使用自己的名称,每个集合还具有一个由用户指定的唯一名称。要从用户角度访问集合,应使用集合名称,即: | |||
% | |||
% http://server:port/_api/collection/collection-name | |||
% 例如:假设集合标识符为7254820,集合名称为demo,则该集合的URL为: | |||
% | |||
% http://localhost:8529/_api/collection/demo | |||
%创建一个集合 | |||
%POST /_api/collection | |||
% | |||
%查询参数 | |||
% waitForSyncReplication(可选):默认为1,这意味着如果所有副本都创建了集合,则服务器将仅向客户端报告成功。如果您想要更快的服务器响应并且不关心完全复制,则设置为0。 | |||
% forceReplicationFactor(可选):默认值为1,这意味着服务器将在创建时检查是否有足够的副本,否则将进行紧急救助。设置为0可禁用此额外检查。 | |||
% | |||
%具有以下属性的JSON对象是必需的: | |||
% name:集合的名称。 | |||
% waitForSync:如果为true,则在从文档创建,更新,替换或删除操作返回之前,将数据同步到磁盘。(默认值:false) | |||
% doCompact:是否压缩集合(默认为true),此选项仅对MMFiles存储引擎有意义。 | |||
% journalSize:日志或数据文件的最大大小,以字节为单位。该值必须至少为1048576(1 MiB)。(默认值为配置参数)此选项仅对MMFiles存储引擎有意义。 | |||
% isSystem:如果为true,则创建一个系统集合。在这种情况下,collection-name 应该以下划线开头。最终用户通常应仅创建非系统集合。在非常特殊的情况下,可能需要API实现者来创建系统集合,但通常会使用常规集合。(默认为false) | |||
% isVolatile:如果为true,则收集数据仅保留在内存中,而不是持久的。卸载集合将导致集合数据被丢弃。停止或重新启动服务器也将导致集合中的数据完全丢失。设置此选项将使结果集合比常规集合快一点,因为ArangoDB不会对磁盘执行任何同步,也不会为数据文件计算任何CRC校验和(因为没有数据文件)。因此,此选项应仅用于高速缓存类型的集合,而不应用于无法通过其他方式重新创建的数据。(默认值为false)此选项仅对MMFiles存储引擎有意义。 | |||
% schema:指定文档的收集级别架构的可选对象。属性键rule,level并且message必须遵循文档架构验证中记录的规则 | |||
% keyOptions:密钥生成的其他选项。如果指定,则keyOptions 应该是包含以下属性的JSON数组或者JSON对象: | |||
% type:指定密钥生成器的类型。当前可用的生成器是 传统的,自动递增的,uuid的和填充的。 | |||
% 在传统的密钥生成器生成升序数字键。在自动增量密钥发生器以上升顺序生成的数字键时,初始偏移量和间隔可以被配置成在填充密钥发生器以上升辞书排序顺序生成的固定长度(16个字节)的密钥。这是与RocksDB配合使用的理想选择 引擎,这将稍微有利于按字典顺序升序插入的键。密钥生成器可以在单服务器或群集中使用。的UUID密钥生成器生成通用唯一的128位密钥,这被存储在十六进制人类可读的格式。该密钥生成器可用于单服务器或群集中,以生成“看似随机的”密钥。此密钥生成器生成的密钥未按字典顺序排序。 | |||
% allowUserKeys:如果设置为true,则允许在文档的_key属性中提供自己的键值 。如果设置为false,那么密钥生成器将仅负责生成密钥,并且在文档的_key属性中提供自己的密钥值被视为错误。 | |||
% 增量:自动增量密钥生成器的增量值。不适用于其他密钥生成器类型。 | |||
% offset:自动增量密钥生成器的初始偏移值。不适用于其他密钥生成器类型。 | |||
% type:(默认值为2):要创建的集合的类型。以下type值有效: | |||
% 2:文件收集 | |||
% 3:边缘收集 | |||
% numberOfShards:(默认值为1):在集群中,此值确定要为集合创建的分片数。在单服务器设置中,此选项没有意义。 | |||
% shardKeys:(默认值为[“ _key”]):在集群中,此属性确定用于确定文档目标分片的文档属性。文档根据其分片键属性的值发送到分片。对文档中所有分片键属性的值进行哈希处理,并将哈希值用于确定目标分片。 注意:分片键属性的值一旦设置就无法更改。在单个服务器设置中,此选项没有意义。 | |||
% plicationFactor:(默认值为1):在集群中,此属性确定每个分片在不同DB-Server上保留多少个副本。值1表示仅保留一个副本(无同步复制)。k的值表示保留k-1个副本。它也可以"satellite"是SatelliteCollection 的字符串,其中复制因子与DB-Servers的数量匹配。 | |||
% 任何两个副本驻留在不同的DB服务器上。它们之间的复制是同步的,也就是说,在报告写入操作成功之前,对“ leader”副本的每个写入操作都会复制到所有“ follower”副本。 | |||
% 如果服务器发生故障,则会自动检测到故障,并且其中一台拥有副本的服务器将接管业务,通常不会报告错误。 | |||
% writeConcern:为此集合写关注点(默认值:1)。它确定在不同的DB服务器上同步每个分片需要多少个副本。如果集群中的副本数量很少,那么分片将拒绝写入。但是,具有足够最新副本的分片写入将同时成功。writeConcern的值 不能大于ReplicationFactor。(仅集群) | |||
% DistributionShardsLike:(默认值为“”):在企业版集群中,此属性将新创建的集合的分片详细信息绑定到指定的现有集合中。 注意:使用此参数会对原型集合产生影响。在删除分片模仿集合之前,不能再删除它。同样,仅模拟集合的备份和还原将生成有关丢失分片原型的警告(可以被覆盖)。 | |||
% shardingStrategy:此属性指定用于集合的分片策略的名称。从ArangoDB 3.4开始,创建新集合时可以选择不同的分片策略。所选的shardingStrategy 值对于集合将保持固定,此后无法更改。这对于使集合保持其分片设置并始终使用相同的初始分片算法查找已分发到分片的文档非常重要。 | |||
% 可用的分片策略为: | |||
% community-compat:版本3.4之前的ArangoDB社区版使用的默认分片 | |||
% enterprise-compat:版本3.4之前的ArangoDB企业版使用的默认分片 | |||
% enterprise-smart-edge-compat:版本3.4之前的ArangoDB Enterprise Edition中的智能边缘集合使用的默认分片 | |||
% hash:从3.4版开始用于新集合的默认分片(不包括智能边缘集合) | |||
% enterprise-hash-smart-edge:从版本3.4开始,用于新的智能边缘集合的默认分片 | |||
% 如果未指定分片策略,则所有集合的默认值将为哈希,所有智能边缘集合的默认值将为enterprise-hash-smart-edge(需要ArangoDB 企业版)。手动覆盖分片策略尚不能提供好处,但是稍后可能会添加其他分片策略。 | |||
% smartJoinAttribute:在企业版集群中,此属性确定集合的属性,该属性必须包含引用的SmartJoin集合的分片键值。此外,此集合中文档的分片键必须包含此属性的值,后跟冒号,然后是文档的实际主键。 | |||
% 此功能只能在企业版中使用,并且需要将集合的 distributedShardsLike属性设置为另一个集合的名称。它还要求将集合的shardKeys属性设置为单个shard key属性,并在末尾添加一个附加的“:”。进一步的限制是,无论何时在集合中存储或更新文档,smartJoinAttribute中存储的值都必须是字符串。 | |||
% 用给定名称创建一个新集合。该请求必须包含具有以下属性的对象。 | |||
% 400:如果缺少集合名称,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则返回HTTP 404。 | |||
% HTTP 200 | |||
newColl(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/collection">>, [], BodyStr). | |||
newColl(PoolNameOrSocket, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/collection", QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
% 删除收藏 | |||
% DELETE /_api/collection/{collection-name} | |||
% 从版本3.4.0开始,不建议使用按其数字ID访问集合。您应该通过它们的名称来引用它们。 | |||
% 路径参数 | |||
% collection-name(必填):要删除的集合的名称。 | |||
% 查询参数 | |||
% isSystem(可选):要删除的集合是否为系统集合。必须将此参数设置为true才能删除系统集合。 | |||
% 删除由collection-name标识的集合。 | |||
% 如果成功删除了该集合,则返回具有以下属性的对象: | |||
% 错误:假 | |||
% id:删除的集合的标识符。 | |||
% 返回码 | |||
% 400:如果缺少集合名称,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则返回HTTP 404。 | |||
delColl(PoolNameOrSocket, CollName) -> | |||
Path = <<"/_api/collection/", CollName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
delColl(PoolNameOrSocket, CollName, IsSystem) -> | |||
case IsSystem of | |||
true -> | |||
Path = <<"/_api/collection/", CollName/binary, "?isSystem=true">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined); | |||
_ -> | |||
Path = <<"/_api/collection/", CollName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined) | |||
end. | |||
% 清除集合 | |||
% PUT /_api/collection/{collection-name}/truncate | |||
% 从版本3.4.0开始,不建议使用按其数字ID访问集合。您应该通过它们的名称来引用它们。 | |||
% 路径参数 | |||
% collection-name(必填):集合的名称。 | |||
% 从集合中删除所有文档,但保留索引不变。 | |||
% 返回码 | |||
% 400:如果缺少集合名称,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则 返回HTTP 404。 | |||
clearColl(PoolNameOrSocket, CollName) -> | |||
Path = <<"/_api/collection/", CollName/binary, "/truncate">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined). | |||
% 返回有关集合的信息 | |||
% GET /_api/collection/{collection-name} | |||
% 从版本3.4.0开始,不建议使用按其数字ID访问集合。您应该通过它们的名称来引用它们。 | |||
% 路径参数 | |||
% collection-name(必填):集合的名称。 | |||
% 结果是一个对象,该对象描述具有以下属性的集合: | |||
% id:集合的标识符。 | |||
% name:集合的名称。 | |||
% status:集合状态为数字。 | |||
% 1:新出生的收藏 | |||
% 2:已卸载 | |||
% 3:已加载 | |||
% 4:在卸载过程中 | |||
% 5:已删除 | |||
% 6:加载 | |||
% 其他每个状态都表示集合已损坏。 | |||
% type:集合的类型,以数字表示。 | |||
% 2:文件收集(正常情况) | |||
% 3:边缘收集 | |||
% isSystem:如果为true,则该集合为系统集合。 | |||
% 返回码 | |||
% 404:如果集合名称未知,则返回HTTP 404。 | |||
collInfo(PoolNameOrSocket, CollName) -> | |||
Path = <<"/_api/collection/", CollName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 读取指定集合的属性 | |||
% GET /_api/collection/{collection-name}/properties | |||
% 从版本3.4.0开始,不建议使用按其数字ID访问集合。您应该通过它们的名称来引用它们。 | |||
% 路径参数 | |||
% collection-name(必填):集合的名称。 | |||
% 400:如果缺少集合名称,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则 返回HTTP 404。 | |||
% HTTP 200 | |||
collProps(PoolNameOrSocket, CollName) -> | |||
Path = <<"/_api/collection/", (CollName)/binary, "/properties">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 计算集合中的文档数量 | |||
% GET /_api/collection/{collection-name}/count | |||
% 从版本3.4.0开始,不建议使用按其数字ID访问集合。您应该通过它们的名称来引用它们。 | |||
% 路径参数 | |||
% collection-name(必填):集合的名称。 | |||
% 除上述内容外,结果还包含文档数。 请注意,这将始终将集合加载到内存中。 | |||
% count:集合内的文档数。 | |||
% 返回码 | |||
% 400:如果缺少集合名称,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则 返回HTTP 404。 | |||
collCount(PoolNameOrSocket, CollName) -> | |||
Path = <<"/_api/collection/", CollName/binary, "/count">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 获取集合的统计信息 | |||
% GET /_api/collection/{collection-name}/figures | |||
% 从版本3.4.0开始,不建议使用按其数字ID访问集合。您应该通过它们的名称来引用它们。 | |||
% 路径参数 | |||
% collection-name(必填):集合的名称。 | |||
% 除上述内容外,结果还包含文档数量和有关集合的其他统计信息。 | |||
% HTTP 200返回有关集合的信息: | |||
% count:集合中当前存在的文档数。 | |||
% figures:收集指标 | |||
% indexes: | |||
% count:为集合定义的索引总数,包括预定义的索引(例如主索引)。 | |||
% size:为索引分配的总内存,以字节为单位。 | |||
% 400:如果缺少集合名称,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则 返回HTTP 404。 | |||
collFigures(PoolNameOrSocket, CollName) -> | |||
Path = <<"/_api/collection/", CollName/binary, "/figures">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 返回负责文档的分片 | |||
% PUT /_api/collection/{collection-name}/responsibleShard | |||
% 路径参数 | |||
% collection-name(必填):集合的名称。 | |||
% 请求正文(json) | |||
% 主体必须包含一个JSON对象,且至少将分片键属性设置为某些值。 | |||
% 返回负责给定文档(如果存在)或如果存在该文档将负责的分片的ID。 | |||
% 该请求必须正文必须包含一个JSON文档,该文档至少将集合的分片键属性设置为某些值。 | |||
% 响应是一个具有shardId属性的JSON对象,其中将包含负责的分片的ID。 | |||
% 注意:此方法仅在群集协调器中可用。 | |||
% 返回码 | |||
% 200:返回负责的分片的ID。 | |||
% 400:如果缺少集合名称,则返回HTTP 400。另外,如果输入文档中不存在所有集合的分片键属性,那么 也会返回HTTP 400。 | |||
% 404:如果集合名称未知,则 返回HTTP 404。 | |||
% 501:如果在单个服务器上调用该方法,则返回HTTP 501。 | |||
% eg: MapData = #{'_key' => testkey, value => 23} | |||
collResponsibleShard(PoolNameOrSocket, CollName, MapData) -> | |||
Path = <<"/_api/collection/", CollName/binary, "/responsibleShard">>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], BodyStr). | |||
% 返回集合的分片ID | |||
% GET /_api/collection/{collection-name}/shards | |||
% 路径参数 | |||
% collection-name(必填):集合的名称。 | |||
% 查询参数 | |||
% 详细信息(可选):如果设置为true,则返回值还将包含集合碎片的负责服务器。 | |||
% 默认情况下,返回带有集合的分片ID的JSON数组。 | |||
% 如果details参数设置为true,它将返回一个以分片ID作为对象属性键的JSON对象,并将每个分片的负责服务器映射到它们。在详细的响应中,领导者碎片将排在阵列的首位。 | |||
% 注意:此方法仅在群集协调器中可用。 | |||
% 返回码 | |||
% 200:返回集合的分片。 | |||
% 400:如果缺少集合名称,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则 返回HTTP 404。 | |||
% 501:如果在单个服务器上调用该方法,则返回HTTP 501。 | |||
collShards(PoolNameOrSocket, CollName) -> | |||
Path = <<"/_api/collection/", CollName/binary, "/shards">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
collShards(PoolNameOrSocket, CollName, IsDetails) -> | |||
case IsDetails of | |||
true -> | |||
Path = <<"/_api/collection/", CollName/binary, "/shards?details=true">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined); | |||
_ -> | |||
Path = <<"/_api/collection/", CollName/binary, "/shards">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined) | |||
end. | |||
% 返回集合修订版ID | |||
% GET /_api/collection/{collection-name}/revision | |||
% 从版本3.4.0开始,不建议使用按其数字ID访问集合。您应该通过它们的名称来引用它们。 | |||
% 路径参数 | |||
% collection-name(必填):集合的名称。 | |||
% 除上述内容外,结果还将包含集合的修订版ID。修订ID是服务器生成的字符串,客户端可以使用该字符串检查自上次修订检查以来集合中的数据是否已更改。 | |||
% revision:集合修订版本ID的字符串形式。 | |||
% 返回码 | |||
% 400:如果缺少集合名称,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则 返回HTTP 404。 | |||
collRev(PoolNameOrSocket, CollName) -> | |||
Path = <<"/_api/collection/", CollName/binary, "/revision">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 返回指定集合的校验和 | |||
% GET /_api/collection/{collection-name}/checksum | |||
% 从版本3.4.0开始,不建议使用按其数字ID访问集合。您应该通过它们的名称来引用它们。 | |||
% 路径参数 | |||
% collection-name(必填):集合的名称。 | |||
% 查询参数 | |||
% withRevisions(可选):在校验和计算中是否包括文档修订版ID。 | |||
% withData(可选):是否在校验和计算中包括文档主体数据。 | |||
% 将计算集合中元数据(键和可选的修订ID)以及文档数据的校验和。 | |||
% 校验和可用于比较不同ArangoDB实例上的两个集合是否包含相同的内容。集合的当前修订版也会返回,因此可以确保针对相同数据状态计算校验和。 | |||
% 默认情况下,校验和将仅根据集合中包含的文档的_key系统属性来计算。对于边缘集合,系统属性_from和_to也将包含在计算中。 | |||
% 通过将可选查询参数withRevisions设置为true,则校验和中将包含修订版ID(_rev系统属性)。 | |||
% 通过为可选查询参数withData提供值为true的值,用户定义的文档属性也将包括在计算中。 注意:包括用户定义的属性将使校验和变慢。 | |||
% 响应是具有以下属性的JSON对象: | |||
% checksum:计算得出的校验和(以数字形式)。 | |||
% 版本:集合修订版本ID的字符串形式。 | |||
% 注意:此方法在群集中不可用。 | |||
% 返回码 | |||
% 400:如果缺少集合名称,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则 返回HTTP 404。 | |||
collChecksum(PoolNameOrSocket, CollName) -> | |||
Path = <<"/_api/collection/", CollName/binary, "/checksum">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
collChecksum(PoolNameOrSocket, CollName, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/collection/", CollName/binary, "/checksum", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 返回所有集合列表 | |||
% GET /_api/collection | |||
% 从版本3.4.0开始,不建议使用按其数字ID访问集合。您应该通过它们的名称来引用它们。 | |||
% 查询参数 | |||
% excludeSystem(可选):是否应从结果中排除系统集合。 | |||
% 返回带有属性集合的对象,该属性集合包含所有集合描述的数组。在名称中还可以使用对象作为对象,在集合名称中作为键使用相同的信息。 | |||
% 通过为可选查询参数excludeSystem提供值为true的值, 将从响应中排除所有系统集合。 | |||
% 返回码 | |||
% 200:收藏列表 | |||
collList(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/collection">>, [], undefined). | |||
collList(PoolNameOrSocket, IsExcludeSystem) -> | |||
case IsExcludeSystem of | |||
false -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/collection">>, [], undefined); | |||
_ -> | |||
Path = <<"/_api/collection?excludeSystem=true">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined) | |||
end. | |||
% 加载集合 | |||
% PUT /_api/collection/{collection-name}/load | |||
% 从版本3.4.0开始,不建议使用按其数字ID访问集合。您应该通过它们的名称来引用它们。 | |||
% 路径参数 | |||
% collection-name(必填):集合的名称。 | |||
% 将集合加载到内存中。成功返回集合。 | |||
% 请求主体对象可以选择包含以下属性: | |||
% count:如果设置,则控制返回值是否应包括集合中的文档数。将count设置为 false可以加快加载集合的速度。为默认值 数为真。 | |||
% 成功后,将返回具有以下属性的对象: | |||
% id:集合的标识符。 | |||
% name:集合的名称。 | |||
% count:集合内的文档数。仅当count输入参数设置为true或未指定时才返回。 | |||
% status:集合状态为数字。 | |||
% type:集合类型。有效类型为: | |||
% 2:文件收集 | |||
% 3:边缘收集 | |||
% isSystem:如果为true,则该集合为系统集合。 | |||
% 返回码 | |||
% 400:如果缺少集合名称,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则 返回HTTP 404。 | |||
loadColl(PoolNameOrSocket, CollName) -> | |||
Path = <<"/_api/collection/", CollName/binary, "/load">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined). | |||
loadColl(PoolNameOrSocket, CollName, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/collection/", CollName/binary, "/load">>, [], BodyStr). | |||
% 卸载集合 | |||
% PUT /_api/collection/{collection-name}/unload | |||
% 从版本3.4.0开始,不建议使用按其数字ID访问集合。您应该通过它们的名称来引用它们。 | |||
% 路径参数 | |||
% collection-name(必需):从内存中删除一个集合。此调用不会删除任何文档。您可以随后使用该集合;在这种情况下,它将再次加载到内存中。成功后,将返回具有以下属性的对象: | |||
% id:集合的标识符。 | |||
% name:集合的名称。 | |||
% status:集合状态为数字。 | |||
% type:集合类型。有效类型为: | |||
% 2:文件收集 | |||
% 3:边缘收集 | |||
% isSystem:如果为true,则该集合为系统集合。 | |||
% 返回码 | |||
% 400:如果缺少集合名称,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则返回HTTP 404。 | |||
unloadColl(PoolNameOrSocket, CollName) -> | |||
Path = <<"/_api/collection/", CollName/binary, "/unload">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined). | |||
% 将索引加载到内存中 | |||
% PUT /_api/collection/{collection-name}/loadIndexesIntoMemory | |||
% 从版本3.4.0开始,不建议使用按其数字ID访问集合。您应该通过它们的名称来引用它们。 | |||
% 路径参数 | |||
% collection-name(必填):此路由尝试将这个collection的所有索引条目缓存到主内存中。因此,它将遍历集合的所有索引,并将索引值而不是整个文档数据存储在内存中。可以在缓存中找到的所有查找都比未存储在缓存中的查找要快得多,因此可以提高性能。还可以保证缓存与存储的数据一致。 | |||
% 目前,此功能仅在RocksDB存储引擎上有用,因为在MMFiles引擎中,所有索引无论如何都在内存中。 | |||
% 在RocksDB上,此函数将遵守所有内存限制,如果要加载的索引小于您的内存限制,则此函数可确保大多数索引值都已缓存。如果索引大于您的内存限制,则此函数将填满直至达到此限制的值,并且暂时无法控制集合中的哪些索引应优先于其他索引。 | |||
% 成功后,此函数返回属性result设置为的对象true | |||
% 返回码 | |||
% 200:如果所有索引都已加载 | |||
% 400:如果缺少集合名称,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则返回HTTP 404。 | |||
collLoadIndexesIntoMemory(PoolNameOrSocket, CollName) -> | |||
Path = <<"/_api/collection/", CollName/binary, "/loadIndexesIntoMemory">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined). | |||
% 更改集合的属性 | |||
% PUT /_api/collection/{collection-name}/properties | |||
% 从版本3.4.0开始,不建议使用按其数字ID访问集合。您应该通过它们的名称来引用它们。 | |||
% 路径参数 | |||
% collection-name(必填):集合的名称。 | |||
% 更改集合的属性。需要具有属性的对象 | |||
% waitForSync:如果为true,则创建或更改文档将等待,直到数据已同步到磁盘。 | |||
% journalSize:日志或数据文件的最大大小,以字节为单位。该值必须至少为1048576(1 MB)。请注意,更改journalSize值时,它仅对创建的其他日志或数据文件有效。现有的日志或数据文件将不受影响。 | |||
% schema:指定文档的收集级别架构的对象。属性键rule,level并且message必须遵循文档架构验证中记录的规则 | |||
% | |||
% 成功后,将返回具有以下属性的对象: | |||
% id:集合的标识符。 | |||
% name:集合的名称。 | |||
% waitForSync:新值。 | |||
% journalSize:新值。 | |||
% status:集合状态为数字。 | |||
% type:集合类型。有效类型为: | |||
% 2:文件收集 | |||
% 3:边缘收集 | |||
% isSystem:如果为true,则该集合为系统集合。 | |||
% isVolatile:如果为true,则收集数据将仅保留在内存中,并且ArangoDB不会将数据写入或同步到磁盘。 | |||
% doCompact:是否将压缩集合。 | |||
% keyOptions:JSON对象,其中包含密钥生成选项: | |||
% type:指定密钥生成器的类型。当前可用的生成器是传统的,自动递增的,uuid的 和填充的。 | |||
% allowUserKeys:如果设置为true,则允许在文档的_key属性中提供自己的键值。如果设置为 false,那么密钥生成器将独自负责生成密钥,并且在文档的_key属性中提供自己的密钥值被视为错误。 | |||
% schema(可选,默认为null):指定文档的收集级别架构的对象。属性键rule,level并且message必须遵循文档架构验证中记录的规则 | |||
% 注意:除了waitForSync,journalSize和name之外,创建集合后就无法更改集合属性。要重命名集合,必须使用重命名端点。 | |||
% 返回码 | |||
% 400:如果缺少集合名称,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则 返回HTTP 404。 | |||
collChangeProps(PoolNameOrSocket, CollName, MapData) -> | |||
Path = <<"/_api/collection/", CollName/binary, "/properties">>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
% 重命名集合 | |||
% PUT /_api/collection/{collection-name}/rename | |||
% 从版本3.4.0开始,不建议使用按其数字ID访问集合。您应该通过它们的名称来引用它们。 | |||
% 路径参数 | |||
% collection-name(必填):要重命名的集合的名称。 | |||
% 重命名集合。需要具有属性的对象 | |||
% name:新名称。 | |||
% 它返回具有属性的对象 | |||
% id:集合的标识符。 | |||
% name:集合的新名称。 | |||
% status:集合状态为数字。 | |||
% type:集合类型。有效类型为: | |||
% 2:文件收集 | |||
% 3:边缘收集 | |||
% isSystem:如果为true,则该集合为系统集合。 | |||
% 如果重命名集合成功,那么该集合还将_graphs在当前数据库中该集合内的所有图形定义中重命名。 | |||
% 注意:此方法在群集中不可用。 | |||
% 返回码 | |||
% 400:如果缺少集合名称,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则 返回HTTP 404。 | |||
renameColl(PoolNameOrSocket, OldName, NewName) -> | |||
Path = <<"/_api/collection/", OldName/binary, "/rename">>, | |||
NameStr = jiffy:encode(NewName), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], <<"{\"name\":", NameStr/binary, "}">>). | |||
% 旋转收藏夹的日记 | |||
% PUT /_api/collection/{collection-name}/rotate | |||
% 路径参数 | |||
% collection-name(必填):集合的名称。 | |||
% 旋转集合的日记帐。集合的当前日志将关闭,并成为只读数据文件。Rotate方法的目的是使文件中的数据可用于压缩(压缩仅对只读数据文件而不对日志执行)。 | |||
% 如果没有当前日志,随后将新数据保存在集合中将自动创建一个新的日志文件。 | |||
% 它返回具有属性的对象 | |||
% 结果:如果旋转成功,则为true | |||
% 注意:此方法特定于MMFiles存储引擎,并且在群集中不可用。 | |||
% 返回码 | |||
% 400:如果该集合当前没有日记,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则返回HTTP 404。 | |||
% 3.7中删掉了该方法 | |||
collRotate(PoolNameOrSocket, CollName) -> | |||
Path = <<"/_api/collection/", CollName/binary, "/rotate">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined). | |||
% 重新计算集合的文档数 | |||
% PUT /_api/collection/{collection-name}/recalculateCount | |||
% 路径参数 | |||
% collection-name(必填):集合的名称。 | |||
% 重新计算集合的文档计数(如果不一致)。 | |||
% 它返回具有属性的对象 | |||
% 结果:如果重新计算文档计数成功,则为true。 | |||
% 注意:此方法特定于RocksDB存储引擎 | |||
% 返回码 | |||
% 200:如果文档计数成功重新计算,则返回HTTP 200。 | |||
% 404:如果集合名称未知,则返回HTTP 404。 | |||
collRecount(PoolNameOrSocket, CollName) -> | |||
Path = <<"/_api/collection/", CollName/binary, "/recalculateCount">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined). |
@ -0,0 +1,98 @@ | |||
-module(agDbMgr). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
%% doc_address:https://www.arangodb.com/docs/stable/http/database-database-management.html | |||
% 使用HTTP管理数据库 | |||
% 这是ArangoDB用于管理数据库的HTTP接口的简介。 | |||
% 数据库的HTTP接口提供创建和删除单个数据库的操作。这些映射到标准HTTP方法POST 和DELETE。还有GET方法来检索现有数据库的数组。 | |||
% 请注意,所有数据库管理操作只能通过默认数据库(_system)访问,而不能通过其他数据库访问。 | |||
% 关于数据库的注意事项 | |||
% 请记住,每个数据库都包含其自己的系统集合,创建数据库时需要对其进行设置。这将使数据库创建花费一些时间。 | |||
% 复制是在 每个数据库级别 或在服务器级别配置的。在每个数据库的设置中,必须在创建新数据库后显式配置任何复制日志记录或申请新数据库,而在使用全局复制应用程序进行服务器级设置的情况下,将自动复制所有数据库。 | |||
% Foxx应用程序也仅在已安装数据库的上下文中可用。新数据库将仅提供对ArangoDB附带的系统应用程序(即当前的Web界面)的访问,而其他的Foxx应用程序则无法访问。为特定数据库明确安装。 | |||
% 数据库信息 | |||
% 检索有关当前数据库的信息 | |||
% GET /_api/database/current | |||
% 检索有关当前数据库的信息 | |||
% 响应是具有以下属性的JSON对象: | |||
% name:当前数据库的名称 | |||
% id:当前数据库的ID(字符串格式) | |||
% path:当前数据库的文件系统路径(字符串格式, mmfiles引擎有效) | |||
% isSystem:当前数据库是否为_system数据库 | |||
% sharding:此数据库中创建的集合的默认分片方法(用于新集合的分片方法(仅适用于集群)) | |||
% ReplicationFactor:此数据库中集合的默认复制因子(仅适用于 集群) | |||
% writeConcern:此数据库中集合的默认写关注点 如果同步的副本数量少于此数量,则碎片将拒绝写入(仅适用于集群) | |||
% 返回码 | |||
% 200:如果成功检索到信息,则返回。 | |||
% 400:如果请求无效,则返回。 | |||
% 404:如果找不到数据库,则返回。 | |||
curDbInfo(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/database/current">>, [], undefined). | |||
% 检索当前用户可以访问的所有数据库的列表 | |||
% GET /_api/database/user | |||
% 检索当前用户可以访问的所有数据库的列表,而无需指定其他用户名或密码。 | |||
% 返回码 | |||
% 200:如果数据库列表编译成功,则返回。 | |||
% 400:如果请求无效,则返回。 | |||
visitDbs(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/database/user">>, [], undefined). | |||
% 检索所有现有数据库的列表 | |||
% GET /_api/database | |||
% 检索所有现有数据库的列表 | |||
% 注意:只能从_system数据库中检索数据库列表。 | |||
% 注意:您现在应该使用GET用户API来获取可用数据库的列表。 | |||
% 返回码 | |||
% 200:如果数据库列表编译成功,则返回。 | |||
% 400:如果请求无效,则返回。 | |||
% 403:如果请求未在_system数据库中执行,则返回。 | |||
allDbs(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/database">>, [], undefined, true). | |||
% 创建一个新的数据库 | |||
% POST /_api/database | |||
% 具有以下属性的JSON对象是必需的: | |||
% name:必须包含一个有效的数据库名称。 | |||
% options:可选对象,可以包含以下属性: | |||
% sharding:用于此数据库中新集合的分片方法。有效值为:“”,“ flexible”或“ single”。前两个是等效的。(仅集群) | |||
% ReplicationFactor:在此数据库中创建的新集合的默认复制因子。特殊值包括“ satellite”(卫星),它将复制集合到每个DB-Server;以及1(禁用复制)。(仅集群) | |||
% writeConcern:在此数据库中创建的新集合的默认写关注。它确定在不同的DB服务器上同步每个分片需要多少个副本。如果集群中的副本数量很少,那么分片将拒绝写入。但是,具有足够最新副本的分片写入将同时成功。writeConcern的值 不能大于ReplicationFactor。(仅集群)users:必须是最初为新数据库创建的用户对象数组。对于已经存在的用户,不会更改用户信息。如果未指定users或不包含任何用户,则将使用空字符串密码创建默认用户 root。这确保了新数据库在创建后将可访问。每个用户对象可以包含以下属性: | |||
% users:必须是最初为新数据库创建的用户对象数组。对于已经存在的用户,不会更改用户信息。如果未指定users或不包含任何用户,则将使用空字符串密码创建默认用户 root。这确保了新数据库在创建后将可访问。每个用户对象可以包含以下属性 | |||
% username:要创建的用户的登录名 | |||
% passwd:用户密码(字符串)。如果未指定,则默认为空字符串。 | |||
% active:一个标志,指示是否应该激活用户帐户。默认值为true。如果设置为false,则用户将无法登录数据库。 | |||
% extra:带有额外用户信息的JSON对象。Extra中包含的数据 将为用户存储,但ArangoDB不会进一步解释。 | |||
% 创建一个新的数据库 | |||
% 响应是一个JSON对象,其属性结果设置为true。 | |||
% 注意:仅可以在_system数据库中创建新数据库。 | |||
% 返回码 | |||
% 201:如果数据库创建成功,则返回。 | |||
% 400:如果请求参数无效或具有指定名称的数据库已存在,则返回。 | |||
% 403:如果请求未在_system数据库中执行,则返回。 | |||
% 409:如果具有指定名称的数据库已经存在,则返回。 | |||
newDb(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/database">>, [], BodyStr, true). | |||
% 删除现有数据库 | |||
% DELETE /_api/database/{database-name} | |||
% 路径参数 | |||
% database-name(必填):数据库的名称 | |||
% 删除数据库以及其中存储的所有数据。 | |||
% 注意:只能从_system数据库中删除数据库。该_SYSTEM数据库本身不能被丢弃。 | |||
% 返回码 | |||
% 200:如果成功删除数据库,则返回。 | |||
% 400:如果请求格式错误,则返回。 | |||
% 403:如果请求未在_system数据库中执行,则返回。 | |||
% 404:如果找不到数据库,则返回。 | |||
delDb(PoolNameOrSocket, Name) -> | |||
Path = <<"/_api/database/", Name/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined, true). |
@ -0,0 +1,500 @@ | |||
-module(agDocuments). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
%% doc_address:https://www.arangodb.com/docs/stable/http/document.html | |||
% 文档的HTTP接口 | |||
% 基础和术语 | |||
% 文件,密钥,句柄和修订 | |||
% ArangoDB中的文档是JSON对象。这些对象可以嵌套(任何深度),并且可以包含列表。每个文档都有一个唯一的 主键,可以在其集合中对其进行标识。此外,每个文档都由其文档句柄 跨同一数据库中所有集合唯一标识。同一文档的不同修订版(由其句柄标识)可以通过其文档修订版来区分 。任何交易都只能看到文档的单个修订版。 | |||
% 这是一个示例文件: | |||
% { | |||
% "_id" : "myusers/3456789", | |||
% "_key" : "3456789", | |||
% "_rev" : "14253647", | |||
% "firstName" : "John", | |||
% "lastName" : "Doe", | |||
% "address" : { | |||
% "street" : "Road To Nowhere 1", | |||
% "city" : "Gotham" | |||
% }, | |||
% "hobbies" : [ | |||
% {"name": "swimming", "howFavorite": 10}, | |||
% {"name": "biking", "howFavorite": 6}, | |||
% {"name": "programming", "howFavorite": 4} | |||
% ] | |||
% } | |||
% 所有文档都包含特殊属性: 文档句柄以字符串形式存储在中_id, 文档主键存储在中 _key, 文档修订版本存储在中 _rev。_key创建文档时,用户可以指定属性的值。创建文档后_id,_key值是不可变的。该_rev值由ArangoDB自动维护。 | |||
% 文件把手 | |||
% 文档句柄唯一标识数据库中的文档。它是一个字符串,由集合名称和文档键(_key属性)组成,以分隔/。 | |||
% | |||
% 文件金钥 | |||
% 文档密钥可以唯一地标识存储在其集合中的文档。查询特定文档时,客户端可以并且应该使用该文档密钥。文档密钥存储在_key每个文档的属性中。键值由ArangoDB自动在集合的主索引中建立索引。因此,通过其键查找文档是快速的操作。创建文档后,文档的_key值是不变的。默认情况下,如果未指定_key属性,则ArangoDB将自动生成文档密钥,否则使用用户指定的_key。 | |||
% | |||
% 通过使用keyOptions属性创建集合,可以在每个集合级别更改此行为。 | |||
% | |||
% 使用keyOptions它可以完全禁止用户指定的键,或者强制使用特定机制来自动生成_key 值。 | |||
% | |||
% 文件修订 | |||
% ArangoDB中的每个文档都有一个修订版本,存储在system属性中 _rev。它由服务器完全管理,对用户只读。 | |||
% | |||
% 它的值应视为不透明的,不保证其格式和属性,但在文档更新后其值将有所不同。更具体地说,_rev值在单个服务器设置中的所有文档和所有集合中都是唯一的。在群集设置中,_rev即使在同一毫秒内编写,一个分片内也可以确保两个不同的文档修订版具有不同的字符串。 | |||
% | |||
% 该_rev属性可以用作查询的前提,以避免 丢失更新情况。也就是说,如果客户端从服务器获取文档,在本地对其进行修改(但未更改_rev属性),然后将其发送回服务器以更新文档,但是与此同时,该文档已被其他操作更改,则修订不会匹配,服务器将取消该操作。如果没有这种机制,客户端将不知不觉地覆盖对文档所做的更改。 | |||
% | |||
% 当更新或替换现有文档时,ArangoDB会将该文档的新版本写入预写日志文件(与存储引擎无关)。写入新版本的文档后,旧版本仍然存在,至少在磁盘上。删除现有文档(版本)时也是如此:文档的旧版本加上删除操作将在磁盘上保留一段时间。 | |||
% | |||
% 因此,在磁盘上可能同时_key存在同一文档的多个修订版(由相同的值标识)。但是,无法访问过时的修订。文档成功更新或删除后,用户将无法再执行查询或其他数据检索操作。此外,一段时间后,内部将删除旧修订。这是为了避免磁盘使用量不断增长。 | |||
% | |||
% 从用户的角度来看,每个时间点仅存在一个文档修订版本_key。没有内置的系统来自动保留对文档所做的所有更改的历史记录,并且无法通过该_rev值恢复文档的旧版本。 | |||
% | |||
% 文件标签 | |||
% ArangoDB尝试尽可能遵守现有的HTTP标准。为此,单文档查询的结果将HTTP标头Etag设置为使用双引号括起来的文档修订版。 | |||
% | |||
% 文档的基本操作(创建,读取,存在,替换,更新,删除)被映射到标准HTTP方法(POST,GET, HEAD,PUT,PATCH和DELETE)。 | |||
% | |||
% 如果修改文档,则可以使用“ 如果匹配”字段来检测冲突。可以使用HTTP方法HEAD检查文档的修订。 | |||
% | |||
% 单个请求中包含多个文档 | |||
% 从ArangoDB 3.0开始,基本文档API已扩展为不仅可以处理单个文档,而且可以在单个请求中处理多个文档。这对于性能至关重要,尤其是在集群情况下,在这种情况下,单个请求可能涉及集群中的多个网络跃点。另一个优点是,它减少了HTTP协议的开销以及客户端和服务器之间的各个网络往返。在单个请求中执行多个文档操作的总体思路是使用JSON对象数组代替单个文档。因此,必须将前提条件的文档密钥,句柄和修订内容嵌入到给定的各个文档中。多个文档操作仅限于单个文档或边集合。看到有关详细信息的API描述。 | |||
% | |||
% 请注意,GET,HEAD和DELETE HTTP操作通常不允许传递消息正文。因此,它们不能用于在一个请求中执行多个文档操作。但是,还有其他端点可以在一个请求中请求和删除多个文档。请参阅批量文档操作。 | |||
% | |||
% 文件的URI | |||
% 可以使用其唯一的URI检索任何文档: | |||
% | |||
% http://server:port/_api/document/<document-handle> | |||
% 例如,假设文档句柄为demo/362549736,则该文档的URL为: | |||
% | |||
% http://localhost:8529/_api/document/demo/362549736 | |||
% 上面的URL架构未明确指定 数据库名称 ,因此_system将使用默认数据库。要明确指定数据库上下文,请使用以下URL模式: | |||
% | |||
% http://server:port/_db/<database-name>/_api/document/<document-handle> | |||
% 例: | |||
% | |||
% http://localhost:8529/_db/mydb/_api/document/demo/362549736 | |||
% 注意:以下示例为了简短起见使用短URL格式。 | |||
% | |||
% 请求文档时,文档修订版本将在“ Etag” HTTP标头中返回。 | |||
% | |||
% 如果使用GET获取文档,并且要检查是否有较新的修订版本,则可以使用If-None-Match标头。如果文档未更改,则返回HTTP 412(前提条件失败)错误。 | |||
% | |||
% 如果要查询,替换,更新或删除文档,则可以使用If-Match标头。如果文档已更改,则操作将中止,并返回HTTP 412错误。 | |||
% 读取单个文档 | |||
% GET /_api/document/{collection}/{key} | |||
% 路径参数 | |||
% collection(必填):要从中读取文档的集合的名称。 | |||
% key (必填):文档密钥。 | |||
% 标头参数 | |||
% If-None-Match(可选):如果给出了“ If-None-Match”标头,则它必须恰好包含一个Etag。如果文档版本与给定的Etag不同,则返回文档。否则,返回HTTP 304。 | |||
% If-Match(可选):如果给出了“ If-Match”标头,则它必须恰好包含一个Etag。如果文档的版本与给定的Etag相同,则返回文档。否则,返回HTTP 412。 | |||
% 返回由document-id标识的文档。返回的文档包含三个特殊属性:_id包含文档标识符,_key包含唯一标识给定集合中的文档的键,_rev包含修订版。 | |||
% 返回码 | |||
% 200:如果找到文档,则返回 | |||
% 304:如果给出“ If-None-Match”标题并且文档具有相同版本,则返回 | |||
% 404:如果找不到文档或集合,则返回 | |||
% 412:如果给出“ If-Match”标头并且找到的文档具有不同版本,则返回412。响应还将在_rev属性中包含找到的文档的当前修订。此外,将返回属性_id和_key。 | |||
getDoc(PoolNameOrSocket, CollName, Key) -> | |||
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
getDoc(PoolNameOrSocket, CollName, Key, Headers) -> | |||
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, Headers, undefined). | |||
% 读取单个文档头 | |||
% HEAD /_api/document/{collection}/{key} | |||
% 路径参数 | |||
% collection (必填):要从中读取文档的集合的名称。 | |||
% key (必填):文档密钥。 | |||
% 标头参数 | |||
% If-None-Match(可选):如果给出了“ If-None-Match”标头,则它必须恰好包含一个Etag。如果当前文档修订版不等于指定的Etag,则返回HTTP 200响应。如果当前文档修订版与指定的Etag相同,则返回HTTP 304。 | |||
% If-Match(可选):如果给出了“ If-Match”标头,则它必须恰好包含一个Etag。如果文档的版本与给定的Etag相同,则返回文档。否则,返回HTTP 412。 | |||
% 类似于GET,但仅返回标头字段,而不返回正文。您可以使用此调用来获取文档的当前版本,或检查文档是否已删除。 | |||
% 返回码 | |||
% 200:如果找到文档,则返回 | |||
% 304:如果给出“ If-None-Match”标题并且文档具有相同版本,则返回 | |||
% 404:如果找不到文档或集合,则返回 | |||
% 412:如果给出“ If-Match”标头并且找到的文档具有不同版本,则返回412。响应还将在Etag标头中包含找到的文档的当前版本。 | |||
getDocHead(PoolNameOrSocket, CollName, Key) -> | |||
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgHead, Path, [], undefined). | |||
getDocHead(PoolNameOrSocket, CollName, Key, Headers) -> | |||
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgHead, Path, Headers, undefined). | |||
% 创建文档 | |||
% POST /_api/document/{collection} | |||
% 路径参数 | |||
% collection (必填):要在其中创建文档的集合的名称。 | |||
% 查询参数 | |||
% collection (可选):集合的名称。这仅是为了向后兼容。在ArangoDB版本<3.0中,URL路径为/ _api / document,并且此查询参数是必需的。这种组合仍然有效,但是建议的方法是在URL路径中指定集合。 | |||
% waitForSync(可选):等待文档已同步到磁盘。 | |||
% returnNew(可选):另外 ,在结果的new属性下返回完整的新文档。 | |||
% returnOld(可选):另外 ,在结果的old属性下返回完整的旧文档。仅在使用覆盖选项时可用。 | |||
% silent(可选):如果设置为true,则将返回一个空对象作为响应。创建的文档将不返回任何元数据。此选项可用于节省一些网络流量。 | |||
% overwrite(可选):如果设置为true,则插入将成为替换插入。如果已经存在具有相同_key的文档,则不会由于违反唯一约束而拒绝新文档,而是将替换旧文档。 | |||
% overwriteMode(可选):此选项取代覆盖并提供以下模式: | |||
% "ignore":如果已经存在具有指定_key值的文档,则不会执行任何操作,也不会执行任何写操作。在这种情况下,插入操作将返回成功。此模式不支持使用来返回旧文档版本RETURN OLD。使用时 RETURN NEW,如果文档已经存在,则将返回null。 | |||
% "replace":如果已经存在具有指定_key值的文档,则该文档将被指定的文档值覆盖。当未指定overwrite模式但覆盖 标志设置为true时,也将使用此模式。 | |||
% "update":如果已经存在具有指定_key值的文档,则将使用指定的文档值对其进行修补(部分更新)。可以通过keepNull和 mergeObjects参数进一步控制覆盖模式。 | |||
% "conflict":如果已经存在具有指定_key值的文档,则返回唯一的违反约束错误,以使插入操作失败。如果未设置覆盖模式,并且覆盖标志为false或未设置,这也是默认行为。 | |||
% keepNull(可选):如果要使用update-insert命令删除现有属性,则可以将URL查询参数keepNull与 false一起使用。这将修改patch命令的行为,以从属性文档中删除属性文件值为null的现有文档。此选项仅控制更新插入行为。 | |||
% mergeObjects(可选):控制如果现有文档和更新插入文档中都存在对象(不是数组),是否将合并对象。如果设置为false,则修补程序文档中的值将覆盖现有文档的值。如果设置为true,则对象将被合并。默认值为true。此选项仅控制更新插入行为。 | |||
% 请求正文(json) | |||
% 单个文档的JSON表示形式。 | |||
% 从正文中给定的文档创建一个新文档,除非已经有给定_key的文档。如果未提供_key,则会自动生成一个新的唯一_key。 | |||
% 正文中可能始终给定的_id和_rev属性被忽略,URL部分或查询参数集合分别计数。 | |||
% 如果成功创建了文档,则Location标头将包含新创建的文档的路径。该Etag的头字段包含了文档的修订。两者都仅在单个文档的情况下设置。 | |||
% 如果silent未设置为true,则响应的主体包含具有以下属性的JSON对象: | |||
% _id包含新创建的文档的文档标识符 | |||
% _key包含文档密钥 | |||
% _rev包含文档修订版 | |||
% 如果collection参数waitForSync为false,则该调用将在文档被接受后立即返回。直到文档同步到磁盘后,它才会等待。 | |||
% 可选地,即使已为整个集合禁用了waitForSync标志,查询参数waitForSync也可用于强制将文档创建操作同步到磁盘。因此,waitForSync查询参数可用于强制执行此特定操作的同步。要使用此功能,请将waitForSync参数设置为true。如果 未指定waitForSync参数或将其设置为false,则将应用集合的默认waitForSync行为。该waitForSync查询参数不能用于禁用同步用于具有默认集合waitForSync值为true。 | |||
% 如果查询参数returnNew为true,则对于每个生成的文档,将在结果中的new属性下返回完整的新文档。 | |||
% 返回码 | |||
% 201:如果文档创建成功并且waitForSync为true,则返回 。 | |||
% 202:如果文档创建成功并且waitForSync为false,则返回 。 | |||
% 400:如果正文不包含一个文档的有效JSON表示形式,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 404:如果collection指定的collection未知,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 409:如果在索引属性中具有相同限定词的文档与现有文档发生冲突并因此违反了该唯一约束,则在单个文档的情况下返回409。在这种情况下,响应主体包含一个错误文档。 | |||
newDoc(PoolNameOrSocket, CollName, MapData) -> | |||
Path = <<"/_api/document/", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
newDoc(PoolNameOrSocket, CollName, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/document/", CollName/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
% 替换文档 | |||
% PUT /_api/document/{collection}/{key} | |||
% 路径参数 | |||
% collection(必需):要在其中替换文档的集合的名称。 | |||
% key(必填):文档密钥。 | |||
% 查询参数 | |||
% waitForSync(可选):等待文档已同步到磁盘。 | |||
% ignoreRevs(可选):默认情况下,或者如果将其设置为true,则忽略给定文档中的_rev属性。如果将其设置为false,则将正文文档中给定的_rev属性作为前提。仅当当前版本是指定的版本时,才替换该文档。 | |||
% returnOld(可选):在结果的old属性下,还返回更改后的文档的完整先前修订。 | |||
% returnNew(可选): 在结果的new属性下还返回完整的新文档。 | |||
% silent(可选):如果设置为true,则将返回一个空对象作为响应。对于已替换的文档,不会返回任何元数据。此选项可用于节省一些网络流量。 | |||
% 标头参数 | |||
% If-Match(可选):您可以使用if-match HTTP标头有条件地根据目标修订版ID替换文档。 | |||
% 请求正文(json) | |||
% 单个文档的JSON表示形式。 | |||
% 将指定的文档替换为正文中的文档,前提是存在这样的文档并且不违反任何前提条件。 | |||
% 该_key属性的值以及用作分片键的属性均不得更改。 | |||
% 如果指定了If-Match标头,并且数据库中文档的修订版与给定的修订版不相等,则将违反先决条件。 | |||
% 如果未给出If-Match且ignoreRevs为false,并且主体中存在_rev属性,并且其值与数据库中文档的修订版不匹配,则将违反先决条件。 | |||
% 如果违反了前提条件,则返回HTTP 412。 | |||
% 如果文档存在并且可以更新,则返回HTTP 201或HTTP 202(取决于waitForSync,请参见下文),Etag标头字段包含文档的新修订版,而Location标头包含完整的URL,在该URL下,可以查询文件。 | |||
% 仅限群集:替换文档可能包含集合的预定义分片键的值。分片键的值被视为提高性能的提示。如果分片键值不正确,ArangoDB可能会回答“ 未找到”错误。 | |||
% 可选地,即使已为整个集合禁用了waitForSync标志,查询参数waitForSync仍可用于强制将文档替换操作同步到磁盘。因此,waitForSync查询参数可用于强制特定操作的同步。要使用此功能,请将waitForSync参数设置为true。如果未指定waitForSync参数或将其设置为 false,则将应用集合的默认waitForSync行为。该waitForSync查询参数不能用于禁用同步用于具有默认集合waitForSync值为true。 | |||
% 如果silent未设置为true,则响应的主体包含一个JSON对象,其中包含有关标识符和修订版的信息。属性 _id包含更新的文档的已知文档ID,_key 包含用于唯一标识给定集合中的文档的键,而属性_rev包含新文档的修订版。 | |||
% 如果查询参数returnOld为true,那么将在结果的old属性下返回文档的完整先前修订版。 | |||
% 如果查询参数returnNew为true,那么将在结果中的new属性下返回完整的新文档。 | |||
% 如果该文档不存在,则返回HTTP 404,并且响应的正文包含错误文档。 | |||
% 返回码 | |||
% 201:如果成功替换了文档并且waitForSync为true,则返回 。 | |||
% 202:如果成功替换了文档并且waitForSync为false,则返回 。 | |||
% 400:如果正文不包含文档的有效JSON表示形式,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 404:如果找不到集合或文档,则返回。 | |||
% 412:如果违反了前提条件,则返回。该响应还将在_rev 属性中包含找到的文档的当前修订。此外,将返回属性_id和_key。 | |||
replaceDoc(PoolNameOrSocket, CollName, Key, MapData) -> | |||
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
replaceDoc(PoolNameOrSocket, CollName, Key, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
replaceDoc(PoolNameOrSocket, CollName, Key, MapData, QueryPars, Headers) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, Headers, BodyStr). | |||
% 更新文档 | |||
% PATCH /_api/document/{collection}/{key} | |||
% 路径参数 | |||
% collection(必填):要在其中更新文档的集合的名称。 | |||
% key(必填):文档密钥。 | |||
% 查询参数 | |||
% keepNull(可选):如果要使用patch命令删除现有属性,则可以将URL查询参数keepNull与false一起使用。这将修改patch命令的行为,以从属性文档中删除属性文件值为null的现有文档。 | |||
% mergeObjects(可选):控制如果现有文档和修补程序文档中都存在对象(不是数组),是否将合并对象。如果设置为false,则修补程序文档中的值将覆盖现有文档的值。如果设置为true,则对象将被合并。默认值为 true。 | |||
% waitForSync(可选):等待文档已同步到磁盘。 | |||
% ignoreRevs(可选):默认情况下,或者如果将其设置为true,则忽略给定文档中的_rev属性。如果将其设置为false,则将正文文档中给定的_rev属性作为前提。仅当当前版本是指定的版本时,文档才会更新。 | |||
% returnOld(可选):在结果的old属性下,还返回更改后的文档的完整先前修订。 | |||
% returnNew(可选): 在结果的new属性下还返回完整的新文档。 | |||
% silent(可选):如果设置为true,则将返回一个空对象作为响应。更新后的文档将不返回任何元数据。此选项可用于节省一些网络流量。 | |||
% 标头参数 | |||
% If-Match(可选):您可以使用if-match HTTP标头有条件地根据目标修订版ID更新文档。 | |||
% 请求正文(json) | |||
% 文档更新的JSON表示形式作为对象。 | |||
% 部分更新document-id标识的文档。请求的正文必须包含具有要修补的属性的JSON文档(补丁文档)。修补程序文档中的所有属性(如果尚不存在)将被添加到现有文档中,如果存在的话将被覆盖在现有文档中。 | |||
% 该_key属性的值以及用作分片键的属性均不得更改。 | |||
% 在补丁文档中将属性值设置为null会导致默认情况下为该属性保存null值。 | |||
% 如果指定了If-Match标头,并且数据库中文档的修订版与给定的修订版不相等,则将违反先决条件。 | |||
% 如果未给出If-Match且ignoreRevs为false,并且主体中存在_rev属性,并且其值与数据库中文档的修订版不匹配,则将违反先决条件。 | |||
% 如果违反了前提条件,则返回HTTP 412。 | |||
% 如果文档存在并且可以更新,则返回HTTP 201或HTTP 202(取决于waitForSync,请参见下文),Etag标头字段包含文档的新修订版(双引号),而Location标头包含一个可以查询文档的完整URL。 | |||
% 仅限群集:修补程序文档可能包含集合的预定义分片键的值。分片键的值被视为提高性能的提示。如果分片键值不正确,ArangoDB可能会以未发现的错误回答 | |||
% 可选地,即使已为整个集合禁用了waitForSync标志,查询参数waitForSync仍可用于强制将更新的文档操作同步到磁盘。因此,waitForSync查询参数可用于强制特定操作的同步。要使用此功能,请将waitForSync参数设置为true。如果未指定waitForSync参数或将其设置为 false,则将应用集合的默认waitForSync行为。该waitForSync查询参数不能用于禁用同步用于具有默认集合waitForSync值为true。 | |||
% 如果silent未设置为true,则响应的主体包含一个JSON对象,其中包含有关标识符和修订版的信息。属性 _id包含更新的文档的已知文档ID,_key 包含用于唯一标识给定集合中的文档的键,而属性_rev包含新文档的修订版。 | |||
% 如果查询参数returnOld为true,那么将在结果的old属性下返回文档的完整先前修订版。 | |||
% 如果查询参数returnNew为true,那么将在结果中的new属性下返回完整的新文档。 | |||
% 如果该文档不存在,则返回HTTP 404,并且响应的正文包含错误文档。 | |||
% 返回码 | |||
% 201:如果文档更新成功并且waitForSync为true,则返回 。 | |||
% 202:如果文档更新成功且waitForSync为false,则返回 。 | |||
% 400:如果正文不包含文档的有效JSON表示形式,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 404:如果找不到集合或文档,则返回。 | |||
% 412:如果违反了前提条件,则返回。该响应还将在_rev 属性中包含找到的文档的当前修订。此外,将返回属性_id和_key。 | |||
updateDoc(PoolNameOrSocket, CollName, Key, MapData) -> | |||
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr). | |||
updateDoc(PoolNameOrSocket, CollName, Key, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr). | |||
updateDoc(PoolNameOrSocket, CollName, Key, MapData, QueryPars, Headers) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, Headers, BodyStr). | |||
% 删除文档 | |||
% DELETE /_api/document/{collection}/{key} | |||
% 路径参数 | |||
% collection(必填):要在其中删除文档的集合的名称。 | |||
% key(必填):文档密钥。 | |||
% 查询参数 | |||
% waitForSync(可选):等待删除操作已同步到磁盘。 | |||
% returnOld(可选):在结果的old属性下,还返回更改后的文档的完整先前修订。 | |||
% silent(可选):如果设置为true,则将返回一个空对象作为响应。对于已删除的文档,不会返回任何元数据。此选项可用于节省一些网络流量。 | |||
% 标头参数 | |||
% If-Match(可选):您可以使用if-match HTTP标头有条件地根据目标修订版ID删除文档。 | |||
% 如果silent未设置为true,则响应的主体包含一个JSON对象,其中包含有关标识符和修订版的信息。属性 _id包含已删除文档的已知文档ID,_key 包含用于唯一标识给定集合中文档的键,属性_rev包含文档修订版。 | |||
% 如果未指定waitForSync参数或将其设置为false,则将应用集合的默认waitForSync行为。该waitForSync查询参数不能用于禁用同步用于具有默认集合waitForSync 的价值真。 | |||
% 如果查询参数returnOld为true,那么将在结果的old属性下返回文档的完整先前修订版。 | |||
% 返回码 | |||
% 200:如果文档已成功删除并且waitForSync为true,则返回 。 | |||
% 202:如果文档已成功删除并且waitForSync为false,则返回 。 | |||
% 404:如果找不到集合或文档,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 412:如果“如果-match”标头或返回转,并给出找到的文件有不同的版本。响应还将在_rev属性中包含找到的文档的当前修订。此外,将返回属性_id和_key。 | |||
delDoc(PoolNameOrSocket, CollName, Key) -> | |||
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
delDoc(PoolNameOrSocket, CollName, Key, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
delDoc(PoolNameOrSocket, CollName, Key, QueryPars, Headers) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, Headers, undefined). | |||
% 批量文件操作 | |||
% ArangoDB支持批量处理文档。批量操作影响 单个集合。使用此API变体可使客户端分摊整批文档中的单个请求的开销。不能保证批量操作可以串行执行,ArangoDB 可以并行执行这些操作。这可以转化为大幅的性能提升,尤其是在集群部署中。 | |||
% 如果在处理一个操作期间发生错误,ArangoDB将继续处理其余操作。错误将作为错误文档在响应正文中内联返回(有关更多详细信息,请参见下文)。此外,X-Arango-Error-Codes标头将包含发生的错误代码及其多重性的映射,例如 1205:10,1210:17,这意味着在10种情况下,错误1205 非法文档句柄;在17种情况下,错误1210 违反了唯一约束。。 | |||
% 通常,批量操作期望输入数组,并且结果主体将包含相同长度的JSON数组。 | |||
% 读取单个文档 | |||
% PUT /_api/document/{collection}#get | |||
% 路径参数 | |||
% collection(必需):要从中读取文档的集合的名称。 | |||
% 查询参数 | |||
% onlyget(必需):该参数必须为true,否则将执行替换操作! | |||
% ignoreRevs(可选):如果该值为true(默认值):如果搜索文档包含_rev字段的值,则仅当文档具有相同的修订值时才返回该文档。否则,将返回前提条件失败错误。 | |||
% 返回正文对象中由_key标识的文档。请求的正文必须包含字符串(要查找的_key值)或搜索文档的JSON数组。 | |||
% 搜索文档必须至少包含_key字段的值。一种用于值_rev 可被指定以验证文档是否具有相同的修订值,除非ignoreRevs设置为false。 | |||
% 仅限群集:搜索文档可能包含集合的预定义分片键的值。分片键的值被视为提高性能的提示。如果分片键值不正确,ArangoDB可能会回答“ 未找到”错误。 | |||
% 返回的文档数组包含三个特殊属性:_id包含文档标识符,_key包含唯一标识给定集合中的文档的键,_rev包含修订版。 | |||
% 返回码 | |||
% 200:如果没有发生错误则返回 | |||
% 400:如果正文不包含文档数组的有效JSON表示形式,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 404:如果找不到集合,则返回。 | |||
% 对于该操作的返回 列表 如果文档不存在 或者_rev条件不满足 则返回列表的中包含相关的错误 可能需要在使用的时候过滤正确和非正确的返回文档 | |||
getDocs(PoolNameOrSocket, CollName, KeyOrMapDataList) -> | |||
QueryBinary = agMiscUtils:spellQueryPars([{onlyget, true}]), | |||
Path = <<"/_api/document/", CollName/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(KeyOrMapDataList), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
getDocs(PoolNameOrSocket, CollName, KeyOrMapDataList, QueryPars) -> | |||
LastQueryPars = | |||
case lists:keyfind(onlyget, 1, QueryPars) of | |||
{onlyget, true} -> | |||
QueryPars; | |||
_ -> | |||
lists:keystore(onlyget, 1, QueryPars, {onlyget, true}) | |||
end, | |||
QueryBinary = agMiscUtils:spellQueryPars(LastQueryPars), | |||
Path = <<"/_api/document/", CollName/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(KeyOrMapDataList), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
% 创建多个文档 | |||
% POST /_api/document/{collection}#multiple | |||
% 路径参数 | |||
% collection(必填):要在其中创建文档的集合的名称。 | |||
% 查询参数 | |||
% collection(可选):集合的名称。这仅是为了向后兼容。在ArangoDB版本<3.0中,URL路径为/ _api / document,并且此查询参数是必需的。这种组合仍然有效,但是建议的方法是在URL路径中指定集合。 | |||
% waitForSync(可选):等待文档已同步到磁盘。 | |||
% returnNew(可选):另外 ,在结果的new属性下返回完整的新文档。 | |||
% returnOld(可选):另外 ,在结果的old属性下返回完整的旧文档。仅在使用覆盖选项时可用。 | |||
% silent(可选):如果设置为true,则将返回一个空对象作为响应。创建的文档将不返回任何元数据。此选项可用于节省一些网络流量。 | |||
% overwrite(可选):如果设置为true,则插入将成为替换插入。如果已经存在具有相同_key的文档,则不会由于违反唯一约束而拒绝新文档,而是将替换旧文档。 | |||
% 请求正文(json) | |||
% 要创建的文档数组。 | |||
% 从正文中给定的文档创建新文档,除非已经有给定_key的文档。如果未提供_key,则会自动生成一个新的唯一_key。 | |||
% 结果主体将包含一个长度与输入数组相同的JSON数组,并且每个条目都包含对应输入的运算结果。如果发生错误,则该条目是一个文档,其属性error设置为true,errorCode设置为发生的错误代码。 | |||
% 正文中可能始终给定的_id和_rev属性被忽略,URL部分或查询参数集合分别计数。 | |||
% 如果silent未设置为true,则响应的主体包含具有以下属性的JSON对象数组: | |||
% _id包含新创建的文档的文档标识符 | |||
% _key包含文档密钥 | |||
% _rev包含文档修订版 | |||
% 如果collection参数waitForSync为false,则在文档被接受后,调用将立即返回。直到文档同步到磁盘后,它才会等待。 | |||
% 可选地,即使已为整个集合禁用了waitForSync标志,查询参数waitForSync也可用于强制将文档创建操作同步到磁盘。因此,waitForSync查询参数可用于强制执行此特定操作的同步。要使用此功能,请将waitForSync参数设置为true。如果 未指定waitForSync参数或将其设置为false,则将应用集合的默认waitForSync行为。该waitForSync查询参数不能用于禁用同步用于具有默认集合waitForSync值为true。 | |||
% 如果查询参数returnNew为true,则对于每个生成的文档,将在结果中的new属性下返回完整的新文档。 | |||
% 应与一些附加的HTTP头的文件已经发生了错误的X阿朗戈-差错代码:被设置,其中包含一个地图,与它们的重数一起发生的错误代码,如在1205:10,1210:17,其意味着在10种情况下发生了错误1205“非法文档句柄”,在17种情况下发生了错误1210“违反唯一约束”。 | |||
% 返回码 | |||
% 201:如果waitForSync为true,并且已处理操作,则返回。 | |||
% 202:如果waitForSync为false并且已处理操作,则返回。 | |||
% 400:如果正文不包含文档数组的有效JSON表示形式,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 404:如果collection指定的collection未知,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 按照MapDataList的顺序返回执行结果 正确或者错误 | |||
newDocs(PoolNameOrSocket, CollName, MapDataList) -> | |||
Path = <<"/_api/document/", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapDataList), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
newDocs(PoolNameOrSocket, CollName, MapDataList, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/document/", CollName/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapDataList), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
% 替换多个文件 | |||
% PUT /_api/document/{collection} | |||
% 路径参数 | |||
% collection(必填):此URL参数是替换文档的集合的名称。 | |||
% 查询参数 | |||
% waitForSync(可选):等待新文档同步到磁盘。 | |||
% ignoreRevs(可选):默认情况下,或者如果将其设置为true,则忽略给定文档中的_rev属性。如果将其设置为false,则将正文文档中给定的_rev属性作为前提。仅当当前版本是指定的版本时,才替换该文档。 | |||
% returnOld(可选):在结果的old属性下还返回更改后的文档的完整先前修订。 | |||
% returnNew(可选): 在结果的new属性下还返回完整的新文档。 | |||
% 请求正文(json) | |||
% 文档数组的JSON表示形式。 | |||
% 用主体中的文档替换指定集合中的多个文档,替换后的文档由 主体文档中的_key属性指定。 | |||
% 该_key属性的值以及用作sharding keys的属性均不得更改。 | |||
% 如果ignoreRevs为false,并且正文中的文档中存在_rev属性,并且其值与数据库中相应文档的修订版不匹配,则将违反先决条件。 | |||
% 仅限群集:替换文档可能包含集合的预定义分片键的值。分片键的值被视为提高性能的提示。如果分片键值不正确,ArangoDB可能会回答“ 未找到”错误。 | |||
% 可选地,即使已为整个集合禁用了waitForSync标志,查询参数waitForSync仍可用于强制将文档替换操作同步到磁盘。因此,waitForSync查询参数可用于强制特定操作的同步。要使用此功能,请将waitForSync参数设置为true。如果未指定waitForSync参数或将其设置为 false,则将应用集合的默认waitForSync行为。该waitForSync查询参数不能用于禁用同步用于具有默认集合waitForSync值为true。 | |||
% 响应的主体包含一个与输入数组长度相同的JSON数组,其中包含有关标识符和替换文档的修订版的信息。在每个条目中,属性 _id包含每个更新文档的已知文档ID, _key包含用于唯一标识给定集合中的文档的键,而属性_rev包含新文档修订版。如果发生错误或违反先决条件,则会构建一个错误对象,该错误对象的属性error设置为true,属性 errorCode设置为错误代码。 | |||
% 如果查询参数returnOld为true,则对于每个生成的文档,将在结果的old属性下返回文档的完整先前修订版。 | |||
% 如果查询参数returnNew为true,则对于每个生成的文档,将在结果中的new属性下返回完整的新文档。 | |||
% 请注意,如果违反任何前提条件或某些文档发生错误,则返回代码仍为201或202,但是会设置其他HTTP标头X-Arango-Error-Codes,其中包含错误代码的映射,与它们的多重性一起发生,如:1200:17,1205:10,这意味着在17种情况下发生了错误1200“修订冲突”,在10种情况下发生了错误1205“非法文档句柄”。 | |||
% 返回码 | |||
% 201:如果waitForSync为true,并且已处理操作,则返回。 | |||
% 202:如果waitForSync为false并且已处理操作,则返回。 | |||
% 400:如果正文不包含文档数组的有效JSON表示形式,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 404:如果找不到集合,则返回。 | |||
replaceDocs(PoolNameOrSocket, CollName, MapDataList) -> | |||
Path = <<"/_api/document/", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapDataList), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
replaceDocs(PoolNameOrSocket, CollName, MapDataList, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/document/", CollName/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapDataList), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
% 更新多个文件 | |||
% PATCH /_api/document/{collection} | |||
% 路径参数 | |||
% collection(必填):要在其中更新文档的集合的名称。 | |||
% 查询参数 | |||
% keepNull(可选):如果要使用patch命令删除现有属性,则可以将URL查询参数keepNull与false一起使用。这将修改patch命令的行为,以从属性文档中删除属性文件值为null的现有文档。 | |||
% mergeObjects(可选):控制如果现有文档和修补程序文档中都存在对象(不是数组),是否将合并对象。如果设置为false,则修补程序文档中的值将覆盖现有文档的值。如果设置为true,则对象将被合并。默认值为 true。 | |||
% waitForSync(可选):等待新文档同步到磁盘。 | |||
% ignoreRevs(可选):默认情况下,或者如果将其设置为true,则忽略给定文档中的_rev属性。如果将其设置为false,则将正文文档中给定的_rev属性作为前提。仅当当前版本是指定的版本时,文档才会更新。 | |||
% returnOld(可选):在结果的old属性下还返回更改后的文档的完整先前修订。 | |||
% returnNew(可选): 在结果的new属性下还返回完整的新文档。 | |||
% 请求正文(json) | |||
% 文档更新数组的JSON表示形式是对象。 | |||
% 部分更新文档,要更新的文档由主体对象中的_key属性指定。请求的正文必须包含文档更新的JSON数组,其中包含要修补的属性(补丁文档)。修补程序文档中的所有属性(如果尚不存在)将被添加到现有文档中,如果存在的话将被覆盖在现有文档中。 | |||
% 该_key属性的值以及用作分片键的属性均不得更改。 | |||
% 在补丁文档中将属性值设置为null会导致默认情况下为该属性保存null值。 | |||
% 如果ignoreRevs为false,并且正文中的文档中存在_rev属性,并且其值与数据库中相应文档的修订版不匹配,则将违反先决条件。 | |||
% 仅限群集:修补程序文档可能包含集合的预定义分片键的值。分片键的值被视为提高性能的提示。如果分片键值不正确,ArangoDB可能会以未发现的错误回答 | |||
% 可选地,即使已为整个集合禁用了waitForSync标志,查询参数waitForSync仍可用于强制将文档替换操作同步到磁盘。因此,waitForSync查询参数可用于强制特定操作的同步。要使用此功能,请将waitForSync参数设置为true。如果未指定waitForSync参数或将其设置为 false,则将应用集合的默认waitForSync行为。该waitForSync查询参数不能用于禁用同步用于具有默认集合waitForSync值为true。 | |||
% 响应的主体包含一个与输入数组长度相同的JSON数组,其中包含有关标识符和已更新文档的修订的信息。在每个条目中,属性 _id包含每个更新文档的已知文档ID, _key包含用于唯一标识给定集合中的文档的键,而属性_rev包含新文档修订版。如果发生错误或违反先决条件,则会构建一个错误对象,该错误对象的属性error设置为true,属性 errorCode设置为错误代码。 | |||
% 如果查询参数returnOld为true,则对于每个生成的文档,将在结果的old属性下返回文档的完整先前修订版。 | |||
% 如果查询参数returnNew为true,则对于每个生成的文档,将在结果中的new属性下返回完整的新文档。 | |||
% 请注意,如果违反任何前提条件或某些文档发生错误,则返回代码仍为201或202,但是会设置其他HTTP标头X-Arango-Error-Codes,其中包含错误代码的映射,与它们的多重性一起发生,如:1200:17,1205:10,这意味着在17种情况下发生了错误1200“修订冲突”,在10种情况下发生了错误1205“非法文档句柄”。 | |||
% 返回码 | |||
% 201:如果waitForSync为true,并且已处理操作,则返回。 | |||
% 202:如果waitForSync为false并且已处理操作,则返回。 | |||
% 400:如果正文不包含文档数组的有效JSON表示形式,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 404:如果找不到集合,则返回。 | |||
updateDocs(PoolNameOrSocket, CollName, MapDataList) -> | |||
Path = <<"/_api/document/", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapDataList), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr). | |||
updateDocs(PoolNameOrSocket, CollName, MapDataList, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/document/", CollName/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapDataList), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr). | |||
% 删除多个文件 | |||
% DELETE /_api/document/{collection} | |||
% 路径参数 | |||
% collection(必填):从中删除文档的集合。 | |||
% 查询参数 | |||
% waitForSync(可选):等待删除操作已同步到磁盘。 | |||
% returnOld(可选):在结果的old属性下,还返回更改后的文档的完整先前修订。 | |||
% ignoreRevs(可选):如果设置为true,则忽略选择器中的任何_rev属性。不执行任何修订检查。 | |||
% 请求正文(json) | |||
% 字符串或文档的JSON数组。 | |||
% 请求的主体是一个由文档选择器组成的数组。选择器可以是带键的字符串,带文档标识符的字符串或带_key属性的对象。此API调用从collection中删除所有指定的文档。如果选择器是一个对象并具有_rev属性,则前提是集合中已删除文档的实际修订版是指定的。 | |||
% 响应的主体是一个与输入数组长度相同的数组。对于每个输入选择器,输出都包含一个JSON对象,其中包含有关操作结果的信息。如果未发生错误,则构建一个对象,其中属性_id包含已删除文档的已知文档ID,_key包含用于唯一标识给定集合中的文档的键,而属性_rev包含文档修订版。在发生错误的情况下,与属性的对象错误设置为真和 的errorCode集到的错误代码被构建的。 | |||
% 如果未指定waitForSync参数或将其设置为false,则将应用集合的默认waitForSync行为。该waitForSync查询参数不能用于禁用同步用于具有默认集合waitForSync 的价值真。 | |||
% 如果查询参数returnOld为true,那么将在结果的old属性下返回文档的完整先前修订版。 | |||
% 请注意,如果违反任何先决条件或某些文档发生错误,则返回代码仍为200或202,但是会设置其他HTTP标头X-Arango-Error-Codes,其中包含错误代码的映射,与它们的多重性一起发生,如:1200:17,1205:10,这意味着在17种情况下发生了错误1200“修订冲突”,在10种情况下发生了错误1205“非法文档句柄”。 | |||
% 返回码 | |||
% 200:如果waitForSync为true,则返回。 | |||
% 202:如果waitForSync为false,则返回。 | |||
% 404:如果找不到集合,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
delDocs(PoolNameOrSocket, CollName, KeyOrMapDataList) -> | |||
Path = <<"/_api/document/", CollName/binary, "/">>, | |||
BodyStr = jiffy:encode(KeyOrMapDataList), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], BodyStr). | |||
delDocs(PoolNameOrSocket, CollName, KeyOrMapDataList, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/document/", CollName/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(KeyOrMapDataList), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], BodyStr). |
@ -0,0 +1,43 @@ | |||
-module(agEdges). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
%% doc_address:https://www.arangodb.com/docs/stable/http/edge.html | |||
% 边的地址和标签 | |||
% ArangoDB中的所有文档都有一个文档句柄。该句柄唯一地标识一个文档。可以使用其唯一的URI检索任何文档: | |||
% | |||
% http://server:port/_api/document/<document-handle> | |||
% 边缘是文档的特殊变体。要访问边缘,请使用与文档相同的URL格式: | |||
% | |||
% http://server:port/_api/document/<document-handle> | |||
% 例如,假定存储在 边缘的_id属性中的文档句柄是demo / 362549736,则该边缘的URL为: | |||
% | |||
% http://localhost:8529/_api/document/demo/362549736 | |||
% 上面的URL方案没有明确指定数据库名称,因此将使用默认数据库。要明确指定数据库上下文,请使用以下URL模式: | |||
% | |||
% http://server:port/_db/<database-name>/_api/document/<document-handle> | |||
% 范例: | |||
% | |||
% http://localhost:8529/_db/mydb/_api/document/demo/362549736 | |||
% 注意:为了简洁起见,以下示例使用简短的URL格式。 | |||
% 获取边 | |||
% GET /_api/edges/{collection-id} | |||
% 路径参数 | |||
% collection-id(必填):边集合的ID或者边集合名。 | |||
% 查询参数 | |||
% vertex(必填):起始顶点的ID。 | |||
% direction(可选):选择 in or out 为边缘方向。如果未设置,则返回任何边。 | |||
% 返回以vertex标识的顶点开始或结束的边数组 。 | |||
% 返回码 | |||
% 200:如果找到边缘集合并检索到边缘,则返回。 | |||
% 400:如果请求包含无效参数,则返回。 | |||
% 404:如果未找到边缘集合,则返回。 | |||
getEdges(PoolNameOrSocket, CollName, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/edges/", CollName/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). |
@ -0,0 +1,43 @@ | |||
-module(agEndPoints). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address:https://www.arangodb.com/docs/stable/http/endpoints.html | |||
% 端点的HTTP接口 | |||
% 该API /_api/endpoint已弃用。对于集群模式,/_api/cluster/endpoints可以找到所有当前的Coordinator端点(请参见下文)。 | |||
% | |||
% ArangoDB服务器可以在多个端点上侦听传入的请求。 | |||
% | |||
% 通常使用“ --server.endpoint”选项在ArangoDB的配置文件或命令行中指定端点。ArangoDB的默认终结点是tcp://127.0.0.1:8529或 tcp:// localhost:8529。 | |||
% | |||
% 请注意,所有端点管理操作只能通过默认数据库(_system)访问,而不能通过其他任何数据库访问。 | |||
% 获取有关所有协调器端点的信息固定链接 | |||
% 此API调用返回有关所有协调器终结点的信息(仅集群)。 | |||
% GET /_api/cluster/endpoints | |||
% 返回带有属性endpoints的对象,该对象包含一个对象数组,每个对象都有一个属性endpoint,其值是带有端点描述的字符串。集群中的每个协调器都有一个条目。此方法仅适用于群集模式下的协调器。如果发生错误,则将error属性设置为 true。 | |||
% HTTP 200 | |||
% error:布尔值标志,指示是否发生错误(在这种情况下为true) | |||
% code:HTTP状态码-200 | |||
% 端点:活动集群端点的列表。 | |||
% 端点:协调器的绑定,例如tcp://[::1]:8530 | |||
% 501: | |||
getClusterEndpoints(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/cluster/endpoints">>, [], undefined, true). | |||
% 所有端点的返回列表永久链接 | |||
% 此API调用返回所有端点(单个服务器)的列表。 | |||
% GET /_api/endpoint | |||
% 此路由不应再使用。从3.4.0版开始,它被视为已弃用。 | |||
% 返回服务器正在侦听的所有已配置端点的数组。 | |||
% 结果是一个JSON对象的JSON数组,每个对象均带有“ entrypoint”作为唯一属性,并且值是描述端点的字符串。 | |||
% 注意:仅在系统数据库中允许检索所有端点的数组。在任何其他数据库中调用此操作将使服务器返回错误。 | |||
% 返回码 | |||
% 200:可以成功确定端点数组时返回。 | |||
% 400:如果未在系统数据库中执行该操作,则返回。 | |||
% 405:如果使用了不受支持的HTTP方法,则服务器将以HTTP 405进行响应。 | |||
@ -0,0 +1,399 @@ | |||
-module(agFoxxServices). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address:https://www.arangodb.com/docs/stable/http/foxx.html | |||
% Foxx HTTP API | |||
% 这些路由允许操作数据库中安装的Foxx服务。 | |||
% | |||
% 有关Foxx及其JavaScript API的更多信息,请参见主要文档的Foxx章节。 | |||
% Foxx服务管理 | |||
% 这是用于管理Foxx服务的ArangoDB HTTP接口的简介。 | |||
% 列出已安装的服务 | |||
% GET /_api/foxx | |||
% 查询参数 | |||
% excludeSystem(可选):是否应从结果中排除系统服务。 | |||
% 获取当前数据库中安装的服务列表。 | |||
% 返回具有以下属性的对象列表: | |||
% mount:服务的安装路径 | |||
% development:如果服务以开发模式运行,则为true | |||
% legacy:如果服务以2.8旧版兼容模式运行,则为true | |||
% 提供:服务清单的提供值或空对象 | |||
% 此外,如果在清单上设置了以下对象,则该对象可能包含以下属性: | |||
% name:标识服务类型的字符串 | |||
% version:与semver兼容的版本字符串 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
getFoxxList(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/foxx">>, [], undefined). | |||
getFoxxList(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/foxx", QueryBinary/binary>>, [], undefined). | |||
% 服务说明 | |||
% 服务元数据 | |||
% GET /_api/foxx/service | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% 在给定的安装路径中获取服务的详细信息。 | |||
% 返回具有以下属性的对象: | |||
% mount:服务的安装路径 | |||
% path:服务的本地文件系统路径 | |||
% development:如果服务以开发模式运行,则为true | |||
% legacy:如果服务以2.8旧版兼容模式运行,则为true | |||
% manifest:服务的规范化JSON清单 | |||
% 此外,如果在清单上设置了以下对象,则该对象可能包含以下属性: | |||
% name:标识服务类型的字符串 | |||
% version:与semver兼容的版本字符串 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
% 400:如果安装路径未知,则返回。 | |||
getFoxxService(PoolNameOrSocket, Mount) -> | |||
Path = <<"/_api/foxx/service?mount=", (agMiscUtils:toBinary(Mount))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 安装新服务 | |||
% POST /_api/foxx | |||
% 查询参数 | |||
% mount(必需):应在其中安装服务的安装路径。 | |||
% 开发(可选):设置true为启用开发模式。 | |||
% 安装程序(可选):设置为false不运行服务的安装脚本。 | |||
% legacy(可选):设置为true以2.8旧版兼容性模式安装服务。 | |||
% 在给定的安装路径上安装给定的新服务。 | |||
% 请求主体可以是以下任何格式: | |||
% application/zip:包含服务的原始zip捆绑包 | |||
% application/javascript:独立的JavaScript文件 | |||
% application/json:服务定义为JSON | |||
% multipart/form-data:服务定义为多部分形式 | |||
% 服务定义是具有以下属性或字段的对象或表单: | |||
% configuration:描述配置值的JSON对象 | |||
% 依赖项:一个描述依赖项设置的JSON对象 | |||
% source:服务器文件系统上的标准URL或绝对路径 | |||
% 使用多部分数据时,源字段也可以是包含zip捆绑包或独立JavaScript文件的文件字段。 | |||
% 使用独立的JavaScript文件时,将执行给定的文件来定义我们服务的HTTP端点。main与在服务清单字段中定义的相同。 | |||
% 如果源是URL,则必须可以从服务器访问该URL。如果source是文件系统路径,则该路径将在服务器上解析。在任何情况下,路径或URL都应解析为zip包,JavaScript文件或(如果是文件系统路径)目录。 | |||
% 请注意,在具有多个协调器的群集中使用文件系统路径时,文件系统路径必须解析为每个协调器上的等效文件。 | |||
% 返回码 | |||
% 201:如果请求成功,则返回。 | |||
installFoxx(PoolNameOrSocket, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx", QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
% 卸载服务 | |||
% DELETE /_api/foxx/service | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% teardown (可选):设置为false不运行服务的拆卸脚本。 | |||
% 从数据库和文件系统中删除给定安装路径中的服务。 | |||
% 成功返回空响应。 | |||
% 返回码 | |||
% 204:如果请求成功,则返回。 | |||
uninstallFoxx(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/service", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
% 更换服务 | |||
% PUT /_api/foxx/service | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% teardown (可选):设置为false不运行旧服务的拆卸脚本。 | |||
% setup (可选):设置为false不运行新服务的安装脚本。 | |||
% legacy(可选):设置为true以2.8 Legacy兼容模式安装新服务。 | |||
% force (可选):设置为true强制安装服务,即使在给定的安装下未安装任何服务也是如此。 | |||
% 从数据库和文件系统中删除给定安装路径中的服务。然后在相同的安装路径上安装给定的新服务。 | |||
% 这与执行旧服务的卸载,然后安装新服务的安全性等效。在删除旧服务之前,将检查新服务的主文件和脚本文件(如果有)是否存在基本语法错误。 | |||
% 请求主体可以是以下任何格式: | |||
% application/zip:包含服务的原始zip捆绑包 | |||
% application/javascript:独立的JavaScript文件 | |||
% application/json:服务定义为JSON | |||
% multipart/form-data:服务定义为多部分形式 | |||
% 服务定义是具有以下属性或字段的对象或表单: | |||
% configuration:描述配置值的JSON对象 | |||
% 依赖项:一个描述依赖项设置的JSON对象 | |||
% source:服务器文件系统上的标准URL或绝对路径 | |||
% 使用多部分数据时,源字段也可以是包含zip捆绑包或独立JavaScript文件的文件字段。 | |||
% 使用独立的JavaScript文件时,将执行给定的文件来定义我们服务的HTTP端点。main与在服务清单字段中定义的相同。 | |||
% 如果源是URL,则必须可以从服务器访问该URL。如果source是文件系统路径,则该路径将在服务器上解析。在任何情况下,路径或URL都应解析为zip包,JavaScript文件或(如果是文件系统路径)目录。 | |||
% 请注意,在具有多个协调器的群集中使用文件系统路径时,文件系统路径必须解析为每个协调器上的等效文件。 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
replaceFoxx(PoolNameOrSocket, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/service", QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
% 升级服务 | |||
% PATCH /_api/foxx/service | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% teardown(可选):设置为true运行旧服务的拆卸脚本。 | |||
% setup(可选):设置为false不运行新服务的安装脚本。 | |||
% legacy(可选):设置为true以2.8 Legacy兼容模式安装新服务。 | |||
% force(可选):设置为true强制安装服务,即使在给定的安装下未安装任何服务也是如此。 | |||
% 将给定的新服务安装在给定的安装路径上当前安装的服务之上。仅建议在同一服务的不同版本之间进行切换时使用。 | |||
% 与替换服务不同,升级服务会保留旧服务的配置和依赖关系(如果有),因此仅应用于将现有服务迁移到新服务或等效服务。 | |||
% 请求主体可以是以下任何格式: | |||
% application/zip:包含服务的原始zip捆绑包 | |||
% application/javascript:独立的JavaScript文件 | |||
% application/json:服务定义为JSON | |||
% multipart/form-data:服务定义为多部分形式 | |||
% 服务定义是具有以下属性或字段的对象或表单: | |||
% configuration:描述配置值的JSON对象 | |||
% 依赖项:一个描述依赖项设置的JSON对象 | |||
% source:服务器文件系统上的标准URL或绝对路径 | |||
% 使用多部分数据时,源字段也可以是包含zip捆绑包或独立JavaScript文件的文件字段。 | |||
% 使用独立的JavaScript文件时,将执行给定的文件来定义我们服务的HTTP端点。main与在服务清单字段中定义的相同。 | |||
% 如果源是URL,则必须可以从服务器访问该URL。如果source是文件系统路径,则该路径将在服务器上解析。在任何情况下,路径或URL都应解析为zip包,JavaScript文件或(如果是文件系统路径)目录。 | |||
% 请注意,在具有多个协调器的群集中使用文件系统路径时,文件系统路径必须解析为每个协调器上的等效文件。 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
upgradeFoxx(PoolNameOrSocket, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/service", QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr). | |||
% Foxx服务配置/依赖关系 | |||
% 这是用于管理Foxx服务配置和依赖关系的ArangoDB HTTP接口的简介。 | |||
% | |||
% 获取配置选项 | |||
% GET /_api/foxx/configuration | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% 在给定的安装路径中获取服务的当前配置。 | |||
% 返回一个对象,该对象将配置选项名称映射到其定义,包括易于理解的标题和当前值(如果有)。 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
getFoxxConfig(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/configuration", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 更新配置选项 | |||
% PATCH /_api/foxx/configuration | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% 请求正文(json) | |||
% JSON对象映射配置选项名称为其新值。任何省略的选项将被忽略。 | |||
% 替换给定服务的配置。 | |||
% 返回一个将所有配置选项名称映射到其新值的对象。 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
updateFoxxConfig(PoolNameOrSocket, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/configuration", QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr). | |||
% 替换配置选项 | |||
% PUT /_api/foxx/configuration | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% 请求正文(json) | |||
% JSON对象映射配置选项名称为其新值。任何省略的选项将重置为默认值或标记为未配置。 | |||
% 完全替换给定服务的配置。 | |||
% 返回一个将所有配置选项名称映射到其新值的对象。 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
replaceFoxxConfig(PoolNameOrSocket, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/configuration", QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
% 获取依赖项选项 | |||
% GET /_api/foxx/dependencies | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% 在给定的安装路径中获取服务的当前依赖关系。 | |||
% 返回一个对象,该对象将依赖项名称映射到它们的定义,包括易于理解的标题和当前的安装路径(如果有)。 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
getFoxxDependencies(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/dependencies", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 更新依赖项选项 | |||
% PATCH /_api/foxx/dependencies | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% 请求正文(json) | |||
% 映射依赖项名称到其新安装路径的JSON对象。任何省略的依赖项将被忽略。 | |||
% 替换给定服务的依赖项。 | |||
% 返回一个将所有依赖项名称映射到其新安装路径的对象。 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
updateFoxxDependencies(PoolNameOrSocket, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/dependencies", QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr). | |||
% 替换依赖项选项 | |||
% PUT /_api/foxx/dependencies | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% 请求正文(json) | |||
% 映射依赖项名称到其新安装路径的JSON对象。任何省略的依赖项将被禁用。 | |||
% 完全替换给定服务的依赖项。 | |||
% 返回一个将所有依赖项名称映射到其新安装路径的对象。 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
replaceFoxxDependencies(PoolNameOrSocket, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/dependencies", QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
% Foxx服务杂项 | |||
% | |||
% 列出服务脚本 | |||
% GET /_api/foxx/scripts | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% 获取服务定义的脚本列表。 | |||
% 返回一个将原始脚本名称映射到人类友好名称的对象。 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
getFoxxScripts(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/scripts", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 运行服务脚本 | |||
% POST /_api/foxx/scripts/{name} | |||
% 路径参数 | |||
% 名称(必填):要运行的脚本的名称。 | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% 请求正文(json) | |||
% 一个任意的JSON值,将被解析并作为第一个参数传递给脚本。 | |||
% 在给定的安装路径上为服务运行给定的脚本。 | |||
% 返回脚本的导出(如果有)。 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
runFoxxScripts(PoolNameOrSocket, ScriptName, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/scripts/", ScriptName/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
% 运行服务测试 | |||
% POST /_api/foxx/tests | |||
% 查询参数 | |||
% mount (必需):已安装服务的安装路径。 | |||
% reporter (可选):测试记者使用。 | |||
% idiomatic (可选):与报告器使用匹配的格式,而与Accept标头无关。 | |||
% filter(可选):仅运行全名(包括完整的测试套件和测试用例)与该字符串匹配的测试。 | |||
% 在给定的安装路径上运行该服务的测试并返回结果。 | |||
% 支持的测试报告者为: | |||
% default:测试用例的简单列表 | |||
% suite:嵌套在套件中的测试用例的对象 | |||
% stream:测试结果的原始流 | |||
% xunit:与XUnit / JUnit兼容的结构 | |||
% tap:原始TAP兼容流 | |||
% 该接受请求报头可以被用于进一步控制的响应格式: | |||
% 使用流报告器时,application/x-ldjson将导致响应主体被格式化为以换行符分隔的JSON流。 | |||
% 使用点击报告程序text/plain或时,text/*将导致响应正文被格式化为纯文本TAP报告。 | |||
% 使用xunit报告程序时,application/xml或text/xml将导致响应正文被格式化为XML而不是JSONML。 | |||
% 否则,响应主体将被格式化为非prettyprinted JSON。 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
runFoxxTest(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/tests", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], undefined). | |||
% 启用开发模式 | |||
% POST /_api/foxx/development | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% 将服务置于开发模式。 | |||
% 当服务在开发模式下运行时,每次服务处理请求时,将从文件系统重新加载服务,并且将重新执行其设置脚本(如果有)。 | |||
% 在具有多个协调器的集群中运行ArangoDB时,请注意,一个协调器上文件系统的更改不会在其他协调器上反映出来。这意味着只要任何服务都在开发模式下运行,就应该将协调器视为不一致。 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
enableFoxxDevelopment(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/development", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], undefined). | |||
% 禁用开发模式 | |||
% DELETE /_api/foxx/development | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% 将给定安装路径上的服务置于生产模式。 | |||
% 在具有多个协调器的集群中运行ArangoDB时,这会将所有其他协调器上的服务替换为该协调器上的版本。 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
disableFoxxDevelopment(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/development", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
% 服务自述文件 | |||
% GET /_api/foxx/readme | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% 获取服务的README或README.md文件的内容(如果有)。 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
% 204:如果未找到自述文件,则返回。 | |||
getFoxxReadme(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/readme", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 招摇说明 | |||
% GET /_api/foxx/swagger | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% 在给定的安装路径中获取服务的Swagger API描述。 | |||
% 响应主体将是服务API的与OpenAPI 2.0兼容的JSON描述。 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
getFoxxSwagger(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/swagger", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 下载服务包 | |||
% POST /_api/foxx/download | |||
% 查询参数 | |||
% mount(必需):已安装服务的安装路径。 | |||
% 下载服务目录的zip捆绑包。 | |||
% 启用开发模式后,将始终创建一个新捆绑包。 | |||
% 否则,捆绑软件将代表该ArangoDB实例上安装的服务的版本。 | |||
% 返回码 | |||
% 200:如果请求成功,则返回。 | |||
% 400:如果安装路径未知,则返回。 | |||
downloadFoxxBundle(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/download", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], undefined). | |||
% 提交本地服务状态 | |||
% POST /_api/foxx/commit | |||
% 查询参数 | |||
% replace(可选):覆盖数据库中的现有服务文件,即使它们已经存在也是如此。 | |||
% 将协调器的本地服务状态提交到数据库。 | |||
% 这可用于解决由于数据丢失而无法自动修复的协调器之间的服务冲突。 | |||
% 返回码 | |||
% 204:如果请求成功,则返回。 | |||
commitFoxxState(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/foxx/commit", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], undefined). |
@ -0,0 +1,961 @@ | |||
-module(agGeneralGraphs). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
%% doc_address:https://www.arangodb.com/docs/stable/http/gharial.html | |||
% 通用图 | |||
% 本章介绍了多集合图形模块的REST接口。它允许您定义分布在多个边缘和文档集合中的图形。无需在查询中包括引用的集合,此模块将为您处理。 | |||
% 管理图表 | |||
% 列出图形模块已知的所有图形。 | |||
% GET /_api/gharial | |||
% 列出此数据库中存储的所有图形。 | |||
% 如果模块可用并且可以列出图形,则返回HTTP 200。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% 图表: | |||
% graph:有关新创建的图的信息 | |||
% name:图形名称 | |||
% edgeDefinitions:图形关系的定义数组。每个都有以下类型: | |||
% orphanCollections:其他顶点集合的数组。这些集合中的文档在此图中没有边。 | |||
% numberOfShards:为图中的每个新集合创建的分片数。 | |||
% _id:此图的内部ID值。 | |||
% _rev:此图的修订版。可用于确保不覆盖对该图的并发修改。 | |||
% ReplicationFactor:图形中每个新集合使用的复制因子。 | |||
% isSmart:标记图形是否为SmartGraph(仅限企业版)。 | |||
% smartGraphAttribute:智能图例中的分片属性名称(仅企业版) | |||
graphList(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/gharial">>, [], undefined). | |||
% 创建一个图 | |||
% 在图形模块中创建一个新图形。 | |||
% POST /_api/gharial | |||
% 查询参数 | |||
% waitForSync(可选):定义请求是否应该等待,直到所有内容都同步到光盘。将更改成功响应代码。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% name:图形名称。 | |||
% edgeDefinitions:图形关系的定义数组。每个都有以下类型: | |||
% collection : 边集合名 | |||
% from: from 顶点集合名数组、 | |||
% to : to 顶点集合名数组 | |||
% orphanCollections:其他顶点集合的数组。这些集合中的文档在此图中没有边。 | |||
% isSmart:定义创建的图形是否应该是智能的。这仅在企业版中有效。 | |||
% options:一个JSON对象,用于定义用于在此图中创建集合的选项。它可以包含以下属性: | |||
% smartGraphAttribute:仅在企业版中有效,如果isSmart为true,则为必需。用于聪明地分割图的顶点的属性名称。此SmartGraph中的每个顶点都必须具有此属性。以后无法修改。 | |||
% numberOfShards:此图中每个集合使用的分片数。以后无法修改。 | |||
% replicationFactor:最初为此图创建集合时使用的复制因子。可以设置为"satellite"创建SatelliteGraph,将忽略 numberOfShards,minReplicationFactor和writeConcern。 | |||
% writeConcern:对图中的新集合进行关注。它确定在不同的DB服务器上同步每个分片需要多少个副本。如果集群中的副本数量很少,那么分片将拒绝写入。但是,具有足够最新副本的分片写入将同时成功。writeConcern的值 不能大于ReplicationFactor。(仅集群) | |||
% 图的创建需要图的名称和其边缘的定义。 另请参见边缘定义。 | |||
% 如果可以创建图形并且为_graphs集合启用了waitForSync 或在请求中指定了图形,则返回HTTP 201。响应主体包含已存储的图形配置。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% graph:有关新创建的图的信息。 | |||
% 如果可以创建图形并且为该_graphs集合禁用了waitForSync,并且未在请求中给出,则返回HTTP 202。响应主体包含已存储的图形配置。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% graph:有关新创建的图的信息。 | |||
% HTTP 400如果请求的格式错误,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 403如果您的用户权限不足,则返回。为了创建图,您至少需要具有以下特权: | |||
% Administrate 访问数据库。 | |||
% Read Only | |||
% 访问此图中使用的每个集合。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 409如果存储图形时发生冲突,则返回。如果已经存储了具有该名称的图形,或者存在一个边缘定义具有相同的边缘集合但在任何其他图形中使用了不同的签名,则可能会发生这种情况 。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
newGraph(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/gharial">>, [], BodyStr). | |||
newGraph(PoolNameOrSocket, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial", QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
% 获取图表 | |||
% GET /_api/gharial/{graph} | |||
% 路径参数 | |||
% 图(必填):图的名称。 | |||
% 选择给定图的信息。将返回边缘定义以及孤立集合。或如果图形不存在,则返回404。 | |||
% HTTP 200如果可以找到该图,则返回该图。结果将具有以下格式: | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% graph:有关新创建的图的信息 | |||
% HTTP 404如果找不到具有该名称的图形,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
getGraph(PoolNameOrSocket, GraphName) -> | |||
Path = <<"/_api/gharial/", GraphName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 删除现有图 | |||
% DELETE /_api/gharial/{graph} | |||
% 路径参数 | |||
% 图(必填):图的名称。 | |||
% 查询参数 | |||
% dropCollections(可选):也删除该图的集合。只有在其他图形中未使用集合时,集合才会被删除。 | |||
% 按名称删除现有图形对象。(可选)也可以删除其他图未使用的所有集合。 | |||
% 201:如果可以删除该图并为该_graphs集合启用了waitForSync ,或者在请求中给出了该图,则返回该值。 | |||
% 202:如果可以删除该图并且为该_graphs集合禁用了waitForSync,并且未在请求中给出,则返回该值。 | |||
% HTTP 403如果您的用户权限不足,则返回。为了删除图,您至少需要具有以下特权: | |||
% Administrate 访问数据库。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 404如果找不到具有该名称的图形,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
delGraph(PoolNameOrSocket, GraphName) -> | |||
Path = <<"/_api/gharial/", GraphName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
delGraph(PoolNameOrSocket, GraphName, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
% 列出此图中使用的所有顶点集合。 | |||
% GET /_api/gharial/{graph}/vertex | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% 列出此图中的所有顶点集合。 | |||
% 如果可以列出集合,则返回HTTP 200。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% collections:此图中所有顶点集合的列表。在edgeDefinitions和孤儿中包括集合。 | |||
% HTTP 404如果找不到具有该名称的图形,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
vertexCollList(PoolNameOrSocket, GraphName) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 向图中添加一个额外的顶点集合。 | |||
% POST /_api/gharial/{graph}/vertex | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% 将顶点集合添加到图形的孤立集合中。如果该集合不存在,则将创建它。 | |||
% 如果可以创建集合并为该_graphs集合启用waitForSync 或在请求中指定了HTTP,则返回HTTP 201。响应主体包含已存储的图形配置。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% graph:有关修改后的图的信息。 | |||
% 如果可以创建集合并且为该_graphs集合禁用了waitForSync 或在请求中指定了HTTP,则返回HTTP 202。响应主体包含已存储的图形配置。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% graph:有关新创建的图的信息 | |||
% HTTP 400如果请求的格式无效,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 403如果您的用户权限不足,则返回。为了修改图,您至少需要具有以下特权: | |||
% Administrate 访问数据库。 | |||
% Read Only 访问此图中使用的每个集合。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 404如果找不到具有该名称的图形,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
%% MapData = #{"collection" => "otherVertices"} | |||
addVertexColl(PoolNameOrSocket, GraphName, MapData) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex">>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
% 从图形中删除额外顶点集合。 | |||
% DELETE /_api/gharial/{graph}/vertex/{collection} | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% collection(必填):顶点集合的名称。 | |||
% 查询参数 | |||
% dropCollection(可选):也删除集合。只有在其他图形中未使用集合时,集合才会被删除。 | |||
% 如果未在其他任何图中使用顶点集合,则从图中删除顶点集合,并有选择地删除该集合。如果边缘定义中不再使用这些顶点集合,则只能删除不再属于这些定义的顶点集合。 | |||
% HTTP 200如果成功从图形中删除了顶点集合并且waitForSync为true,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% graph:有关新创建的图的信息 | |||
% HTTP 202如果请求成功,但waitForSync为false,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% graph:有关新创建的图的信息 | |||
% HTTP 400如果顶点集合仍在边缘定义中使用,则返回。在这种情况下,它仍不能从图形中删除,必须先从边定义中删除。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 403如果您的用户权限不足,则返回。为了删除顶点,您至少需要具有以下特权: | |||
% Administrate 访问数据库。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 404如果找不到具有该名称的图形,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
delVertexColl(PoolNameOrSocket, GraphName, CollName) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
delVertexColl(PoolNameOrSocket, GraphName, CollName, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
% 列出所有边缘定义 | |||
% GET /_api/gharial/{graph}/edge | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% 列出此图中的所有边集合。 | |||
% 如果可以列出边缘定义,则返回HTTP 200。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% collections:此图中所有顶点集合的列表。在edgeDefinitions和孤儿中包括集合。 | |||
% HTTP 404如果找不到具有该名称的图形,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
edgeDefList(PoolNameOrSocket, GraphName) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 向图添加新的边定义 | |||
% POST /_api/gharial/{graph}/edge | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% collection:要使用的边缘集合的名称。 | |||
% from:一个或多个可以包含源顶点的顶点集合。 | |||
% to:一个或多个可以包含目标顶点的顶点集合。 | |||
% 向图形添加其他边定义。 | |||
% 这个边缘清晰度必须包含一个集合,并且每个的阵列从和到顶点集合。仅当未在任何其他图形中使用该定义或将其与完全相同的定义一起使用时,才可以添加边定义。在一个图中不可能存储从“ v1”到“ v2”的定义“ e”,而在另一张图中存储从“ v2”到“ v1”的定义“ e”。 | |||
% HTTP 201如果可以成功添加定义并且为_graphs集合启用了waitForSync,则返回。响应主体包含已存储的图形配置。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% graph:有关修改后的图的信息。 | |||
% HTTP 202如果可以成功添加定义并且对该_graphs集合禁用了waitForSync,则返回。响应主体包含已存储的图形配置。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% graph:有关修改后的图的信息。 | |||
% HTTP 400如果无法添加定义,则返回。这可能是因为其格式错误,或者是在其他具有不同签名的图形中使用了该定义。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 403如果您的用户权限不足,则返回。为了修改图,您至少需要具有以下特权: | |||
% Administrate 访问数据库。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 404如果找不到具有该名称的图形,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
addEdgeDef(PoolNameOrSocket, GraphName, MapData) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge">>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
% 替换现有的边缘定义 | |||
% PUT /_api/gharial/{graph}/edge/{definition}#definition | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% definition(必填):定义中使用的边集合的名称。 | |||
% | |||
% 查询参数 | |||
% waitForSync(可选):定义请求是否应等待直到同步到磁盘。 | |||
% dropCollections(可选):也删除集合。只有在其他图形中未使用集合时,集合才会被删除。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% collection:要使用的边缘集合的名称。 | |||
% from:一个或多个可以包含源顶点的顶点集合。 | |||
% to:一个或多个可以包含目标顶点的顶点集合。 | |||
% 更改一个特定的边定义。这将修改数据库中所有已知图中所有出现的该定义。 | |||
% HTTP 201如果请求成功并且waitForSync为true,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% graph:有关修改后的图的信息。 | |||
% HTTP 202如果请求成功,但waitForSync为false,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% graph:有关修改后的图的信息。 | |||
% HTTP 400如果在图形中未找到具有此名称的边定义,或者新定义的格式不正确且无法使用,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 403如果您的用户权限不足,则返回。为了删除顶点,您至少需要具有以下特权: | |||
% Administrate 访问数据库。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 404如果找不到具有该名称的图形,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% EdgeDefName 名字 要在定义列表中存在 MapData中的collection 也要存在 只是替换 from to 中的集合 | |||
replaceEdgeDef(PoolNameOrSocket, GraphName, EdgeDefName, MapData) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", EdgeDefName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
replaceEdgeDef(PoolNameOrSocket, GraphName, EdgeDefName, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", EdgeDefName/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
% 从图形中删除边缘定义 | |||
% DELETE /_api/gharial/{graph}/edge/{definition}#definition | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% definition(必填):定义中使用的边集合的名称。 | |||
% 查询参数 | |||
% waitForSync(可选):定义请求是否应等待直到同步到磁盘。 | |||
% dropCollections(可选):也删除集合。只有在其他图形中未使用集合时,集合才会被删除。 | |||
% 从图形中删除一个边缘定义。这只会删除边缘集合,顶点集合保持不变,并且仍可以在查询中使用。 | |||
% HTTP 201如果可以从图形中删除边缘定义并且waitForSync为true,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% graph:有关修改后的图的信息。 | |||
% HTTP 202如果可以从图形中删除边缘定义并且waitForSync为false,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% graph:有关修改后的图的信息。 | |||
% HTTP 403如果您的用户权限不足,则返回。为了删除顶点,您至少需要具有以下特权: | |||
% Administrate 访问数据库。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 404如果找不到该名称的图形,或者在图形中找不到此名称的边定义,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
delEdgeDef(PoolNameOrSocket, GraphName, EdgeDefName) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", EdgeDefName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
delEdgeDef(PoolNameOrSocket, GraphName, EdgeDefName, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", EdgeDefName/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
% 处理顶点 | |||
% 创建一个新顶点 | |||
% POST /_api/gharial/{graph}/vertex/{collection} | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% collection(必填):顶点应插入的顶点集合的名称。 | |||
% 查询参数 | |||
% waitForSync(可选):定义请求是否应等待直到同步到磁盘。 | |||
% returnNew(可选):定义响应是否应包含文档的完整新版本。 | |||
% 请求正文(对象) | |||
% 主体必须是要存储的JSON对象。 | |||
% 将顶点添加到给定的集合。 | |||
% HTTP 201如果可以添加顶点并且waitForSync为true,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% vertex:顶点的内部属性。 | |||
% new:完整的新编写的顶点文档。包括请求主体中的所有书面属性以及ArangoDB生成的所有内部属性。仅在returnNew为true时存在。 | |||
% HTTP 202如果请求成功,但waitForSync为false,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% vertex:存储顶点时生成的内部属性。不包括请求正文中给定的任何属性。 | |||
% new:完整的新编写的顶点文档。包括请求主体中的所有书面属性以及ArangoDB生成的所有内部属性。仅在returnNew为true时存在。 | |||
% HTTP 403如果您的用户权限不足,则返回。为了将顶点插入到图中,您至少需要具有以下特权: | |||
% Read Only 访问数据库。 | |||
% Write 访问给定的集合。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 404如果找不到具有该名称的图形,则返回。或者,如果找到了图,但是此集合不是图的一部分。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
newVertex(PoolNameOrSocket, GraphName, CollName, MapData) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
newVertex(PoolNameOrSocket, GraphName, CollName, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
% 获取现有顶点 | |||
% GET /_api/gharial/{graph}/vertex/{collection}/{vertex} | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% collection(必需):顶点所属的顶点集合的名称。 | |||
% vertex(必填):顶点的_key属性。 | |||
% 查询参数 | |||
% rev(可选):必须包含修订。如果设置了此选项,则仅在具有此修订版本的情况下才返回文档。另请参见if-match标头作为替代方法。 | |||
% 标头参数 | |||
% if-match(可选):如果给出了“ If-Match”标头,则它必须恰好包含一个Etag。如果文档的版本与给定的Etag相同,则返回文档。否则,返回HTTP 412。或者,您可以在查询参数rev中提供Etag 。 | |||
% if-none-match(可选):如果给出了“ If-None-Match”标头,则它必须恰好包含一个Etag。仅当文档的版本与给定的Etag不同时,才返回文档。否则,返回HTTP 304。 | |||
% 从给定的集合中获取一个顶点。 | |||
% HTTP 200如果可以找到顶点,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% vertex:完整的顶点。 | |||
% HTTP 304如果给出了if-none-match标头,并且当前存储的顶点仍具有此修订值,则返回。因此,在上一次调用者获取顶点之间没有更新。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 403如果您的用户权限不足,则返回。为了更新图中的顶点,您至少需要具有以下特权: | |||
% Read Only 访问数据库。 | |||
% Read Only 访问给定的集合。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% 在以下情况下返回HTTP 404: | |||
% 找不到具有该名称的图。 | |||
% 该集合不属于图形。 | |||
% 顶点不存在。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 412如果给出了if-match标头,则返回,但是存储的文档版本不同。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
getVertex(PoolNameOrSocket, GraphName, CollName, VertexKey) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
getVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
getVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, QueryPars, Headers) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, Headers, undefined). | |||
% 更新现有顶点 | |||
% PATCH /_api/gharial/{graph}/vertex/{collection}/{vertex} | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% collection(必需):顶点所属的顶点集合的名称。 | |||
% vertex(必填):顶点的_key属性。 | |||
% 查询参数 | |||
% waitForSync(可选):定义请求是否应等待直到同步到磁盘。 | |||
% keepNull(可选):定义是否应存储设置为null的值。默认情况下(true),给定的documents(s)属性将设置为null。如果此参数为false,则将属性从文档中删除。 | |||
% returnOld(可选):定义是否应在响应对象内返回已删除文档的表示。 | |||
% returnNew(可选):定义是否应在响应对象中返回新文档的表示形式。 | |||
% 标头参数 | |||
% if-match(可选):如果给出了“ If-Match”标头,则它必须恰好包含一个Etag。如果文档的版本与给定的Etag相同,则文档将被更新。否则,返回HTTP 412。或者,您可以在URL的属性rev中提供Etag。 | |||
% 请求正文(对象) | |||
% 主体必须包含一个JSON对象,该对象完全包含应被覆盖的属性,所有其他属性保持不变。 | |||
% 更新集合中特定顶点的数据。 | |||
% HTTP 200如果可以更新顶点且waitForSync为true,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% vertex:顶点的内部属性。 | |||
% new:完整的新编写的顶点文档。包括请求主体中的所有书面属性以及ArangoDB生成的所有内部属性。仅在returnNew为true时存在。 | |||
% old:完整的覆盖顶点文档。包括此操作之前存储的所有属性。仅在returnOld为true时存在。 | |||
% HTTP 202如果请求成功,则返回,并且waitForSync为false。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% vertex:顶点的内部属性。 | |||
% new:完整的新编写的顶点文档。包括请求主体中的所有书面属性以及ArangoDB生成的所有内部属性。仅在returnNew为true时存在。 | |||
% old:完整的覆盖顶点文档。包括此操作之前存储的所有属性。仅在returnOld为true时存在。 | |||
% HTTP 403如果您的用户权限不足,则返回。为了更新图中的顶点,您至少需要具有以下特权: | |||
% Read Only 访问数据库。 | |||
% Write 访问给定的集合。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% 在以下情况下返回HTTP 404: | |||
% 找不到具有该名称的图。 | |||
% 该集合不属于图形。 | |||
% 要更新的顶点不存在。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 412如果给出了if-match标头,则返回,但是存储的文档版本不同。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
updateVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, MapData) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr). | |||
updateVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr). | |||
updateVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, MapData, QueryPars, Headers) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, Headers, BodyStr). | |||
% 替换现有的顶点 | |||
% PUT /_api/gharial/{graph}/vertex/{collection}/{vertex} | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% collection(必需):顶点所属的顶点集合的名称。 | |||
% vertex(必填):顶点的_key属性。 | |||
% 查询参数 | |||
% waitForSync(可选):定义请求是否应等待直到同步到磁盘。 | |||
% keepNull(可选):定义是否应存储设置为null的值。默认情况下,密钥不会从文档中删除。 | |||
% returnOld(可选):定义是否应在响应对象内返回已删除文档的表示。 | |||
% returnNew(可选):定义是否应在响应对象中返回新文档的表示形式。 | |||
% 标头参数 | |||
% if-match(可选):如果给出了“ If-Match”标头,则它必须恰好包含一个Etag。如果文档的版本与给定的Etag相同,则文档将被更新。否则,返回HTTP 412。或者,您可以在URL的属性rev中提供Etag。 | |||
% 请求正文(对象) | |||
% 主体必须是要存储的JSON对象。 | |||
% 替换集合中顶点的数据。 | |||
% HTTP 200如果可以替换顶点且waitForSync为true,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% vertex:顶点的内部属性。 | |||
% new:完整的新编写的顶点文档。包括请求主体中的所有书面属性以及ArangoDB生成的所有内部属性。仅在returnNew为true时存在。 | |||
% old:完整的覆盖顶点文档。包括此操作之前存储的所有属性。仅在returnOld为true时存在。 | |||
% HTTP 202如果可以替换顶点且waitForSync为false,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% vertex:顶点的内部属性。 | |||
% new:完整的新编写的顶点文档。包括请求主体中的所有书面属性以及ArangoDB生成的所有内部属性。仅在returnNew为true时存在。 | |||
% old:完整的覆盖顶点文档。包括此操作之前存储的所有属性。仅在returnOld为true时存在。 | |||
% HTTP 403如果您的用户权限不足,则返回。为了替换图中的顶点,您至少需要具有以下特权: | |||
% Read Only 访问数据库。 | |||
% Write 访问给定的集合。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% 在以下情况下返回HTTP 404: | |||
% 找不到具有该名称的图。 | |||
% 该集合不属于图形。 | |||
% 要替换的顶点不存在。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 412如果给出了 if -match标头,则返回,但是存储的文档版本不同。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
replaceVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, MapData) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
replaceVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
replaceVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, MapData, QueryPars, Headers) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, Headers, BodyStr). | |||
% 从图中删除顶点 | |||
% DELETE /_api/gharial/{graph}/vertex/{collection}/{vertex} | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% collection(必需):顶点所属的顶点集合的名称。 | |||
% vertex(必填):顶点的_key属性。 | |||
% 查询参数 | |||
% waitForSync(可选):定义请求是否应等待直到同步到磁盘。 | |||
% returnOld(可选):定义是否应在响应对象内返回已删除文档的表示。 | |||
% 标头参数 | |||
% if-match(可选):如果给出了“ If-Match”标头,则它必须恰好包含一个Etag。如果文档的版本与给定的Etag相同,则文档将被更新。否则,返回HTTP 412。或者,您可以在URL的属性rev中提供Etag。 | |||
% 从集合中删除一个顶点。 | |||
% HTTP 200如果可以删除顶点,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% removed:设置为true,如果删除成功。 | |||
% old:完整的删除的顶点文档。包括此操作之前存储的所有属性。仅在returnOld为true时存在。 | |||
% HTTP 202如果请求成功,但waitForSync为false,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% removed:设置为true,如果删除成功。 | |||
% old:完整的删除的顶点文档。包括此操作之前存储的所有属性。仅在returnOld为true时存在。 | |||
%HTTP 403如果您的用户权限不足,则返回。为了删除图中的顶点,您至少需要具有以下特权: | |||
% Read Only 访问数据库。 | |||
% Write 访问给定的集合。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% 在以下情况下返回HTTP 404: | |||
% 找不到具有该名称的图。 | |||
% 该集合不属于图形。 | |||
% 要删除的顶点不存在。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 412如果给出了if-match标头,则返回,但是存储的文档版本不同。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
delVertex(PoolNameOrSocket, GraphName, CollName, VertexKey) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
delVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
delVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, QueryPars, Headers) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, Headers, undefined). | |||
% 处理边缘 | |||
% 在现有图形中创建边 | |||
% POST /_api/gharial/{graph}/edge/{collection} | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% collection(必填):边缘所属的边缘集合的名称。 | |||
% 查询参数 | |||
% waitForSync(可选):定义请求是否应等待直到同步到磁盘。 | |||
% returnNew(可选):定义响应是否应包含文档的完整新版本。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% _from:此边的源顶点。必须在使用的边定义内有效。 | |||
% _to:此边的目标顶点。必须在使用的边定义内有效。 | |||
% 在集合中创建新边缘。在主体内,边缘必须包含一个_from和_to值,以引用图中的有效顶点。此外,边缘在所使用的边缘集合的定义中必须有效 。 | |||
% HTTP 201如果可以创建边缘并且waitForSync为true,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% edge:边缘的内部属性。 | |||
% new:完整的新编写的边缘文档。包括请求主体中的所有书面属性以及ArangoDB生成的所有内部属性。仅在returnNew为true时存在。 | |||
% HTTP 202如果请求成功,但waitForSync为false,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% edge:边缘的内部属性。 | |||
% new:完整的新编写的边缘文档。包括请求主体中的所有书面属性以及ArangoDB生成的所有内部属性。仅在returnNew为true时存在。 | |||
% HTTP 400如果输入文档无效,则返回。例如,这可以是如果_from或_to丢失。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 403如果您的用户权限不足,则返回。为了将边插入图形中,您至少需要具有以下特权: | |||
% Read Only 访问数据库。 | |||
% Write 访问给定的集合。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% 在以下任何一种情况下返回的HTTP 404: | |||
% 找不到具有该名称的图形。 | |||
% 该边缘集合不是图形的一部分。 | |||
% 无论是_from或_to顶点不存在。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
newEdge(PoolNameOrSocket, GraphName, CollName, MapData) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
newEdge(PoolNameOrSocket, GraphName, CollName, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
% 获得边 | |||
% GET /_api/gharial/{graph}/edge/{collection}/{edge} | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% collection(必填):边缘所属的边缘集合的名称。 | |||
% edge(必填):边缘的_key属性。 | |||
% 查询参数 | |||
% rev(可选):必须包含修订。如果设置了此选项,则仅在具有此修订版本的情况下才返回文档。另请参见if-match标头作为替代方法。 | |||
% 标头参数 | |||
% if-match(可选):如果给出了“ If-Match”标头,则它必须恰好包含一个Etag。如果文档的版本与给定的Etag相同,则返回文档。否则,返回HTTP 412。或者,您可以在URL的属性rev中提供Etag。 | |||
% if-none-match(可选):如果给出了“ If-None-Match”标头,则它必须恰好包含一个Etag。仅当文档的版本与给定的Etag不同时,才返回文档。否则,返回HTTP 304。 | |||
% 从给定的集合中获取一条边。 | |||
% HTTP 200如果可以找到边缘,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% edge:完整的边缘。 | |||
% HTTP 304如果给出了if-none-match标头,并且当前存储的边缘仍具有此修订值,则返回。因此,调用者上一次获取边缘之间没有更新。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 403如果您的用户权限不足,则返回。为了更新图中的顶点,您至少需要具有以下特权: | |||
% Read Only 访问数据库。 | |||
% Read Only 访问给定的集合。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% 在以下情况下返回HTTP 404: | |||
% 找不到具有该名称的图。 | |||
% 该集合不属于图形。 | |||
% 边不存在。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 412如果给出了if-match标头,则返回,但是存储的文档版本不同。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
getEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
getEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
getEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, QueryPars, Headers) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, Headers, undefined). | |||
% 修改现有边 | |||
% PATCH /_api/gharial/{graph}/edge/{collection}/{edge} | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% collection(必填):边缘所属的边缘集合的名称。 | |||
% edge(必填):顶点的_key属性。 | |||
% 查询参数 | |||
% waitForSync(可选):定义请求是否应等待直到同步到磁盘。 | |||
% keepNull(可选):定义是否应存储设置为null的值。默认情况下(true),给定的documents(s)属性将设置为null。如果此参数为false,则将从文档中删除该属性。 | |||
% returnOld(可选):定义是否应在响应对象内返回已删除文档的表示。 | |||
% returnNew(可选):定义是否应在响应对象中返回新文档的表示形式。 | |||
% 标头参数 | |||
% if-match(可选):如果给出了“ If-Match”标头,则它必须恰好包含一个Etag。如果文档的版本与给定的Etag相同,则文档将被更新。否则,返回HTTP 412。或者,您可以在URL的属性rev中提供Etag。 | |||
% 请求正文(对象) | |||
% 主体必须包含一个JSON对象,该对象完全包含应被覆盖的属性,所有其他属性保持不变。 | |||
% 更新集合中特定边的数据。 | |||
% HTTP 200如果可以更新边缘,并且waitForSync为false,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% edge:边缘的内部属性。 | |||
% new:完整的新编写的边缘文档。包括请求主体中的所有书面属性以及ArangoDB生成的所有内部属性。仅在returnNew为true时存在。 | |||
% old:完整的覆盖边缘文档。包括此操作之前存储的所有属性。仅在returnOld为true时存在。 | |||
% HTTP 202如果请求成功,但waitForSync为false,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% edge:边缘的内部属性。 | |||
% new:完整的新编写的边缘文档。包括请求主体中的所有书面属性以及ArangoDB生成的所有内部属性。仅在returnNew为true时存在。 | |||
% old:完整的覆盖边缘文档。包括此操作之前存储的所有属性。仅在returnOld为true时存在。 | |||
% HTTP 403如果您的用户权限不足,则返回。为了更新图中的边,您至少需要具有以下特权: | |||
% Read Only 访问数据库。 | |||
% Write 访问给定的集合。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% 在以下情况下返回HTTP 404: | |||
% 找不到具有该名称的图。 | |||
% 该集合不属于图形。 | |||
% 要更新的边缘不存在。 | |||
% 无论是_from或_to顶点不存在(如果更新)。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 412如果给出了if-match标头,则返回,但是存储的文档版本不同。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
updateEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, MapData) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr). | |||
updateEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr). | |||
updateEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, MapData, Headers, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, Headers, BodyStr). | |||
% 替换现有边的内容 | |||
% PUT /_api/gharial/{graph}/edge/{collection}/{edge} | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% collection(必填):边缘所属的边缘集合的名称。 | |||
% edge(必填):顶点的_key属性。 | |||
% 查询参数 | |||
% waitForSync(可选):定义请求是否应等待直到同步到磁盘。 | |||
% keepNull(可选):定义是否应存储设置为null的值。默认情况下,密钥不会从文档中删除。 | |||
% returnOld(可选):定义是否应在响应对象内返回已删除文档的表示。 | |||
% returnNew(可选):定义是否应在响应对象中返回新文档的表示形式。 | |||
% 标头参数 | |||
% if-match(可选):如果给出了“ If-Match”标头,则它必须恰好包含一个Etag。如果文档的版本与给定的Etag相同,则文档将被更新。否则,返回HTTP 412。或者,您可以在URL的属性rev中提供Etag。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% _from:此边的源顶点。必须在使用的边定义内有效。 | |||
% _to:此边的目标顶点。必须在使用的边定义内有效。 | |||
% 替换集合中边的数据。 | |||
% HTTP 201如果请求成功但waitForSync为true,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% edge:边的内部属性 | |||
% new:完整的新编写的边缘文档。包括请求主体中的所有书面属性以及ArangoDB生成的所有内部属性。仅在returnNew为true时存在。 | |||
% old:完整的覆盖边缘文档。包括此操作之前存储的所有属性。仅在returnOld为true时存在。 | |||
% HTTP 202如果请求成功,但waitForSync为false,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% edge:边的内部属性 | |||
% new:完整的新编写的边缘文档。包括请求主体中的所有书面属性以及ArangoDB生成的所有内部属性。仅在returnNew为true时存在。 | |||
% old:完整的覆盖边缘文档。包括此操作之前存储的所有属性。仅在returnOld为true时存在。 | |||
% HTTP 403如果您的用户权限不足,则返回。为了替换图中的边,您至少需要具有以下特权: | |||
% Read Only 访问数据库。 | |||
% Write 访问给定的集合。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% 在以下情况下返回HTTP 404: | |||
% 找不到具有该名称的图。 | |||
% 该集合不属于图形。 | |||
% 替换的边不存在。 | |||
% 无论是_from或_to顶点不存在。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 412如果给出了if-match标头,则返回,但是存储的文档版本不同。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
replaceEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, MapData) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
replaceEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
replaceEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, MapData, QueryPars, Headers) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary, QueryBinary/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, Headers, BodyStr). | |||
% 从图形中删除边 | |||
% DELETE /_api/gharial/{graph}/edge/{collection}/{edge} | |||
% 路径参数 | |||
% graph(必填):图的名称。 | |||
% collection(必填):边缘所属的边缘集合的名称。 | |||
% edge(必填):边缘的_key属性。 | |||
% 查询参数 | |||
% waitForSync(可选):定义请求是否应等待直到同步到磁盘。 | |||
% returnOld(可选):定义是否应在响应对象内返回已删除文档的表示。 | |||
% 标头参数 | |||
% if-match(可选):如果给出了“ If-Match”标头,则它必须恰好包含一个Etag。如果文档的版本与给定的Etag相同,则文档将被更新。否则,返回HTTP 412。或者,您可以在URL的属性rev中提供Etag。 | |||
% 从集合中删除边缘。 | |||
% HTTP 200如果可以删除边缘,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% removed:设置为true,如果删除成功。 | |||
% old:完整的已删除边缘文档。包括此操作之前存储的所有属性。仅在returnOld为true时存在。 | |||
% HTTP 202如果请求成功,但waitForSync为false,则返回。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是错误的。 | |||
% code:响应代码。 | |||
% removed:设置为true,如果删除成功。 | |||
% old:完整的已删除边缘文档。包括此操作之前存储的所有属性。仅在returnOld为true时存在。 | |||
% HTTP 403如果您的用户权限不足,则返回。为了删除图中的顶点,您至少需要具有以下特权: | |||
% Read Only 访问数据库。 | |||
% Write 访问给定的集合。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% 在以下情况下返回HTTP 404: | |||
% 找不到具有该名称的图。 | |||
% 该集合不属于图形。 | |||
% 要删除的边缘不存在。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
% HTTP 412如果给出了if-match标头,则返回,但是存储的文档版本不同。 | |||
% error:如果有错误,则标记(true),否则(false)。这个回应是真的。 | |||
% code:响应代码。 | |||
% errorNum:发生错误的ArangoDB错误号。 | |||
% errorMessage:为此错误创建的消息。 | |||
delEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey) -> | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
delEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
delEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, Headers, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary, QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, Headers, undefined). | |||
@ -0,0 +1,116 @@ | |||
-module(agHotBackup). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address:https://www.arangodb.com/docs/stable/http/hot-backup.html | |||
% HTTP接口进行热备份和还原 | |||
% 在v3.5.1中引入 | |||
% 这是用于热备份和还原的ArangoDB HTTP接口的简介。 | |||
% 热备份仅在企业版中可用 。 | |||
% 热备份 | |||
% 热备份接近整个 ArangoDB服务的即时一致快照 。这包括在任何给定时间的所有数据库,集合,索引,视图定义和用户。 | |||
% 对于创建,可以指定标签,如果省略,则将其替换为生成的UUID。然后,此标签与时间戳组合以为创建的热备份生成标识符。随后,所有其他API在这些标识符上运行。 | |||
% 以下API专用于处理POST操作。 | |||
% 使用API之前,请确保了解热备份的所有方面,以及所有要求和限制。 | |||
% 创建本地备份 | |||
% POST /_admin/backup/create | |||
% 具有以下属性的JSON对象是必需的: | |||
% label:此备份的标签。该标签与时间戳字符串一起使用,创建唯一的备份标识符<timestamp>_<label>。如果未指定标签,则假定为空字符串,并为此ID的此部分创建默认的UUID。 | |||
% timeout:操作尝试获取一致快照的时间(以秒为单位)。默认值为120秒。 | |||
% allowInconsistent:如果将此标志设置为,true并且在给定的超时时间内无法获取全局事务锁定,则会进行可能不一致的备份。此标志的默认值为false,在这种情况下,超时将导致HTTP 408错误。 | |||
% force:如果将此标志设置为,true并且在给定的超时时间内无法获取全局事务锁定,则将强制中止所有正在运行的事务,以确保可以创建一致的备份。这不包括JavaScript事务。它等待事务最多中止 timeout几秒钟。因此,使用force请求超时将增加一倍。中止交易几乎肯定不是您想要的应用程序。在存在中间提交的情况下,它甚至可能破坏交易的原子性。仅在需要不惜一切代价进行一致备份的情况下,使用风险自负。推荐的默认值是false。如果同时 allowInconsistent并force设置为true,则后者优先,事务中止。仅在群集中可用。 | |||
% 使用给定标签“尽快”创建一致的备份,非常类似于及时快照。短语“尽快”中的歧义是指下一个窗口,在该窗口中可以获得跨所有数据库的全局写锁定以保证一致性。请注意,备份首先与原始数据位于同一台计算机和硬盘驱动器上。确保将其上传到远程站点以进行实际备份。 | |||
% 返回码 | |||
% 201:如果一切正常,则返回代码201。 | |||
% 400:如果使用错误的参数或除之外的任何HTTP方法调用了create命令POST,则返回HTTP 400。具体细节在返回的错误文档中有详细说明。 | |||
% 408:如果操作无法在超时时间内获得全局事务锁定,则返回HTTP 408。 | |||
newBackup(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/backup/create">>, [], BodyStr). | |||
% 从本地备份还原 | |||
% POST /_admin/backup/restore | |||
% 具有以下属性的JSON对象是必需的: | |||
% id:要还原的备份的ID。 | |||
% 使用给定的ID及时从快照恢复一致的备份。备份快照必须位于本地ArangoDB服务上。 | |||
% 返回码 | |||
% 200:如果可以恢复备份,则返回。请注意,单个服务器和群集之间不可避免存在差异。在单个服务器中,请求成功返回,但是还原仅在之后执行。在集群中,仅在成功完成还原操作后才返回请求。群集行为显然是所需的行为,但是在单个实例中,无法在重新启动期间保持连接打开。 | |||
% 400:如果使用错误的参数或除以外的任何HTTP方法调用了restore命令POST,则返回HTTP 400。具体细节在返回的错误文档中有详细说明。 | |||
restoreBackup(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/backup/restore">>, [], BodyStr). | |||
% 删除特定的本地备份 | |||
% POST /_admin/backup/delete | |||
% 具有以下属性的JSON对象是必需的: | |||
% id:此备份的标识符。 | |||
% 删除给定标识的特定本地备份id。 | |||
% 返回码 | |||
% 200:如果一切正常,则返回此代码200。 | |||
% 400:如果使用错误的参数或除以外的任何HTTP方法调用delete命令POST,则返回HTTP 400。 | |||
% 404:如果id找不到与该标识符相对应的备份。 | |||
delBackup(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/backup/delete">>, [], BodyStr). | |||
% 列出所有本地备份 | |||
% POST /_admin/backup/list | |||
% 具有以下属性的JSON对象是必需的: | |||
% id:正文可以为空(在这种情况下,将列出所有可用的备份),也可以是具有attribute的对象,该属性id为字符串。在后一种情况下,返回的列表仅限于具有给定ID的备份。 | |||
% 列出所有本地找到的备份。 | |||
% 返回码 | |||
% 200:如果一切正常,则返回代码200。 | |||
% 400:如果使用错误的参数调用list命令,则 返回HTTP 400。 | |||
% 404:如果id给出了ID或ID列表,但未找到给定ID作为备份的标识符,则返回HTTP 404 NOT FOUND。 | |||
% 405:如果使用以外的任何HTTP方法调用了list命令POST,则返回HTTP 405 METHOD NOT ALLOWED。 | |||
getBackupList(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/backup/list">>, [], undefined). | |||
% 结果由list热备份的一个对象组成id,其中id唯一地标识了特定的热备份,version描绘了用于创建任何单个热备份的ArangoDB的版本,并datetime显示了创建热备份的时间。 | |||
% 其他参数包括:备份大小(以字节为单位)sizeInBytes,单个数据文件nrFiles的数量(以),创建时的数据库服务器nrDBServers的数量(以),备份部分的数量(在当前可访问的db服务器上找到的)为nrPiecesPresent。 | |||
% 如果创建的备份允许不一致,则将其表示为potentiallyInconsistent。的available布尔参数被紧密地连接到备份到存在并且准备好被所有分贝服务器上恢复。它是true 除非当前可访问的数据库服务器数量与备份中列出的 | |||
% 数据库服务器数量不匹配。 | |||
% 将备份上传到远程存储库 | |||
% 上传特定的本地备份 | |||
% POST /_admin/backup/upload | |||
% 具有以下属性的JSON对象是必需的: | |||
% id:此备份的标识符。计划上载操作时,这是必需的。在这种情况下,请忽略该uploadId 属性。 | |||
% remoteRepository:远程存储库的URL。计划上载操作时,这是必需的。在这种情况下,请忽略该uploadId属性。所提供的存储库URL进行了规范化和验证,如下所示:必须出现一个冒号,以分隔配置节名称和路径。冒号之前的URL前缀必须作为键存在于以下配置对象中。冒号前不能出现斜线。多个反斜杠折叠为1, ..并相应.地应用。本地存储库必须是绝对路径,并且必须以开头/。尾随/被删除。 | |||
% config:远程存储库的配置。计划上载操作时,这是必需的。在这种情况下,请忽略该uploadId 属性。有关对象的说明,请参见手册中的arangobackup程序说明config。 | |||
% uploadId:上传ID,用于指定要查询的上传操作进度或要中止的上传操作。如果指定此参数,则忽略上述所有主体参数。 | |||
% abort:true如果正在运行的上载操作应中止,则将其设置为。在这种情况下,唯一需要的其他body参数是uploadId。 | |||
% 将特定的本地备份上载到远程存储库,或查询先前计划的上载操作的进度,或中止正在运行的上载操作。 | |||
% 返回码 | |||
% 200:如果一切正常,如果查询进度或操作中止,则返回代码200。 | |||
% 202:如果一切正常,如果计划了新操作,则返回代码202。 | |||
% 400:如果使用错误的参数或除以外的任何HTTP方法调用了上载命令POST,则返回HTTP 400。 | |||
% 401:如果对转储存储库的身份验证失败,则返回HTTP 400。 | |||
% 404:如果id 找不到对应于标识符的备份,或者没有已知的上载操作uploadId。 | |||
uploadBackup(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/backup/upload">>, [], BodyStr). | |||
% 下载特定的本地备份 | |||
% POST /_admin/backup/download | |||
% 具有以下属性的JSON对象是必需的: | |||
% id:此备份的标识符。计划下载操作时,这是必需的。在这种情况下,请忽略该downloadId 属性。 | |||
% remoteRepository:远程存储库的URL。计划下载操作时,这是必需的。在这种情况下,请忽略该downloadId属性。所提供的存储库URL进行了规范化和验证,如下所示:必须出现一个冒号,以分隔配置节名称和路径。冒号之前的URL前缀必须作为键存在于以下配置对象中。冒号前不能出现斜线。多个反斜杠折叠为1, ..并相应.地应用。本地存储库必须是绝对路径,并且必须以开头/。尾随/被删除。 | |||
% config:远程存储库的配置。计划下载操作时,这是必需的。在这种情况下,请忽略该downloadId 属性。有关对象的说明,请参见手册中的arangobackup程序说明config。 | |||
% downloadId:下载ID,用于指定要查询的下载操作进度或要中止的下载操作。如果指定此参数,则忽略上述所有主体参数。 | |||
% abort:true如果正在运行的下载操作应中止,则将其设置为。在这种情况下,唯一需要的其他body参数是downloadId。 | |||
% 从远程存储库下载特定的本地备份,或查询先前计划的下载操作的进度,或中止正在运行的下载操作。 | |||
% 返回码 | |||
% 200:如果一切正常,如果查询进度或操作中止,则返回代码200。 | |||
% 202:如果一切正常,如果计划了新操作,则返回代码202。 | |||
% 400:如果使用错误的参数或除以外的任何HTTP方法调用了download命令POST,则返回HTTP 400。 | |||
% 401:如果对转储存储库的身份验证失败,则返回HTTP 401。 | |||
% 404:如果id 找不到与该标识符相对应的备份,或者如果没有已知的与的下载操作downloadId。 | |||
downloadBackup(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/backup/download">>, [], BodyStr). |
@ -0,0 +1,413 @@ | |||
-module(agIndexes). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address:https://www.arangodb.com/docs/stable/http/indexes.html | |||
% 索引的HTTP接口 | |||
% 索引 | |||
% 这是对ArangoDB索引的HTTP接口的一般介绍。有各种索引类型的特殊部分。 | |||
% 索引 | |||
% 索引用于允许快速访问文档。对于每个集合,总是有主索引,它是文档关键字(_key属性)的哈希索引 。不能删除或更改该索引。 边缘集合还将具有一个自动创建的边缘索引,该索引无法修改。该索引可通过_from和_to属性快速访问文档。 | |||
% 可以通过定义应建立索引的属性名称来创建大多数用户土地索引。一些索引类型只允许索引一个属性(例如全文索引),而其他索引类型只允许索引多个属性。 | |||
% _id任何索引类型均不支持在用户定义的索引中使用system属性。 | |||
% 索引句柄 | |||
% 索引句柄唯一地标识数据库中的索引。它是一个字符串,由集合名称和由/分隔的索引标识符组成。如果索引被声明为唯一,那么对索引属性的访问应该很快。如果索引属性仅包含很少的不同值,则性能会降低。 | |||
% 主索引 | |||
% 将自动为每个集合创建一个主索引。它索引文档的主键,这些主键存储在_keysystem属性中。主索引是唯一的,可用于_key和_id属性的查询。无法显式创建或删除主索引。 | |||
% 边缘索引 | |||
% 将自动为边缘集合创建边缘索引。它包含顶点文档之间的连接,并在查询顶点的连接边时调用。无法显式创建或删除边缘索引。边缘索引是唯一的。 | |||
% 哈希指数固定链接 | |||
% 哈希索引是未排序的索引,可用于通过相等查找查找单个文档。 | |||
% | |||
% 跳过清单索引固定链接 | |||
% 跳过列表是可用于查找单个文档或文档范围的排序索引。 | |||
% 永久索引 | |||
% 持久索引是可用于查找单个文档或文档范围的排序索引。与其他索引相反,持久索引的内容存储在磁盘上,因此在加载集合时不需要从文档在内存中重建。 | |||
% TTL(生存时间)索引 | |||
% TTL索引可用于自动从集合中删除过期的文档。过期的文档最终将由后台线程删除。 | |||
% 全文索引 | |||
% 全文索引可用于在文档中查找单词或单词前缀。全文索引只能在一个属性上设置,并且将对文档中包含的所有具有该属性文本值的单词进行索引。仅索引(指定)最小长度的单词。使用libicu提供的单词边界分析来完成单词标记化,该分析考虑了服务器启动时提供的所选语言。单词以小写形式索引。该索引支持完全匹配查询(全字)和前缀查询。 | |||
% 索引地址 | |||
% ArangoDB中的所有索引都有唯一的句柄。该索引句柄标识一个索引,并由ArangoDB管理。所有索引都在URI下找到 | |||
% http://server:port/_api/index/index-handle | |||
% 例如:假设索引句柄为demo / 63563528,则该索引的URL为: | |||
% http://localhost:8529/_api/index/demo/63563528 | |||
% 返回索引 | |||
% GET /_api/index/{index-id} | |||
% 路径参数 | |||
% index-id(必需):索引标识符。 | |||
% 结果是一个描述索引的对象。它至少具有以下属性: | |||
% id:索引的标识符 | |||
% type:索引类型 | |||
% 所有其他属性都取决于类型。例如,某些索引提供 唯一或稀疏标志,而另一些则不提供。一些索引还在结果的selectivityEstimate属性中提供了选择性估计。 | |||
% 返回码 | |||
% 200:如果索引存在,则返回HTTP 200。 | |||
% 404:如果索引不存在,则 返回HTTP 404。 | |||
getIndexInfo(PoolNameOrSocket, IndexId) -> | |||
Path = <<"/_api/index/", (agMiscUtils:toBinary(IndexId))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 创建一个索引 | |||
% POST /_api/index#general | |||
% 查询参数 | |||
% collection (必填):集合名称。 | |||
% 请求正文(json) | |||
% 在集合collection中创建一个新索引。期望包含索引详细信息的对象。 | |||
% 必须在 索引详细信息的type属性中指定要创建的索引的类型。根据索引类型,可能需要在请求中指定其他其他属性才能创建索引。 | |||
% 索引要求索引详细信息的fields属性中的被索引属性。根据索引类型,可以为一个或多个属性建立索引。在后一种情况下,需要一个字符串数组。 | |||
% 用户定义的索引不支持索引系统属性_id。使用_id作为索引属性手动创建索引将失败,并显示错误。 | |||
% (可选)索引名称可以在name属性中指定为字符串。索引名称与集合名称具有相同的限制。如果未指定任何值,将自动生成一个值。 | |||
% 某些索引可以创建为唯一或非唯一变体。通过在索引详细信息中指定唯一标志,可以控制大多数索引的唯一性。将其设置为true将创建唯一索引。将其设置为false或忽略unique属性将创建一个非唯一索引。 | |||
% 注意:以下索引类型不支持唯一性,并且对这些类型使用unique属性可能会导致错误: | |||
% 地理索引 | |||
% 全文索引 | |||
% 注意:集群中不支持非分片键上的唯一索引。 | |||
% | |||
% 可以选择在稀疏变量中创建哈希,跳过列表和持久索引。如果索引详细信息中的sparse属性设置为true,则将创建一个稀疏索引。稀疏索引不会索引未设置任何索引属性或为null的文档。 | |||
% | |||
% 类型为hash或skiplist的数组索引支持可选的重复数据删除属性。它控制将来自同一文档的重复索引值插入唯一数组索引是否会导致唯一约束错误。默认值为true,因此每个非唯一索引值的单个实例将插入每个文档的索引中。无论此属性的值如何,尝试将值插入到索引中已存在的索引始终将失败。 | |||
% | |||
% 返回码 | |||
% 200:如果索引已经存在,则返回HTTP 200。 | |||
% 201:如果索引尚不存在并且可以创建,则 返回HTTP 201。 | |||
% 400:如果发布了无效的索引描述或使用了目标索引不支持的属性,则返回HTTP 400。 | |||
% 404:如果集合未知,则返回HTTP 404。 | |||
newIndex(PoolNameOrSocket, CollName, MapData) -> | |||
Path = <<"/_api/index?collection=", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr). | |||
% 删除索引 | |||
% DELETE /_api/index/{index-id} | |||
% 路径参数 | |||
% index-id(必需):索引ID。 | |||
% 删除带有index-id的索引。 | |||
% 返回码 | |||
% 200:如果可以删除索引,则返回HTTP 200。 | |||
% 404:如果index-id未知,则返回HTTP 404。 | |||
delIndex(PoolNameOrSocket, IndexId) -> | |||
Path = <<"/_api/index/", (agMiscUtils:toBinary(IndexId))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
% 返回集合的所有索引 | |||
% GET /_api/index | |||
% 查询参数 | |||
% collection (必填):集合名称。 | |||
% 返回一个对象,该对象的属性索引包含给定集合的所有索引描述的数组。在标识符中还可以使用与索引句柄作为键的对象相同的信息。 | |||
% 返回码 | |||
% 200:返回一个JSON对象,其中包含该集合的索引列表。 | |||
getIndexList(PoolNameOrSocket, CollName) -> | |||
Path = <<"/_api/index?collection=", CollName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 使用哈希索引 | |||
% 如果存在合适的哈希索引,/_api/simple/by-example则将使用该索引执行示例查询。 | |||
% 创建一个哈希索引 | |||
% POST /_api/index#hash | |||
% 查询参数 | |||
% collection(必填):集合名称。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% type:必须等于“ hash”。 | |||
% fields:属性路径的数组。 | |||
% unique:如果为true,则创建一个唯一索引。 | |||
% sparse:如果为true,则创建一个稀疏索引。 | |||
% deduplicate:如果为false,则关闭数组值的重复数据删除。 | |||
% 如果不存在,则为集合collection-name创建哈希索引。该调用需要一个包含索引详细信息的对象。 | |||
% 在稀疏索引中,所有不包含至少一个指定索引属性(即field)或在任何指定索引属性中都为null的文档将从索引中排除。如果设置了唯一标志,则不会对此类文档建立索引,也不会将其用于唯一性检查。 | |||
% 在非稀疏索引中,将为这些文档建立索引(对于不存在索引的属性,将使用null值),并且如果设置了唯一标志,则将对它们进行唯一性检查。 | |||
% 注意:集群中不支持非分片键上的唯一索引。 | |||
% 返回码 | |||
% 200:如果索引已经存在,则返回HTTP 200。 | |||
% 201:如果索引尚不存在并且可以创建,则 返回HTTP 201。 | |||
% 400:如果集合中已经包含文档,并且您尝试创建唯一哈希索引以使某些文档违反唯一性,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则返回HTTP 404。 | |||
newIndexOfHash(PoolNameOrSocket, CollName, MapData) -> | |||
case MapData of | |||
#{type := <<"hash">>} -> | |||
Path = <<"/_api/index?collection=", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr); | |||
#{<<"type">> := <<"hash">>} -> | |||
Path = <<"/_api/index?collection=", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr); | |||
_ -> | |||
{error, param} | |||
end. | |||
% 返回与给定示例匹配的集合的所有文档 | |||
% PUT /_api/simple/by-example | |||
% 此路由不应再使用。从3.4.0版开始,不推荐使用简单查询的所有端点。它们被AQL查询取代。 | |||
% 直到ArangoDB版本3.2.13和3.3.7,此API相当昂贵。一种更轻量的替代方法是使用HTTP Cursor API。从版本3.2.14和3.3.8开始,此性能影响不再是问题,因为API的内部实现已更改。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% collection:要查询的集合的名称。 | |||
% example:示例文档。 | |||
% skip:查询中要跳过的文档数(可选)。 | |||
% limit:要返回的最大文档数。该跳跃 是在之前应用极限的限制。(可选的) | |||
% batchSize:一次往返从服务器传输到客户端的最大结果文档数。如果未设置此属性,则将使用服务器控制的默认值。甲BATCHSIZE的值 0是不允许的。 | |||
% 这将找到与给定示例匹配的所有文档。 | |||
% 返回包含结果的游标,有关详细信息,请参见HTTP Cursor。 | |||
% 返回码 | |||
% 201:查询执行成功返回。 | |||
% 400:如果正文不包含查询的有效JSON表示形式,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 404:如果collection指定的collection未知,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 返回与给定示例匹配的集合的一个文档 | |||
% PUT /_api/simple/first-example | |||
% 此路由不应再使用。从3.4.0版开始,不推荐使用简单查询的所有端点。它们被AQL查询取代。 | |||
% 直到ArangoDB版本3.2.13和3.3.7,此API相当昂贵。一种更轻量的替代方法是使用HTTP Cursor API。从版本3.2.14和3.3.8开始,此性能影响不再是问题,因为API的内部实现已更改。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% collection:要查询的集合的名称。 | |||
% example:示例文档。 | |||
% 这将返回与给定示例匹配的第一个文档。 | |||
% 如果没有文档与示例匹配,则返回包含文档或HTTP 404的结果。 | |||
% 如果集合中有多个文档与指定的示例匹配,则仅返回其中一个文档,并且不确定返回哪个匹配的文档。 | |||
% 返回码 | |||
% 200:成功执行查询后返回。 | |||
% 400:如果正文不包含查询的有效JSON表示形式,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 404:如果collection指定的collection未知,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 使用跳过列表索引 | |||
% 如果存在合适的跳过列表索引,则/_api/simple/range其他操作将使用该索引来执行查询。 | |||
% 创建一个跳过列表 | |||
% POST /_api/index#skiplist | |||
% 查询参数 | |||
% collection(必填):集合名称。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% type:必须等于“ skiplist”。 | |||
% fields:属性路径的数组。 | |||
% unique:如果为true,则创建一个唯一索引。 | |||
% sparse:如果为true,则创建一个稀疏索引。 | |||
% deduplicate:如果为false,则关闭数组值的重复数据删除。 | |||
% 为集合collection-name创建一个跳过列表索引(如果尚不存在)。该调用需要一个包含索引详细信息的对象。 | |||
% 在稀疏索引中,所有不包含至少一个指定索引属性(即field)或在任何指定索引属性中都为null的文档将从索引中排除。如果设置了唯一标志,则不会对此类文档建立索引,也不会将其用于唯一性检查。 | |||
% 在非稀疏索引中,将为这些文档建立索引(对于不存在索引的属性,将使用null值),并且如果设置了唯一标志,则将对它们进行唯一性检查。 | |||
% 注意:集群中不支持非分片键上的唯一索引。 | |||
% 返回码 | |||
% 200:如果索引已经存在,则返回HTTP 200。 | |||
% 201:如果索引尚不存在并且可以创建,则 返回HTTP 201。 | |||
% 400:如果集合中已经包含文档,并且您尝试以存在违反唯一性的文档的方式创建唯一的跳过列表索引,则返回HTTP 400。 | |||
% 404:如果集合名称未知,则返回HTTP 404。 | |||
newIndexOfSkipList(PoolNameOrSocket, CollName, MapData) -> | |||
case MapData of | |||
#{type := <<"skiplist">>} -> | |||
Path = <<"/_api/index?collection=", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr); | |||
#{<<"type">> := <<"skiplist">>} -> | |||
Path = <<"/_api/index?collection=", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr); | |||
_ -> | |||
{error, param} | |||
end. | |||
% 使用持久索引 | |||
% 如果存在合适的持久索引,则/_api/simple/range其他操作将使用该索引执行查询。 | |||
% | |||
% 创建一个持久索引 | |||
% POST /_api/index#persistent | |||
% 查询参数 | |||
% collection(必填):集合名称。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% type:必须等于“ persistent”。 | |||
% fields:属性路径的数组。 | |||
% unique:如果为true,则创建一个唯一索引。 | |||
% sparse:如果为true,则创建一个稀疏索引。 | |||
% 为集合collection-name创建一个持久索引(如果尚不存在)。该调用需要一个包含索引详细信息的对象。 | |||
% 在稀疏索引中,所有不包含至少一个指定索引属性(即field)或在任何指定索引属性中都为null的文档将从索引中排除。如果设置了唯一标志,则不会对此类文档建立索引,也不会将其用于唯一性检查。 | |||
% 在非稀疏索引中,将为这些文档建立索引(对于不存在索引的属性,将使用null值),并且如果设置了唯一标志,则将对它们进行唯一性检查。 | |||
% 注意:集群中不支持非分片键上的唯一索引。 | |||
% 返回码 | |||
% 200:如果索引已经存在,则返回HTTP 200。 | |||
% 201:如果索引尚不存在并且可以创建,则 返回HTTP 201。 | |||
% 400:如果集合中已经包含文档,并且您尝试以存在违反唯一性的文档的方式创建唯一的持久索引,那么将返回HTTP 400。 | |||
% 404:如果集合名称未知,则返回HTTP 404。 | |||
newIndexOfPersistent(PoolNameOrSocket, CollName, MapData) -> | |||
case MapData of | |||
#{type := <<"persistent">>} -> | |||
Path = <<"/_api/index?collection=", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr); | |||
#{<<"type">> := <<"persistent">>} -> | |||
Path = <<"/_api/index?collection=", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr); | |||
_ -> | |||
{error, param} | |||
end. | |||
% 使用TTL(生存时间)索引 | |||
% | |||
% 创建一个TTL(生存时间)索引 | |||
% POST /_api/index#ttl | |||
% 查询参数 | |||
% collection(必填):集合名称。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% type:必须等于“ ttl”。 | |||
% fields:一个具有唯一属性路径的数组。 | |||
% expireAfter:文档创建后经过的时间(以秒为单位),之后文档被视为“过期”。 | |||
% 为集合collection-name创建TTL索引(如果尚不存在)。该调用需要一个包含索引详细信息的对象。 | |||
% 返回码 | |||
% 200:如果索引已经存在,则返回HTTP 200。 | |||
% 201:如果索引尚不存在并且可以创建,则 返回HTTP 201。 | |||
% 400:如果集合已经包含另一个TTL索引,则返回HTTP 400,因为每个集合最多可以有一个TTL索引。 | |||
% 404:如果集合名称未知,则返回HTTP 404。 | |||
newIndexOfTtl(PoolNameOrSocket, CollName, MapData) -> | |||
case MapData of | |||
#{type := <<"ttl">>} -> | |||
Path = <<"/_api/index?collection=", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr); | |||
#{<<"type">> := <<"ttl">>} -> | |||
Path = <<"/_api/index?collection=", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr); | |||
_ -> | |||
{error, param} | |||
end. | |||
% 创建地理索引 | |||
% POST /_api/index#geo | |||
% 查询参数 | |||
% collection (必填):集合名称。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% type:必须等于“ geo”。 | |||
% fields:具有一个或两个属性路径的数组。 | |||
% 如果它是一个具有一个属性路径location的数组,那么将使用location作为坐标的路径在所有文档上创建地理空间索引。该属性的值必须是具有至少两个double值的数组。数组必须包含纬度(第一个值)和经度(第二个值)。所有没有属性路径或值不适合的文档都将被忽略。 | |||
% 如果它是具有两个属性路径latitude和经度的数组,则将使用纬度 和经度在所有文档上创建地理空间索引作为路径的纬度和经度。属性纬度和属性经度的值必须加倍。所有没有属性路径或值不适合的文档都将被忽略。 | |||
% | |||
% geoJson:如果在某个位置上构建了地理空间索引,并且geoJson为true,则数组内的顺序为经度和纬度。这对应于http://geojson.org/geojson-spec.html#positions中描述的格式 | |||
% 在集合collection-name中创建地理空间索引(如果尚不存在)。期望包含索引详细信息的对象。 | |||
% 地理位置索引总是稀疏的,这意味着不包含索引属性或索引属性中具有非数字值的文档将不会被索引。 | |||
% | |||
% 返回码 | |||
% 200:如果索引已经存在,则返回HTTP 200。 | |||
% 201:如果索引尚不存在并且可以创建,则 返回HTTP 201。 | |||
% 404:如果集合名称未知,则返回HTTP 404。 | |||
newIndexOfGeo(PoolNameOrSocket, CollName, MapData) -> | |||
case MapData of | |||
#{type := <<"geo">>} -> | |||
Path = <<"/_api/index?collection=", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr); | |||
#{<<"type">> := <<"geo">>} -> | |||
Path = <<"/_api/index?collection=", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr); | |||
_ -> | |||
{error, param} | |||
end. | |||
%返回给定位置附近集合的所有文档 | |||
%PUT /_api/simple/near | |||
%此路由不应再使用。从3.4.0版开始,不推荐使用简单查询的所有端点。它们被AQL查询取代。 | |||
%具有以下属性的JSON对象是必需的: | |||
%collection:要查询的集合的名称。 | |||
%latitude:坐标的纬度。 | |||
%经度:坐标的经度。 | |||
%distance:如果给定,则用于将距离返回给定坐标的属性键。(可选的)。如果指定,则以米为单位返回距离。 | |||
%skip:查询中要跳过的文档数。(可选的) | |||
%limit:要返回的最大文档数。该跳跃是在之前应用极限的限制。默认值为100。(可选) | |||
%geo:如果给定,则为要使用的地理索引的标识符。(可选的) | |||
%默认值将在给定坐标附近最多找到100个文档。返回的数组根据距离排序,最近的文档在返回数组中排在最前面。如果附近有等距离的文档,则从该组文档中随机选择文档,直到达到限制。 | |||
%为了使用Near运算符,必须为集合定义一个地理索引。该索引还定义了哪个属性保存文档的坐标。如果您有多个地理空间索引,则可以使用geo字段来选择特定的索引。 | |||
%返回包含结果的游标,有关详细信息,请参见HTTP Cursor。 | |||
%注意:自ArangoDB 2.6起,不推荐使用近乎简单的查询。在将来的ArangoDB版本中可能会删除此API。使用Near运算符从集合中检索文档的首选方法是使用NEAR函数发出AQL查询,如下所示: | |||
%FOR doc IN NEAR(@@collection, @latitude, @longitude, @limit) | |||
%RETURN doc` | |||
%返回码 | |||
%201:查询执行成功返回。 | |||
%400:如果正文不包含查询的有效JSON表示形式,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
%404:如果collection指定的collection未知,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 查找坐标周围半径内的文档 | |||
% 返回给定半径内集合的所有文档 | |||
% PUT /_api/simple/within | |||
% 此路由不应再使用。从3.4.0版开始,不推荐使用简单查询的所有端点。它们被AQL查询取代。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% collection:要查询的集合的名称。 | |||
% latitude:坐标的纬度。 | |||
% 经度:坐标的经度。 | |||
% radius:最大半径(以米为单位)。 | |||
% distance:如果给定,则用于将距离返回给定坐标的属性键。(可选的)。如果指定,则以米为单位返回距离。 | |||
% limit:要返回的最大文档数。该跳跃是在之前应用极限的限制。默认值为100。(可选) | |||
% geo:如果给定,则为要使用的地理索引的标识符。(可选的) | |||
% 这将找到围绕坐标(纬度,经度)的给定半径内的所有文档。返回的列表按距离排序。 | |||
% 为了使用内部运算符,必须为集合定义一个地理索引。该索引还定义了哪个属性保存文档的坐标。如果您有多个地理空间索引,则可以使用geo字段来选择特定的索引。 | |||
% 返回包含结果的游标,有关详细信息,请参见HTTP Cursor。 | |||
% 注意:从ArangoDB 2.6开始不推荐使用内部简单查询。在将来的ArangoDB版本中可能会删除此API。使用Near运算符从集合中检索文档的首选方法是使用WITHIN函数发出AQL查询,如下所示: | |||
% FOR doc IN WITHIN(@@collection, @latitude, @longitude, @radius, @distanceAttributeName) | |||
% RETURN doc | |||
% 返回码 | |||
% 201:查询执行成功返回。 | |||
% 400:如果正文不包含查询的有效JSON表示形式,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 404:如果collection指定的collection未知,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 创建全文索引 | |||
% POST /_api/index#fulltext | |||
% 查询参数 | |||
% collection (必填):集合名称。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% type:必须等于“全文”。 | |||
% fields:属性名称的数组。当前,数组仅限于一个属性。 | |||
% minLength:要索引的单词的最小字符长度。如果未指定,则默认为服务器定义的值。因此,建议在创建索引时显式设置此值。 | |||
% 为集合collection-name创建全文索引(如果尚不存在)。该调用需要一个包含索引详细信息的对象。 | |||
% 返回码 | |||
% 200:如果索引已经存在,则返回HTTP 200。 | |||
% 201:如果索引尚不存在并且可以创建,则 返回HTTP 201。 | |||
% 404:如果集合名称未知,则返回HTTP 404。 | |||
newIndexOfFulltext(PoolNameOrSocket, CollName, MapData) -> | |||
case MapData of | |||
#{type := <<"fulltext">>} -> | |||
Path = <<"/_api/index?collection=", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr); | |||
#{<<"type">> := <<"fulltext">>} -> | |||
Path = <<"/_api/index?collection=", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr); | |||
_ -> | |||
{error, param} | |||
end. | |||
% 全文索引查询 | |||
% 通过全文查询返回集合的文档 | |||
% PUT /_api/simple/fulltext | |||
% 此路由不应再使用。从3.4.0版开始,不推荐使用简单查询的所有端点。它们被AQL查询取代。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% collection:要查询的集合的名称。 | |||
% attribute:包含文本的属性。 | |||
% 查询:全文查询。请参阅全文查询 以获取详细信息。 | |||
% skip:查询中要跳过的文档数(可选)。 | |||
% limit:要返回的最大文档数。该跳跃 是在之前应用极限的限制。(可选的) | |||
% index:要使用的全文索引的标识符。 | |||
% 这将发现从集合匹配指定的全文查询的所有文档的查询。 | |||
% 为了使用全文运算符,必须为集合和指定的属性定义全文索引。 | |||
% 返回包含结果的游标,有关详细信息,请参见HTTP Cursor。 | |||
% 注意:从ArangoDB 2.6起不推荐使用全文本简单查询。在将来的ArangoDB版本中可能会删除此API。使用Near运算符从集合中检索文档的首选方法是使用FULLTEXT AQL函数发出AQL查询 ,如下所示: | |||
% FOR doc IN FULLTEXT(@@collection, @attributeName, @queryString, @limit) | |||
% RETURN doc | |||
% 返回码 | |||
% 201:查询执行成功返回。 | |||
% 400:如果正文不包含查询的有效JSON表示形式,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
% 404:如果collection指定的collection未知,则返回。在这种情况下,响应主体包含一个错误文档。 | |||
@ -0,0 +1,241 @@ | |||
-module(agMiscFuns). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
%% doc_address:https://www.arangodb.com/docs/stable/http/miscellaneous-functions.html | |||
% 各种功能的HTTP接口 | |||
% 这是ArangoDB的其他功能的HTTP接口的概述。 | |||
% 返回服务器版本号 | |||
% GET /_api/version | |||
% 查询参数 | |||
% details(可选):如果设置为true,则响应将包含一个details属性,其中包含有关所包含组件及其版本的附加信息。详细信息对象的属性名称和内部信息可能会因平台和ArangoDB版本而异。 | |||
% 返回服务器名称和版本号。响应是具有以下属性的JSON对象: | |||
% 在所有情况下均返回HTTP 200。 | |||
% 服务器:将始终包含arango | |||
% version:服务器版本字符串。该字符串的格式为“ major”。小的。子 ”。major和minor将是数字,sub 可能包含数字或文本版本。 | |||
% details:一个可选的JSON对象,带有其他详细信息。仅当请求中的details查询参数设置为true时,才返回此值。 | |||
% architecture:CPU体系结构,即64位 | |||
% arm: false-这不在ARM cpu上运行 | |||
% asan:是否已在asan地址清理器打开的情况下进行编译?(应该是错误的) | |||
% asm-crc32:我们有汇编程序实现的CRC函数吗? | |||
% assertions:我们是否有在(=>开发人员版本)中编译的断言 | |||
% boost-version:我们绑定哪个boost版本 | |||
% build-date:创建此二进制文件的日期 | |||
% build-repository:引用从其编译的git-ID | |||
% compiler:我们使用了哪个编译器 | |||
% cplusplus:C ++标准版本 | |||
% debug: 生产二进制文件为false | |||
% endianness:目前仅支持很少 | |||
% failure-tests: 对于生产二进制文件为false(禁用致命错误的功能) | |||
% fd-client-event-handler:我们使用哪种方法来处理fd-set,民意调查应该在linux上进行。 | |||
% fd-setsize:如果不轮询,则fd setsize对于文件描述符的最大数目有效 | |||
% full-version-string:完整版本字符串 | |||
% icu-version:我们捆绑哪个版本的ICU | |||
% jemalloc: 如果我们使用jemalloc,则为true | |||
% maintenanceer-mode: 如果这是生产二进制文件,则为false | |||
% openssl-version:我们链接哪个openssl版本? | |||
% platform:主机os- linux,windows或darwin | |||
% reactor-type: epoll TODO | |||
% rockdb-version:此发行版捆绑的rocksdb版本 | |||
% server-version:ArangoDB发行版本 | |||
% sizeof int:整数的字节数 | |||
% sizeof void :* void指针的字节数 | |||
% sse42:我们是否具有启用SSE 4.2的CPU? | |||
% unaligned-access:此系统是否支持未对齐的内存访问? | |||
% v8-version:捆绑的V8 JavaScript引擎版本 | |||
% vpack-version:使用的velocypack实现的版本 | |||
% zlib-version:捆绑的zlib的版本 | |||
% mode:我们作为-[ 服务器,控制台,脚本 ]中的一种运行的模式 | |||
% host:主机ID | |||
srvVersion(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/version">>, [], undefined). | |||
srvVersion(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/version", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 返回运行服务器的引擎类型 | |||
% GET /_api/engine | |||
% 返回服务器配置为使用的存储引擎。响应是具有以下属性的JSON对象: | |||
% 在所有情况下均返回HTTP 200。 | |||
% 名称:将是mmfiles或rocksdb | |||
srvEngine(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/engine">>, [], undefined). | |||
% 将WAL同步到磁盘。 | |||
% PUT /_admin/wal/flush | |||
% 查询参数 | |||
% waitForSync(可选):在预写日志中尚未同步的数据同步到磁盘之前,是否应该阻塞该操作。 | |||
% waitForCollector(可选):在预写日志垃圾收集器收集刷新日志中的数据之前,是否应阻塞该操作。请注意,如果存在长时间运行的事务并且将预写日志垃圾收集器无法完成垃圾收集,则将此选项设置为true可能会阻塞很长时间。 | |||
% 刷新预写日志。通过刷新当前活动的预写日志文件,可以将其中的数据传输到收集日志和数据文件中。这对于确保收集的所有数据都存在于收集日志和数据文件中(例如,在转储收集的数据时)很有用。 | |||
% 返回码 | |||
% 200:操作成功返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
flushWal(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_admin/wal/flush">>, [], undefined). | |||
flushWal(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_admin/wal/flush", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined). | |||
% 获取当前配置。 | |||
% GET /_admin/wal/properties | |||
% 检索预写日志的配置。结果是具有以下属性的JSON对象: | |||
% allowOversizeEntries:是否可以执行和存储大于单个日志文件的操作 | |||
% logfileSize:每个预写日志文件的大小 | |||
% HistoricLogfiles:要保留的最大历史日志文件数 | |||
% reserveLogfiles:ArangoDB在后台分配的最大保留日志文件数 | |||
% syncInterval:尚未同步的预写日志数据的自动同步时间间隔(以毫秒为单位) | |||
% valveWait:如果发生写限制,则操作在中止之前将等待的最大等待时间(以毫秒为单位) | |||
% acceleratorWhenPending:达到该数量时将激活写限制的未处理垃圾收集操作的数量。值为 0表示将不触发写限制。 | |||
% 返回码 | |||
% 200:操作成功返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
getWalProps(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/wal/properties">>, [], undefined). | |||
% 配置Wal的参数 | |||
% PUT /_admin/wal/properties | |||
% 配置预写日志的行为。请求的主体必须是具有以下属性的JSON对象: | |||
% allowOversizeEntries:是否可以执行和存储大于单个日志文件的操作 | |||
% logfileSize:每个预写日志文件的大小 | |||
% HistoricLogfiles:要保留的最大历史日志文件数 | |||
% reserveLogfiles:ArangoDB在后台分配的最大保留日志文件数 | |||
% valveWait:如果发生写限制,则操作在中止之前将等待的最大等待时间(以毫秒为单位) | |||
% acceleratorWhenPending:达到该数量时将激活写限制的未处理垃圾收集操作的数量。值为 0表示将不触发写限制。 | |||
% 指定以上任何属性都是可选的。未指定的属性将被忽略,并且它们的配置也不会被修改。 | |||
% 返回码 | |||
% 200:操作成功返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
setWalProps(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_admin/wal/properties">>, BodyStr, undefined). | |||
% 返回有关当前正在运行的事务的信息 | |||
% GET /_admin/wal/transactions | |||
% 返回有关当前正在运行的事务的信息。结果是具有以下属性的JSON对象: | |||
% runningTransactions:当前正在运行的事务数 | |||
% minLastCollected:最近收集的日志文件的最小ID(在每个正在运行的事务的开始处)。如果没有事务在运行,则为null。 | |||
% minLastSealed:最后密封的日志文件的最小ID(在每个运行的事务的开始处)。如果没有事务在运行,则为null。 | |||
% 返回码 | |||
% 200:操作成功返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
getTransactions(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/wal/transactions">>, [], undefined). | |||
% 获取系统的当前时间 | |||
% GET /_admin/time | |||
% 该调用返回一个属性为time的对象。它包含当前系统时间(以Unix时间戳为单位,精度为微秒)。 | |||
% HTTP 200时间已成功返回。 | |||
% error:布尔值标志,指示是否发生错误(在这种情况下为false) | |||
% code:HTTP状态码 | |||
% time:当前系统时间(以Unix时间戳记为单位,服务器以微秒为单位) | |||
curDbTime(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/time">>, [], undefined). | |||
% 返回当前请求永久链接固定链接 | |||
% 发回所发送的内容,标题,帖子正文等。 | |||
% POST /_admin/echo | |||
% 请求正文(对象) | |||
% 主体可以是任何类型,只需转发即可。 | |||
% 调用返回一个带有服务器请求信息的对象 | |||
% HTTP 200 Echo成功返回。 | |||
% authorized:会话是否被授权 | |||
% user:当前发送此请求的用户 | |||
% database:执行此请求的数据库 | |||
% url:原始请求URL | |||
% protocol:传输,['http','https','velocystream']之一 | |||
% server: | |||
% address:此请求发送到的端点的绑定地址 | |||
% port:此请求发送到的端口 | |||
% client:客户端连接的属性 | |||
% address:客户端的IP地址 | |||
% port:tcp连接的客户端端口 | |||
% id:服务器生成的id | |||
% internals:服务器内部结构的内容 | |||
% prefix:数据库的前缀 | |||
% headers:您发送的HTTP标头的列表 | |||
% requestType:在这种情况下为POST,如果您使用其他HTTP-Verb,您将看到它(获取/删除,...) | |||
% requestBody:我们发送的POST正文的字符串化版本 | |||
% parameters:包含查询参数的对象 | |||
% cookies:您发送的cookie列表 | |||
% suffix: | |||
% rawSuffix: | |||
% path:此请求的相对路径 | |||
% rawRequestBody:已发送字符的数字列表 | |||
echo(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/echo">>, [], BodyStr). | |||
% 返回数据库的版本。 | |||
% GET /_admin/database/target-version | |||
% 返回此服务器所需的数据库版本。版本将在结果的版本属性中返回。 | |||
% 返回码 | |||
% 200:在所有情况下均返回。 | |||
targetVersion(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/database/target-version">>, [], undefined). | |||
% 启动关机序列 | |||
% DELETE /_admin/shutdown | |||
% 此调用将启动干净的关机序列。需要管理权限 | |||
% 返回码 | |||
% 200:在所有情况下OK都将返回,成功时将在结果缓冲区中返回。 | |||
shutDown(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, <<"/_admin/shutdown">>, [], undefined). | |||
% 在服务器上执行脚本。 | |||
% POST /_admin/execute | |||
% 请求正文(字符串) | |||
% 要执行的主体。 | |||
% 将服务器主体中的javascript代码作为不带参数的函数主体执行。如果您有return语句,那么您产生的返回值将作为内容类型 application / json返回。如果参数returnAsJSON设置为 true,则结果将是一个直接描述返回值的JSON对象,否则将返回JSON.stringify生成的字符串。 | |||
% 请注意,只有在使用option启动服务器时,此API端点才会存在--javascript.allow-admin-execute true。 | |||
% 此选项的默认值为false,它禁用用户定义代码的执行并完全禁用此API端点。这也是建议的生产设置。 | |||
% 返回码 | |||
% 200:一切正常或发生超时时返回。在后一种情况下,将返回指示超时的application / json类型的主体。根据returnAsJSON的不同,这是一个json对象或纯字符串。 | |||
% 403:如果ArangoDB不在集群模式下运行,则返回。 | |||
% 404:如果未为群集操作编译ArangoDB,则返回404。 | |||
execute(PoolNameOrSocket, BodyStr) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/execute">>, [], BodyStr). | |||
% 返回服务器的状态信息。 | |||
% GET /_admin/status | |||
% 返回有关服务器的状态信息。 | |||
% 这是供支持人员手动使用的,决不能用于监视或自动测试。结果如有更改,恕不另行通知。 | |||
% 该调用返回具有以下属性的对象: | |||
% server:总是arango。 | |||
% license:社区或企业。 | |||
% version:服务器版本,以字符串形式。 | |||
% mode:服务器或控制台。 | |||
% host:主机名,请参阅ServerState。 | |||
% serverInfo.role:单个,协调器,主要,代理。 | |||
% serverInfo.writeOpsEnabled:布尔值,如果启用写入,则为true。 | |||
% serverInfo.maintenance:布尔值,如果启用了维护模式,则为true。 | |||
% agency.endpoints:可能的代理商端点的列表。 | |||
% agency,协调员或主要代理商也将拥有 | |||
% serverInfo.persistedId:保留的ide,例如“ CRDN-e427b441-5087-4a9a-9983-2fb1682f3e2a”。 | |||
% 协调员或主要人员也将拥有 | |||
% serverInfo.state:服务中 | |||
% serverInfo.address:服务器的地址,例如tcp:// [:: 1]:8530。 | |||
% serverInfo.serverId:服务器端,例如“ CRDN-e427b441-5087-4a9a-9983-2fb1682f3e2a”。 | |||
% 协调员还将有 | |||
% coordinator.foxxmaster:foxx主服务器的服务器ID。 | |||
% coordinator.isFoxxmaster:布尔值,如果服务器是foxx主服务器,则为true。 | |||
% 代理商也将拥有 | |||
% agent.id:此代理的服务器ID。 | |||
% agent.leaderId:领导者的服务器ID。 | |||
% agent。Leading:布尔值,如果为领先则为 true。 | |||
% agent.endpoint:此代理的端点。 | |||
% agent.term:当前术语编号。 | |||
% 返回码 | |||
% 200:状态信息返回成功 | |||
dbStatus(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/status">>, [], undefined). | |||
@ -0,0 +1,75 @@ | |||
-module(agRepairJobs). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
%% doc_address:https://www.arangodb.com/docs/stable/http/repairs.html | |||
% 维修工作 | |||
% DistributionShardsLike | |||
% 在版本3.2.12和3.3.4之前,集合创建中存在一个错误,这可能导致违反该属性,即其碎片与distributeShardsLike设置中的原型集合完全一样地分布在DB-Server上 。 | |||
% | |||
% 使用此API之前,请仔细阅读所有内容! | |||
% | |||
% 有一项可以安全还原此属性的作业。但是,在作业运行期间, | |||
% | |||
% 在replicationFactor 不能改变任何受影响的集合或原型集合(即集distributeShardsLike,包括 SmartGraphs) | |||
% 这些原型之一的碎片也不应移动 | |||
% 修复过程中应避免关闭数据库服务器和关闭数据库服务器。在任何给定时间也只能执行一项维修工作。如果不满足这些要求,通常会导致作业中止,但仍然可以安全地重新启动它。但是,replicationFactor在修理过程中进行更改可能会使其处于无法人工干预而无法修理的状态! | |||
% 关闭执行作业的协调器将中止它,但是可以安全地在另一个协调器上重新启动它。但是,即使作业停止后,仍可能会有碎片移动。如果在移动完成之前再次启动该作业,则修复受影响的集合将失败,但是可以安全地重新启动修复。 | |||
% | |||
% 如果有任何受影响的集合replicationFactor等于数据库服务器的总数,则修复可能会中止。在这种情况下,有必要将其减少replicationFactor一个(或添加一个DB-Server)。这项工作不会自动执行。 | |||
% | |||
% 通常,如果作业的任何假设失败,在开始或维修期间,该作业都会中止。它可以再次启动,并从当前状态恢复。 | |||
% | |||
% 使用测试GET /_admin/repairs/distributeShardsLike | |||
% 使用GET将不会触发任何修复,而只会计算并返回修复群集所需的操作。这样,您还可以检查是否有需要维修的东西。 | |||
%% 检查修复 | |||
%% GET /_admin/repairs/distributeShardsLike | |||
checkRepair(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/repairs/distributeShardsLike">>, [], undefined). | |||
% 如果要修复某些内容,则响应将具有该属性 collections,<db>/<collection>其中包含每个必须修复的集合的条目。每个集合还作为单独的error属性,true如果该集合(以及false 其他情况)发生错误,则将作为错误。如果error为true,则还将设置属性errorNum和 errorMessage,并且在某些情况下还将errorDetails 提供有关如何处理特定错误的附加信息。 | |||
% 用修复POST /_admin/repairs/distributeShardsLike | |||
% 由于此工作可能需要移动大量数据,因此可能需要一段时间,具体取决于受影响集合的大小。因此,这不应该被同步调用,而只能通过 Async Results 来调用:即,设置标头x-arango-async: store将作业放入后台,并在稍后获取其结果。否则,该请求很可能会导致超时,并且响应将丢失!除非协调器已停止,否则该作业仍将继续,但是无法找到它是否仍在运行,或者之后无法获得成功或错误信息。 | |||
% 可以像这样在后台启动作业: | |||
% | |||
% $ wget --method=POST --header='x-arango-async: store' -qSO - http://localhost:8529/_admin/repair/distributeShardsLike | |||
% HTTP/1.1 202 Accepted | |||
% X-Content-Type-Options: nosniff | |||
% X-Arango-Async-Id: 152223973119118 | |||
% Server: ArangoDB | |||
% Connection: Keep-Alive | |||
% Content-Type: text/plain; charset=utf-8 | |||
% Content-Length: 0 | |||
% | |||
% 这条线非常重要: | |||
% | |||
% X-Arango-Async-Id: 152223973119118 | |||
% 因为它包含作业ID,可用于以后获取作业的状态和结果。GET婷/_api/job/pending和/_api/job/done将列出未完成或者完成,分别作业的作业ID。 | |||
% | |||
% 这也可以通过GET测试方法来完成。 | |||
% | |||
% 必须使用job api来获取状态和结果。它会204在作业运行时返回。实际的响应将仅返回一次,然后删除作业,并且api将返回404。因此,建议将响应直接写到文件中以供以后检查。抓取的结果是通过调用/_api/job通过 PUT: | |||
% | |||
% $ wget --method=PUT -qSO - http://localhost:8529/_api/job/152223973119118 | jq . | |||
% HTTP/1.1 200 OK | |||
% X-Content-Type-Options: nosniff | |||
% X-Arango-Async-Id: 152223973119118 | |||
% Server: ArangoDB | |||
% Connection: Keep-Alive | |||
% Content-Type: application/json; charset=utf-8 | |||
% Content-Length: 53 | |||
% { | |||
% "error": false, | |||
% "code": 200, | |||
% "message": "Nothing to do." | |||
% } | |||
%% 修复 | |||
%% POST /_admin/repairs/distributeShardsLike | |||
doRepair(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/repairs/distributeShardsLike">>, [], undefined). |
@ -0,0 +1,734 @@ | |||
-module(agReplication). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address:https://www.arangodb.com/docs/3.6/http/replications.html | |||
% 用于复制的HTTP接口 | |||
% 复制 | |||
% 这是对ArangoDB的HTTP复制接口的介绍。复制架构和组件中更详细地描述了 复制。 | |||
% | |||
% HTTP复制接口有四个主要用途: | |||
% 从服务器获取初始数据(例如用于备份,或用于在启动连续复制应用程序之前进行数据的初始同步) | |||
% 查询主机状态 | |||
% 从主服务器获取连续更改(用于更改的增量同步) | |||
% 在从属服务器上管理复制应用程序(启动,停止,配置,查询状态) | |||
% 请注意,如果使用每个数据库的设置(与服务器级复制相反,从v3.3.0开始可用),则必须为每个数据库分别配置复制系统,并且复制多个数据库的数据将需要进行多个操作。 | |||
% 复制转储命令 | |||
% 该库存方法可用于查询ArangoDB数据库的当前设置的集合加上他们的指标。客户可以使用此方法来概述数据库中存在哪些集合。他们可以使用此信息来启动数据的全部或部分同步,例如启动备份或增量数据同步 | |||
% 返回集合及其索引的概述 | |||
% GET /_api/replication/inventory | |||
% 查询参数 | |||
% includeSystem(可选):在结果中包括系统集合。默认值为true。 | |||
% global (可选):在响应中包括所有数据库。仅适用于_system默认值为false。 | |||
% batchId(必填):此API调用的RocksDB引擎需要有效的batchId | |||
% 返回服务器上可用的集合和索引的数组。复制客户端可以使用此阵列来启动与服务器的初始同步。 | |||
% 响应将包含具有collection和state和 tick属性的JSON对象。 | |||
% 集合是具有以下子属性的集合数组: | |||
% parameters:集合属性 | |||
% indexes:集合索引的数组。主索引和边缘索引不包含在此数组中。 | |||
% 该状态属性包含复制记录器的当前状态。它包含以下子属性: | |||
% running:复制记录器当前是否处于活动状态。注意:从ArangoDB 2.2开始,该值将始终为true | |||
% lastLogTick:复制记录器已写入的最后一个滴答的值 | |||
% time:服务器上的当前时间 | |||
% 复制客户端应注意返回的lastLogTick值。然后,他们可以使用转储方法获取集合的数据直到lastLogTick的值,并在此滴答值之后查询连续复制日志中的日志事件。 | |||
% 要在服务器上创建集合的完整副本,复制客户端可以执行以下步骤: | |||
% 调用/ inventory API方法。这将从服务器返回lastLogTick值以及集合和索引的数组。 | |||
% 对于/ inventory返回的每个集合,请在本地创建集合,然后调用/ dump将集合数据流式传输到客户端,直到lastLogTick的值 为止。之后,客户端可以在/ inventory报告的集合上创建索引。 | |||
% 如果客户端要从记录器服务器连续流式传输复制日志事件,则需要执行以下附加步骤: | |||
% 客户端应首先调用/ logger-follow来获取在客户端调用/ inventory之后记录的第一批复制事件。 | |||
% 对/ logger-follow的调用应使用from参数,其值应为/ inventory报告的lastLogTick的值 。调用/ logger-follow将返回 x-arango-replication-lastincluded,其中将包含响应中包含的最后一个滴答值。 | |||
% 然后,客户端可以连续调用/ logger-follow以递增地获取上次传输后发生的新复制事件。 | |||
% 调用应使用from参数,并带有 上一个响应的x-arango-replication-lastincluded头的值。如果没有更多的复制事件,则响应将为空,客户端可以休眠一会儿,然后再试一次。 | |||
% 注意:在协调器上,此请求必须具有查询参数DBserver,该参数 必须是DBserver的ID。相同的请求被同步转发到该DBserver。如果此属性未在协调程序情况下绑定,则是错误的。 | |||
% 注意::global顶层对象使用参数包含一个键databases ,每个键下的一个键代表一个datbase名称,并且值符合上述说明。 | |||
% 返回码 | |||
% 200:如果请求成功执行,则返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果组装响应时发生错误,则返回500。 | |||
getRepInventory(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/replication/inventory", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 创建新的转储批次 | |||
% 处理转储批处理命令 | |||
% POST /_api/replication/batch | |||
% 注意:这些调用对用户而言并不有趣。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% ttl:新批处理的生存时间(以秒为单位) | |||
% 具有批处理配置的JSON对象。 | |||
% 创建一个新的转储批次并返回批次的ID。 | |||
% 响应是具有以下属性的JSON对象: | |||
% id:批次的ID | |||
% 注意:在协调器上,此请求必须具有查询参数DBserver,该参数 必须是DB-Server的ID。相同的请求被同步转发到该DB服务器。如果在Coordinator情况下未绑定此属性,则会出错。 | |||
% 返回码 | |||
% 200:批量创建成功,返回 | |||
% 400:如果ttl值无效,或者在Coordinator上未指定DBserver属性或该属性非法,则返回400 。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
newRepBatch(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/replication/batch">>, [], BodyStr). | |||
% 删除现有的转储批次固定链接 | |||
% 处理转储批处理命令 | |||
% DELETE /_api/replication/batch/{id} | |||
% 注意:这些调用对用户而言并不有趣。 | |||
% 路径参数 | |||
% id(必填):批次的ID。 | |||
% 删除现有的转储批处理,从而允许恢复压缩和清理。 | |||
% 注意:在协调器上,此请求必须具有查询参数DBserver,该参数 必须是DB-Server的ID。相同的请求被同步转发到该DB服务器。如果在Coordinator情况下未绑定此属性,则会出错。 | |||
% 返回码 | |||
% 204:批量删除成功,返回。 | |||
% 400:如果找不到批次,则返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
delRepBatch(PoolNameOrSocket, BatchId) -> | |||
Path = <<"/_api/replication/batch/", (agMiscUtils:toBinary(BatchId))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
% 延长现有的转储批次固定链接 | |||
% 处理转储批处理命令 | |||
% PUT /_api/replication/batch/{id} | |||
% 注意:这些调用对用户而言并不有趣。 | |||
% 路径参数 | |||
% id(必填):批次的ID。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% ttl:新批次的生存时间(以秒为单位) | |||
% 使用批次的ID和提供的ttl值来扩展现有转储批次的ttl。 | |||
% 如果可以成功扩展批次的ttl,则响应为空。 | |||
% 注意:在协调器上,此请求必须具有查询参数DBserver,该参数 必须是DB-Server的ID。相同的请求被同步转发到该DB服务器。如果在Coordinator情况下未绑定此属性,则会出错。 | |||
% 返回码 | |||
% 204:如果成功扩展了批次的ttl,则返回。 | |||
% 400:如果ttl值无效或未找到批次,则返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 该转储方法可用于从特定集合中获取数据。由于dump命令的结果可能非常庞大,所以dump可能不会一次返回集合中的所有数据。而是,复制客户端可能会重复调用dump命令,直到没有更多数据要提取为止。dump命令不仅会返回集合中的当前文档,还会返回文档的更新和删除。 | |||
% 请注意,dump方法将仅返回文档,日记帐和数据文件中的更新,删除。仅存储在预写日志中的操作将不会返回。为了确保这些操作包含在转储中,必须先清除预写日志。 | |||
% 为了获得相同的数据状态,复制客户端应按照提供的顺序使用转储结果的各个部分。 | |||
prolongRepBatch(PoolNameOrSocket, BatchId, MapData) -> | |||
Path = <<"/_api/replication/batch/", (agMiscUtils:toBinary(BatchId))/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
% 返回集合的数据 | |||
% 返回一个集合的全部内容 | |||
% GET /_api/replication/dump | |||
% 查询参数 | |||
% collection (必填):要转储的集合的名称或ID。 | |||
% chunkSize(可选):返回结果的大约最大大小。 | |||
% batchId(必填):要使用的快照的ID | |||
% 从集合中返回请求范围内的数据。 | |||
% 所述CHUNKSIZE查询参数可用于控制结果的大小。必须以字节为单位指定。该CHUNKSIZE值也仅是被兑现。否则,chunkSize值太低可能导致服务器无法仅将一个条目放入结果中并返回它。因此,只有在将条目写入结果后才能查询chunkSize值。如果结果大小大于 chunkSize,则服务器将以与响应中已经存在的条目一样多的条目进行响应。如果结果大小仍小于chunkSize,则如果还有更多数据要返回,则服务器将尝试返回更多数据。 | |||
% 如果未指定chunkSize,则将使用某些服务器端默认值。 | |||
% 结果的Content-Type是application / x-arango-dump。这是一种易于处理的格式,所有条目都放入响应正文中的单独行中。 | |||
% 每行本身是一个JSON对象,至少具有以下属性: | |||
% tick:操作的tick属性 | |||
% key:文档/边缘的密钥或删除操作中使用的密钥 | |||
% rev:文档/边缘或删除操作的修订版ID | |||
% data:类型2300和2301的实际文档/边缘数据。完整的文档/边缘数据将被返回,甚至进行更新。 | |||
% type:条目的类型。类型的可能值为: | |||
% 2300:文档插入/更新 | |||
% 2301:边缘插入/更新 | |||
% 2302:删除文档/边缘 | |||
% 注意:调用此方法时,插入和更新之间没有区别。 | |||
% 返回码 | |||
% 200:如果成功执行了请求并返回了数据,则返回。标头 x-arango-replication-lastincluded设置为最后返回的文档的刻度。 | |||
% 204:如果请求已成功执行,但没有可用内容,则返回。在这种情况下,标题x-arango-replication-lastincluded是0。 | |||
% 404:找不到集合时返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果组装响应时发生错误,则返回500。 | |||
getRepDump(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/replication/dump", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 返回Merkle树以进行收集 | |||
% 检索与集合关联的Merkle树 | |||
% GET /_api/replication/revisions/tree | |||
% 此基于修订的复制终结点仅适用于RocksDB引擎以及ArangoDB v3.7.0或更高版本中创建的集合。 | |||
% 查询参数 | |||
% collection(必填):要查询的集合的名称或ID。 | |||
% batchId(必填):要使用的快照的ID | |||
% 返回集合中的Merkle树。 | |||
% 结果将是以下格式的JSON / VelocyPack: | |||
% | |||
% { | |||
% version: <Number>, | |||
% branchingFactor: <Number> | |||
% maxDepth: <Number>, | |||
% rangeMin: <String, revision>, | |||
% rangeMax: <String, revision>, | |||
% nodes: [ | |||
% { count: <Number>, hash: <String, revision> }, | |||
% { count: <Number>, hash: <String, revision> }, | |||
% ... | |||
% { count: <Number>, hash: <String, revision> } | |||
% ] | |||
% } | |||
% 目前,只有一个版本1,因此暂时可以忽略此版本。 | |||
% 每个<String, revision>值类型都是一个64位值,编码为11个字符的字符串,使用与我们的文档_rev值相同的编码。原因是64位值不一定必须用JavaScript完整表示,因为它会将所有数字作为浮点进行处理,并且只能2^53-1忠实地表示。 | |||
% 节点数应对应于具有给定maxDepth和 的完整树branchingFactor。节点按级别顺序树遍历进行布局,因此根节点位于index 0,其子节点位于index ,[1, branchingFactor]依此类推。 | |||
% 返回码 | |||
% 200:如果成功执行了请求并返回了数据,则返回。 | |||
% 401:如果缺少必要的参数,则返回 | |||
% 404:找不到集合或快照时返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果组装响应时发生错误,则返回500。 | |||
% 501:如果使用mmfiles调用或在不支持按版本同步的集合上返回,则返回 | |||
getRepTree(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/replication/revisions/tree", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 为集合永久链接重建Merkle树 | |||
% 重建与集合关联的Merkle树 | |||
% POST /_api/replication/revisions/tree | |||
% 此基于修订的复制终结点仅适用于RocksDB引擎以及ArangoDB v3.7.0或更高版本中创建的集合。 | |||
% 查询参数 | |||
% collection(必填):要查询的集合的名称或ID。 | |||
% 重建集合的Merkle树。 | |||
% 如果成功,将不会有返回机构。 | |||
% 返回码 | |||
% 204:如果请求成功执行,则返回。 | |||
% 401:如果缺少必要的参数,则返回 | |||
% 404:集合或找不到时返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果组装响应时发生错误,则返回500。 | |||
% 501:如果使用mmfiles调用或在不支持按版本同步的集合上返回,则返回 | |||
resetRepTree(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/replication/revisions/tree", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], undefined). | |||
% 返回要求范围内的修订版ID 永久链接 | |||
% 检索请求范围内的文档的修订ID | |||
% PUT /_api/replication/revisions/ranges | |||
% 此基于修订的复制终结点仅适用于RocksDB引擎以及ArangoDB v3.7.0或更高版本中创建的集合。 | |||
% 查询参数 | |||
% collection(必填):要查询的集合的名称或ID。 | |||
% batchId(必填):要使用的快照的ID | |||
% resume(可选):如果先前的请求被截断,则恢复的修订版本 | |||
getRepRanges(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/replication/revisions/ranges", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined). | |||
% 通过修订返回文档 | |||
% 通过修订检索文档 | |||
% PUT /_api/replication/revisions/documents | |||
% 此基于修订的复制终结点仅适用于RocksDB引擎以及ArangoDB v3.7.0或更高版本中创建的集合。 | |||
% 查询参数 | |||
% collection(必填):要查询的集合的名称或ID。 | |||
% batchId(必填):要使用的快照的ID | |||
% 通过修订返回文档 | |||
% 请求的主体应为JSON / VelocyPack,并应包含一个字符串编码的修订ID数组: | |||
% [ | |||
% <String, revision>, | |||
% <String, revision>, | |||
% ... | |||
% <String, revision> | |||
% ] | |||
% 特别是,修订版本应按其解码值的升序排序。 | |||
% 结果将是文档对象的JSON / VelocyPack数组。如果没有对应于特定请求版本的文档,则将在其位置返回一个空对象。 | |||
% 如果响应很长,则响应可能会被截断。在这种情况下,响应数组的长度将小于请求数组的长度,并且可以对省略的文档进行后续请求。 | |||
% 每个<String, revision>值类型都是一个64位值,编码为11个字符的字符串,使用与我们的文档_rev值相同的编码。原因是64位值不一定必须用JavaScript完整表示,因为它会将所有数字作为浮点进行处理,并且只能2^53-1忠实地表示。 | |||
% 返回码 | |||
% 200:如果成功执行了请求并返回了数据,则返回。 | |||
% 401:如果必要的参数丢失或不正确,则返回 | |||
% 404:找不到集合或快照时返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果组装响应时发生错误,则返回500。 | |||
% 501:如果使用mmfiles调用或在不支持按版本同步的集合上返回,则返回 | |||
getRepDoc(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/replication/revisions/documents", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined). | |||
% 从远程端点同步数据永久 | |||
% 开始复制 | |||
% PUT /_api/replication/sync | |||
% 具有以下属性的JSON对象是必需的: | |||
% endpoint:要连接的主端点(例如“ tcp://192.168.173.13:8529”)。 | |||
% database:主数据库上的数据库名称(如果未指定,则默认为本地当前数据库的名称)。 | |||
% username:连接到端点时要使用的可选ArangoDB用户名。 | |||
% password:连接到端点时使用的密码。 | |||
% includeSystem:是否将应用系统收集操作 | |||
% incremental:如果设置为true,则将使用增量同步方法来同步集合中的数据。当集合已经在本地存在并且仅需要从远程端点转移剩余的差异时,此方法很有用。在这种情况下,增量同步可以比完全同步更快。默认值为false,这意味着将传输来自远程集合的完整数据。 | |||
% strictType:用于集合过滤的可选字符串值。指定时,允许的值包括include或exclude。 | |||
% restrictCollections:集合用于在使用的可选阵列 restrictType。如果limitType为include,则仅指定的集合将被同步。如果limitType为exclude,则将同步除指定集合以外的所有集合。 | |||
% initialSyncMaxWaitTime:在获取初始收集数据时,初始同步将等待主服务器响应的最长时间(以秒为单位)。此等待时间可用于控制初始同步将在多长时间后放弃等待响应并失败。如果设置为0,则将忽略此值。 | |||
% 启动从远程端点到本地ArangoDB数据库的完整数据同步。 | |||
% 该同步方法可以通过复制客户端使用一个ArangoDB数据库连接到远程端点,取收集和索引,以及收集数据的远程列表。因此,它将在远程ArangoDB数据库上创建数据状态的本地备份。同步在每个数据库级别进行。 | |||
% sync首先会从远程端点获取集合和索引的列表。通过调用远程数据库的清单 API来实现。然后它将清除本地ArangoDB数据库中的数据,并且在启动后会将收集数据从远程数据库传输到本地ArangoDB数据库。它将通过调用远程数据库的转储 API 从远程数据库中提取数据,直到获取所有数据为止。 | |||
% 如果成功,响应的主体是具有以下属性的JSON对象: | |||
% 集合:从端点转移来的一系列集合 | |||
% lastLogTick:传输开始时端点上的最后一个日志滴答。使用这个值作为从后开始连续同步时的价值。 | |||
% 警告:调用此方法将把数据从远程端点上的集合同步到本地ArangoDB数据库。本地集合中的所有数据将被清除,并替换为端点中的数据。 | |||
% 请谨慎使用! | |||
% 注意:集群中的协调器不支持此方法。 | |||
% 返回码 | |||
% 200:如果请求成功执行,则返回。 | |||
% 400:配置不完整或格式错误,返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果同步期间发生错误,则返回。 | |||
% 501:在集群中的协调器上调用此操作时返回。 | |||
startRepSync(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/replication/sync">>, [], BodyStr). | |||
% 返回集合和索引的集群清单 | |||
% 重建集群中的集合和索引的概述 | |||
% GET /_api/replication/clusterInventory | |||
% 查询参数 | |||
% includeSystem(可选):在结果中包括系统集合。默认值为true。 | |||
% 返回群集上可用的集合和索引的数组。 | |||
% 响应将是一个JSON对象数组,每个集合一个。每个集合精确地包含两个关键的“参数”和“索引”。此信息来自计划/收藏/ {DB-名称} / * 的机构,只是对指标属性有搬迁到其调整到arangodump的数据格式。 | |||
% 返回码 | |||
% 200:如果请求成功执行,则返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果组装响应时发生错误,则返回500。 | |||
getRepClusterInv(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/replication/clusterInventory", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 复制记录器命令 | |||
% 早期版本的ArangoDB允许启动,停止和配置复制记录器。这些命令在ArangoDB 2.2中是多余的,因为所有数据修改操作均写入服务器的预写日志,并且不再由单独的记录器处理。 | |||
% 自ArangoDB 2.2以来,剩下的唯一有用的操作是查询记录器的当前状态并获取记录器写入的最新更改。这些操作将从预写日志中返回状态和数据。 | |||
% 复制记录器命令 | |||
% 返回复制记录器的状态 | |||
% GET /_api/replication/logger-state | |||
% 返回服务器复制记录器的当前状态。该状态将包括有关记录器是否正在运行以及有关最后记录的滴答值的信息。此刻度值对于增量获取数据很重要。 | |||
% 响应的主体包含具有以下属性的JSON对象: | |||
% state:当前记录器状态,是带有以下子属性的JSON对象: | |||
% running:记录仪是否正在运行 | |||
% lastLogTick:记录器记录的最新滴答的滴答值。此值可用于增量获取日志数据。 | |||
% totalEvents:自服务器启动以来记录的事件总数。在记录仪的多次停止和重新启动之间不会重置该值。 | |||
% time:记录器服务器上的当前日期和时间 | |||
% server:具有以下子属性的JSON对象: | |||
% version:记录器服务器的版本 | |||
% serverId:记录器服务器的ID | |||
% client:通过连接到记录器的复制客户端返回上一次获取状态。每个客户端均作为具有以下属性的JSON对象返回: | |||
% syncerId:客户端同步器的ID | |||
% serverId:客户端的服务器ID | |||
% lastServedTick:通过logger-follow API 向此客户端提供的最后一个滴答值 | |||
% time:此客户端最后一次调用logger-follow API的日期和时间 | |||
% 返回码 | |||
% 200:如果可以成功确定记录器状态,则返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果无法确定记录器状态,则返回。 | |||
getRepLoggerState(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/replication/logger-state">>, [], undefined). | |||
% 返回日志条目永久链接固定链接 | |||
% 从服务器获取日志行 | |||
% GET /_api/replication/logger-follow | |||
% 此路由不应再使用。从3.4.0版开始,它被视为已弃用。 | |||
% 查询参数 | |||
% from(可选):结果的排他性下界刻度值。 | |||
% 到(可选):结果的包含上限刻度值。 | |||
% chunkSize(可选):返回结果的大约最大大小。 | |||
% includeSystem(可选):在结果中包括系统集合。默认值为true。 | |||
% 从服务器的复制日志中返回数据。初始同步数据后,复制客户端可以调用此方法。该方法将从记录器服务器返回所有“最近”的日志条目,并且客户端可以在本地重播和应用这些条目,以使它们进入与记录器服务器相同的数据状态。 | |||
% 客户端可以重复调用此方法,以从记录器服务器增量获取所有更改。在这种情况下,它们应提供from值,这样它们将仅自上次获取以来返回日志事件。 | |||
% 当不使用from查询参数时,记录器服务器将从其复制日志的开头开始返回日志条目。当使用from 参数时,记录器服务器将仅返回滴答值大于指定的from值的日志条目(注意:滴答值等于from的日志条目将被排除)。增量获取日志数据时,请使用from值。 | |||
% 的到查询参数可被用于任选地限制上部结合的结果到一定刻度值。如果使用,结果将仅包含滴答值最大为(包括)到的日志事件。在增量读取中,无需使用to参数。仅在只需要部分变更日志的特殊情况下才有意义。 | |||
% 所述CHUNKSIZE查询参数可用于控制结果的大小。必须以字节为单位指定。该CHUNKSIZE值也仅是被兑现。否则,chunkSize值太低可能导致服务器无法仅将一个日志条目放入结果中并将其返回。因此,只有在将日志条目写入结果后才能查询chunkSize值。如果结果大小大于 chunkSize,则服务器将以与响应中已经存在的日志条目一样多的日志条目进行响应。如果结果大小仍小于chunkSize,则如果还有更多数据要返回,则服务器将尝试返回更多数据。 | |||
% 如果未指定chunkSize,则将使用某些服务器端默认值。 | |||
% 结果的Content-Type是application / x-arango-dump。这是一种易于处理的格式,所有日志事件进入响应正文中的单独行。每个日志事件本身都是一个JSON对象,至少具有以下属性: | |||
% tick:日志事件刻度值 | |||
% type:日志事件类型 | |||
% 各个日志事件还将具有其他属性,具体取决于事件类型。用于多种事件类型的一些常见属性是: | |||
% cid:事件用于的集合的ID | |||
% tid:包含该事件的交易的ID | |||
% 密钥:文档密钥 | |||
% rev:文档修订版ID | |||
% data:原始文档数据 | |||
% 有关各个复制事件类型及其数据结构的详细说明,请参见“ 操作类型”。 | |||
% 响应还将包含以下HTTP标头: | |||
% x-arango-replication-active:记录器是否处于活动状态。客户可以使用该标志来指示其轮询频率。如果记录器未处于活动状态,并且没有其他可用的复制事件,则客户机中止或进入睡眠状态很长时间,然后稍后重试以检查记录器是否已激活可能是明智的。 | |||
% x-arango-replication-lastincluded:结果中最后一个包含值的刻度值。在增量日志取,该值可以被用作从以下请求值。请注意,如果结果为空,则该值为0。客户端不应在下一个请求中将此值用作from的值(否则服务器将从日志的开头再次返回日志事件)。 | |||
% x-arango-replication-lasttick:记录服务器已记录的最后一个滴答值(不一定包含在结果中)。通过比较最后的报价和最后包括的报价值,客户端可以大致了解仍有多少事件需要提取。 | |||
% x-arango-replication-checkmore:是否已经存在客户端可以立即获取的更多日志数据。如果有更多日志数据可用,则客户端可以再次调用logger-follow,并将其 值调整为从中取出其余日志条目,直到没有更多日志条目为止。 | |||
% 如果没有更多的日志数据要获取,则客户端可能决定在再次调用记录器之前先进入睡眠状态。 | |||
% 注意:集群中的协调器不支持此方法。 | |||
% 返回码 | |||
% 200:如果成功执行了请求,则返回该值,并且存在可用于请求范围的日志事件。在这种情况下,响应主体将不会为空。 | |||
% 204:如果成功执行了请求,则返回该值,但是对于所请求的范围没有可用的日志事件。在这种情况下,响应主体将为空。 | |||
% 400:如果from或to值无效,则返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果组装响应时发生错误,则返回500。 | |||
% 501:在集群中的协调器上调用此操作时返回。 | |||
% 此路由不应再使用。从3.4.0版开始,它被视为已弃用。 不予实现 | |||
% 从服务器返回第一个可用的滴答值 | |||
% GET /_api/replication/logger-first-tick | |||
% 返回可以从服务器的复制日志提供的第一个可用的滴答值。在确定某些数据(由刻度值标识)是否仍可用于复制之后,复制客户端可以调用此方法。 | |||
% 结果是一个包含属性firstTick的JSON对象。此属性包含服务器的复制日志中可用的最小刻度值。 | |||
% 注意:集群中的协调器不支持此方法。 | |||
% 返回码 | |||
% 200:如果请求成功执行,则返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果组装响应时发生错误,则返回500。 | |||
% 501:在集群中的协调器上调用此操作时返回。 | |||
getRepLoggerFirstTick(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/replication/logger-first-tick">>, [], undefined). | |||
% 返回日志文件中可用的刻度值范围 | |||
% GET /_api/replication/logger-tick-ranges | |||
% 返回所有当前可用的WAL日志文件的刻度值的当前可用范围。刻度值可用于确定某些数据(由刻度值标识)是否仍可用于复制。 | |||
% 响应的主体包含一个JSON数组。每个数组成员都是一个描述单个日志文件的对象。每个对象都具有以下属性: | |||
% datafile:日志文件的名称 | |||
% status:数据文件的状态,以文本形式显示(例如,“密封”,“打开”) | |||
% tickMin:日志文件中包含的最小刻度值 | |||
% tickMax:日志文件中包含的最大刻度值 | |||
% 返回码 | |||
% 200:如果刻度范围可以成功确定,则返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果无法确定记录器状态,则返回。 | |||
% 501:在集群中的协调器上调用此操作时返回。 | |||
getRepLoggerTickRanges(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/replication/logger-tick-ranges">>, [], undefined). | |||
% 复制应用程序命令 | |||
% applier命令允许远程启动,停止和查询ArangoDB数据库复制应用程序的状态和配置。 | |||
% 复制应用程序命令 | |||
% applier命令允许远程启动,停止和查询ArangoDB数据库复制应用程序的状态和配置。 | |||
% 获取当前的复制配置 | |||
% GET /_api/replication/applier-config | |||
% 查询参数 | |||
% global (可选):如果设置为true,则返回所有数据库的全局复制应用程序的配置。如果设置为false,则返回所选数据库中复制应用程序的配置(默认)。 | |||
% 返回复制应用程序的配置。 | |||
% 响应的主体是带有配置的JSON对象。配置中可能存在以下属性: | |||
% 端点:要连接的记录器服务器(例如“ tcp://192.168.173.13:8529”)。 | |||
% database:要连接的数据库的名称(例如“ _system”)。 | |||
% username:连接到端点时要使用的可选ArangoDB用户名。 | |||
% password:连接到端点时使用的密码。 | |||
% maxConnectRetries:应用程序连续进行的最大连接尝试次数。如果应用程序无法通过此尝试次数建立与端点的连接,它将停止运行。 | |||
% connectTimeout:尝试连接到端点时的超时(以秒为单位)。该值用于每次连接尝试。 | |||
% requestTimeout:对端点的单个请求的超时(以秒为单位)。 | |||
% chunkSize:请求的日志传输包的最大大小,该大小在联系端点时使用。 | |||
% autoStart:是否在(下一个及之后)服务器上自动启动复制应用程序 | |||
% adaptivePolling:复制应用程序是否将使用自适应轮询。 | |||
% includeSystem:是否将应用系统收集操作 | |||
% autoResync:如果主服务器无法提供从属服务器请求的日志数据,或者在复制开始且找不到滴答值时,从属服务器是否应与主服务器执行全自动重新同步。 | |||
% autoResyncRetries:启用和启用自动重新同步后,将连续执行的重新同步重试次数。将其设置为0将有效地禁用autoResync。将其设置为其他值将限制重试的次数。如果重新同步总是失败,这有助于防止无休止的重试。 | |||
% initialSyncMaxWaitTime:在获取初始收集数据时,初始同步将等待主服务器响应的最长时间(以秒为单位)。此等待时间可用于控制初始同步将在多长时间后放弃等待响应并失败。即使将autoResync设置为true时,该值也适用于连续复制,因为当主服务器无法提供从属服务器所需的日志数据时,此值可能会重新启动初始同步。如果设置为0,则将忽略此值。 | |||
% connectionRetryWaitTime:如果出现连接问题,应用程序在重试连接到主服务器之前将有意空闲的时间(以秒为单位)。如果设置为0,则将忽略此值。 | |||
% idleMinWaitTime:如果主服务器已经发送了所有日志数据,那么在从主服务器获取更多日志数据之前,应用程序将有意空闲的最短等待时间(以秒为单位)。该等待时间可用于控制复制应用程序向主服务器发送HTTP日志获取请求的频率,以防主服务器上没有写入活动。如果设置为0,则将忽略此值。 | |||
% idleMaxWaitTime:如果主服务器已经发送了所有日志数据并且之前进行了日志获取尝试而没有更多日志数据,那么在从主服务器获取更多日志数据之前,应用程序将有意空闲的最大等待时间(以秒为单位) 。该等待时间可用于控制复制应用程序向主服务器发送HTTP日志获取请求的最大频率,以防主服务器上长时间没有写入活动。仅当选项adaptivePolling设置为true时,才使用此配置值。如果设置为0,则将忽略此值。 | |||
% requireFromPresent:如果设置为true,那么复制应用程序将在启动时检查主服务器上是否仍存在从中开始复制或恢复复制的起始时间。否则,将丢失数据。如果 requireFromPresent为true,则复制应用程序将中止并显示相应的错误消息。如果设置为false,那么复制应用程序仍将启动,并忽略数据丢失。 | |||
% verbose:如果设置为true,那么将为复制应用程序执行的所有操作发出一条日志行。这仅应用于调试复制问题。 | |||
% restrictType:为配置restrictCollections | |||
% restrictCollections:集合的可选阵列包括或排除的基础上,设定restrictType | |||
% 返回码 | |||
% 200:如果请求成功执行,则返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果组装响应时发生错误,则返回500。 | |||
getRepApplierConfig(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/replication/applier-config">>, [], undefined). | |||
getRepApplierConfig(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/replication/applier-config", QueryBinary/binary>>, [], undefined). | |||
% 设置申请者的配置值 | |||
% PUT /_api/replication/applier-config | |||
% 查询参数 | |||
% 全局(可选):如果设置为true,则为所有数据库调整全局复制应用程序的配置。如果设置为false,则调整所选数据库中复制应用程序的配置(默认)。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% 端点:要连接的记录器服务器(例如“ tcp://192.168.173.13:8529”)。必须指定端点。 | |||
% database:端点上数据库的名称。如果未指定,则默认为当前本地数据库名称。 | |||
% username:连接到端点时要使用的可选ArangoDB用户名。 | |||
% password:连接到端点时使用的密码。 | |||
% maxConnectRetries:应用程序连续进行的最大连接尝试次数。如果应用程序无法通过此尝试次数建立与端点的连接,它将停止运行。 | |||
% connectTimeout:尝试连接到端点时的超时(以秒为单位)。该值用于每次连接尝试。 | |||
% requestTimeout:对端点的单个请求的超时(以秒为单位)。 | |||
% chunkSize:请求的日志传输包的最大大小,该大小在联系端点时使用。 | |||
% autoStart:是否在(下一个及之后)服务器上自动启动复制应用程序 | |||
% adaptivePolling:如果设置为true,则复制应用程序将进入睡眠状态,睡眠时间会越来越长,以防端点上的记录器服务器没有更多的复制事件可应用。因此,对于只有很少更改的情况,使用自适应轮询对于减少应用程序和记录器服务器的工作量很有用。不利之处在于,使用自适应轮询时,复制应用程序检测到记录服务器上有新的复制事件的时间可能会更长。 | |||
% 将adaptivePolling设置为false将使复制者以固定的时间间隔与记录器服务器联系,而不管记录器服务器是频繁提供更新还是很少提供更新。 | |||
% includeSystem:是否将应用系统收集操作 | |||
% autoResync:如果主服务器无法提供从属服务器请求的日志数据,或者在复制开始且找不到滴答值时,从属服务器是否应与主服务器执行全自动重新同步。 | |||
% autoResyncRetries:启用和启用自动重新同步后,将连续执行的重新同步重试次数。将其设置为0 将有效地禁用autoResync。将其设置为其他值将限制重试的次数。如果重新同步总是失败,这有助于防止无休止的重试。 | |||
% initialSyncMaxWaitTime:在获取初始收集数据时,初始同步将等待主服务器响应的最长时间(以秒为单位)。此等待时间可用于控制初始同步将在多长时间后放弃等待响应并失败。即使将autoResync设置为true时,该值也适用于连续复制,因为当主服务器无法提供从属服务器所需的日志数据时,此值可能会重新启动初始同步。如果设置为0,则将忽略此值。 | |||
% connectionRetryWaitTime:如果出现连接问题,应用程序在重试连接到主服务器之前将有意空闲的时间(以秒为单位)。如果设置为0,则将忽略此值。 | |||
% idleMinWaitTime:如果主服务器已经发送了所有日志数据,那么在从主服务器获取更多日志数据之前,应用程序将有意空闲的最短等待时间(以秒为单位)。该等待时间可用于控制复制应用程序向主服务器发送HTTP日志获取请求的频率,以防主服务器上没有写入活动。如果设置为0,则将忽略此值。 | |||
% idleMaxWaitTime:如果主服务器已经发送了所有日志数据并且之前进行了日志获取尝试而没有更多日志数据,那么在从主服务器获取更多日志数据之前,应用程序将有意空闲的最大等待时间(以秒为单位) 。该等待时间可用于控制复制应用程序向主服务器发送HTTP日志获取请求的最大频率,以防主服务器上长时间没有写入活动。仅当选项adaptivePolling设置为true时,才使用此配置值 。如果设置为0,则将忽略此值。 | |||
% requireFromPresent:如果设置为true,那么复制应用程序将在启动时检查主服务器上是否仍存在从中开始复制或恢复复制的起始时间。否则,将丢失数据。如果 requireFromPresent为true,则复制应用程序将中止并显示相应的错误消息。如果设置为false,那么复制应用程序仍将启动,并忽略数据丢失。 | |||
% verbose:如果设置为true,那么将为复制应用程序执行的所有操作发出一条日志行。这仅应用于调试复制问题。 | |||
% restrictType:为配置restrictCollections ; 必须包含或排除 | |||
% restrictCollections:集合的阵列要包括或排除的基础上,设定restrictType | |||
% 设置复制应用程序的配置。仅当应用程序未运行时才能更改配置。更新后的配置将立即保存,但仅在应用程序的下一次启动时生效。 | |||
% 如果成功,响应的主体是具有更新配置的JSON对象。 | |||
% 返回码 | |||
% 200:如果请求成功执行,则返回。 | |||
% 400:如果配置不完整或格式错误,或者当前正在运行复制应用程序,则返回400。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果组装响应时发生错误,则返回500。 | |||
setRepApplierConfig(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/replication/applier-config">>, [], BodyStr). | |||
setRepApplierConfig(PoolNameOrSocket, MapData, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/replication/applier-config", QueryBinary/binary>>, [], BodyStr). | |||
% 启动复制应用程序 | |||
% PUT /_api/replication/applier-start | |||
% 查询参数 | |||
% 全局(可选):如果设置为true,则为所有数据库启动全局复制应用程序。如果设置为false,则在所选数据库中启动复制应用程序(默认)。 | |||
% from(可选):从其开始应用的远程lastLogTick值。如果未指定,则使用上一次申请者运行中最后保存的刻度。如果没有保存以前的申请者状态,则申请者将从记录器服务器日志的开头开始。 | |||
% 启动复制程序。如果复制应用程序已经在运行,它将立即返回。 | |||
% 如果复制应用程序尚未运行,则将检查该应用程序配置,如果复制程序已完成,则将在后台线程中启动该应用程序。这意味着即使应用程序在运行时遇到任何错误,也不会在对此方法的响应中报告这些错误。 | |||
% 要在启动应用程序后检测复制应用程序错误,请改用 / _api / replication / applier-state API。 | |||
% 返回码 | |||
% 200:如果请求成功执行,则返回。 | |||
% 400:如果复制申请人未完全配置或配置无效,则返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果组装响应时发生错误,则返回500。 | |||
startRepApplier(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/replication/applier-start">>, [], undefined). | |||
startRepApplier(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/replication/applier-start", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined). | |||
% 停止复制 | |||
% PUT /_api/replication/applier-stop | |||
% 查询参数 | |||
% 全局(可选):如果设置为true,则停止所有数据库的全局复制应用程序。如果设置为false,则在所选数据库中停止复制应用程序(默认)。 | |||
% 停止复制程序。如果复制应用程序未运行,它将立即返回。 | |||
% 返回码 | |||
% 200:如果请求成功执行,则返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果组装响应时发生错误,则返回500。 | |||
stopRepApplier(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/replication/applier-stop">>, [], undefined). | |||
stopRepApplier(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/replication/applier-stop", QueryBinary/binary>>, [], undefined). | |||
% 输出复制的当前状态 | |||
% GET /_api/replication/applier-state | |||
% 查询参数 | |||
% 全局(可选):如果设置为true,则返回所有数据库的全局复制应用程序的状态。如果设置为false,则返回所选数据库中复制应用程序的状态(默认)。 | |||
% 返回复制应用程序的状态,无论该应用程序当前是否正在运行。 | |||
% 响应是具有以下属性的JSON对象: | |||
% state:具有以下子属性的JSON对象: | |||
% running:申请者是否处于活动状态并正在运行 | |||
% lastAppliedContinuousTick:应用者已应用的连续复制日志中的最后一个滴答值。 | |||
% lastProcessedContinuousTick:申请人已处理的连续复制日志中的最后一个滴答值。 | |||
% 通常,最后应用和最后处理的滴答值应相同。对于事务操作,复制应用程序将首先处理传入的日志事件,然后再应用它们,因此处理的滴答值可能会高于所应用的滴答值。在申请人遇到事务的事务提交日志事件之前,情况将一直如此。 | |||
% lastAvailableContinuousTick:远程服务器可以为所有数据库提供的最后一个滴答值。 | |||
% ticksBehind:仅当应用程序当前正在运行时,此属性才存在。它将提供申请者已应用/看到的内容与远程服务器提供的最后一个日志滴答值之间的日志滴答数。如果该值为零,则两个服务器都处于同步状态。如果该值不为零,则远程服务器具有尚未提取和处理应用程序的其他数据,或者远程服务器可能具有其他不适用于该应用程序的数据。 | |||
% 客户端应用程序可以使用它来确定大致距离远程服务器后面的应用程序,并且可以定期检查该值是增加(应用程序落后)还是减小(应用程序赶上)。 | |||
% 请注意,由于远程服务器将仅为其所有数据库保留最后一个日志滴答值,但是复制可能只限于应用程序中的某些数据库,因此使用全局应用程序时,此值更有意义。此外,由于对由于复制配置而无法复制的系统集合的写入,远程服务器提供的最后一个日志滴答可能会增加。因此,在某些情况下,报告的值可能会夸大现实。 | |||
% time:应用服务器上的时间。 | |||
% totalRequests:应用程序向端点发出的请求总数。 | |||
% totalFailedConnects:应用程序进行的失败连接尝试总数。 | |||
% totalEvents:应用程序已处理的日志事件总数。 | |||
% totalOperationsExcluded:由于restrictCollections而被排除的日志事件总数。 | |||
% progress:一个JSON对象,其中包含有关复制应用程序进度的详细信息。如果有报告进度,则它包含以下子属性: | |||
% 消息:进度的文字描述 | |||
% 时间:记录进度的日期和时间 | |||
% failedConnects:当前失败的连接尝试次数 | |||
% lastError:一个JSON对象,其中包含有关应用程序上发生的最后一个错误的详细信息。如果发生错误,它包含以下子属性: | |||
% errorNum:数字错误代码 | |||
% errorMessage:文本错误描述 | |||
% 时间:发生错误的日期和时间 | |||
% 如果没有发生错误,lastError将为空。 | |||
% server:具有以下子属性的JSON对象: | |||
% version:应用服务器的版本 | |||
% serverId:应用服务器的ID | |||
% 端点:应用程序连接到的端点(如果应用程序处于活动状态)或将连接到端点(如果应用程序当前处于非活动状态) | |||
% database:应用程序连接到的数据库的名称(如果应用程序处于活动状态)或将连接(如果应用程序当前处于非活动状态) | |||
% 请注意,所有返回的“刻度”值都没有特定的单位。刻度值仅在相互比较时才有意义。较高的报价值表示比较低的报价值“时间更晚”。 | |||
% 返回码 | |||
% 200:如果请求成功执行,则返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果组装响应时发生错误,则返回500。 | |||
getRepApplierState(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/replication/applier-state">>, [], undefined). | |||
getRepApplierState(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/replication/applier-state", QueryBinary/binary>>, [], undefined). | |||
% 将角色更改为奴隶 | |||
% PUT /_api/replication/make-slave | |||
% 具有以下属性的JSON对象是必需的: | |||
% 端点:要连接的主端点(例如“ tcp://192.168.173.13:8529”)。 | |||
% database:主数据库上的数据库名称(如果未指定,则默认为本地当前数据库的名称)。 | |||
% username:连接到主服务器时使用的可选ArangoDB用户名。 | |||
% password:连接到主服务器时使用的密码。 | |||
% includeSystem:是否将应用系统收集操作 | |||
% strictType:用于集合过滤的可选字符串值。指定时,允许的值包括include或exclude。 | |||
% restrictCollections:集合用于在使用的可选阵列restrictType。如果limitType为include,则仅指定的集合将被同步。如果limitType为exclude,则将同步除指定集合以外的所有集合。 | |||
% maxConnectRetries:应用程序连续进行的最大连接尝试次数。如果应用程序无法通过此尝试次数建立与端点的连接,它将停止运行。 | |||
% connectTimeout:尝试连接到端点时的超时(以秒为单位)。该值用于每次连接尝试。 | |||
% requestTimeout:对端点的单个请求的超时(以秒为单位)。 | |||
% chunkSize:请求的日志传输包的最大大小,该大小在联系端点时使用。 | |||
% adaptivePolling:复制应用程序是否将使用自适应轮询。 | |||
% autoResync:如果主服务器无法提供从属服务器请求的日志数据,或者在复制开始且找不到刻度值时,从属服务器是否应与主服务器执行自动重新同步。 | |||
% autoResyncRetries:启用和启用自动重新同步后,将连续执行的重新同步重试次数。将其设置为0将有效地禁用autoResync。将其设置为其他值将限制重试的次数。如果重新同步总是失败,这有助于防止无休止的重试。 | |||
% initialSyncMaxWaitTime:在获取初始收集数据时,初始同步将等待主服务器响应的最长时间(以秒为单位)。此等待时间可用于控制初始同步将在多长时间后放弃等待响应并失败。即使将autoResync设置为true时,该值也适用于连续复制,因为当主服务器无法提供从属服务器所需的日志数据时,此值可能会重新启动初始同步。如果设置为0,则将忽略此值。 | |||
% connectionRetryWaitTime:如果出现连接问题,应用程序在重试连接到主服务器之前将有意空闲的时间(以秒为单位)。如果设置为0,则将忽略此值。 | |||
% idleMinWaitTime:如果主服务器已经发送了所有日志数据,那么在从主服务器获取更多日志数据之前,应用程序将有意空闲的最短等待时间(以秒为单位)。该等待时间可用于控制复制应用程序向主服务器发送HTTP日志获取请求的频率,以防主服务器上没有写入活动。如果设置为0,则将忽略此值。 | |||
% idleMaxWaitTime:如果主服务器已经发送了所有日志数据并且之前进行了日志获取尝试而没有更多日志数据,那么在从主服务器获取更多日志数据之前,应用程序将有意空闲的最大等待时间(以秒为单位) 。该等待时间可用于控制复制应用程序向主服务器发送HTTP日志获取请求的最大频率,以防主服务器上长时间没有写入活动。仅当选项adaptivePolling设置为true时,才使用此配置值 。如果设置为0,则将忽略此值。 | |||
% requireFromPresent:如果设置为true,则复制应用程序将在其连续复制开始时检查转储阶段的开始时间是否仍存在于主服务器上。否则,将丢失数据。如果 requireFromPresent为true,则复制应用程序将中止并显示相应的错误消息。如果设置为false,那么复制应用程序仍将启动,并忽略数据丢失。 | |||
% verbose:如果设置为true,那么将为复制应用程序执行的所有操作发出一条日志行。这仅应用于调试复制问题。 | |||
% 启动从远程端点到本地ArangoDB数据库的完整数据同步,然后启动连续复制。该操作在每个数据库级别上进行。 | |||
% 同步之前,将删除所有本地数据库数据。 | |||
% 如果成功,响应的主体是具有以下属性的JSON对象: | |||
% state:具有以下子属性的JSON对象: | |||
% running:申请者是否处于活动状态并正在运行 | |||
% lastAppliedContinuousTick:应用者已应用的连续复制日志中的最后一个滴答值。 | |||
% lastProcessedContinuousTick:申请人已处理的连续复制日志中的最后一个滴答值。 | |||
% 通常,最后应用和最后处理的滴答值应相同。对于事务操作,复制应用程序将首先处理传入的日志事件,然后再应用它们,因此处理的滴答值可能会高于所应用的滴答值。在申请人遇到事务的事务提交日志事件之前,情况将一直如此。 | |||
% lastAvailableContinuousTick:远程服务器可以提供的最后一个滴答值。 | |||
% ticksBehind:仅当应用程序当前正在运行时,此属性才存在。它将提供申请者已应用/看到的内容与远程服务器提供的最后一个日志滴答值之间的日志滴答数。如果该值为零,则两个服务器都处于同步状态。如果该值不为零,则远程服务器具有尚未提取和处理应用程序的其他数据,或者远程服务器可能具有其他不适用于该应用程序的数据。 | |||
% 客户端应用程序可以使用它来确定大致距离远程服务器后面的应用程序,并且可以定期检查该值是增加(应用程序落后)还是减小(应用程序赶上)。 | |||
% 请注意,由于远程服务器将仅为其所有数据库保留最后一个日志滴答值,但是复制可能只限于应用程序中的某些数据库,因此使用全局应用程序时,此值更有意义。此外,由于对由于复制配置而无法复制的系统集合的写入,远程服务器提供的最后一个日志滴答可能会增加。因此,在某些情况下,报告的值可能会夸大现实。 | |||
% time:应用服务器上的时间。 | |||
% totalRequests:应用程序向端点发出的请求总数。 | |||
% totalFailedConnects:应用程序进行的失败连接尝试总数。 | |||
% totalEvents:应用程序已处理的日志事件总数。 | |||
% totalOperationsExcluded:由于restrictCollections而被排除的日志事件总数。 | |||
% progress:一个JSON对象,其中包含有关复制应用程序进度的详细信息。如果有报告进度,则它包含以下子属性: | |||
% 消息:进度的文字描述 | |||
% 时间:记录进度的日期和时间 | |||
% failedConnects:当前失败的连接尝试次数 | |||
% lastError:一个JSON对象,其中包含有关应用程序上发生的最后一个错误的详细信息。如果发生错误,它包含以下子属性: | |||
% errorNum:数字错误代码 | |||
% errorMessage:文本错误描述 | |||
% 时间:发生错误的日期和时间 | |||
% 如果没有发生错误,lastError将为空。 | |||
% server:具有以下子属性的JSON对象: | |||
% version:应用服务器的版本 | |||
% serverId:应用服务器的ID | |||
% 端点:应用程序连接到的端点(如果应用程序处于活动状态)或将连接到端点(如果应用程序当前处于非活动状态) | |||
% database:应用程序连接到的数据库的名称(如果应用程序处于活动状态)或将连接(如果应用程序当前处于非活动状态) | |||
% 请注意,所有返回的“刻度”值都没有特定的单位。刻度值仅在相互比较时才有意义。较高的报价值表示比较低的报价值“时间更晚”。 | |||
% 警告:调用此方法会将来自远程主服务器上的集合中的数据同步到本地ArangoDB数据库。本地集合中的所有数据将被清除,并替换为主数据库中的数据。 | |||
% 请谨慎使用! | |||
% 还请记住,此命令可能需要很长时间才能完成并返回。这是因为它将首先与主机进行完全的数据同步,这将花费与数据量大致成比例的时间。 | |||
% 注意:集群中的协调器不支持此方法。 | |||
% 返回码 | |||
% 200:如果请求成功执行,则返回。 | |||
% 400:配置不完整或格式错误,返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果在同步过程中或开始连续复制时发生错误,则返回。 | |||
% 501:在集群中的协调器上调用此操作时返回。 | |||
changeRepMakeSlave(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/replication/make-slave">>, [], BodyStr). | |||
%其他复制命令 | |||
%返回服务器ID | |||
%获取此服务器的唯一标识符 | |||
%GET /_api/replication/server-id | |||
%返回服务器ID。其他复制API方法也返回该ID,该方法是确定服务器ID的简便方法。 | |||
%响应的主体是带有属性serverId的JSON对象。服务器ID作为字符串返回。 | |||
%返回码 | |||
%200:如果请求成功执行,则返回。 | |||
%405:使用无效的HTTP方法时返回。 | |||
%500:如果组装响应时发生错误,则返回500。 | |||
getRepServerId(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/replication/server-id">>, [], undefined). | |||
% WAL 固定链接操作中可用的返回刻度范围 | |||
% 返回预写日志中可用的刻度线范围 | |||
% GET /_api/wal/range | |||
% 返回所有WAL文件的当前刻度值范围。刻度值可用于确定某些数据(由刻度值标识)是否仍可用于复制。 | |||
% 响应的主体包含一个JSON对象。 | |||
% tickMin:可用的最小刻度 | |||
% tickMax:可用的最大刻度 | |||
% time:服务器时间,以字符串形式,格式为“ YYYY-MM-DDTHH:MM:SSZ” | |||
% server:具有字段version和serverId的对象 | |||
% 返回码 | |||
% 200:如果刻度范围可以成功确定,则返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果无法确定服务器操作状态,则返回。 | |||
% 501:在集群中的协调器上调用此操作时返回。 | |||
getWalRange(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/wal/range">>, [], undefined). | |||
% 返回最后一个可用的刻度值固定链接 | |||
% 返回最后一个可用的刻度值 | |||
% GET /_api/wal/lastTick | |||
% 返回可以从服务器的复制日志提供的最后一个可用的滴答值。这对应于最新成功操作的滴答声。 | |||
% 结果是一个包含属性tick,time和server的JSON对象。 | |||
% tick:包含最后可用的刻度,时间 | |||
% time:服务器时间,以字符串形式,格式为“ YYYY-MM-DDTHH:MM:SSZ” | |||
% server:具有字段version和serverId的对象 | |||
% 注意:集群中的协调器不支持此方法。 | |||
% 返回码 | |||
% 200:如果请求成功执行,则返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果组装响应时发生错误,则返回500。 | |||
% 501:在集群中的协调器上调用此操作时返回。 | |||
getWalLastTick(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/wal/lastTick">>, [], undefined). | |||
% 获取最近的操作 | |||
% GET /_api/wal/tail | |||
% 查询参数 | |||
% 全局(可选):是否应包括所有数据库的操作。设置为false时, 仅包括当前数据库的操作。值true仅在_system数据库上有效。默认值为false。 | |||
% from(可选):结果的排他性下界刻度值。在连续调用此API时,应将其设置为x-arango-replication-lastincluded标头返回的值(除非标头包含0)。 | |||
% 到(可选):结果的包含上限刻度值。 | |||
% lastScanned(可选):应该设置为x-arango-replication-lastscanned标头的值,或者第一次尝试设置为0。这使rocksdb引擎可以通过多个响应分解大型事务。 | |||
% chunkSize(可选):返回结果的大约最大大小。 | |||
% syncerId(可选):用于尾随结果的客户端ID。服务器将使用它来保留操作,直到客户端取回它们为止。必须为正整数。 请注意,必须使用this或serverId来获取使用rocksdb存储引擎读取的所有操作。 | |||
% serverId(可选):客户端计算机的ID。如果未设置syncerId,则服务器将使用它来保留操作,直到客户端获取它们为止。必须为正整数。 请注意,必须使用this或syncerId才能有机会读取rocksdb存储引擎的所有操作。 | |||
% clientInfo(可选):客户端的简短描述,仅用于提供信息。 | |||
% barrierId(可选):用于保留WAL条目的障碍的ID。请注意,这仅对于MMFiles存储引擎是必需的 | |||
% 从服务器的预写日志(也称为复制日志)中返回数据。初始同步数据后,复制客户端可以调用此方法。该方法将从服务器返回所有“最近”记录的操作。客户端可以在本地重播和应用这些操作,以便它们获得与服务器相同的数据状态。 | |||
% 客户端可以重复调用此方法以从服务器增量获取所有更改。在这种情况下,它们应提供from值,这样它们将仅自上次获取以来返回日志事件。 | |||
% 当不使用from查询参数时,服务器将从其复制日志的开头开始返回日志条目。当使用from 参数时,服务器将仅返回滴答值大于指定的from值的日志条目(注意:滴答值等于from的日志条目将被排除)。增量获取日志数据时,请使用from值。 | |||
% 的到查询参数可被用于任选地限制上部结合的结果到一定刻度值。如果使用,结果将仅包含滴答值最大为(包括)到的日志事件。在增量读取中,无需使用to参数。仅在只需要部分变更日志的特殊情况下才有意义。 | |||
% 所述CHUNKSIZE查询参数可用于控制结果的大小。必须以字节为单位指定。该CHUNKSIZE值也仅是被兑现。否则,chunkSize值太低可能导致服务器无法仅将一个日志条目放入结果中并将其返回。因此,只有在将日志条目写入结果后才能查询chunkSize值。如果结果大小大于 chunkSize,则服务器将以与响应中已经存在的日志条目一样多的日志条目进行响应。如果结果大小仍小于chunkSize,则如果还有更多数据要返回,则服务器将尝试返回更多数据。 | |||
% 如果未指定chunkSize,则将使用某些服务器端默认值。 | |||
% 结果的Content-Type是application / x-arango-dump。这是一种易于处理的格式,所有日志事件进入响应正文中的单独行。每个日志事件本身都是一个JSON对象,至少具有以下属性: | |||
% tick:日志事件刻度值 | |||
% type:日志事件类型 | |||
% 各个日志事件还将具有其他属性,具体取决于事件类型。用于多种事件类型的一些常见属性是: | |||
% cuid:事件所属的View或collection的全局唯一ID | |||
% db:事件用于的数据库名称 | |||
% tid:包含该事件的交易的ID | |||
% data:原始文档数据 | |||
% 有关各个复制事件类型及其数据结构的详细说明,请参见“ 操作类型”。 | |||
% 响应还将包含以下HTTP标头: | |||
% x-arango-replication-active:记录器是否处于活动状态。客户可以使用该标志来指示其轮询频率。如果记录器未处于活动状态,并且没有其他可用的复制事件,则客户机中止或进入睡眠状态很长时间,然后稍后重试以检查记录器是否已激活可能是明智的。 | |||
% x-arango-replication-lastincluded:结果中最后一个包含值的刻度值。在增量日志取,该值可以被用作从以下请求值。请注意,如果结果为空,则该值为0。客户端不应在下一个请求中将此值用作from的值(否则服务器将从日志的开头再次返回日志事件)。 | |||
% x-arango-replication-lastscanned:计算操作日志时服务器扫描的最后一个滴答声。这可能包括服务器由于各种原因未退还给您的操作(即,该值已被过滤或跳过)。您可以在lastScanned标头中使用此值,以使rocksdb引擎分解多个响应上的请求。 | |||
% x-arango-replication-lasttick:服务器已在其预写日志中记录的最后一个滴答值(不一定包含在结果中)。通过比较最后的报价和最后包括的报价值,客户端可以大致了解仍有多少事件需要提取。 | |||
% 如果服务器从from参数中指定的刻度开始返回所有刻度值,则x-arango-replication-frompresent设置为true。如果将此设置为false,则服务器不再具有这些操作,并且客户端可能错过了操作。 | |||
% x-arango-replication-checkmore:是否已经存在客户端可以立即获取的更多日志数据。如果有更多日志数据可用,则客户端可以再次调用logger-follow,并将其 值调整为从中取出其余日志条目,直到没有更多日志条目为止。 | |||
% 如果没有更多的日志数据要获取,则客户端可能决定在再次调用记录器之前先进入睡眠状态。 | |||
% 注意:集群中的协调器不支持此方法。 | |||
% 返回码 | |||
% 200:如果成功执行了请求,则返回该值,并且存在可用于请求范围的日志事件。在这种情况下,响应主体将不会为空。 | |||
% 204:如果成功执行了请求,则返回该值,但是对于所请求的范围没有可用的日志事件。在这种情况下,响应主体将为空。 | |||
% 400:如果from或to值无效,则返回。 | |||
% 405:使用无效的HTTP方法时返回。 | |||
% 500:如果组装响应时发生错误,则返回500。 | |||
% 501:在集群中的协调器上调用此操作时返回。 | |||
getWalTail(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/wal/tail">>, [], undefined). | |||
getWalTail(PoolNameOrSocket, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/wal/tail", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
@ -0,0 +1,10 @@ | |||
-module(agSimpleQueries). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address:https://www.arangodb.com/docs/stable/http/simple-query.html | |||
% 从版本3.4.0开始不推荐使用简单查询API。这些端点不应再使用。它们被AQL查询取代。 |
@ -0,0 +1,92 @@ | |||
-module(agTasks). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address:https://www.arangodb.com/docs/stable/http/traversal.html | |||
% HTTP任务接口 | |||
% 接下来,您将看到ArangoDB的任务HTTP接口。 | |||
% 还为每个API操作提供了一些示例。 | |||
% | |||
% 提取所有任务或一项任务 | |||
% GET /_api/tasks/ | |||
% 获取服务器上的所有现有任务 | |||
% HTTP 200任务列表 | |||
% **:所有任务的列表 | |||
getTaskList(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/tasks/">>, [], undefined). | |||
% 检索一个当前活动的服务器任务 | |||
% GET /_api/tasks/{id} | |||
% 路径参数 | |||
% id(必填):要提取的任务的ID。 | |||
% 在ID指定的服务器上获取一个现有任务 | |||
% HTTP 200请求的任务 | |||
% **:相关功能 | |||
getTask(PoolNameOrSocket, TaskId) -> | |||
Path = <<"/_api/tasks/", (agMiscUtils:toBinary(TaskId))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 创建一个新任务 | |||
% POST /_api/tasks | |||
% 具有以下属性的JSON对象是必需的: | |||
% name:任务名称 | |||
% command:要执行的JavaScript代码 | |||
% params:要传递给命令的参数 | |||
% period:执行之间的秒数 | |||
% offset:初始延迟的秒数 | |||
% | |||
% 用生成的ID创建一个新任务 | |||
% HTTP 200任务已注册 | |||
% id:标识任务的字符串 | |||
% created:创建此任务的时间戳 | |||
% type:这是什么类型的任务[ periodic,timed] | |||
% 定期重复执行的任务 | |||
% 定时是在特定时间执行一次的任务 | |||
% period:这个任务应该运行的每个period秒 | |||
% offset:距创建的时间戳的时间偏移(以秒为单位) | |||
% command:此任务的javascript函数 | |||
% database:此任务所属的数据库 | |||
% code:状态码,在这种情况下为200。 | |||
% error:在这种情况下为false | |||
% 400:如果帖子正文不正确,则返回HTTP 400。 | |||
newTask(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/tasks">>, [], BodyStr). | |||
% 注册具有预定义ID的新任务;与负载均衡器不兼容 | |||
% PUT /_api/tasks/{id} | |||
% 路径参数 | |||
% id(必填):要创建的任务的id | |||
% 具有以下属性的JSON对象是必需的: | |||
% name:任务名称 | |||
% command:要执行的JavaScript代码 | |||
% params:要传递给命令的参数 | |||
% period:执行之间的秒数 | |||
% offset:初始延迟的秒数 | |||
% 注册具有指定ID的新任务 | |||
% 返回码 | |||
% 400:如果任务ID已经存在或其余主体不正确,则返回HTTP 400。 | |||
newTask(PoolNameOrSocket, TaskId, MapData) -> | |||
Path = <<"/_api/tasks/", (agMiscUtils:toBinary(TaskId))/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
% 删除一个当前活动的服务器任务 | |||
% DELETE /_api/tasks/{id} | |||
% 路径参数 | |||
% id(必填):要删除的任务的id。 | |||
% 删除服务器上ID标识的任务。 | |||
% HTTP 200如果删除了任务,则返回HTTP 200。 | |||
% code:状态码,在这种情况下为200。 | |||
% error:在这种情况下为false | |||
% HTTP 404如果任务ID未知,则返回HTTP 404。 | |||
% code:状态码,在这种情况下为404。 | |||
% error:在这种情况下为true | |||
% errorMessage:一条纯文本消息,指出出了什么问题。 | |||
delTask(PoolNameOrSocket, TaskId) -> | |||
Path = <<"/_api/tasks/", (agMiscUtils:toBinary(TaskId))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). |
@ -0,0 +1,204 @@ | |||
-module(agTransactions). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address:https://www.arangodb.com/docs/stable/http/transaction.html | |||
% 交易的HTTP接口 | |||
% 交易次数 | |||
% ArangoDB的事务在服务器上执行。交易可以由客户以两种不同的方式执行: | |||
% | |||
% 通过流事务 API | |||
% 通过JavaScript交易 API | |||
% 两者之间的区别不难理解,下面列出了一个简短的入门。有关ArangoDB中事务如何工作以及ArangoDB可以提供哪些保证的详细说明,请参阅Transactions。 | |||
% | |||
% 流交易 | |||
% 流事务使您可以使用单独的开始和提交/中止命令执行多文档事务。这类似于传统RDBMS使用BEGIN,COMMIT和ROLLBACK操作的方式。 | |||
% | |||
% 这是大型交易的推荐API。但是,客户端负责确保不再需要事务时提交或中止事务,以避免占用资源。 | |||
% | |||
% JavaScript交易 | |||
% JS-Transactions允许您向服务器发送一段专用的JavaScript代码(即一个函数),该代码将以事务方式执行。 | |||
% | |||
% 在该功能结束时,将自动提交事务,并且该事务完成的所有更改都将保留。除了初始启动请求之外,客户端不需要任何交互。 | |||
% 局限性固定链接 | |||
% 在协调器上强制执行流事务的最大生命周期和事务大小,以确保事务不会阻止群集正常运行: | |||
% | |||
% 两次操作之间的最大闲置超时时间为10秒 | |||
% 每个数据库服务器的最大事务大小为128 MB | |||
% 这些限制也适用于单个服务器上的流事务。 | |||
% | |||
% 强制执行限制对于释放被放弃的事务使用的资源很有用,例如,从由于编程错误而被客户端应用程序放弃的事务中,或者从由于客户端连接中断而留下的事务中释放资源。 | |||
% 流事务的HTTP接口 | |||
% 在v3.5.0中引入 | |||
% 流事务使您可以使用单独的开始和提交/中止命令执行多文档事务。这类似于传统RDBMS使用BEGIN,COMMIT和ROLLBACK操作的方式。 | |||
% 要使用流事务,客户端首先将 事务的配置发送到ArangoDB服务器。 | |||
% 与JS-Transaction相反,此事务的定义必须仅包含将要使用的集合以及(可选)ArangoDB支持的各种事务选项。不支持任何动作属性。 | |||
% 流事务API 与ArangoDB中的其他API 结合使用。要将事务用于支持的操作,客户端需要在每个请求的x-arango-trx-id标头中指定事务标识符。这将自动使这些操作使用指定的事务。 | |||
% 支持的事务性API操作包括: | |||
% Document API中的所有操作 | |||
% 通过Collection API的文档数 | |||
% 通过Collection API截断集合 | |||
% 通过Cursor API创建AQL游标 | |||
% 处理 托管图的顶点和边(通用图 / Gharial API,从v3.5.1开始) | |||
% 请注意,客户端始终需要首先启动事务,并且需要显式指定用于写访问的集合。客户端负责确保不再需要事务时提交或中止事务。这样可以避免占用ArangoDB服务器上的资源。 | |||
% 事务将获取集合锁,用于MMFiles存储引擎中的读写操作以及RocksDB中的写操作。因此,建议尽量缩短交易时间。 | |||
% 有关ArangoDB中事务如何工作的更详细描述,请参阅Transactions。 | |||
% 另请参阅: | |||
% 局限性 | |||
% 已知的问题 | |||
% 开始服务器端事务 | |||
% POST /_api/transaction/begin | |||
% 具有以下属性的JSON对象是必需的: | |||
% collections:collections必须是一个JSON对象,可以具有一个或所有子属性read,write或Exclusive,每个子属性 都是collection名称的数组或单个collection name作为字符串。必须使用write或Exclusive属性声明将在事务中写入的集合,否则它将失败,而仅从中读取的未声明集合将被延迟添加。有关 更多信息,请参见锁定和隔离。 | |||
% waitForSync:一个可选的布尔标志,如果设置了该标志,将强制事务在返回之前将所有数据写入磁盘。 | |||
% allowImplicit:允许从未声明的集合中读取。 | |||
% lockTimeout:一个可选的数值,可用于设置等待收集锁的超时时间。如果未指定,将使用默认值。将lockTimeout设置为0将使ArangoDB不会在等待锁定时超时。 | |||
% maxTransactionSize:事务大小限制(以字节为单位)。仅受RocksDB存储引擎的尊敬。 | |||
% 事务描述必须在POST请求的正文中传递。如果可以在服务器上启动事务,则将返回HTTP 201。 | |||
% 对于成功启动的事务,返回的JSON对象具有以下属性: | |||
% error:布尔值标志,指示是否发生错误( 在这种情况下为false) | |||
% code:HTTP状态码 | |||
% 结果:结果包含 | |||
% id:交易的标识符 | |||
% status:包含字符串“ running” | |||
% 如果缺少交易规范或格式错误,则服务器将使用HTTP 400或HTTP 404进行响应。 | |||
% 然后,响应的主体将包含带有其他错误详细信息的JSON对象。该对象具有以下属性: | |||
% error:布尔值标志,指示发生错误(在这种情况下为true) | |||
% code:HTTP状态码 | |||
% errorNum:服务器错误号 | |||
% errorMessage:描述性错误消息 | |||
% 返回码 | |||
% 201:如果事务正在服务器上运行, 则将返回HTTP 201。 | |||
% 400:如果事务规范丢失或格式不正确,则服务器将使用HTTP 400进行响应。 | |||
% 404:如果事务规范包含未知集合,则服务器将使用HTTP 404进行响应。 | |||
beginTransaction(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/transaction/begin">>, [], BodyStr). | |||
% 提取服务器端事务的状态 | |||
% GET /_api/transaction/{transaction-id} | |||
% 路径参数 | |||
% transaction-id(必填):交易标识符。 | |||
% 结果是一个描述事务状态的对象。它至少具有以下属性: | |||
% id:交易的标识符 | |||
% status:交易的状态。“运行”,“承诺”或“中止”之一。 | |||
% 返回码 | |||
% 200:如果事务已在服务器上完全执行并提交, 则将返回HTTP 200。 | |||
% 400:如果指定的事务标识符丢失或格式错误,则服务器将使用HTTP 400进行响应。 | |||
% 404:如果找不到具有指定标识符的交易,则服务器将使用HTTP 404进行响应。 | |||
getTransactionStatus(PoolNameOrSocket, TransactionId) -> | |||
Path = <<"/_api/transaction/", (agMiscUtils:toBinary(TransactionId))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 提交或中止正在运行的事务必须由客户端完成。在完成使用事务后,不要提交或中止事务是一个坏习惯。这将迫使服务器保留资源和收集锁,直到整个事务超时为止。 | |||
% 提交交易 | |||
% 提交服务器端事务 | |||
% PUT /_api/transaction/{transaction-id} | |||
% 路径参数 | |||
% transaction-id(必填):交易标识符, | |||
% 提交正在运行的服务器端事务。提交是幂等操作。多次提交事务不是错误。 | |||
% 如果可以提交事务,则将返回HTTP 200。返回的JSON对象具有以下属性: | |||
% error:布尔值标志,指示是否发生错误( 在这种情况下为false) | |||
% code:HTTP状态码 | |||
% 结果:结果包含 | |||
% id:交易的标识符 | |||
% status:包含字符串“ committed” | |||
% 如果找不到事务,不允许提交或事务被中止,则服务器将使用HTTP 400,HTTP 404或HTTP 409进行响应。 | |||
% 然后,响应的主体将包含带有其他错误详细信息的JSON对象。该对象具有以下属性: | |||
% error:布尔值标志,指示发生错误(在这种情况下为true) | |||
% code:HTTP状态码 | |||
% errorNum:服务器错误号 | |||
% errorMessage:描述性错误消息 | |||
% 返回码 | |||
% 200:如果已提交事务, 则将返回HTTP 200。 | |||
% 400:如果无法提交事务,则服务器将使用HTTP 400进行响应。 | |||
% 404:如果未找到事务,则服务器将使用HTTP 404进行响应。 | |||
% 409:如果事务已经中止,则服务器将使用HTTP 409进行响应。 | |||
commitTransaction(PoolNameOrSocket, TransactionId) -> | |||
Path = <<"/_api/transaction/", (agMiscUtils:toBinary(TransactionId))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined). | |||
% 中止服务器端事务 | |||
% DELETE /_api/transaction/{transaction-id} | |||
% 路径参数 | |||
% transaction-id(必填):交易标识符, | |||
% 中止正在运行的服务器端事务。中止是一个幂等的操作。多次中止事务不是错误。 | |||
% 如果可以中止该事务,则将返回HTTP 200。返回的JSON对象具有以下属性: | |||
% error:布尔值标志,指示是否发生错误( 在这种情况下为false) | |||
% code:HTTP状态码 | |||
% 结果:结果包含 | |||
% id:交易的标识符 | |||
% status:包含字符串“ aborted” | |||
% 如果找不到事务,不允许中止或已提交事务,则服务器将使用HTTP 400,HTTP 404或HTTP 409进行响应。 | |||
% 然后,响应的主体将包含带有其他错误详细信息的JSON对象。该对象具有以下属性: | |||
% error:布尔值标志,指示发生错误(在这种情况下为true) | |||
% code:HTTP状态码 | |||
% errorNum:服务器错误号 | |||
% errorMessage:描述性错误消息 | |||
% 返回码 | |||
% 200:如果事务中止, 将返回HTTP 200。 | |||
% 400:如果无法中止事务,则服务器将使用HTTP 400进行响应。 | |||
% 404:如果未找到事务,则服务器将使用HTTP 404进行响应。 | |||
% 409:如果事务已经提交,则服务器将以HTTP 409响应。 | |||
abortTransaction(PoolNameOrSocket, TransactionId) -> | |||
Path = <<"/_api/transaction/", (agMiscUtils:toBinary(TransactionId))/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
% 返回当前运行的服务器端事务 | |||
% GET /_api/transaction | |||
% 结果是一个对象,该对象使用transactions属性进行描述,其中包含一个事务数组。在群集中,阵列将包含来自所有协调器的事务。 | |||
% 每个数组条目都包含一个具有以下属性的对象: | |||
% id:交易的ID | |||
% status:交易的状态 | |||
% 返回码 | |||
% 200:如果可以成功检索事务列表,则将返回HTTP 200。 | |||
getTransactionList(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/transaction">>, [], undefined). | |||
% JavaScript交易的HTTP接口 | |||
% ArangoDB的JS事务在服务器上执行。客户端可以通过将事务描述发送给服务器来启动事务。 | |||
% ArangoDB中的JS事务不提供单独的BEGIN,COMMIT和ROLLBACK 操作。取而代之的是,ArangoDB JS事务由JavaScript函数描述,然后JavaScript函数中的代码将以事务方式执行。 | |||
% 在该功能结束时,将自动提交事务,并且该事务完成的所有更改都将保留。如果在事务执行过程中引发异常,则将回滚事务中执行的所有操作。 | |||
% 有关ArangoDB中事务如何工作的更详细描述,请参阅Transactions。 | |||
% 执行服务器端事务 | |||
% POST /_api/transaction | |||
% 具有以下属性的JSON对象是必需的: | |||
% collections:collections必须是一个JSON对象,可以具有一个或所有子属性read,write或Exclusive,每个子属性 都是collection名称的数组或单个collection name作为字符串。必须使用write或Exclusive属性声明将在事务中写入的集合,否则它将失败,而仅从中读取的未声明集合将被延迟添加。可以将可选的子属性allowImplicit设置为false, 以使事务在未声明声明的读取集合的情况下失败。如果可能,应完全声明要读取的集合,以避免死锁。看到锁定和隔离 以获取更多信息。 | |||
% action:要执行的实际交易操作,采用字符串化JavaScript代码的形式。该代码将在服务器端执行,并具有后期绑定。因此,至关重要的是,在操作中指定的代码 正确设置其所需的所有变量。如果操作中指定的代码以return语句结尾, 则如果事务成功提交,则REST API还将在result属性中返回返回的值。 | |||
% waitForSync:一个可选的布尔标志,如果设置了该标志,将强制事务在返回之前将所有数据写入磁盘。 | |||
% allowImplicit:允许从未声明的集合中读取。 | |||
% lockTimeout:一个可选的数值,可用于设置等待收集锁的超时时间。如果未指定,将使用默认值。将lockTimeout设置为0将使ArangoDB不会在等待锁定时超时。 | |||
% params:传递给操作的可选参数。 | |||
% maxTransactionSize:事务大小限制(以字节为单位)。仅受RocksDB存储引擎的尊敬。 | |||
% 事务描述必须在POST请求的正文中传递。 | |||
% 如果事务已在服务器上完全执行并提交, 则将返回HTTP 200。另外,在操作中定义的代码的返回值将在result属性中返回。 | |||
% 对于成功提交的事务,返回的JSON对象具有以下属性: | |||
% error:布尔值标志,指示是否发生错误( 在这种情况下为false) | |||
% code:HTTP状态码 | |||
% result:交易的返回值 | |||
% 如果缺少交易规范或格式错误,则服务器将使用HTTP 400进行响应。 | |||
% 然后,响应的主体将包含带有其他错误详细信息的JSON对象。该对象具有以下属性: | |||
% error:布尔值标志,指示发生错误(在这种情况下为true) | |||
% code:HTTP状态码 | |||
% errorNum:服务器错误号 | |||
% errorMessage:描述性错误消息 | |||
% 如果事务提交失败(无论是由于操作代码中引发的异常 还是内部错误),服务器都会以错误进行响应。其他任何错误都将使用返回码 HTTP 400,HTTP 409或HTTP 500返回。 | |||
% 返回码 | |||
% 200:如果事务已在服务器上完全执行并提交, 则将返回HTTP 200。 | |||
% 400:如果事务规范丢失或格式不正确,则服务器将使用HTTP 400进行响应。 | |||
% 404:如果事务规范包含未知集合,则服务器将使用HTTP 404进行响应。 | |||
% 500:用户抛出的异常将使服务器以HTTP 500的返回码进行响应 | |||
executeTransaction(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/transaction">>, [], BodyStr). | |||
@ -0,0 +1,13 @@ | |||
-module(agTraversals). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address:https://www.arangodb.com/docs/stable/http/traversal.html | |||
% 执行遍历永久链接 | |||
% 执行服务器端遍历 | |||
% POST /_api/traversal | |||
% 此路由不应再使用。从3.4.0版开始,它被视为已弃用。它被AQL图形遍历取代。 |
@ -0,0 +1,240 @@ | |||
-module(agUserMgr). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address:https://www.arangodb.com/docs/stable/http/user-management.html | |||
% 用户管理的HTTP接口 | |||
% 这是用于管理用户的ArangoDB HTTP接口的简介。 | |||
% 该界面提供了添加,更新和删除用户的简单方法。通过此接口管理的所有用户都将存储在系统集合 _users中。您永远不要直接操作_users集合。 | |||
% 该专用接口有意不提供常规文档REST API中可用的所有功能。 | |||
% 请注意,ArangoDB的复制中不包括用户操作。 | |||
% 创建一个新用户。 | |||
% POST /_api/user | |||
% 具有以下属性的JSON对象是必需的: | |||
% user:用户名(字符串)。这是强制性的。 | |||
% passwd:用户密码(字符串)。如果未指定密码,将使用空字符串。如果传递特殊值ARANGODB_DEFAULT_ROOT_PASSWORD,则密码将设置为存储在环境变量中的值 ARANGODB_DEFAULT_ROOT_PASSWORD。这可用于将实例变量传递给ArangoDB。例如,来自Amazon的实例标识符。 | |||
% active:一个可选标志,用于指定用户是否处于活动状态。如果未指定,则默认为true | |||
% extra:一个可选的JSON对象,其中包含有关用户的任意额外数据。 | |||
% 创建一个新用户。您需要服务器访问级别“ 管理 ”才能执行此REST调用。 | |||
% 返回码 | |||
% 201:如果服务器可以添加用户,则返回 | |||
% 400:如果JSON格式不正确或请求中缺少必需数据。 | |||
% 401:如果您没有对_system 数据库的访问数据库访问级别,则返回。 | |||
% 403:如果您没有访问服务器访问级别,则返回。 | |||
% 409:如果已经存在同名用户,则返回。 | |||
newUser(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/user">>, [], BodyStr). | |||
% 设置数据库访问级别。 | |||
% PUT /_api/user/{user}/database/{dbname} | |||
% 路径参数 | |||
% user (必填):用户名。 | |||
% dbname(必填):数据库的名称。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% grant:使用“ rw”将数据库访问级别设置为Administrate。 | |||
% 使用“ ro”将数据库访问级别设置为Access。 | |||
% 使用“none”将数据库访问级别设置为“ 无访问权”。 | |||
% 为用户user的数据库dbname设置数据库访问级别。您需要管理服务器访问级别才能执行此REST调用。 | |||
% 返回码 | |||
% 200:如果访问级别更改成功,则返回。 | |||
% 400:如果JSON格式不正确或请求中缺少必需数据。 | |||
% 401:如果您没有对_system 数据库的访问数据库访问级别,则返回。 | |||
% 403:如果您没有访问服务器访问级别,则返回。 | |||
setUserDbAccessLevel(PoolNameOrSocket, UserName, DbName, MapData) -> | |||
Path = <<"/_api/user/", UserName/binary, "/database/", DbName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
% 设置收集访问级别。 | |||
% PUT /_api/user/{user}/database/{dbname}/{collection} | |||
% 路径参数 | |||
% user(必填):用户名。 | |||
% dbname(必填):数据库的名称。 | |||
% collection (必填):集合的名称。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% grant:使用“ rw”将收集级别访问权限设置为“ 读/写”。 | |||
% 使用“ ro”将收集级别访问权限设置为“ 只读”。 | |||
% 使用“none”将收集级别访问权限设置为“ 无访问权限”。 | |||
% | |||
% 设置用于收集访问级别收集在数据库DBNAME 用户的用户。您需要管理服务器访问级别才能执行此REST调用。 | |||
% 返回码 | |||
% 200:成功修改访问权限后返回。 | |||
% 400:如果JSON格式不正确或请求中缺少必需数据。 | |||
% 401:如果您没有对_system 数据库的访问数据库访问级别,则返回。 | |||
% 403:如果您没有访问服务器访问级别,则返回。 | |||
setUserCollAccessLevel(PoolNameOrSocket, UserName, DbName, CollName, MapData) -> | |||
Path = <<"/_api/user/", UserName/binary, "/database/", DbName/binary, "/", CollName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
% 清除数据库访问级别,恢复为默认访问级别 | |||
% DELETE /_api/user/{user}/database/{dbname} | |||
% 路径参数 | |||
% user(必填):用户名。 | |||
% dbname(必填):数据库的名称。 | |||
% | |||
% 清除用户user的数据库dbname的数据库访问级别。因此,将使用默认的数据库访问级别。如果没有定义的默认数据库访问级别,则默认为No access。您需要获得_system数据库的权限才能执行此REST调用。 | |||
% | |||
% 返回码 | |||
% 202:成功更改访问权限后返回。 | |||
% 400:如果JSON格式不正确或请求中缺少必需数据。。 | |||
clearUserDbAccessLevel(PoolNameOrSocket, UserName, DbName) -> | |||
Path = <<"/_api/user/", UserName/binary, "/database/", DbName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
% 清除集合访问级别,恢复为默认访问级别 | |||
% DELETE /_api/user/{user}/database/{dbname}/{collection} | |||
% 路径参数 | |||
% user(必填):用户名。 | |||
% dbname(必填):数据库的名称。 | |||
% collection(必填):集合的名称。 | |||
% | |||
% 清除用户user的数据库dbname中集合集合的集合访问级别。因此,将使用默认的集合访问级别。如果没有定义的默认集合访问级别,则默认为No access。您需要具有_system数据库的权限才能执行此REST调用。 | |||
% 返回码 | |||
% 202:成功更改访问权限后返回。 | |||
% 400:如果有错误 | |||
clearUserCollAccessLevel(PoolNameOrSocket, UserName, DbName, CollName) -> | |||
Path = <<"/_api/user/", UserName/binary, "/database/", DbName/binary, "/", CollName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
% 列出用户的可访问数据库 | |||
% GET /_api/user/{user}/database/ | |||
% 路径参数 | |||
% 用户(必填):您要查询数据库的用户名。 | |||
% 查询参数 | |||
% full(可选):返回所有数据库和所有集合的完整访问级别集。 | |||
% 获取指定用户可用的数据库列表。您需要 管理的服务器访问级别,以执行该REST调用。 | |||
% 该调用将返回一个具有指定用户按数据库访问权限的JSON对象。该结果对象将包含数据库名称作为对象键,以及相关的权限的数据库值。 | |||
% 如果您指定full,则结果将包含数据库的权限以及集合的权限。 | |||
% 返回码 | |||
% 200:如果可以返回可用数据库列表,则返回。 | |||
% 400:如果访问权限不正确等 | |||
% 401:如果您没有对_system 数据库的访问数据库访问级别,则返回。 | |||
% 403:如果您没有访问服务器访问级别,则返回。 | |||
getUserDbList(PoolNameOrSocket, UserName) -> | |||
Path = <<"/_api/user/", UserName/binary, "/database/">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
getUserDbList(PoolNameOrSocket, UserName, QueryPars) -> | |||
QueryBinary = agMiscUtils:spellQueryPars(QueryPars), | |||
Path = <<"/_api/user/", UserName/binary, "/database/", QueryBinary/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 获取特定的数据库访问级别 | |||
% GET /_api/user/{user}/database/{dbname} | |||
% 路径参数 | |||
% user(必填):您要查询数据库的用户名。 | |||
% dbname(必填):要查询的数据库的名称 | |||
% 获取特定数据库的数据库访问级别 | |||
% 返回码 | |||
% 200:如果可以返回访问级别,则返回 | |||
% 400:如果访问权限不正确等 | |||
% 401:如果您没有对_system 数据库的访问数据库访问级别,则返回。 | |||
% 403:如果您没有访问服务器访问级别,则返回。 | |||
getUserDbAccessLevel(PoolNameOrSocket, UserName, DbName) -> | |||
Path = <<"/_api/user/", UserName/binary, "/database/", DbName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 获取特定集合访问级别 | |||
% GET /_api/user/{user}/database/{dbname}/{collection} | |||
% 路径参数 | |||
% user(必填):您要查询数据库的用户名。 | |||
% dbname(必填):要查询的数据库的名称 | |||
% collection(必填):集合的名称 | |||
% 返回特定集合的集合访问级别 | |||
% 返回码 | |||
% 200:如果可以返回访问级别,则返回 | |||
% 400:如果访问权限不正确等 | |||
% 401:如果您没有对_system 数据库的访问数据库访问级别,则返回。 | |||
% 403:如果您没有访问服务器访问级别,则返回。 | |||
getUserCollAccessLevel(PoolNameOrSocket, UserName, DbName, CollName) -> | |||
Path = <<"/_api/user/", UserName/binary, "/database/", DbName/binary, "/", CollName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 替换现有用户属性。 | |||
% PUT /_api/user/{user} | |||
% 路径参数 | |||
% user(必填):用户名 | |||
% 具有以下属性的JSON对象是必需的: | |||
% passwd:用户密码(字符串)。必须指定密码,但是密码允许使用空字符串 | |||
% active:一个可选标志,用于指定用户是否处于活动状态。如果未指定,则默认为true | |||
% extra:一个可选的JSON对象,其中包含有关用户的任意额外数据。 | |||
% 替换现有用户的数据。现有用户的名称必须在user中指定。您需要服务器访问级别“ 管理 ”才能执行此REST调用。另外,用户可以更改他/她自己的数据。 | |||
% 返回码 | |||
% 200:如果用户数据可以被服务器替换,则返回。 | |||
% 400:JSON格式不正确或请求中缺少必需数据 | |||
% 401:如果您没有对_system 数据库的访问数据库访问级别,则返回。 | |||
% 403:如果您没有访问服务器访问级别,则返回。 | |||
% 404:指定的用户不存在 | |||
replaceUser(PoolNameOrSocket, UserName, MapData) -> | |||
Path = <<"/_api/user/", UserName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
% 修改现有用户的属性 | |||
% PATCH /_api/user/{user} | |||
% 路径参数 | |||
% user(必填):用户名 | |||
% 具有以下属性的JSON对象是必需的: | |||
% passwd:用户密码(字符串)。必须指定密码,但是密码允许使用空字符串 | |||
% active:一个可选标志,用于指定用户是否处于活动状态。如果未指定,则默认为true | |||
% extra:一个可选的JSON对象,其中包含有关用户的任意额外数据。 | |||
% 部分更新现有用户的数据。现有用户的名称必须在user中指定。您需要服务器访问级别“ 管理 ”才能执行此REST调用。另外,用户可以更改他/她自己的数据。 | |||
% 返回码 | |||
% 200:如果用户数据可以被服务器替换,则返回。 | |||
% 400:JSON格式不正确或请求中缺少必需数据。 | |||
% 401:如果您没有对_system 数据库的访问数据库访问级别,则返回。 | |||
% 403:如果您没有访问服务器访问级别,则返回。 | |||
% 404:指定的用户不存在 | |||
updateUser(PoolNameOrSocket, UserName, MapData) -> | |||
Path = <<"/_api/user/", UserName/binary>>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr). | |||
% 永久删除用户。 | |||
% DELETE /_api/user/{user} | |||
%路径参数 | |||
% user(必填):用户名 | |||
%删除由user标识的现有用户。您需要管理的服务器访问级别,以执行该REST调用。 | |||
%返回码 | |||
% 202:如果服务器删除了用户,则返回 | |||
% 401:如果您没有对_system 数据库的访问数据库访问级别,则返回。 | |||
% 403:如果您没有访问服务器访问级别,则返回。 | |||
% 404:指定的用户不存在 | |||
delUser(PoolNameOrSocket, UserName) -> | |||
Path = <<"/_api/user/", UserName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). | |||
% 获取用户的属性。 | |||
% GET /_api/user/{user} | |||
% 路径参数 | |||
% user(必填):用户名 | |||
% 获取有关指定用户的数据。您可以获取有关您自己的信息,或者您需要管理服务器访问级别才能执行此REST调用。 | |||
% 返回码 | |||
% 200:找到用户 | |||
% 401:如果您没有对_system 数据库的访问数据库访问级别,则返回。 | |||
% 403:如果您没有访问服务器访问级别,则返回。 | |||
% 404:指定用户名不存在。 | |||
getUser(PoolNameOrSocket, UserName) -> | |||
Path = <<"/_api/user/", UserName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 获取用户的属性。 | |||
% GET /_api/user/ | |||
% 获取有关所有用户的数据。您需要管理服务器访问级别才能执行此REST调用。否则,您只会获得有关您自己的信息。 | |||
% 调用成功后将返回至少具有以下属性的JSON对象: | |||
% user:用户名(字符串)。 | |||
% active:一个可选标志,用于指定用户是否处于活动状态。 | |||
% extra:一个可选的JSON对象,其中包含有关用户的任意额外数据。 | |||
% 返回码 | |||
% 200:找到的用户 | |||
% 401:如果您没有对_system 数据库的访问数据库访问级别,则返回。 | |||
% 403:如果您没有访问服务器访问级别,则返回。 | |||
getUserList(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/user/">>, [], undefined). |
@ -0,0 +1,216 @@ | |||
-module(agViews). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-compile([export_all, nowarn_export_all]). | |||
% doc_address:https://www.arangodb.com/docs/stable/http/views.html | |||
% 视图的HTTP接口 | |||
% 观看次数 | |||
% 这是ArangoDB Views的HTTP接口的简介。 | |||
% | |||
% 视图 | |||
% 视图由文档组成。它由其标识符唯一标识。它也有一个唯一的名称,客户端应该使用它来标识和访问它。视图可以重命名。这将更改视图名称,但不会更改视图标识符。视图具有创建视图时用户指定的类型。 | |||
% | |||
% 当前唯一可用的视图类型是ArangoSearch。 | |||
% | |||
% 查看标识符 | |||
% 视图标识符使您可以引用数据库中的视图。它是一个字符串值,在数据库中是唯一的。ArangoDB当前使用64位无符号整数值在内部维护View ID。当将View ID返回给客户端时,ArangoDB会将它们放入字符串中,以确保不支持不支持大整数的客户端不裁剪View ID。客户端在本地存储或使用它们时,应将ArangoDB返回的View ID视为不透明字符串。 | |||
% | |||
% 查看名称 | |||
% 视图名称标识数据库中的视图。它是一个字符串,在数据库中是唯一的。与View标识符不同,它是由View的创建者提供的。视图名称必须仅由字母,数字和_(下划线)和-(破折号)字符组成。请参阅ArangoDB中的命名约定,以获取有关有效视图名称的更多信息。 | |||
% | |||
% 视图的地址 | |||
% ArangoDB中的所有视图都有唯一的标识符和唯一的名称。ArangoDB在内部使用视图的唯一标识符来查找视图。但是,此标识符由ArangoDB管理,并且用户无法对其进行控制。为了允许用户使用自己的名称,每个视图还具有一个由用户指定的唯一名称。要从用户角度访问视图,应使用视图名称,即: | |||
% | |||
% http://server:port/_api/view/<view-name> | |||
% 例如:假设视图标识符为7254820,视图名称为demo,则该视图的URL为: | |||
% http://localhost:8529/_api/view/demo | |||
% 创建一个ArangoSearch视图 | |||
% POST /_api/view#arangosearch | |||
% 具有以下属性的JSON对象是必需的: | |||
% name:视图的名称。 | |||
% type:视图的类型。必须等于“ arangosearch”。此选项是不变的。 | |||
% links:期望一个对象,其属性键为要链接的集合的名称,链接属性为属性值。有关 详细信息,请参见 ArangoSearch查看链接属性。 | |||
% primarySort:可以定义一个主要排序顺序以启用AQL优化。如果查询遍历View的所有文档,想要按属性值对它们进行排序,并且要按(最左边的)字段对它们进行排序,以及它们的排序方向与primarySort定义匹配,则SORT操作将被优化。此选项是不变的。 | |||
% 需要一个对象数组,每个对象指定一个字段(属性路径)和一个排序方向("asc升序,"desc"降序): [ { "field": "attr", "direction": "asc"}, … ] | |||
% cleanupIntervalStep:在删除ArangoSearch数据目录中的未使用文件之间至少等待这么多次提交(默认值:2,以禁用使用:0)。对于合并策略经常合并段的情况(即大量的commit + consolidate),较低的值将导致浪费大量磁盘空间。对于合并策略很少合并段(即,很少的插入/删除)的情况,较高的值将影响性能,而不会带来任何附加好处。 | |||
% 背景: 每次执行“提交”或“合并”操作时,都会在磁盘上创建View内部数据结构的新状态。一旦不再有任何用户可用,就会释放旧状态/快照。但是,释放状态/快照的文件保留在磁盘上,只能通过“清理”操作将其删除。 | |||
% commitIntervalMsec:在提交View数据存储更改和使文档对查询可见之前,至少要等待这么多毫秒(默认值:1000,禁用使用:0)。对于插入/更新很多的情况,较低的值(直到提交)将导致索引无法解决这些问题,并且内存使用量将继续增长。对于插入/更新次数很少的情况,较高的值将影响性能并浪费每个提交调用的磁盘空间,而没有任何其他好处。 | |||
% 背景: 对于数据检索,ArangoSearch视图遵循“最终一致”的概念,即,最终ArangoDB中的所有数据将通过相应的查询表达式进行匹配。引入了ArangoSearch View“提交”操作的概念,以控制直到相应的查询表达式实际反映出文档添加/删除的时间的上限。一旦“提交”操作完成,在“提交”操作开始之前添加/删除的所有文档将由后续ArangoDB事务中调用的查询反映,正在进行的ArangoDB事务仍将继续返回可重复读取状态。 | |||
% integrationIntervalMsec:在应用'consolidationPolicy'合并View数据存储与可能释放文件系统上的空间之间至少等待这么多毫秒(默认值:10000,禁用使用:0)。对于存在大量数据修改操作的情况,较高的值可能会使数据存储区消耗更多的空间和文件句柄。对于少量数据修改操作的情况,较低的值将影响性能,因为没有可用于合并的细分受众群。 | |||
% 背景: 对于数据修改,ArangoSearch视图遵循“版本化数据存储”的概念。因此,一旦不再有旧数据的用户,就可以删除旧版本的数据。清理和压缩操作的频率由“ consolidationIntervalMsec”控制,并通过“ consolidationPolicy”选择压缩候选对象。 | |||
% consolidationPolicy:巩固应用策略为选择哪段应该合并(默认:{}) | |||
% 背景: 随着每ArangoDB交易进行的插入文档的一个或多个ArangoSearch内部段被创建。同样,对于已删除的文档,包含此类文档的段会将这些文档标记为“已删除”。随着时间的流逝,这种方法会导致创建许多小的和稀疏的段。“合并”操作选择一个或多个段,并将其所有有效文档复制到单个新段中,从而使搜索算法的性能更佳,并且一旦不再使用旧段,就可以释放额外的文件句柄。 | |||
% 子属性: | |||
% type(字符串,可选):根据“合并”操作的类型定义的几种可能的可配置公式选择候选候选者。当前支持的类型是: | |||
% "tier"(默认值):根据段字节大小和活动文档计数(由定制属性指定)进行合并。如果使用此类型,则下面的segments*和minScore属性可用。 | |||
% "bytes_accum":当并且仅 {threshold} > (segment_bytes + sum_of_merge_candidate_segment_bytes) / all_segment_bytes 当即所有候选段字节大小的总和小于总段字节大小乘以{threshold}。如果使用此类型,则下面的threshold属性可用。 | |||
% threshold(数字,可选):范围内的值[0.0, 1.0] | |||
% segmentsBytesFloor(数字,可选):定义值(以字节为单位),以将所有较小的段视为与合并选择相等(默认值:2097152) | |||
% segmentsBytesMax(数字,可选):所有合并段的最大允许大小(以字节为单位)(默认值:5368709120) | |||
% segmentsMax(数字,可选):将被评估为合并候选者的最大细分数(默认值:10) | |||
% segmentsMin(数字,可选):将被评估为合并候选者的最小细分数(默认值:1) | |||
% minScore(数字,可选):(默认值:0) | |||
% | |||
% writebufferIdle:池中缓存的最大写程序(段)数(默认值:64,使用0禁用,不可变) | |||
% writebufferActive:执行事务的并发活动写程序(段)的最大数量。其他编写器(段)等待当前活动的编写器(段)完成(默认值:0,使用0禁用,不可变) | |||
% writebufferSizeMax:在触发写入器(段)刷新之前,每个写入器(段)的最大内存字节大小。0value会关闭所有写入器(缓冲区)的此限制,并且将根据为刷新线程定义的值(ArangoDB服务器启动选项)定期刷新数据。0由于潜在的高内存消耗,应谨慎使用该值(默认值:33554432,使用0禁用,不可变) | |||
% 创建一个具有给定名称和属性的新视图(如果尚不存在)。 | |||
% 返回码 | |||
% 400:如果name或type属性丢失或无效,则 返回HTTP 400错误。 | |||
% 409:如果已经存在一个名为name的视图,则返回HTTP 409错误。 | |||
newView(PoolNameOrSocket, MapData) -> | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/view">>, [], BodyStr). | |||
% 返回一个视图 | |||
% GET /_api/view/{view-name} | |||
% 路径参数 | |||
% view-name(必填):视图的名称。 | |||
% 结果是一个对象,简要描述了具有以下属性的视图: | |||
% id:视图的标识符 | |||
% name:视图的名称 | |||
% type:视图的类型为字符串 | |||
% 返回码 | |||
% 404:如果视图名称未知,则返回HTTP 404。 | |||
getViewInfo(PoolNameOrSocket, ViewName) -> | |||
Path = <<"/_api/view/", ViewName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 返回所有视图 | |||
% GET /_api/view | |||
% 返回一个对象,该对象包含数据库中所有视图的列表,无论它们的类型如何。它是具有以下属性的对象数组: | |||
% ID | |||
% 名称 | |||
% 类型 | |||
% 返回码 | |||
% 200:视图列表 | |||
getViewList(PoolNameOrSocket) -> | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/view">>, [], undefined). | |||
% 读取指定视图的属性 | |||
% GET /_api/view/{view-name}/properties | |||
% 路径参数 | |||
% view-name(必填):视图的名称。 | |||
% 返回一个对象,其中包含由view-name标识的View的定义。 | |||
% 结果是一个具有特定视图完整描述的对象,包括与视图类型相关的属性。 | |||
% 返回码 | |||
% 400:如果缺少视图名称,则返回HTTP 400。 | |||
% 404:如果视图名称未知,则返回HTTP 404。 | |||
getViewProps(PoolNameOrSocket, ViewName) -> | |||
Path = <<"/_api/view/", ViewName/binary, "/properties">>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined). | |||
% 更改ArangoSearch视图的所有属性 | |||
% PUT /_api/view/{view-name}/properties#ArangoSearch | |||
% 路径参数 | |||
% view-name(必填):视图的名称。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% links:期望一个对象,其属性键为要链接的集合的名称,链接属性为属性值。有关 详细信息,请参见 ArangoSearch查看链接属性。 | |||
% cleanupIntervalStep:在删除ArangoSearch数据目录中的未使用文件之间至少等待这么多次提交(默认值:2,以禁用使用:0)。对于合并策略经常合并段的情况(即大量的commit + consolidate),较低的值将导致浪费大量磁盘空间。对于合并策略很少合并段(即,很少的插入/删除)的情况,较高的值将影响性能,而不会带来任何附加好处。 | |||
% 背景: 每次执行“提交”或“合并”操作时,都会在磁盘上创建View内部数据结构的新状态。一旦不再有任何用户可用,就会释放旧状态/快照。但是,释放状态/快照的文件保留在磁盘上,只能通过“清理”操作将其删除。 | |||
% commitIntervalMsec:在提交View数据存储更改和使文档对查询可见之前,至少要等待这么多毫秒(默认值:1000,禁用使用:0)。对于插入/更新很多的情况,较低的值(直到提交)将导致索引无法解决这些问题,并且内存使用量将继续增长。对于插入/更新次数很少的情况,较高的值将影响性能并浪费每个提交调用的磁盘空间,而没有任何其他好处。 | |||
% 背景: 对于数据检索,ArangoSearch视图遵循“最终一致”的概念,即,最终ArangoDB中的所有数据将通过相应的查询表达式进行匹配。引入了ArangoSearch View“提交”操作的概念,以控制直到相应的查询表达式实际反映出文档添加/删除的时间的上限。一旦“提交”操作完成,在“提交”操作开始之前添加/删除的所有文档将由后续ArangoDB事务中调用的查询反映,正在进行的ArangoDB事务仍将继续返回可重复读取状态。 | |||
% integrationIntervalMsec:在应用'consolidationPolicy'合并View数据存储与可能释放文件系统上的空间之间至少等待这么多毫秒(默认值:10000,禁用使用:0)。对于存在大量数据修改操作的情况,较高的值可能会使数据存储区消耗更多的空间和文件句柄。对于少量数据修改操作的情况,较低的值将影响性能,因为没有可用于合并的细分受众群。 | |||
% 背景: 对于数据修改,ArangoSearch视图遵循“版本化数据存储”的概念。因此,一旦不再有旧数据的用户,就可以删除旧版本的数据。清理和压缩操作的频率由“ consolidationIntervalMsec”控制,并通过“ consolidationPolicy”选择压缩候选对象。 | |||
% consolidationPolicy:巩固应用策略为选择哪段应该合并(默认:{}) | |||
% 背景: 随着每ArangoDB交易进行的插入文档的一个或多个ArangoSearch内部段被创建。同样,对于已删除的文档,包含此类文档的段会将这些文档标记为“已删除”。随着时间的流逝,这种方法会导致创建许多小的和稀疏的段。“合并”操作选择一个或多个段,并将其所有有效文档复制到单个新段中,从而使搜索算法的性能更佳,并且一旦不再使用旧段,就可以释放额外的文件句柄。 | |||
% 子属性: | |||
% type(字符串,可选):根据“合并”操作的类型定义的几种可能的可配置公式选择候选候选者。当前支持的类型是: | |||
% "tier"(默认值):根据段字节大小和活动文档计数(由定制属性指定)进行合并。如果使用此类型,则下面的segments*和minScore属性可用。 | |||
% "bytes_accum":当并且仅 {threshold} > (segment_bytes + sum_of_merge_candidate_segment_bytes) / all_segment_bytes 当即所有候选段字节大小的总和小于总段字节大小乘以{threshold}。如果使用此类型,则下面的threshold属性可用。 | |||
% threshold(数字,可选):范围内的值[0.0, 1.0] | |||
% segmentsBytesFloor(数字,可选):定义值(以字节为单位),以将所有较小的段视为与合并选择相等(默认值:2097152) | |||
% segmentsBytesMax(数字,可选):所有合并段的最大允许大小(以字节为单位)(默认值:5368709120) | |||
% segmentsMax(数字,可选):将被评估为合并候选者的最大细分数(默认值:10) | |||
% segmentsMin(数字,可选):将被评估为合并候选者的最小细分数(默认值:1) | |||
% minScore(数字,可选):(默认值:0) | |||
% 通过替换它们来更改视图的属性。 | |||
% 成功后,将返回具有以下属性的对象: | |||
% id:视图的标识符 | |||
% name:视图的名称 | |||
% type:视图类型 | |||
% 所有其他ArangoSearch View实施特定的属性 | |||
% 返回码 | |||
% 400:如果缺少视图名称,则返回HTTP 400。 | |||
% 404:如果视图名称未知,则返回HTTP 404。 | |||
changeViewAllProps(PoolNameOrSocket, ViewName, MapData) -> | |||
Path = <<"/_api/view/", ViewName/binary, "/properties">>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr). | |||
% 部分更改ArangoSearch视图的属性 | |||
% PATCH /_api/view/{view-name}/properties#ArangoSearch | |||
% 路径参数 | |||
% view-name(必填):视图的名称。 | |||
% 具有以下属性的JSON对象是必需的: | |||
% links:期望一个对象,其属性键为要链接的集合的名称,链接属性为属性值。有关 详细信息,请参见 ArangoSearch查看链接属性。 | |||
% cleanupIntervalStep:在删除ArangoSearch数据目录中的未使用文件之间至少等待这么多次提交(默认值:2,以禁用使用:0)。对于合并策略经常合并段的情况(即大量的commit + consolidate),较低的值将导致浪费大量磁盘空间。对于合并策略很少合并段(即,很少的插入/删除)的情况,较高的值将影响性能,而不会带来任何附加好处。 | |||
% 背景: 每次执行“提交”或“合并”操作时,都会在磁盘上创建View内部数据结构的新状态。一旦不再有任何用户可用,就会释放旧状态/快照。但是,释放状态/快照的文件保留在磁盘上,只能通过“清理”操作将其删除。 | |||
% commitIntervalMsec:在提交View数据存储更改和使文档对查询可见之前,至少要等待这么多毫秒(默认值:1000,禁用使用:0)。对于插入/更新很多的情况,较低的值(直到提交)将导致索引无法解决这些问题,并且内存使用量将继续增长。对于插入/更新次数很少的情况,较高的值将影响性能并浪费每个提交调用的磁盘空间,而没有任何其他好处。 | |||
% 背景: 对于数据检索,ArangoSearch视图遵循“最终一致”的概念,即,最终ArangoDB中的所有数据将通过相应的查询表达式进行匹配。引入了ArangoSearch View“提交”操作的概念,以控制直到相应的查询表达式实际反映出文档添加/删除的时间的上限。一旦“提交”操作完成,在“提交”操作开始之前添加/删除的所有文档将由后续ArangoDB事务中调用的查询反映,正在进行的ArangoDB事务仍将继续返回可重复读取状态。 | |||
% integrationIntervalMsec:在应用'consolidationPolicy'合并View数据存储与可能释放文件系统上的空间之间至少等待这么多毫秒(默认值:10000,禁用使用:0)。对于存在大量数据修改操作的情况,较高的值可能会使数据存储区消耗更多的空间和文件句柄。对于少量数据修改操作的情况,较低的值将影响性能,因为没有可用于合并的细分受众群。 | |||
% 背景: 对于数据修改,ArangoSearch视图遵循“版本化数据存储”的概念。因此,一旦不再有旧数据的用户,就可以删除旧版本的数据。清理和压缩操作的频率由“ consolidationIntervalMsec”控制,并通过“ consolidationPolicy”选择压缩候选对象。 | |||
% consolidationPolicy:巩固应用策略为选择哪段应该合并(默认:{}) | |||
% 背景: 随着每ArangoDB交易进行的插入文档的一个或多个ArangoSearch内部段被创建。同样,对于已删除的文档,包含此类文档的段会将这些文档标记为“已删除”。随着时间的流逝,这种方法会导致创建许多小的和稀疏的段。“合并”操作选择一个或多个段,并将其所有有效文档复制到单个新段中,从而使搜索算法的性能更佳,并且一旦不再使用旧段,就可以释放额外的文件句柄。 | |||
% 子属性: | |||
% type(字符串,可选):根据“合并”操作的类型定义的几种可能的可配置公式选择候选候选者。当前支持的类型是: | |||
% "tier"(默认值):根据段字节大小和活动文档计数(由定制属性指定)进行合并。如果使用此类型,则下面的segments*和minScore属性可用。 | |||
% "bytes_accum":当并且仅 {threshold} > (segment_bytes + sum_of_merge_candidate_segment_bytes) / all_segment_bytes 当即所有候选段字节大小的总和小于总段字节大小乘以{threshold}。如果使用此类型,则下面的threshold属性可用。 | |||
% threshold(数字,可选):范围内的值[0.0, 1.0] | |||
% segmentsBytesFloor(数字,可选):定义值(以字节为单位),以将所有较小的段视为与合并选择相等(默认值:2097152) | |||
% segmentsBytesMax(数字,可选):所有合并段的最大允许大小(以字节为单位)(默认值:5368709120) | |||
% segmentsMax(数字,可选):将被评估为合并候选者的最大细分数(默认值:10) | |||
% segmentsMin(数字,可选):将被评估为合并候选者的最小细分数(默认值:1) | |||
% minScore(数字,可选):(默认值:0) | |||
% 通过更新指定的属性来更改视图的属性。 | |||
% 成功后,将返回具有以下属性的对象: | |||
% id:视图的标识符 | |||
% name:视图的名称 | |||
% type:视图类型 | |||
% 所有其他ArangoSearch View实施特定的属性 | |||
% 返回码 | |||
% 400:如果缺少视图名称,则返回HTTP 400。 | |||
% 404:如果视图名称未知,则返回HTTP 404。 | |||
changeViewPartProps(PoolNameOrSocket, ViewName, MapData) -> | |||
Path = <<"/_api/view/", ViewName/binary, "/properties">>, | |||
BodyStr = jiffy:encode(MapData), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr). | |||
% 重命名视图 | |||
% PUT /_api/view/{view-name}/rename | |||
% 路径参数 | |||
% view-name(必填):要重命名的View的名称。 | |||
% 重命名视图。需要具有属性的对象 | |||
% 名称:新名称 | |||
% 它返回具有属性的对象 | |||
% id:视图的标识符。 | |||
% name:视图的新名称。 | |||
% type:视图类型。 | |||
% 注意:此方法在群集中不可用。 | |||
% 返回码 | |||
% 400:如果缺少视图名称,则返回HTTP 400。 | |||
% 404:如果视图名称未知,则返回HTTP 404。 | |||
renameView(PoolNameOrSocket, ViewName, NewViewName) -> | |||
Path = <<"/_api/view/", ViewName/binary, "/rename">>, | |||
NameStr = jiffy:encode(NewViewName), | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], <<"{\"name\":", NameStr/binary, "}">>). | |||
% 删除视图 | |||
% DELETE /_api/view/{view-name} | |||
% 路径参数 | |||
% view-name(必填):要放置的视图的名称。 | |||
% 删除由view-name标识的View 。 | |||
% 如果成功删除了View,则返回具有以下属性的对象: | |||
% 错误:假 | |||
% id:放置的视图的标识符 | |||
% 返回码 | |||
% 400:如果缺少视图名称,则返回HTTP 400。 | |||
% 404:如果视图名称未知,则返回HTTP 404。 | |||
delView(PoolNameOrSocket, ViewName) -> | |||
Path = <<"/_api/view/", ViewName/binary>>, | |||
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined). |
@ -0,0 +1,78 @@ | |||
-module(agAgencyPoolMgrExm). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-export([ | |||
start_link/3 | |||
, init_it/3 | |||
, system_code_change/4 | |||
, system_continue/3 | |||
, system_get_state/1 | |||
, system_terminate/4 | |||
]). | |||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genExm start %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |||
-spec start_link(module(), term(), [proc_lib:spawn_option()]) -> {ok, pid()}. | |||
start_link(Name, Args, SpawnOpts) -> | |||
proc_lib:start_link(?MODULE, init_it, [Name, self(), Args], infinity, SpawnOpts). | |||
init_it(Name, Parent, Args) -> | |||
case safeRegister(Name) of | |||
true -> | |||
process_flag(trap_exit, true), | |||
moduleInit(Parent, Args); | |||
{false, Pid} -> | |||
proc_lib:init_ack(Parent, {error, {alreadyStarted, Pid}}) | |||
end. | |||
-spec system_code_change(term(), module(), undefined | term(), term()) -> {ok, term()}. | |||
system_code_change(State, _Module, _OldVsn, _Extra) -> | |||
{ok, State}. | |||
-spec system_continue(pid(), [], {module(), atom(), pid(), term()}) -> ok. | |||
system_continue(_Parent, _Debug, {Parent, State}) -> | |||
loop(Parent, State). | |||
-spec system_get_state(term()) -> {ok, term()}. | |||
system_get_state(State) -> | |||
{ok, State}. | |||
-spec system_terminate(term(), pid(), [], term()) -> none(). | |||
system_terminate(Reason, _Parent, _Debug, _State) -> | |||
exit(Reason). | |||
safeRegister(Name) -> | |||
try register(Name, self()) of | |||
true -> true | |||
catch | |||
_:_ -> {false, whereis(Name)} | |||
end. | |||
moduleInit(Parent, Args) -> | |||
case agAgencyPoolMgrIns:init(Args) of | |||
{ok, State} -> | |||
proc_lib:init_ack(Parent, {ok, self()}), | |||
loop(Parent, State); | |||
{stop, Reason} -> | |||
proc_lib:init_ack(Parent, {error, Reason}), | |||
exit(Reason) | |||
end. | |||
loop(Parent, State) -> | |||
receive | |||
{system, From, Request} -> | |||
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], {Parent, State}); | |||
{'EXIT', Parent, Reason} -> | |||
terminate(Reason, State); | |||
Msg -> | |||
{ok, NewState} = agAgencyPoolMgrIns:handleMsg(Msg, State), | |||
loop(Parent, NewState) | |||
end. | |||
terminate(Reason, State) -> | |||
agAgencyPoolMgrIns:terminate(Reason, State), | |||
exit(Reason). | |||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genExm end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |||
@ -0,0 +1,188 @@ | |||
-module(agAgencyPoolMgrIns). | |||
-include("agHttpCli.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-export([ | |||
startPool/2 | |||
, startPool/3 | |||
, stopPool/1 | |||
, getOneAgency/1 | |||
%% genExm API | |||
, init/1 | |||
, handleMsg/2 | |||
, terminate/2 | |||
]). | |||
%% k-v beam cache | |||
-define(ETS_AG_Pool, ets_ag_Pool). | |||
-define(ETS_AG_Agency, ets_ag_Agency). | |||
%% TODO maybe spawn_opt params need optimize | |||
-define(agencySpec(ServerMod, ServerName, Args), | |||
#{ | |||
id => ServerName | |||
, start => {ServerMod, start_link, [ServerName, Args, [{min_heap_size, 10240}, {min_bin_vheap_size, 524288}, {fullsweep_after, 2048}]]} | |||
, restart => transient | |||
, shutdown => infinity | |||
, type => worker | |||
, modules => [ServerMod] | |||
}). | |||
-spec init(Args :: term()) -> ok. | |||
init(_Args) -> | |||
ets:new(?ETS_AG_Pool, [named_table, set, protected]), | |||
ets:new(?ETS_AG_Agency, [named_table, set, protected]), | |||
agKvsToBeam:load(?agBeamPool, []), | |||
agKvsToBeam:load(?agBeamAgency, []), | |||
{ok, undefined}. | |||
handleMsg({'$gen_call', From, {miStartPool, PoolName, DbCfgs, AgencyOpts}}, State) -> | |||
dealStart(PoolName, DbCfgs, AgencyOpts), | |||
gen_server:reply(From, ok), | |||
{ok, State}; | |||
handleMsg({'$gen_call', From, {miStopPool, Name}}, State) -> | |||
delaStop(Name), | |||
gen_server:reply(From, ok), | |||
{ok, State}; | |||
handleMsg(_Msg, State) -> | |||
?WARN(?MODULE, "receive unexpected msg: ~p", [_Msg]), | |||
{ok, State}. | |||
terminate(_Reason, _State) -> | |||
ets:delete(?ETS_AG_Pool), | |||
ets:delete(?ETS_AG_Agency), | |||
agKvsToBeam:load(?agBeamPool, []), | |||
agKvsToBeam:load(?agBeamAgency, []), | |||
ok. | |||
-spec startPool(poolName(), dbCfgs()) -> ok | {error, poolNameUsed}. | |||
startPool(PoolName, DbCfgs) -> | |||
startPool(PoolName, DbCfgs, []). | |||
-spec startPool(poolName(), dbCfgs(), agencyCfgs()) -> ok | {error, poolNameUsed}. | |||
startPool(PoolName, DbCfgs, AgencyCfgs) -> | |||
case ?agBeamPool:getv(PoolName) of | |||
undefined -> | |||
gen_server:call(?agAgencyPoolMgr, {miStartPool, PoolName, DbCfgs, AgencyCfgs}); | |||
_ -> | |||
{error, poolNameUsed} | |||
end. | |||
-spec stopPool(poolName()) -> ok | {error, poolNotStarted}. | |||
stopPool(PoolName) -> | |||
case ?agBeamPool:getv(PoolName) of | |||
undefined -> | |||
{error, poolNotStarted}; | |||
_ -> | |||
gen_server:call(?agAgencyPoolMgr, {miStopPool, PoolName}, infinity) | |||
end. | |||
dealStart(PoolName, DbCfgs, AgencyCfgs) -> | |||
#dbOpts{poolSize = PoolSize, protocol = Protocol} = DbOpts = agMiscUtils:dbOpts(DbCfgs), | |||
AgencyOpts = agMiscUtils:agencyOpts(AgencyCfgs), | |||
cacheAddPool(PoolName, DbOpts), | |||
startChildren(PoolName, Protocol, PoolSize, AgencyOpts), | |||
cacheAddAgency(PoolName, PoolSize), | |||
case persistent_term:get(PoolName, undefined) of | |||
undefined -> | |||
IndexRef = atomics:new(1, [{signed, false}]), | |||
persistent_term:put(PoolName, IndexRef); | |||
_ -> | |||
ignore | |||
end, | |||
ok. | |||
delaStop(PoolName) -> | |||
case ?agBeamPool:getv(PoolName) of | |||
undefined -> | |||
{error, poolNotStarted}; | |||
#dbOpts{poolSize = PoolSize} -> | |||
stopChildren(agencyNames(PoolName, PoolSize)), | |||
cacheDelPool(PoolName), | |||
cacheDelAgency(PoolName), | |||
ok | |||
end. | |||
agencyName(PoolName, Index) -> | |||
list_to_atom(atom_to_list(PoolName) ++ "_" ++ integer_to_list(Index)). | |||
agencyNames(PoolName, PoolSize) -> | |||
[agencyName(PoolName, N) || N <- lists:seq(1, PoolSize)]. | |||
agencyMod(tcp) -> | |||
agTcpAgencyExm; | |||
agencyMod(ssl) -> | |||
agSslAgencyExm; | |||
agencyMod(_) -> | |||
agTcpAgencyExm. | |||
-spec startChildren(atom(), protocol(), poolSize(), agencyOpts()) -> ok. | |||
startChildren(PoolName, Protocol, PoolSize, AgencyOpts) -> | |||
AgencyMod = agencyMod(Protocol), | |||
AgencyNames = agencyNames(PoolName, PoolSize), | |||
AgencySpecs = [?agencySpec(AgencyMod, AgencyName, {PoolName, AgencyName, AgencyOpts}) || AgencyName <- AgencyNames], | |||
[supervisor:start_child(agAgencyPool_sup, AgencySpec) || AgencySpec <- AgencySpecs], | |||
ok. | |||
stopChildren([AgencyName | T]) -> | |||
case supervisor:terminate_child(agAgencyPool_sup, AgencyName) of | |||
ok -> | |||
ok; | |||
{error, TerReason} -> | |||
?WARN(agAgencyPoolMgrIns, ":terminate_child: ~p error reason: ~p ~n", [AgencyName, TerReason]) | |||
end, | |||
case supervisor:delete_child(agAgencyPool_sup, AgencyName) of | |||
ok -> | |||
ok; | |||
{error, DelReason} -> | |||
?WARN(agAgencyPoolMgrIns, ":delete_child: ~p error reason: ~p ~n", [AgencyName, DelReason]) | |||
end, | |||
stopChildren(T); | |||
stopChildren([]) -> | |||
ok. | |||
cacheAddPool(Key, Value) -> | |||
ets:insert(?ETS_AG_Pool, {Key, Value}), | |||
KVS = ets:tab2list(?ETS_AG_Pool), | |||
agKvsToBeam:load(?agBeamPool, KVS), | |||
ok. | |||
cacheDelPool(Key) -> | |||
ets:delete(?ETS_AG_Pool, Key), | |||
KVS = ets:tab2list(?ETS_AG_Pool), | |||
agKvsToBeam:load(?agBeamPool, KVS), | |||
ok. | |||
cacheAddAgency(PoolName, PoolSize) -> | |||
NameList = [{{PoolName, N}, agencyName(PoolName, N)} || N <- lists:seq(1, PoolSize)], | |||
ets:insert(?ETS_AG_Agency, NameList), | |||
KVS = ets:tab2list(?ETS_AG_Agency), | |||
agKvsToBeam:load(?agBeamAgency, KVS), | |||
ok. | |||
cacheDelAgency(PoolName) -> | |||
ets:match_delete(?ETS_AG_Agency, {{PoolName, '_'}, '_'}), | |||
KVS = ets:tab2list(?ETS_AG_Agency), | |||
agKvsToBeam:load(?agBeamAgency, KVS), | |||
ok. | |||
-spec getOneAgency(atom()) -> atom() | {error, term()}. | |||
getOneAgency(PoolName) -> | |||
case ?agBeamPool:getv(PoolName) of | |||
undefined -> | |||
{error, pool_not_found}; | |||
#dbOpts{poolSize = PoolSize} -> | |||
Ref = persistent_term:get(PoolName), | |||
AgencyIdx = atomics:add_get(Ref, 1, 1), | |||
case AgencyIdx >= PoolSize of | |||
true -> | |||
atomics:put(Ref, 1, 0), | |||
?agBeamAgency:getv({PoolName, AgencyIdx}); | |||
_ -> | |||
?agBeamAgency:getv({PoolName, AgencyIdx}) | |||
end | |||
end. |
@ -0,0 +1,15 @@ | |||
-module(agAgencyPool_sup). | |||
-behaviour(supervisor). | |||
-export([ | |||
start_link/0 | |||
, init/1 | |||
]). | |||
-spec start_link() -> {ok, pid()}. | |||
start_link() -> | |||
supervisor:start_link({local, ?MODULE}, ?MODULE, []). | |||
init([]) -> | |||
{ok, {{one_for_one, 100, 3600}, []}}. |
@ -0,0 +1,96 @@ | |||
-module(agAgencyUtils). | |||
-include("agHttpCli.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-export([ | |||
cancelTimer/1 | |||
, dealClose/3 | |||
, reconnectTimer/2 | |||
, agencyReply/2 | |||
, agencyReply/4 | |||
, initReconnectState/3 | |||
, resetReconnectState/1 | |||
, updateReconnectState/1 | |||
]). | |||
-spec dealClose(srvState(), cliState(), term()) -> {ok, srvState(), cliState()}. | |||
dealClose(SrvState, #cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, curInfo = CurInfo} = ClientState, Reply) -> | |||
agencyReply(CurInfo, Reply), | |||
agencyReplyAll(RequestsOuts, RequestsIns, Reply), | |||
reconnectTimer(SrvState, ClientState#cliState{requestsIns = [], requestsOuts = [], backlogNum = 0, status = leisure, curInfo = undefined, recvState = undefined}). | |||
-spec reconnectTimer(srvState(), cliState()) -> {ok, srvState(), cliState()}. | |||
reconnectTimer(#srvState{reconnectState = undefined} = SrvState, CliState) -> | |||
{ok, {SrvState#srvState{socket = undefined}, CliState}}; | |||
reconnectTimer(#srvState{reconnectState = ReconnectState} = SrvState, CliState) -> | |||
#reconnectState{current = Current} = MewReconnectState = agAgencyUtils:updateReconnectState(ReconnectState), | |||
TimerRef = erlang:send_after(Current, self(), ?miDoNetConnect), | |||
{ok, SrvState#srvState{reconnectState = MewReconnectState, socket = undefined, timerRef = TimerRef}, CliState}. | |||
-spec agencyReply(term(), term()) -> ok. | |||
agencyReply({undefined, _RequestId, TimerRef}, _Reply) -> | |||
agAgencyUtils:cancelTimer(TimerRef); | |||
agencyReply({PidForm, RequestId, TimerRef}, Reply) -> | |||
agAgencyUtils:cancelTimer(TimerRef), | |||
catch PidForm ! #miRequestRet{requestId = RequestId, reply = Reply}, | |||
ok; | |||
agencyReply(undefined, _RequestRet) -> | |||
ok. | |||
-spec agencyReply(undefined | pid(), requestId(), undefined | reference(), term()) -> ok. | |||
agencyReply(undefined, _RequestId, TimerRef, _Reply) -> | |||
agAgencyUtils:cancelTimer(TimerRef), | |||
ok; | |||
agencyReply(FormPid, RequestId, TimerRef, Reply) -> | |||
agAgencyUtils:cancelTimer(TimerRef), | |||
catch FormPid ! #miRequestRet{requestId = RequestId, reply = Reply}, | |||
ok. | |||
-spec agencyReplyAll(list(), list(), term()) -> ok. | |||
agencyReplyAll(RequestsOuts, RequestsIns, Reply) -> | |||
[agencyReply(FormPid, RequestId, undefined, Reply) || #miRequest{requestId = RequestId, fromPid = FormPid} <- RequestsOuts], | |||
[agencyReply(FormPid, RequestId, undefined, Reply) || #miRequest{requestId = RequestId, fromPid = FormPid} <- lists:reverse(RequestsIns)], | |||
ok. | |||
-spec cancelTimer(undefined | reference()) -> ok. | |||
cancelTimer(undefined) -> ok; | |||
cancelTimer(TimerRef) -> | |||
case erlang:cancel_timer(TimerRef) of | |||
false -> | |||
receive | |||
{timeout, TimerRef, _Msg} -> | |||
%% discard the timeout msg | |||
ok | |||
after 0 -> | |||
ok | |||
end; | |||
_ -> | |||
%% Timer already run | |||
ok | |||
end. | |||
-spec initReconnectState(boolean(), pos_integer(), pos_integer()) -> reconnectState() | undefined. | |||
initReconnectState(IsReconnect, Min, Max) -> | |||
case IsReconnect of | |||
true -> | |||
#reconnectState{min = Min, max = Max, current = Min}; | |||
false -> | |||
undefined | |||
end. | |||
-spec resetReconnectState(undefined | reconnectState()) -> reconnectState() | undefined. | |||
resetReconnectState(#reconnectState{min = Min} = ReconnectState) -> | |||
ReconnectState#reconnectState{current = Min}. | |||
-spec updateReconnectState(reconnectState()) -> reconnectState(). | |||
updateReconnectState(#reconnectState{current = Current, max = Max} = ReconnectState) -> | |||
NewCurrent = Current + Current, | |||
ReconnectState#reconnectState{current = minCur(NewCurrent, Max)}. | |||
minCur(A, B) when B >= A -> | |||
A; | |||
minCur(_, B) -> | |||
B. | |||
@ -0,0 +1,278 @@ | |||
-module(agHttpCli). | |||
-include("agHttpCli.hrl"). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-export([ | |||
%% Common Request API | |||
callAgency/5 | |||
, callAgency/6 | |||
, callAgency/7 | |||
, castAgency/5 | |||
, castAgency/6 | |||
, castAgency/7 | |||
, castAgency/8 | |||
, receiveRequestRet/2 | |||
%% Pools API | |||
, startPool/2 | |||
, startPool/3 | |||
, stopPool/1 | |||
%% Single Process DbAPI | |||
, connectDb/1 | |||
, disConnectDb/1 | |||
, getCurDbInfo/1 | |||
, useDatabase/2 | |||
]). | |||
-spec callAgency(poolNameOrSocket(), method(), path(), headers(), body()) -> term() | {error, term()}. | |||
callAgency(PoolNameOrSocket, Method, Path, Headers, Body) -> | |||
callAgency(PoolNameOrSocket, Method, Path, Headers, Body, false, ?DEFAULT_TIMEOUT). | |||
-spec callAgency(poolNameOrSocket(), method(), path(), headers(), body(), boolean()) -> term() | {error, atom()}. | |||
callAgency(PoolNameOrSocket, Method, Path, Headers, Body, IsSystem) -> | |||
callAgency(PoolNameOrSocket, Method, Path, Headers, Body, IsSystem, ?DEFAULT_TIMEOUT). | |||
-spec callAgency(poolNameOrSocket(), method(), path(), headers(), body(), boolean(), timeout()) -> term() | {error, atom()}. | |||
callAgency(PoolNameOrSocket, Method, Path, Headers, Body, IsSystem, Timeout) -> | |||
case castAgency(PoolNameOrSocket, Method, Path, Headers, Body, self(), IsSystem, Timeout) of | |||
{waitRRT, RequestId, MonitorRef} -> | |||
receiveRequestRet(RequestId, MonitorRef); | |||
{error, _Reason} = Err -> | |||
Err; | |||
Ret -> | |||
Ret | |||
end. | |||
-spec castAgency(poolNameOrSocket(), method(), path(), headers(), body()) -> {ok, requestId()} | {error, atom()}. | |||
castAgency(PoolNameOrSocket, Method, Path, Headers, Body) -> | |||
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, self(), false, ?DEFAULT_TIMEOUT). | |||
-spec castAgency(poolNameOrSocket(), method(), path(), headers(), body(), boolean()) -> {ok, requestId()} | {error, atom()}. | |||
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, IsSystem) -> | |||
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, self(), IsSystem, ?DEFAULT_TIMEOUT). | |||
-spec castAgency(poolNameOrSocket(), method(), path(), headers(), body(), boolean(), timeout()) -> {ok, requestId()} | {error, atom()}. | |||
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, IsSystem, Timeout) -> | |||
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, self(), IsSystem, Timeout). | |||
-spec castAgency(poolNameOrSocket(), method(), path(), headers(), body(), pid(), boolean(), timeout()) -> {ok, requestId()} | {error, atom()}. | |||
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, Pid, IsSystem, Timeout) -> | |||
OverTime = | |||
case Timeout of | |||
infinity -> infinity; | |||
_ -> | |||
erlang:monotonic_time(millisecond) + Timeout | |||
end, | |||
case erlang:is_atom(PoolNameOrSocket) of | |||
true -> | |||
case agAgencyPoolMgrIns:getOneAgency(PoolNameOrSocket) of | |||
{error, pool_not_found} = Err -> | |||
Err; | |||
undefined -> | |||
{error, undefined_server}; | |||
AgencyName -> | |||
MonitorRef = erlang:monitor(process, AgencyName), | |||
RequestId = {AgencyName, MonitorRef}, | |||
catch AgencyName ! #miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = Pid, overTime = OverTime, isSystem = IsSystem}, | |||
{waitRRT, RequestId, MonitorRef} | |||
end; | |||
_ -> | |||
case getCurDbInfo(PoolNameOrSocket) of | |||
{DbName, UserPassWord, Host, Protocol} -> | |||
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]), | |||
case Protocol of | |||
tcp -> | |||
case gen_tcp:send(PoolNameOrSocket, Request) of | |||
ok -> | |||
receiveTcpData(undefined, PoolNameOrSocket, binary:compile_pattern(<<"\r\n">>), binary:compile_pattern(<<"\r\n\r\n">>), Method == ?AgHead); | |||
{error, Reason} = Err -> | |||
?WARN(castAgency, ":gen_tcp send error: ~p ~n", [Reason]), | |||
disConnectDb(PoolNameOrSocket), | |||
Err | |||
end; | |||
ssl -> | |||
case ssl:send(PoolNameOrSocket, Request) of | |||
ok -> | |||
receiveSslData(undefined, PoolNameOrSocket, binary:compile_pattern(<<"\r\n">>), binary:compile_pattern(<<"\r\n\r\n">>), Method == ?AgHead); | |||
{error, Reason} = Err -> | |||
?WARN(castAgency, ":ssl send error: ~p ~n", [Reason]), | |||
disConnectDb(PoolNameOrSocket), | |||
Err | |||
end | |||
end; | |||
_ -> | |||
{error, dbinfoNotFound} | |||
end | |||
end. | |||
-spec receiveRequestRet(requestId(), reference()) -> {StatusCode :: non_neg_integer(), Body :: binary(), Headers :: binary()} | {error, term()}. | |||
receiveRequestRet(RequestId, MonitorRef) -> | |||
receive | |||
#miRequestRet{requestId = RequestId, reply = Reply} -> | |||
erlang:demonitor(MonitorRef), | |||
case Reply of | |||
{_StatusCode, Body, _Headers} -> | |||
case Body of | |||
<<>> -> | |||
erlang:setelement(2, Reply, #{}); | |||
_ -> | |||
erlang:setelement(2, Reply, jiffy:decode(Body, [return_maps, copy_strings])) | |||
end; | |||
_ -> | |||
Reply | |||
end; | |||
{'DOWN', MonitorRef, process, _Pid, Reason} -> | |||
{error, {agencyDown, Reason}} | |||
end. | |||
-spec receiveTcpData(recvState() | undefined, socket(), binary:cp(), binary:cp(), boolean()) -> {ok, term(), term()} | {error, term()}. | |||
receiveTcpData(RecvState, Socket, Rn, RnRn, IsHeadMethod) -> | |||
receive | |||
{tcp, Socket, Data} -> | |||
try agHttpProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of | |||
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} -> | |||
case Body of | |||
<<>> -> | |||
{ok, #{}, StatusCode, Headers}; | |||
_ -> | |||
{ok, jiffy:decode(Body, [return_maps, copy_strings]), StatusCode, Headers} | |||
end; | |||
{ok, NewRecvState} -> | |||
receiveTcpData(NewRecvState, Socket, Rn, RnRn, IsHeadMethod); | |||
{error, Reason} -> | |||
?WARN(receiveTcpData, "handle tcp data error: ~p ~n", [Reason]), | |||
disConnectDb(Socket), | |||
{error, {tcpDataError, Reason}} | |||
catch | |||
E:R:S -> | |||
?WARN(receiveTcpData, "handle tcp data crash: ~p:~p~n~p ~n ", [E, R, S]), | |||
disConnectDb(Socket), | |||
{error, handledataError} | |||
end; | |||
{tcp_closed, Socket} -> | |||
disConnectDb(Socket), | |||
{error, tcp_closed}; | |||
{tcp_error, Socket, Reason} -> | |||
disConnectDb(Socket), | |||
{error, {tcp_error, Reason}} | |||
end. | |||
-spec receiveSslData(recvState() | undefined, socket(), binary:cp(), binary:cp(), boolean()) -> {ok, term(), term()} | {error, term()}. | |||
receiveSslData(RecvState, Socket, Rn, RnRn, IsHeadMethod) -> | |||
receive | |||
{ssl, Socket, Data} -> | |||
try agHttpProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of | |||
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} -> | |||
case Body of | |||
<<>> -> | |||
{ok, #{}, StatusCode, Headers}; | |||
_ -> | |||
{ok, jiffy:decode(Body, [return_maps, copy_strings]), StatusCode, Headers} | |||
end; | |||
{ok, NewRecvState} -> | |||
receiveSslData(NewRecvState, Socket, Rn, RnRn, IsHeadMethod); | |||
{error, Reason} -> | |||
?WARN(receiveSslData, "handle tcp data error: ~p ~n", [Reason]), | |||
disConnectDb(Socket), | |||
{error, {sslDataError, Reason}} | |||
catch | |||
E:R:S -> | |||
?WARN(receiveSslData, "handle tcp data crash: ~p:~p~n~p ~n ", [E, R, S]), | |||
disConnectDb(Socket), | |||
{error, handledataError} | |||
end; | |||
{ssl_closed, Socket} -> | |||
disConnectDb(Socket), | |||
{error, ssl_closed}; | |||
{ssl_error, Socket, Reason} -> | |||
disConnectDb(Socket), | |||
{error, {ssl_error, Reason}} | |||
end. | |||
-spec startPool(poolName(), dbCfgs()) -> ok | {error, poolNameUsed}. | |||
startPool(PoolName, DbCfgs) -> | |||
agAgencyPoolMgrIns:startPool(PoolName, DbCfgs, []). | |||
-spec startPool(poolName(), dbCfgs(), agencyCfgs()) -> ok | {error, poolNameUsed}. | |||
startPool(PoolName, DbCfgs, AgencyCfgs) -> | |||
agAgencyPoolMgrIns:startPool(PoolName, DbCfgs, AgencyCfgs). | |||
-spec stopPool(poolName()) -> ok | {error, poolNotStarted}. | |||
stopPool(PoolName) -> | |||
agAgencyPoolMgrIns:stopPool(PoolName). | |||
-spec connectDb(dbCfgs()) -> {ok, socket()} | {error, term()}. | |||
connectDb(DbCfgs) -> | |||
#dbOpts{ | |||
host = Host, | |||
port = Port, | |||
hostname = HostName, | |||
dbName = DbName, | |||
protocol = Protocol, | |||
userPassword = UserPassword, | |||
socketOpts = SocketOpts | |||
} = agMiscUtils:dbOpts(DbCfgs), | |||
case inet:getaddrs(HostName, inet) of | |||
{ok, IPList} -> | |||
Ip = agMiscUtils:randomElement(IPList), | |||
case Protocol of | |||
tcp -> | |||
case gen_tcp:connect(Ip, Port, SocketOpts, ?DEFAULT_CONNECT_TIMEOUT) of | |||
{ok, Socket} -> | |||
setCurDbInfo(Socket, DbName, UserPassword, Host, Protocol), | |||
{ok, Socket}; | |||
{error, Reason} = Err -> | |||
?WARN(connectDb, "connect error: ~p~n", [Reason]), | |||
Err | |||
end; | |||
ssl -> | |||
case ssl:connect(Ip, Port, SocketOpts, ?DEFAULT_CONNECT_TIMEOUT) of | |||
{ok, Socket} -> | |||
setCurDbInfo(Socket, DbName, UserPassword, Host, Protocol), | |||
{ok, Socket}; | |||
{error, Reason} = Err -> | |||
?WARN(connectDb, "connect error: ~p~n", [Reason]), | |||
Err | |||
end | |||
end; | |||
{error, Reason} = Err -> | |||
?WARN(connectDb, "getaddrs error: ~p~n", [Reason]), | |||
Err | |||
end. | |||
-spec disConnectDb(socket()) -> ok | {error, term()}. | |||
disConnectDb(Socket) -> | |||
case erlang:erase({'$agDbInfo', Socket}) of | |||
undefined -> | |||
ignore; | |||
{_DbName, _UserPassword, _Host, Protocol} -> | |||
case Protocol of | |||
tcp -> | |||
gen_tcp:close(Socket); | |||
ssl -> | |||
ssl:close(Socket) | |||
end | |||
end. | |||
-spec setCurDbInfo(socket(), binary(), tuple(), host(), protocol()) -> term(). | |||
setCurDbInfo(Socket, DbName, UserPassword, Host, Protocol) -> | |||
erlang:put({'$agDbInfo', Socket}, {DbName, UserPassword, Host, Protocol}). | |||
-spec getCurDbInfo(socket()) -> term(). | |||
getCurDbInfo(Socket) -> | |||
erlang:get({'$agDbInfo', Socket}). | |||
-spec useDatabase(socket(), binary()) -> ok. | |||
useDatabase(Socket, NewDbName) -> | |||
case erlang:get({'$agDbInfo', Socket}) of | |||
undefined -> | |||
ignore; | |||
{_DbName, UserPassword, Host, Protocol} -> | |||
erlang:put({'$agDbInfo', Socket}, {<<"/_db/", NewDbName/binary>>, UserPassword, Host, Protocol}) | |||
end, | |||
ok. |
@ -0,0 +1,281 @@ | |||
-module(agHttpProtocol). | |||
-include("agHttpCli.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-export([ | |||
headers/1 | |||
, request/7 | |||
, response/2 | |||
, response/5 | |||
]). | |||
%% <<"Content-Type: application/json; charset=utf-8">>, | |||
-spec request(boolean(), body(), method(), host(), binary(), path(), headers()) -> iolist(). | |||
request(true, undefined, Method, Host, _DbName, Path, Headers) -> | |||
[ | |||
Method, <<"/_db/_system">>, Path, <<" HTTP/1.1\r\nHost: ">>, Host, | |||
<<"\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: 0\r\n">>, | |||
spellHeaders(Headers), <<"\r\n">> | |||
]; | |||
request(false, undefined, Method, Host, DbName, Path, Headers) -> | |||
[ | |||
Method, DbName, Path, <<" HTTP/1.1\r\nHost: ">>, Host, | |||
<<"\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: 0\r\n">>, | |||
spellHeaders(Headers), <<"\r\n">> | |||
]; | |||
request(false, Body, Method, Host, DbName, Path, Headers) -> | |||
ContentLength = integer_to_binary(iolist_size(Body)), | |||
NewHeaders = [{<<"Content-Length">>, ContentLength} | Headers], | |||
[ | |||
Method, DbName, Path, <<" HTTP/1.1\r\nHost: ">>, Host, | |||
<<"\r\nContent-Type: application/json; charset=utf-8\r\n">>, | |||
spellHeaders(NewHeaders), <<"\r\n">>, Body | |||
]; | |||
request(true, Body, Method, Host, _DbName, Path, Headers) -> | |||
ContentLength = integer_to_binary(iolist_size(Body)), | |||
NewHeaders = [{<<"Content-Length">>, ContentLength} | Headers], | |||
[ | |||
Method, <<"/_db/_system">>, Path, <<" HTTP/1.1\r\nHost: ">>, Host, | |||
<<"\r\nContent-Type: application/json; charset=utf-8\r\n">>, | |||
spellHeaders(NewHeaders), <<"\r\n">>, Body | |||
]. | |||
-spec response(binary(), boolean()) -> {ok, recvState(), binary()} | error(). | |||
response(Data, IsHeadMethod) -> | |||
response(undefined, binary:compile_pattern(<<"\r\n">>), binary:compile_pattern(<<"\r\n\r\n">>), Data, IsHeadMethod). | |||
-spec response(undefined | recvState(), binary:cp(), binary:cp(), binary(), boolean()) -> {ok, recvState()} | error(). | |||
response(undefined, Rn, RnRn, Data, IsHeadMethod) -> | |||
case parseStatusLine(Data, Rn) of | |||
{StatusCode, Rest} -> | |||
case splitHeaders(Rest, Rn, RnRn) of | |||
{undefined, Headers, Body} -> | |||
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = undefined, body = Body}}; | |||
{0, Headers, Rest} -> | |||
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = 0, body = Rest}}; | |||
{chunked, Headers, Body} -> | |||
case IsHeadMethod orelse StatusCode == 204 orelse StatusCode == 304 orelse (StatusCode < 200 andalso StatusCode >= 100) of | |||
true -> | |||
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = 0, body = Body}}; | |||
_ -> | |||
RecvState = #recvState{stage = body, contentLength = chunked, statusCode = StatusCode, headers = Headers}, | |||
response(RecvState, Rn, RnRn, Body, IsHeadMethod) | |||
end; | |||
{ContentLength, Headers, Body} -> | |||
BodySize = erlang:size(Body), | |||
if | |||
BodySize == ContentLength -> | |||
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}}; | |||
BodySize > ContentLength -> | |||
?WARN(agTcpAgencyIns, "11 contentLength get to long data why? more: ~p ~n", [BodySize - ContentLength]), | |||
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}}; | |||
true -> | |||
case IsHeadMethod orelse StatusCode == 204 orelse StatusCode == 304 orelse (StatusCode < 200 andalso StatusCode >= 100) of | |||
true -> | |||
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}}; | |||
_ -> | |||
{ok, #recvState{stage = body, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}} | |||
end | |||
end; | |||
not_enough_data -> | |||
{ok, #recvState{stage = header, body = Data}} | |||
end; | |||
not_enough_data -> | |||
{ok, #recvState{stage = header, body = Data}}; | |||
{error, Reason} -> | |||
{error, Reason} | |||
end; | |||
response(#recvState{stage = body, contentLength = chunked, body = Body, buffer = Buffer} = RecvState, Rn, _RnRn, Data, _IsHeadMethod) -> | |||
NewBuffer = <<Buffer/binary, Data/binary>>, | |||
case parseChunks(NewBuffer, Rn, []) of | |||
{ok, AddBody, _Rest} -> | |||
LastBody = <<Body/binary, AddBody/binary>>, | |||
{done, RecvState#recvState{stage = done, body = LastBody}}; | |||
{not_enough_data, AddBody, Rest} -> | |||
NewBody = <<Body/binary, AddBody/binary>>, | |||
{ok, RecvState#recvState{body = NewBody, buffer = Rest}}; | |||
{error, Reason} -> | |||
{error, Reason} | |||
end; | |||
response(#recvState{stage = body, contentLength = ContentLength, body = Body} = RecvState, _Rn, _RnRn, Data, _IsHeadMethod) -> | |||
CurData = <<Body/binary, Data/binary>>, | |||
BodySize = erlang:size(CurData), | |||
if | |||
BodySize == ContentLength -> | |||
{done, RecvState#recvState{stage = done, body = CurData}}; | |||
BodySize > ContentLength -> | |||
?WARN(agTcpAgencyIns, "22 contentLength get to long data why? more: ~p ~n", [BodySize - ContentLength]), | |||
{done, #recvState{stage = done, body = CurData}}; | |||
true -> | |||
{ok, RecvState#recvState{body = CurData}} | |||
end; | |||
response(#recvState{stage = header, body = OldBody}, Rn, RnRn, Data, IsHeadMethod) -> | |||
CurBody = <<OldBody/binary, Data/binary>>, | |||
case parseStatusLine(CurBody, Rn) of | |||
{StatusCode, Rest} -> | |||
case splitHeaders(Rest, Rn, RnRn) of | |||
{undefined, Headers, Body} -> | |||
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = undefined, body = Body}}; | |||
{0, Headers, Body} -> | |||
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = 0, body = Body}}; | |||
{chunked, Headers, Rest} -> | |||
case IsHeadMethod orelse StatusCode == 204 orelse StatusCode == 304 orelse (StatusCode < 200 andalso StatusCode >= 100) of | |||
true -> | |||
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = 0, body = <<>>}}; | |||
_ -> | |||
RecvState = #recvState{stage = body, contentLength = chunked, statusCode = StatusCode, headers = Headers}, | |||
response(RecvState, Rn, RnRn, Rest, IsHeadMethod) | |||
end; | |||
{ContentLength, Headers, Body} -> | |||
BodySize = erlang:size(Body), | |||
if | |||
BodySize == ContentLength -> | |||
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}}; | |||
BodySize > ContentLength -> | |||
?WARN(agTcpAgencyIns, "33 contentLength get to long data why? more: ~p ~n", [BodySize - ContentLength]), | |||
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}}; | |||
true -> | |||
case IsHeadMethod orelse StatusCode == 204 orelse StatusCode == 304 orelse (StatusCode < 200 andalso StatusCode >= 100) of | |||
true -> | |||
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}}; | |||
_ -> | |||
{ok, #recvState{stage = body, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}} | |||
end | |||
end; | |||
not_enough_data -> | |||
{ok, #recvState{stage = header, body = CurBody}} | |||
end; | |||
not_enough_data -> | |||
{ok, #recvState{stage = header, body = CurBody}}; | |||
{error, Reason} -> | |||
{error, Reason} | |||
end. | |||
spellHeaders(Headers) -> | |||
<<<<Key/binary, ": ", Value/binary, "\r\n">> || {Key, Value} <- Headers>>. | |||
splitHeaders(Data, Rn, RnRn) -> | |||
case binary:split(Data, RnRn) of | |||
[Data] -> | |||
not_enough_data; | |||
[Headers, Body] -> | |||
HeadersList = binary:split(Headers, Rn, [global]), | |||
ContentLength = contentLength(HeadersList), | |||
{ContentLength, Headers, Body} | |||
end. | |||
contentLength([]) -> | |||
undefined; | |||
contentLength([<<"Content-Length: ", Rest/binary>> | _T]) -> | |||
binary_to_integer(Rest); | |||
contentLength([<<"content-length: ", Rest/binary>> | _T]) -> | |||
binary_to_integer(Rest); | |||
contentLength([<<"Transfer-Encoding: chunked">> | _T]) -> | |||
chunked; | |||
contentLength([<<"transfer-encoding: chunked">> | _T]) -> | |||
chunked; | |||
contentLength([_ | T]) -> | |||
contentLength(T). | |||
parseStatusLine(Data, Rn) -> | |||
case binary:split(Data, Rn) of | |||
[Data] -> | |||
not_enough_data; | |||
[Line, Rest] -> | |||
case parseStatusReason(Line) of | |||
{ok, StatusCode} -> | |||
{StatusCode, Rest}; | |||
{error, Reason} -> | |||
{error, Reason} | |||
end | |||
end. | |||
parseStatusReason(<<"HTTP/1.1 200 OK">>) -> | |||
{ok, 200}; | |||
parseStatusReason(<<"HTTP/1.1 204 No Content">>) -> | |||
{ok, 204}; | |||
parseStatusReason(<<"HTTP/1.1 301 Moved Permanently">>) -> | |||
{ok, 301}; | |||
parseStatusReason(<<"HTTP/1.1 302 Found">>) -> | |||
{ok, 302}; | |||
parseStatusReason(<<"HTTP/1.1 403 Forbidden">>) -> | |||
{ok, 403}; | |||
parseStatusReason(<<"HTTP/1.1 404 Not Found">>) -> | |||
{ok, 404}; | |||
parseStatusReason(<<"HTTP/1.1 500 Internal Server Error">>) -> | |||
{ok, 500}; | |||
parseStatusReason(<<"HTTP/1.1 502 Bad Gateway">>) -> | |||
{ok, 502}; | |||
parseStatusReason(<<"HTTP/1.1 ", N1, N2, N3, " ", _Reason/bits>>) | |||
when $0 =< N1, N1 =< $9, | |||
$0 =< N2, N2 =< $9, | |||
$0 =< N3, N3 =< $9 -> | |||
StatusCode = (N1 - $0) * 100 + (N2 - $0) * 10 + (N3 - $0), | |||
{ok, StatusCode}; | |||
parseStatusReason(<<"HTTP/1.0 ", _/binary>>) -> | |||
{error, unsupported_feature}; | |||
parseStatusReason(_) -> | |||
{error, bad_request}. | |||
parseChunks(Data, Rn, Acc) -> | |||
case parseChunk(Data, Rn) of | |||
done -> | |||
{ok, iolist_to_binary(lists:reverse(Acc)), <<>>}; | |||
{ok, Body, Rest} -> | |||
parseChunks(Rest, Rn, [Body | Acc]); | |||
not_enough_data -> | |||
{not_enough_data, iolist_to_binary(lists:reverse(Acc)), Data}; | |||
{error, Reason} -> | |||
{error, Reason} | |||
end. | |||
parseChunk(Data, Rn) -> | |||
case binary:split(Data, Rn) of | |||
[Size, Rest] -> | |||
case parseChunkSize(Size) of | |||
undefined -> | |||
{error, invalid_chunk_size}; | |||
0 -> | |||
done; | |||
HexSize -> | |||
parseChunkBody(Rest, HexSize) | |||
end; | |||
[Data] -> | |||
not_enough_data | |||
end. | |||
parseChunkBody(Data, Size) -> | |||
case Data of | |||
<<Body:Size/binary, "\r\n", Rest/binary>> -> | |||
{ok, Body, Rest}; | |||
_ -> | |||
not_enough_data | |||
end. | |||
parseChunkSize(Bin) -> | |||
try | |||
binary_to_integer(Bin, 16) | |||
catch | |||
error:badarg -> | |||
undefined | |||
end. | |||
-spec headers(recvState()) -> {ok, headers()} | {error, invalidHeaders}. | |||
headers(#recvState{headers = Headers}) -> | |||
parseHeaders(Headers, []). | |||
parseHeaders([], Acc) -> | |||
{ok, lists:reverse(Acc)}; | |||
parseHeaders([Header | T], Acc) -> | |||
case binary:split(Header, <<":">>) of | |||
[Header] -> | |||
{error, invalidHeaders}; | |||
[Key, <<>>] -> | |||
parseHeaders(T, [{Key, undefined} | Acc]); | |||
[Key, <<" ", Value/binary>>] -> | |||
parseHeaders(T, [{Key, Value} | Acc]) | |||
end. | |||
@ -0,0 +1,45 @@ | |||
-module(agKvsToBeam). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-export([ | |||
load/2 | |||
]). | |||
%% attention: the map type data can not as the key | |||
-type key() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple(). | |||
-type value() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple() | map(). | |||
-spec load(term(), [{key(), value()}]) -> ok. | |||
load(Module, KVs) -> | |||
Forms = forms(Module, KVs), | |||
{ok, Module, Bin} = compile:forms(Forms), | |||
code:soft_purge(Module), | |||
{module, Module} = code:load_binary(Module, atom_to_list(Module), Bin), | |||
ok. | |||
forms(Module, KVs) -> | |||
%% -module(Module). | |||
Mod = erl_syntax:attribute(erl_syntax:atom(module), [erl_syntax:atom(Module)]), | |||
%% -export([getv/0]). | |||
ExportList = [erl_syntax:arity_qualifier(erl_syntax:atom(getv), erl_syntax:integer(1))], | |||
Export = erl_syntax:attribute(erl_syntax:atom(export), [erl_syntax:list(ExportList)]), | |||
%% getv(K) -> V | |||
Function = erl_syntax:function(erl_syntax:atom(getv), lookup_clauses(KVs, [])), | |||
[erl_syntax:revert(X) || X <- [Mod, Export, Function]]. | |||
lookup_clause(Key, Value) -> | |||
Var = erl_syntax:abstract(Key), | |||
Body = erl_syntax:abstract(Value), | |||
erl_syntax:clause([Var], [], [Body]). | |||
lookup_clause_anon() -> | |||
Var = erl_syntax:variable("_"), | |||
Body = erl_syntax:atom(undefined), | |||
erl_syntax:clause([Var], [], [Body]). | |||
lookup_clauses([], Acc) -> | |||
lists:reverse(lists:flatten([lookup_clause_anon() | Acc])); | |||
lookup_clauses([{Key, Value} | T], Acc) -> | |||
lookup_clauses(T, [lookup_clause(Key, Value) | Acc]). |
@ -0,0 +1,127 @@ | |||
-module(agMiscUtils). | |||
-include("agHttpCli.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-export([ | |||
parseUrl/1 | |||
, dbOpts/1 | |||
, agencyOpts/1 | |||
, warnMsg/3 | |||
, getListValue/3 | |||
, randomElement/1 | |||
, toBinary/1 | |||
, spellQueryPars/1 | |||
, getHeaderValue/2 | |||
, lookHeader/2 | |||
]). | |||
-spec parseUrl(binary()) -> dbOpts() | {error, invalidUrl}. | |||
parseUrl(<<"http://", Rest/binary>>) -> | |||
parseUrl(tcp, Rest); | |||
parseUrl(<<"https://", Rest/binary>>) -> | |||
parseUrl(ssl, Rest); | |||
parseUrl(_) -> | |||
{error, invalidUrl}. | |||
-spec parseUrl(protocol(), binary()) -> dbOpts(). | |||
parseUrl(Protocol, Rest) -> | |||
{Host, _Path} = | |||
case binary:split(Rest, <<"/">>, [trim]) of | |||
[UrlHost] -> | |||
{UrlHost, <<"/">>}; | |||
[UrlHost, UrlPath] -> | |||
{UrlHost, <<"/", UrlPath/binary>>} | |||
end, | |||
{Hostname, Port} = | |||
case binary:split(Host, <<":">>, [trim]) of | |||
[Host] -> | |||
case Protocol of | |||
tcp -> | |||
{Host, 80}; | |||
ssl -> | |||
{Host, 443} | |||
end; | |||
[UrlHostname, UrlPort] -> | |||
{UrlHostname, binary_to_integer(UrlPort)} | |||
end, | |||
#dbOpts{host = Host, port = Port, hostname = binary_to_list(Hostname), protocol = Protocol}. | |||
-spec dbOpts(list()) -> dbOpts(). | |||
dbOpts(DbCfgs) -> | |||
BaseUrl = ?GET_FROM_LIST(baseUrl, DbCfgs, ?DEFAULT_BASE_URL), | |||
DbName = ?GET_FROM_LIST(dbName, DbCfgs, ?DEFAULT_DBNAME), | |||
User = ?GET_FROM_LIST(user, DbCfgs, ?DEFAULT_USER), | |||
Password = ?GET_FROM_LIST(password, DbCfgs, ?DEFAULT_PASSWORD), | |||
PoolSize = ?GET_FROM_LIST(poolSize, DbCfgs, ?DEFAULT_POOL_SIZE), | |||
SocketOpts = ?GET_FROM_LIST(socketOpts, DbCfgs, ?DEFAULT_SOCKET_OPTS), | |||
DbOpts = agMiscUtils:parseUrl(BaseUrl), | |||
UserPasswordBase64 = {<<"Authorization">>, <<"Basic ", (base64:encode(<<User/binary, ":", Password/binary>>))/binary>>}, | |||
DbOpts#dbOpts{dbName = <<"/_db/", DbName/binary>>, userPassword = UserPasswordBase64, poolSize = PoolSize, socketOpts = SocketOpts}. | |||
-spec agencyOpts(list()) -> agencyOpts(). | |||
agencyOpts(AgencyCfgs) -> | |||
IsReconnect = ?GET_FROM_LIST(reconnect, AgencyCfgs, ?DEFAULT_IS_RECONNECT), | |||
BacklogSize = ?GET_FROM_LIST(backlogSize, AgencyCfgs, ?DEFAULT_BACKLOG_SIZE), | |||
Min = ?GET_FROM_LIST(reconnectTimeMin, AgencyCfgs, ?DEFAULT_RECONNECT_MIN), | |||
Max = ?GET_FROM_LIST(reconnectTimeMax, AgencyCfgs, ?DEFAULT_RECONNECT_MAX), | |||
#agencyOpts{reconnect = IsReconnect, backlogSize = BacklogSize, reconnectTimeMin = Min, reconnectTimeMax = Max}. | |||
-spec getListValue(term(), list(), term()) -> term(). | |||
getListValue(Key, List, Default) -> | |||
case lists:keyfind(Key, 1, List) of | |||
false -> | |||
Default; | |||
{Key, Value} -> | |||
Value | |||
end. | |||
-spec warnMsg(term(), string(), [term()]) -> ok. | |||
warnMsg(Tag, Format, Data) -> | |||
error_logger:warning_msg("[~p] " ++ Format, [Tag | Data]). | |||
-spec randomElement([term()]) -> term(). | |||
randomElement([X]) -> | |||
X; | |||
randomElement([_ | _] = List) -> | |||
T = list_to_tuple(List), | |||
element(rand:uniform(tuple_size(T)), T). | |||
-spec toBinary(term()) -> binary(). | |||
toBinary(Value) when is_integer(Value) -> integer_to_binary(Value); | |||
toBinary(Value) when is_list(Value) -> list_to_binary(Value); | |||
toBinary(Value) when is_float(Value) -> float_to_binary(Value, [{decimals, 6}, compact]); | |||
toBinary(Value) when is_atom(Value) -> atom_to_binary(Value, utf8); | |||
toBinary(Value) when is_binary(Value) -> Value; | |||
toBinary([Tuple | PropList] = Value) when is_list(PropList) and is_tuple(Tuple) -> | |||
lists:map(fun({K, V}) -> {toBinary(K), toBinary(V)} end, Value); | |||
toBinary(Value) -> term_to_binary(Value). | |||
-spec spellQueryPars(list()) -> binary(). | |||
spellQueryPars([]) -> | |||
<<>>; | |||
spellQueryPars([{Key, Value}]) -> | |||
<<"?", (toBinary(Key))/binary, "=", (toBinary(Value))/binary>>; | |||
spellQueryPars([{Key, Value} | Tail]) -> | |||
FirstBinary = <<"?", (toBinary(Key))/binary, "=", (toBinary(Value))/binary>>, | |||
TailBinary = <<<<"&", (toBinary(OtherKey))/binary, "=", (toBinary(OtherValue))/binary>> || {OtherKey, OtherValue} <- Tail>>, | |||
<<FirstBinary/binary, TailBinary/binary>>. | |||
-spec getHeaderValue(binary(), binary()) -> binary(). | |||
getHeaderValue(Header, HeaderBin) -> | |||
HeadersList = binary:split(HeaderBin, <<"\r\n">>, [global]), | |||
lookHeader(Header, HeadersList). | |||
-spec lookHeader(binary, list()) -> binary(). | |||
lookHeader(_Header, []) -> | |||
undefined; | |||
lookHeader(Header, [H | T]) -> | |||
case binary:split(H, <<": ">>) of | |||
[Header, Value] -> | |||
Value; | |||
_ -> | |||
lookHeader(Header, T) | |||
end. | |||
@ -0,0 +1,77 @@ | |||
-module(agSslAgencyExm). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-export([ | |||
start_link/3 | |||
, init_it/3 | |||
, system_code_change/4 | |||
, system_continue/3 | |||
, system_get_state/1 | |||
, system_terminate/4 | |||
]). | |||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genExm start %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |||
-spec start_link(module(), term(), [proc_lib:spawn_option()]) -> {ok, pid()}. | |||
start_link(ServerName, Args, SpawnOpts) -> | |||
proc_lib:start_link(?MODULE, init_it, [ServerName, self(), Args], infinity, SpawnOpts). | |||
init_it(ServerName, Parent, Args) -> | |||
case safeRegister(ServerName) of | |||
true -> | |||
process_flag(trap_exit, true), | |||
moduleInit(Parent, Args); | |||
{false, Pid} -> | |||
proc_lib:init_ack(Parent, {error, {alreadyStarted, Pid}}) | |||
end. | |||
-spec system_code_change(term(), module(), undefined | term(), term()) -> {ok, term()}. | |||
system_code_change(MiscState, _Module, _OldVsn, _Extra) -> | |||
{ok, MiscState}. | |||
-spec system_continue(pid(), [], {module(), term(), term()}) -> ok. | |||
system_continue(_Parent, _Debug, {Parent, SrvState, CliState}) -> | |||
loop(Parent, SrvState, CliState). | |||
-spec system_get_state(term()) -> {ok, term()}. | |||
system_get_state({_Parent, SrvState, _CliState}) -> | |||
{ok, SrvState}. | |||
-spec system_terminate(term(), pid(), [], term()) -> none(). | |||
system_terminate(Reason, _Parent, _Debug, {_Parent, SrvState, CliState}) -> | |||
terminate(Reason, SrvState, CliState). | |||
safeRegister(ServerName) -> | |||
try register(ServerName, self()) of | |||
true -> true | |||
catch | |||
_:_ -> {false, whereis(ServerName)} | |||
end. | |||
moduleInit(Parent, Args) -> | |||
case agSslAgencyIns:init(Args) of | |||
{ok, SrvState, CliState} -> | |||
proc_lib:init_ack(Parent, {ok, self()}), | |||
loop(Parent, SrvState, CliState); | |||
{stop, Reason} -> | |||
proc_lib:init_ack(Parent, {error, Reason}), | |||
exit(Reason) | |||
end. | |||
loop(Parent, SrvState, CliState) -> | |||
receive | |||
{system, From, Request} -> | |||
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], {Parent, SrvState, CliState}); | |||
{'EXIT', Parent, Reason} -> | |||
terminate(Reason, SrvState, CliState); | |||
Msg -> | |||
{ok, NewSrvState, NewCliState} = agSslAgencyIns:handleMsg(Msg, SrvState, CliState), | |||
loop(Parent, NewSrvState, NewCliState) | |||
end. | |||
terminate(Reason, SrvState, CliState) -> | |||
agSslAgencyIns:terminate(Reason, SrvState, CliState), | |||
exit(Reason). | |||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genExm end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
@ -0,0 +1,401 @@ | |||
-module(agSslAgencyIns). | |||
-include("agHttpCli.hrl"). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-export([ | |||
%% Inner Behavior API | |||
init/1 | |||
, handleMsg/3 | |||
, terminate/3 | |||
]). | |||
-spec init(term()) -> no_return(). | |||
init({PoolName, AgencyName, #agencyOpts{reconnect = Reconnect, backlogSize = BacklogSize, reconnectTimeMin = Min, reconnectTimeMax = Max}}) -> | |||
ReconnectState = agAgencyUtils:initReconnectState(Reconnect, Min, Max), | |||
self() ! ?miDoNetConnect, | |||
{ok, #srvState{poolName = PoolName, serverName = AgencyName, rn = binary:compile_pattern(<<"\r\n">>), rnrn = binary:compile_pattern(<<"\r\n\r\n">>), reconnectState = ReconnectState}, #cliState{backlogSize = BacklogSize}}. | |||
-spec handleMsg(term(), srvState(), cliState()) -> {ok, term(), term()}. | |||
handleMsg(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem} = MiRequest, | |||
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState, | |||
#cliState{backlogNum = BacklogNum, backlogSize = BacklogSize, requestsIns = RequestsIns, status = Status} = CliState) -> | |||
case Socket of | |||
undefined -> | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, noSocket}), | |||
{ok, SrvState, CliState}; | |||
_ -> | |||
case BacklogNum >= BacklogSize of | |||
true -> | |||
?WARN(ServerName, ":backlog full curNum:~p Total: ~p ~n", [BacklogNum, BacklogSize]), | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, backlogFull}), | |||
{ok, SrvState, CliState}; | |||
_ -> | |||
case Status of | |||
leisure -> %% 空闲状态 | |||
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]), | |||
case ssl:send(Socket, Request) of | |||
ok -> | |||
TimerRef = | |||
case OverTime of | |||
infinity -> | |||
undefined; | |||
_ -> | |||
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}]) | |||
end, | |||
{ok, SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, backlogNum = BacklogNum + 1, curInfo = {FromPid, RequestId, TimerRef}}}; | |||
{error, Reason} -> | |||
?WARN(ServerName, ":send error: ~p ~p ~p ~n", [Reason, FromPid, RequestId]), | |||
ssl:close(Socket), | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, {socketSendError, Reason}}), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {socketSendError, Reason}}) | |||
end; | |||
_ -> | |||
{ok, SrvState, CliState#cliState{requestsIns = [MiRequest | RequestsIns], backlogNum = BacklogNum + 1}} | |||
end | |||
end | |||
end; | |||
handleMsg({ssl, Socket, Data}, | |||
#srvState{serverName = ServerName, rn = Rn, rnrn = RnRn, socket = Socket} = SrvState, | |||
#cliState{isHeadMethod = IsHeadMethod, backlogNum = BacklogNum, curInfo = CurInfo, requestsIns = RequestsIns, requestsOuts = RequestsOuts, recvState = RecvState} = CliState) -> | |||
try agHttpProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of | |||
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} -> | |||
agAgencyUtils:agencyReply(CurInfo, {StatusCode, Body, Headers}), | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}}; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}); | |||
[MiRequest | Outs] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
{ok, NewRecvState} -> | |||
{ok, SrvState, CliState#cliState{recvState = NewRecvState}}; | |||
{error, Reason} -> | |||
?WARN(ServerName, "handle ssl data error: ~p ~p ~n", [Reason, CurInfo]), | |||
ssl:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {sslDataError, Reason}}) | |||
catch | |||
E:R:S -> | |||
?WARN(ServerName, "handle ssl data crash: ~p:~p~n~p~n ~p~n ", [E, R, S, CurInfo]), | |||
ssl:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {{error, agencyHandledataError}}) | |||
end; | |||
handleMsg({timeout, TimerRef, waiting_over}, | |||
#srvState{socket = Socket} = SrvState, | |||
#cliState{backlogNum = BacklogNum, curInfo = {FromPid, RequestId, TimerRef}} = CliState) -> | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}), | |||
%% 之前的数据超时之后 要关闭ssl 然后重新建立连接 以免后面该ssl收到该次超时数据 影响后面请求的接收数据 导致数据错乱 | |||
ssl:close(Socket), | |||
handleMsg(?miDoNetConnect, SrvState#srvState{socket = undefined}, CliState#cliState{backlogNum = BacklogNum - 1}); | |||
handleMsg({ssl_closed, Socket}, | |||
#srvState{socket = Socket, serverName = ServerName} = SrvState, | |||
CliState) -> | |||
?WARN(ServerName, "connection closed~n", []), | |||
ssl:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, ssl_closed}); | |||
handleMsg({ssl_error, Socket, Reason}, | |||
#srvState{socket = Socket, serverName = ServerName} = SrvState, | |||
CliState) -> | |||
?WARN(ServerName, "connection error: ~p~n", [Reason]), | |||
ssl:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {ssl_error, Reason}}); | |||
handleMsg(?miDoNetConnect, | |||
#srvState{poolName = PoolName, serverName = ServerName, reconnectState = ReconnectState} = SrvState, | |||
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts} = CliState) -> | |||
case ?agBeamPool:getv(PoolName) of | |||
#dbOpts{host = Host, port = Port, hostname = HostName, dbName = DbName, userPassword = UserPassword, socketOpts = SocketOpts} -> | |||
case dealConnect(ServerName, HostName, Port, SocketOpts) of | |||
{ok, Socket} -> | |||
NewReconnectState = agAgencyUtils:resetReconnectState(ReconnectState), | |||
%% 新建连接之后 需要重置之前的buff之类状态数据 | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState#srvState{userPassWord = UserPassword, dbName = DbName, host = Host, reconnectState = NewReconnectState, socket = Socket}, CliState#cliState{status = leisure, curInfo = undefined, recvState = undefined}}; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsIns = [], status = leisure, curInfo = undefined, recvState = undefined}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsIns = [], requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsOuts = [], status = leisure, curInfo = undefined, recvState = undefined}); | |||
[MiRequest | Outs] -> | |||
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
{error, _Reason} -> | |||
agAgencyUtils:reconnectTimer(SrvState, CliState) | |||
end; | |||
_Ret -> | |||
?WARN(ServerName, "deal connect not found agBeamPool:getv(~p) ret ~p is error ~n", [PoolName, _Ret]) | |||
end; | |||
handleMsg(Msg, #srvState{serverName = ServerName} = SrvState, CliState) -> | |||
?WARN(ServerName, "unknown msg: ~p~n", [Msg]), | |||
{ok, SrvState, CliState}. | |||
-spec terminate(term(), srvState(), cliState()) -> ok. | |||
terminate(_Reason, | |||
#srvState{socket = Socket} = SrvState, | |||
CliState) -> | |||
{ok, NewSrvState, NewCliState} = overAllWork(SrvState, CliState), | |||
ssl:close(Socket), | |||
agAgencyUtils:dealClose(NewSrvState, NewCliState, {error, shutdown}), | |||
ok. | |||
-spec overAllWork(srvState(), cliState()) -> {ok, srvState(), cliState()}. | |||
overAllWork(SrvState, #cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, status = Status} = CliState) -> | |||
case Status of | |||
leisure -> | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState}; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = []}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs}) | |||
end; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = []}); | |||
[MiRequest | Outs] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs}) | |||
end; | |||
_ -> | |||
overReceiveSslData(SrvState, CliState) | |||
end. | |||
-spec overDealQueueRequest(miRequest(), srvState(), cliState()) -> {ok, srvState(), cliState()}. | |||
overDealQueueRequest(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem}, | |||
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState, | |||
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, backlogNum = BacklogNum} = CliState) -> | |||
case erlang:monotonic_time(millisecond) > OverTime of | |||
true -> | |||
%% 超时了 | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}), | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1}}; | |||
[MiRequest] -> | |||
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1}) | |||
end; | |||
[MiRequest] -> | |||
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1}); | |||
[MiRequest | Outs] -> | |||
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1}) | |||
end; | |||
_ -> | |||
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]), | |||
case ssl:send(Socket, Request) of | |||
ok -> | |||
TimerRef = | |||
case OverTime of | |||
infinity -> | |||
undefined; | |||
_ -> | |||
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}]) | |||
end, | |||
overReceiveSslData(SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, curInfo = {FromPid, RequestId, TimerRef}}); | |||
{error, Reason} -> | |||
?WARN(ServerName, ":send error: ~p~n", [Reason]), | |||
ssl:close(Socket), | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, socketSendError}), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, socketSendError}) | |||
end | |||
end. | |||
-spec overReceiveSslData(srvState(), cliState()) -> {ok, srvState(), cliState()}. | |||
overReceiveSslData(#srvState{poolName = PoolName, serverName = ServerName, rn = Rn, rnrn = RnRn, socket = Socket} = SrvState, | |||
#cliState{isHeadMethod = IsHeadMethod, backlogNum = BacklogNum, curInfo = CurInfo, requestsIns = RequestsIns, requestsOuts = RequestsOuts, recvState = RecvState} = CliState) -> | |||
receive | |||
{ssl, Socket, Data} -> | |||
try agHttpProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of | |||
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} -> | |||
agAgencyUtils:agencyReply(CurInfo, {StatusCode, Body, Headers}), | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}}; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}); | |||
[MiRequest | Outs] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
{ok, NewRecvState} -> | |||
overReceiveSslData(SrvState, CliState#cliState{recvState = NewRecvState}); | |||
{error, Reason} -> | |||
?WARN(overReceiveSslData, "handle ssl data error: ~p ~n", [Reason]), | |||
ssl:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {sslDataError, Reason}}) | |||
catch | |||
E:R:S -> | |||
?WARN(overReceiveSslData, "handle ssl data crash: ~p:~p~n~p ~n ", [E, R, S]), | |||
ssl:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {ssl_error, handledataError}}) | |||
end; | |||
{timeout, TimerRef, waiting_over} -> | |||
case CurInfo of | |||
{_PidForm, _RequestId, TimerRef} -> | |||
ssl:close(Socket), | |||
agAgencyUtils:agencyReply(CurInfo, {error, timeout}), | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}}; | |||
[MiRequest] -> | |||
case ?agBeamPool:getv(PoolName) of | |||
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} -> | |||
case dealConnect(ServerName, HostName, Port, SocketOpts) of | |||
{ok, NewSocket} -> | |||
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsIns = [], status = leisure, curInfo = undefined, recvState = undefined}); | |||
{error, _Reason} -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}}) | |||
end; | |||
_Ret -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}}) | |||
end; | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
case ?agBeamPool:getv(PoolName) of | |||
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} -> | |||
case dealConnect(ServerName, HostName, Port, SocketOpts) of | |||
{ok, NewSocket} -> | |||
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsIns = [], requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined}); | |||
{error, _Reason} -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}}) | |||
end; | |||
_Ret -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}}) | |||
end | |||
end; | |||
[MiRequest] -> | |||
case ?agBeamPool:getv(PoolName) of | |||
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} -> | |||
case dealConnect(ServerName, HostName, Port, SocketOpts) of | |||
{ok, NewSocket} -> | |||
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsOuts = [], status = leisure, curInfo = undefined, recvState = undefined}); | |||
{error, _Reason} -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {new_ssl_connect_error_over, _Reason}}) | |||
end; | |||
_Ret -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}}) | |||
end; | |||
[MiRequest | Outs] -> | |||
case ?agBeamPool:getv(PoolName) of | |||
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} -> | |||
case dealConnect(ServerName, HostName, Port, SocketOpts) of | |||
{ok, NewSocket} -> | |||
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined}); | |||
{error, _Reason} -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}}) | |||
end; | |||
_Ret -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}}) | |||
end | |||
end; | |||
_ -> | |||
?WARN(overReceiveSslData, "receive waiting_over TimerRef not match: ~p~n", [TimerRef]), | |||
overReceiveSslData(SrvState, CliState) | |||
end; | |||
{ssl_closed, Socket} -> | |||
ssl:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, ssl_closed}); | |||
{ssl_error, Socket, Reason} -> | |||
ssl:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {ssl_error, Reason}}); | |||
#miRequest{} = MiRequest -> | |||
overReceiveSslData(SrvState, CliState#cliState{requestsIns = [MiRequest | RequestsIns], backlogNum = BacklogNum + 1}); | |||
_Msg -> | |||
?WARN(overReceiveSslData, "receive unexpect msg: ~p~n", [_Msg]), | |||
overReceiveSslData(SrvState, CliState) | |||
end. | |||
-spec dealConnect(atom(), hostName(), port(), socketOpts()) -> {ok, socket()} | {error, term()}. | |||
dealConnect(ServerName, HostName, Port, SocketOptions) -> | |||
case inet:getaddrs(HostName, inet) of | |||
{ok, IPList} -> | |||
Ip = agMiscUtils:randomElement(IPList), | |||
case ssl:connect(Ip, Port, SocketOptions, ?DEFAULT_CONNECT_TIMEOUT) of | |||
{ok, Socket} -> | |||
{ok, Socket}; | |||
{error, Reason} -> | |||
?WARN(ServerName, "connect error: ~p~n", [Reason]), | |||
{error, Reason} | |||
end; | |||
{error, Reason} -> | |||
?WARN(ServerName, "getaddrs error: ~p~n", [Reason]), | |||
{error, Reason} | |||
end. | |||
-spec dealQueueRequest(miRequest(), srvState(), cliState()) -> {ok, srvState(), cliState()}. | |||
dealQueueRequest(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem}, | |||
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState, | |||
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, backlogNum = BacklogNum} = CliState) -> | |||
case erlang:monotonic_time(millisecond) > OverTime of | |||
true -> | |||
%% 超时了 | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}), | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1}}; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1}) | |||
end; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1}); | |||
[MiRequest | Outs] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1}) | |||
end; | |||
_ -> | |||
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]), | |||
case ssl:send(Socket, Request) of | |||
ok -> | |||
TimerRef = | |||
case OverTime of | |||
infinity -> | |||
undefined; | |||
_ -> | |||
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}]) | |||
end, | |||
{ok, SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, curInfo = {FromPid, RequestId, TimerRef}}}; | |||
{error, Reason} -> | |||
?WARN(ServerName, ":send error: ~p~n", [Reason]), | |||
ssl:close(Socket), | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, socketSendError}), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, socketSendError}) | |||
end | |||
end. | |||
@ -0,0 +1,77 @@ | |||
-module(agTcpAgencyExm). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-export([ | |||
start_link/3 | |||
, init_it/3 | |||
, system_code_change/4 | |||
, system_continue/3 | |||
, system_get_state/1 | |||
, system_terminate/4 | |||
]). | |||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genExm start %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |||
-spec start_link(module(), term(), [proc_lib:spawn_option()]) -> {ok, pid()}. | |||
start_link(ServerName, Args, SpawnOpts) -> | |||
proc_lib:start_link(?MODULE, init_it, [ServerName, self(), Args], infinity, SpawnOpts). | |||
init_it(ServerName, Parent, Args) -> | |||
case safeRegister(ServerName) of | |||
true -> | |||
process_flag(trap_exit, true), | |||
moduleInit(Parent, Args); | |||
{false, Pid} -> | |||
proc_lib:init_ack(Parent, {error, {alreadyStarted, Pid}}) | |||
end. | |||
-spec system_code_change(term(), module(), undefined | term(), term()) -> {ok, term()}. | |||
system_code_change(MiscState, _Module, _OldVsn, _Extra) -> | |||
{ok, MiscState}. | |||
-spec system_continue(pid(), [], {module(), term(), term()}) -> ok. | |||
system_continue(_Parent, _Debug, {Parent, SrvState, CliState}) -> | |||
loop(Parent, SrvState, CliState). | |||
-spec system_get_state(term()) -> {ok, term()}. | |||
system_get_state({_Parent, SrvState, _CliState}) -> | |||
{ok, SrvState}. | |||
-spec system_terminate(term(), pid(), [], term()) -> none(). | |||
system_terminate(Reason, _Parent, _Debug, {_Parent, SrvState, CliState}) -> | |||
terminate(Reason, SrvState, CliState). | |||
safeRegister(ServerName) -> | |||
try register(ServerName, self()) of | |||
true -> true | |||
catch | |||
_:_ -> {false, whereis(ServerName)} | |||
end. | |||
moduleInit(Parent, Args) -> | |||
case agTcpAgencyIns:init(Args) of | |||
{ok, SrvState, CliState} -> | |||
proc_lib:init_ack(Parent, {ok, self()}), | |||
loop(Parent, SrvState, CliState); | |||
{stop, Reason} -> | |||
proc_lib:init_ack(Parent, {error, Reason}), | |||
exit(Reason) | |||
end. | |||
loop(Parent, SrvState, CliState) -> | |||
receive | |||
{system, From, Request} -> | |||
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], {Parent, SrvState, CliState}); | |||
{'EXIT', Parent, Reason} -> | |||
terminate(Reason, SrvState, CliState); | |||
Msg -> | |||
{ok, NewSrvState, NewCliState} = agTcpAgencyIns:handleMsg(Msg, SrvState, CliState), | |||
loop(Parent, NewSrvState, NewCliState) | |||
end. | |||
terminate(Reason, SrvState, CliState) -> | |||
agTcpAgencyIns:terminate(Reason, SrvState, CliState), | |||
exit(Reason). | |||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genExm end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
@ -0,0 +1,400 @@ | |||
-module(agTcpAgencyIns). | |||
-include("agHttpCli.hrl"). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-export([ | |||
%% Inner Behavior API | |||
init/1 | |||
, handleMsg/3 | |||
, terminate/3 | |||
]). | |||
-spec init(term()) -> no_return(). | |||
init({PoolName, AgencyName, #agencyOpts{reconnect = Reconnect, backlogSize = BacklogSize, reconnectTimeMin = Min, reconnectTimeMax = Max}}) -> | |||
ReconnectState = agAgencyUtils:initReconnectState(Reconnect, Min, Max), | |||
self() ! ?miDoNetConnect, | |||
{ok, #srvState{poolName = PoolName, serverName = AgencyName, rn = binary:compile_pattern(<<"\r\n">>), rnrn = binary:compile_pattern(<<"\r\n\r\n">>), reconnectState = ReconnectState}, #cliState{backlogSize = BacklogSize}}. | |||
-spec handleMsg(term(), srvState(), cliState()) -> {ok, term(), term()}. | |||
handleMsg(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem} = MiRequest, | |||
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState, | |||
#cliState{backlogNum = BacklogNum, backlogSize = BacklogSize, requestsIns = RequestsIns, status = Status} = CliState) -> | |||
case Socket of | |||
undefined -> | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, noSocket}), | |||
{ok, SrvState, CliState}; | |||
_ -> | |||
case BacklogNum >= BacklogSize of | |||
true -> | |||
?WARN(ServerName, ":backlog full curNum:~p Total: ~p ~n", [BacklogNum, BacklogSize]), | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, backlogFull}), | |||
{ok, SrvState, CliState}; | |||
_ -> | |||
case Status of | |||
leisure -> %% 空闲模式 | |||
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]), | |||
case gen_tcp:send(Socket, Request) of | |||
ok -> | |||
TimerRef = | |||
case OverTime of | |||
infinity -> | |||
undefined; | |||
_ -> | |||
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}]) | |||
end, | |||
{ok, SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, backlogNum = BacklogNum + 1, curInfo = {FromPid, RequestId, TimerRef}}}; | |||
{error, Reason} -> | |||
?WARN(ServerName, ":send error: ~p ~p ~p ~n", [Reason, FromPid, RequestId]), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, {socketSendError, Reason}}), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {socketSendError, Reason}}) | |||
end; | |||
_ -> | |||
{ok, SrvState, CliState#cliState{requestsIns = [MiRequest | RequestsIns], backlogNum = BacklogNum + 1}} | |||
end | |||
end | |||
end; | |||
handleMsg({tcp, Socket, Data}, | |||
#srvState{serverName = ServerName, rn = Rn, rnrn = RnRn, socket = Socket} = SrvState, | |||
#cliState{isHeadMethod = IsHeadMethod, backlogNum = BacklogNum, curInfo = CurInfo, requestsIns = RequestsIns, requestsOuts = RequestsOuts, recvState = RecvState} = CliState) -> | |||
try agHttpProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of | |||
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} -> | |||
agAgencyUtils:agencyReply(CurInfo, {StatusCode, Body, Headers}), | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}}; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}); | |||
[MiRequest | Outs] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
{ok, NewRecvState} -> | |||
{ok, SrvState, CliState#cliState{recvState = NewRecvState}}; | |||
{error, Reason} -> | |||
?WARN(ServerName, "handle tcp data error: ~p ~p ~n", [Reason, CurInfo]), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcpDataError, Reason}}) | |||
catch | |||
E:R:S -> | |||
?WARN(ServerName, "handle tcp data crash: ~p:~p~n~p~n ~p ~n ", [E, R, S, CurInfo]), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, agencyHandledataError}) | |||
end; | |||
handleMsg({timeout, TimerRef, waiting_over}, | |||
#srvState{socket = Socket} = SrvState, | |||
#cliState{backlogNum = BacklogNum, curInfo = {FromPid, RequestId, TimerRef}} = CliState) -> | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}), | |||
%% 之前的数据超时之后 要关闭tcp 然后重新建立连接 以免后面该tcp收到该次超时数据 影响后面请求的接收数据 导致数据错乱 | |||
gen_tcp:close(Socket), | |||
handleMsg(?miDoNetConnect, SrvState#srvState{socket = undefined}, CliState#cliState{backlogNum = BacklogNum - 1}); | |||
handleMsg({tcp_closed, Socket}, | |||
#srvState{socket = Socket, serverName = ServerName} = SrvState, | |||
CliState) -> | |||
?WARN(ServerName, "connection closed~n", []), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, tcp_closed}); | |||
handleMsg({tcp_error, Socket, Reason}, | |||
#srvState{socket = Socket, serverName = ServerName} = SrvState, | |||
CliState) -> | |||
?WARN(ServerName, "connection error: ~p~n", [Reason]), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcp_error, Reason}}); | |||
handleMsg(?miDoNetConnect, | |||
#srvState{poolName = PoolName, serverName = ServerName, reconnectState = ReconnectState} = SrvState, | |||
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts} = CliState) -> | |||
case ?agBeamPool:getv(PoolName) of | |||
#dbOpts{host = Host, port = Port, hostname = HostName, dbName = DbName, userPassword = UserPassword, socketOpts = SocketOpts} -> | |||
case dealConnect(ServerName, HostName, Port, SocketOpts) of | |||
{ok, Socket} -> | |||
NewReconnectState = agAgencyUtils:resetReconnectState(ReconnectState), | |||
%% 新建连接之后 需要重置之前的buff之类状态数据 | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState#srvState{userPassWord = UserPassword, dbName = DbName, host = Host, reconnectState = NewReconnectState, socket = Socket}, CliState#cliState{status = leisure, curInfo = undefined, recvState = undefined}}; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsIns = [], status = leisure, curInfo = undefined, recvState = undefined}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsIns = [], requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsOuts = [], status = leisure, curInfo = undefined, recvState = undefined}); | |||
[MiRequest | Outs] -> | |||
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
{error, _Reason} -> | |||
agAgencyUtils:reconnectTimer(SrvState, CliState) | |||
end; | |||
_Ret -> | |||
?WARN(ServerName, "deal connect not found agBeamPool:getv(~p) ret ~p is error ~n", [PoolName, _Ret]) | |||
end; | |||
handleMsg(Msg, #srvState{serverName = ServerName} = SrvState, CliState) -> | |||
?WARN(ServerName, "unknown msg: ~p~n", [Msg]), | |||
{ok, SrvState, CliState}. | |||
-spec terminate(term(), srvState(), cliState()) -> ok. | |||
terminate(_Reason, | |||
#srvState{socket = Socket} = SrvState, | |||
CliState) -> | |||
{ok, NewSrvState, NewCliState} = overAllWork(SrvState, CliState), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(NewSrvState, NewCliState, {error, shutdown}), | |||
ok. | |||
-spec overAllWork(srvState(), cliState()) -> {ok, srvState(), cliState()}. | |||
overAllWork(SrvState, #cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, status = Status} = CliState) -> | |||
case Status of | |||
leisure -> | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState}; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = []}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs}) | |||
end; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = []}); | |||
[MiRequest | Outs] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs}) | |||
end; | |||
_ -> | |||
overReceiveTcpData(SrvState, CliState) | |||
end. | |||
-spec overDealQueueRequest(miRequest(), srvState(), cliState()) -> {ok, srvState(), cliState()}. | |||
overDealQueueRequest(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem}, | |||
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState, | |||
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, backlogNum = BacklogNum} = CliState) -> | |||
case erlang:monotonic_time(millisecond) > OverTime of | |||
true -> | |||
%% 超时了 | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}), | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1}}; | |||
[MiRequest] -> | |||
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1}) | |||
end; | |||
[MiRequest] -> | |||
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1}); | |||
[MiRequest | Outs] -> | |||
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1}) | |||
end; | |||
_ -> | |||
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]), | |||
case gen_tcp:send(Socket, Request) of | |||
ok -> | |||
TimerRef = | |||
case OverTime of | |||
infinity -> | |||
undefined; | |||
_ -> | |||
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}]) | |||
end, | |||
overReceiveTcpData(SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, curInfo = {FromPid, RequestId, TimerRef}}); | |||
{error, Reason} -> | |||
?WARN(ServerName, ":send error: ~p~n", [Reason]), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, socketSendError}), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, socketSendError}) | |||
end | |||
end. | |||
-spec overReceiveTcpData(srvState(), cliState()) -> {ok, srvState(), cliState()}. | |||
overReceiveTcpData(#srvState{poolName = PoolName, serverName = ServerName, rn = Rn, rnrn = RnRn, socket = Socket} = SrvState, | |||
#cliState{isHeadMethod = IsHeadMethod, backlogNum = BacklogNum, curInfo = CurInfo, requestsIns = RequestsIns, requestsOuts = RequestsOuts, recvState = RecvState} = CliState) -> | |||
receive | |||
{tcp, Socket, Data} -> | |||
try agHttpProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of | |||
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} -> | |||
agAgencyUtils:agencyReply(CurInfo, {StatusCode, Body, Headers}), | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}}; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}); | |||
[MiRequest | Outs] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
{ok, NewRecvState} -> | |||
overReceiveTcpData(SrvState, CliState#cliState{recvState = NewRecvState}); | |||
{error, Reason} -> | |||
?WARN(overReceiveTcpData, "handle tcp data error: ~p ~n", [Reason]), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcpDataError, Reason}}) | |||
catch | |||
E:R:S -> | |||
?WARN(overReceiveTcpData, "handle tcp data crash: ~p:~p~n~p ~n ", [E, R, S]), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcp_error, handledataError}}) | |||
end; | |||
{timeout, TimerRef, waiting_over} -> | |||
case CurInfo of | |||
{_PidForm, _RequestId, TimerRef} -> | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:agencyReply(CurInfo, {error, timeout}), | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}}; | |||
[MiRequest] -> | |||
case ?agBeamPool:getv(PoolName) of | |||
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} -> | |||
case dealConnect(ServerName, HostName, Port, SocketOpts) of | |||
{ok, NewSocket} -> | |||
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsIns = [], status = leisure, curInfo = undefined, recvState = undefined}); | |||
{error, _Reason} -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}}) | |||
end; | |||
_Ret -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}}) | |||
end; | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
case ?agBeamPool:getv(PoolName) of | |||
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} -> | |||
case dealConnect(ServerName, HostName, Port, SocketOpts) of | |||
{ok, NewSocket} -> | |||
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsIns = [], requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined}); | |||
{error, _Reason} -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}}) | |||
end; | |||
_Ret -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}}) | |||
end | |||
end; | |||
[MiRequest] -> | |||
case ?agBeamPool:getv(PoolName) of | |||
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} -> | |||
case dealConnect(ServerName, HostName, Port, SocketOpts) of | |||
{ok, NewSocket} -> | |||
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsOuts = [], status = leisure, curInfo = undefined, recvState = undefined}); | |||
{error, _Reason} -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}}) | |||
end; | |||
_Ret -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}}) | |||
end; | |||
[MiRequest | Outs] -> | |||
case ?agBeamPool:getv(PoolName) of | |||
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} -> | |||
case dealConnect(ServerName, HostName, Port, SocketOpts) of | |||
{ok, NewSocket} -> | |||
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined}); | |||
{error, _Reason} -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}}) | |||
end; | |||
_Ret -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}}) | |||
end | |||
end; | |||
_ -> | |||
?WARN(overReceiveTcpData, "receive waiting_over TimerRef not match: ~p~n", [TimerRef]), | |||
overReceiveTcpData(SrvState, CliState) | |||
end; | |||
{tcp_closed, Socket} -> | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, tcp_closed}); | |||
{tcp_error, Socket, Reason} -> | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcp_error, Reason}}); | |||
#miRequest{} = MiRequest -> | |||
overReceiveTcpData(SrvState, CliState#cliState{requestsIns = [MiRequest | RequestsIns], backlogNum = BacklogNum + 1}); | |||
_Msg -> | |||
?WARN(overReceiveTcpData, "receive unexpect msg: ~p~n", [_Msg]), | |||
overReceiveTcpData(SrvState, CliState) | |||
end. | |||
-spec dealConnect(atom(), hostName(), port(), socketOpts()) -> {ok, socket()} | {error, term()}. | |||
dealConnect(ServerName, HostName, Port, SocketOptions) -> | |||
case inet:getaddrs(HostName, inet) of | |||
{ok, IPList} -> | |||
Ip = agMiscUtils:randomElement(IPList), | |||
case gen_tcp:connect(Ip, Port, SocketOptions, ?DEFAULT_CONNECT_TIMEOUT) of | |||
{ok, Socket} -> | |||
{ok, Socket}; | |||
{error, Reason} -> | |||
?WARN(ServerName, "connect error: ~p~n", [Reason]), | |||
{error, Reason} | |||
end; | |||
{error, Reason} -> | |||
?WARN(ServerName, "getaddrs error: ~p~n", [Reason]), | |||
{error, Reason} | |||
end. | |||
-spec dealQueueRequest(miRequest(), srvState(), cliState()) -> {ok, srvState(), cliState()}. | |||
dealQueueRequest(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem}, | |||
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState, | |||
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, backlogNum = BacklogNum} = CliState) -> | |||
case erlang:monotonic_time(millisecond) > OverTime of | |||
true -> | |||
%% 超时了 | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}), | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1}}; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1}) | |||
end; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1}); | |||
[MiRequest | Outs] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1}) | |||
end; | |||
_ -> | |||
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]), | |||
case gen_tcp:send(Socket, Request) of | |||
ok -> | |||
TimerRef = | |||
case OverTime of | |||
infinity -> | |||
undefined; | |||
_ -> | |||
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}]) | |||
end, | |||
{ok, SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, curInfo = {FromPid, RequestId, TimerRef}}}; | |||
{error, Reason} -> | |||
?WARN(ServerName, ":send error: ~p~n", [Reason]), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, socketSendError}), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, socketSendError}) | |||
end | |||
end. | |||
@ -0,0 +1,77 @@ | |||
-module(agVstAgencyExm). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-export([ | |||
start_link/3 | |||
, init_it/3 | |||
, system_code_change/4 | |||
, system_continue/3 | |||
, system_get_state/1 | |||
, system_terminate/4 | |||
]). | |||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genExm start %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |||
-spec start_link(module(), term(), [proc_lib:spawn_option()]) -> {ok, pid()}. | |||
start_link(ServerName, Args, SpawnOpts) -> | |||
proc_lib:start_link(?MODULE, init_it, [ServerName, self(), Args], infinity, SpawnOpts). | |||
init_it(ServerName, Parent, Args) -> | |||
case safeRegister(ServerName) of | |||
true -> | |||
process_flag(trap_exit, true), | |||
moduleInit(Parent, Args); | |||
{false, Pid} -> | |||
proc_lib:init_ack(Parent, {error, {alreadyStarted, Pid}}) | |||
end. | |||
-spec system_code_change(term(), module(), undefined | term(), term()) -> {ok, term()}. | |||
system_code_change(MiscState, _Module, _OldVsn, _Extra) -> | |||
{ok, MiscState}. | |||
-spec system_continue(pid(), [], {module(), term(), term()}) -> ok. | |||
system_continue(_Parent, _Debug, {Parent, SrvState, CliState}) -> | |||
loop(Parent, SrvState, CliState). | |||
-spec system_get_state(term()) -> {ok, term()}. | |||
system_get_state({_Parent, SrvState, _CliState}) -> | |||
{ok, SrvState}. | |||
-spec system_terminate(term(), pid(), [], term()) -> none(). | |||
system_terminate(Reason, _Parent, _Debug, {_Parent, SrvState, CliState}) -> | |||
terminate(Reason, SrvState, CliState). | |||
safeRegister(ServerName) -> | |||
try register(ServerName, self()) of | |||
true -> true | |||
catch | |||
_:_ -> {false, whereis(ServerName)} | |||
end. | |||
moduleInit(Parent, Args) -> | |||
case agTcpAgencyIns:init(Args) of | |||
{ok, SrvState, CliState} -> | |||
proc_lib:init_ack(Parent, {ok, self()}), | |||
loop(Parent, SrvState, CliState); | |||
{stop, Reason} -> | |||
proc_lib:init_ack(Parent, {error, Reason}), | |||
exit(Reason) | |||
end. | |||
loop(Parent, SrvState, CliState) -> | |||
receive | |||
{system, From, Request} -> | |||
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], {Parent, SrvState, CliState}); | |||
{'EXIT', Parent, Reason} -> | |||
terminate(Reason, SrvState, CliState); | |||
Msg -> | |||
{ok, NewSrvState, NewCliState} = agVstAgencyIns:handleMsg(Msg, SrvState, CliState), | |||
loop(Parent, NewSrvState, NewCliState) | |||
end. | |||
terminate(Reason, SrvState, CliState) -> | |||
agTcpAgencyIns:terminate(Reason, SrvState, CliState), | |||
exit(Reason). | |||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genExm end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
@ -0,0 +1,400 @@ | |||
-module(agVstAgencyIns). | |||
-include("agHttpCli.hrl"). | |||
-include("erlArango.hrl"). | |||
-compile(inline). | |||
-compile({inline_size, 128}). | |||
-export([ | |||
%% Inner Behavior API | |||
init/1 | |||
, handleMsg/3 | |||
, terminate/3 | |||
]). | |||
-spec init(term()) -> no_return(). | |||
init({PoolName, AgencyName, #agencyOpts{reconnect = Reconnect, backlogSize = BacklogSize, reconnectTimeMin = Min, reconnectTimeMax = Max}}) -> | |||
ReconnectState = agAgencyUtils:initReconnectState(Reconnect, Min, Max), | |||
self() ! ?miDoNetConnect, | |||
{ok, #srvState{poolName = PoolName, serverName = AgencyName, rn = binary:compile_pattern(<<"\r\n">>), rnrn = binary:compile_pattern(<<"\r\n\r\n">>), reconnectState = ReconnectState}, #cliState{backlogSize = BacklogSize}}. | |||
-spec handleMsg(term(), srvState(), cliState()) -> {ok, term(), term()}. | |||
handleMsg(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem} = MiRequest, | |||
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState, | |||
#cliState{backlogNum = BacklogNum, backlogSize = BacklogSize, requestsIns = RequestsIns, status = Status} = CliState) -> | |||
case Socket of | |||
undefined -> | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, noSocket}), | |||
{ok, SrvState, CliState}; | |||
_ -> | |||
case BacklogNum >= BacklogSize of | |||
true -> | |||
?WARN(ServerName, ":backlog full curNum:~p Total: ~p ~n", [BacklogNum, BacklogSize]), | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, backlogFull}), | |||
{ok, SrvState, CliState}; | |||
_ -> | |||
case Status of | |||
leisure -> %% 空闲模式 | |||
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]), | |||
case gen_tcp:send(Socket, Request) of | |||
ok -> | |||
TimerRef = | |||
case OverTime of | |||
infinity -> | |||
undefined; | |||
_ -> | |||
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}]) | |||
end, | |||
{ok, SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, backlogNum = BacklogNum + 1, curInfo = {FromPid, RequestId, TimerRef}}}; | |||
{error, Reason} -> | |||
?WARN(ServerName, ":send error: ~p ~p ~p ~n", [Reason, FromPid, RequestId]), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, {socketSendError, Reason}}), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {socketSendError, Reason}}) | |||
end; | |||
_ -> | |||
{ok, SrvState, CliState#cliState{requestsIns = [MiRequest | RequestsIns], backlogNum = BacklogNum + 1}} | |||
end | |||
end | |||
end; | |||
handleMsg({tcp, Socket, Data}, | |||
#srvState{serverName = ServerName, rn = Rn, rnrn = RnRn, socket = Socket} = SrvState, | |||
#cliState{isHeadMethod = IsHeadMethod, backlogNum = BacklogNum, curInfo = CurInfo, requestsIns = RequestsIns, requestsOuts = RequestsOuts, recvState = RecvState} = CliState) -> | |||
try agHttpProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of | |||
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} -> | |||
agAgencyUtils:agencyReply(CurInfo, {StatusCode, Body, Headers}), | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}}; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}); | |||
[MiRequest | Outs] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
{ok, NewRecvState} -> | |||
{ok, SrvState, CliState#cliState{recvState = NewRecvState}}; | |||
{error, Reason} -> | |||
?WARN(ServerName, "handle tcp data error: ~p ~p ~n", [Reason, CurInfo]), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcpDataError, Reason}}) | |||
catch | |||
E:R:S -> | |||
?WARN(ServerName, "handle tcp data crash: ~p:~p~n~p~n ~p ~n ", [E, R, S, CurInfo]), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, agencyHandledataError}) | |||
end; | |||
handleMsg({timeout, TimerRef, waiting_over}, | |||
#srvState{socket = Socket} = SrvState, | |||
#cliState{backlogNum = BacklogNum, curInfo = {FromPid, RequestId, TimerRef}} = CliState) -> | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}), | |||
%% 之前的数据超时之后 要关闭tcp 然后重新建立连接 以免后面该tcp收到该次超时数据 影响后面请求的接收数据 导致数据错乱 | |||
gen_tcp:close(Socket), | |||
handleMsg(?miDoNetConnect, SrvState#srvState{socket = undefined}, CliState#cliState{backlogNum = BacklogNum - 1}); | |||
handleMsg({tcp_closed, Socket}, | |||
#srvState{socket = Socket, serverName = ServerName} = SrvState, | |||
CliState) -> | |||
?WARN(ServerName, "connection closed~n", []), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, tcp_closed}); | |||
handleMsg({tcp_error, Socket, Reason}, | |||
#srvState{socket = Socket, serverName = ServerName} = SrvState, | |||
CliState) -> | |||
?WARN(ServerName, "connection error: ~p~n", [Reason]), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcp_error, Reason}}); | |||
handleMsg(?miDoNetConnect, | |||
#srvState{poolName = PoolName, serverName = ServerName, reconnectState = ReconnectState} = SrvState, | |||
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts} = CliState) -> | |||
case ?agBeamPool:getv(PoolName) of | |||
#dbOpts{host = Host, port = Port, hostname = HostName, dbName = DbName, userPassword = UserPassword, socketOpts = SocketOpts} -> | |||
case dealConnect(ServerName, HostName, Port, SocketOpts) of | |||
{ok, Socket} -> | |||
NewReconnectState = agAgencyUtils:resetReconnectState(ReconnectState), | |||
%% 新建连接之后 需要重置之前的buff之类状态数据 | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState#srvState{userPassWord = UserPassword, dbName = DbName, host = Host, reconnectState = NewReconnectState, socket = Socket}, CliState#cliState{status = leisure, curInfo = undefined, recvState = undefined}}; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsIns = [], status = leisure, curInfo = undefined, recvState = undefined}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsIns = [], requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsOuts = [], status = leisure, curInfo = undefined, recvState = undefined}); | |||
[MiRequest | Outs] -> | |||
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
{error, _Reason} -> | |||
agAgencyUtils:reconnectTimer(SrvState, CliState) | |||
end; | |||
_Ret -> | |||
?WARN(ServerName, "deal connect not found agBeamPool:getv(~p) ret ~p is error ~n", [PoolName, _Ret]) | |||
end; | |||
handleMsg(Msg, #srvState{serverName = ServerName} = SrvState, CliState) -> | |||
?WARN(ServerName, "unknown msg: ~p~n", [Msg]), | |||
{ok, SrvState, CliState}. | |||
-spec terminate(term(), srvState(), cliState()) -> ok. | |||
terminate(_Reason, | |||
#srvState{socket = Socket} = SrvState, | |||
CliState) -> | |||
{ok, NewSrvState, NewCliState} = overAllWork(SrvState, CliState), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(NewSrvState, NewCliState, {error, shutdown}), | |||
ok. | |||
-spec overAllWork(srvState(), cliState()) -> {ok, srvState(), cliState()}. | |||
overAllWork(SrvState, #cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, status = Status} = CliState) -> | |||
case Status of | |||
leisure -> | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState}; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = []}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs}) | |||
end; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = []}); | |||
[MiRequest | Outs] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs}) | |||
end; | |||
_ -> | |||
overReceiveTcpData(SrvState, CliState) | |||
end. | |||
-spec overDealQueueRequest(miRequest(), srvState(), cliState()) -> {ok, srvState(), cliState()}. | |||
overDealQueueRequest(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem}, | |||
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState, | |||
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, backlogNum = BacklogNum} = CliState) -> | |||
case erlang:monotonic_time(millisecond) > OverTime of | |||
true -> | |||
%% 超时了 | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}), | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1}}; | |||
[MiRequest] -> | |||
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1}) | |||
end; | |||
[MiRequest] -> | |||
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1}); | |||
[MiRequest | Outs] -> | |||
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1}) | |||
end; | |||
_ -> | |||
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]), | |||
case gen_tcp:send(Socket, Request) of | |||
ok -> | |||
TimerRef = | |||
case OverTime of | |||
infinity -> | |||
undefined; | |||
_ -> | |||
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}]) | |||
end, | |||
overReceiveTcpData(SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, curInfo = {FromPid, RequestId, TimerRef}}); | |||
{error, Reason} -> | |||
?WARN(ServerName, ":send error: ~p~n", [Reason]), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, socketSendError}), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, socketSendError}) | |||
end | |||
end. | |||
-spec overReceiveTcpData(srvState(), cliState()) -> {ok, srvState(), cliState()}. | |||
overReceiveTcpData(#srvState{poolName = PoolName, serverName = ServerName, rn = Rn, rnrn = RnRn, socket = Socket} = SrvState, | |||
#cliState{isHeadMethod = IsHeadMethod, backlogNum = BacklogNum, curInfo = CurInfo, requestsIns = RequestsIns, requestsOuts = RequestsOuts, recvState = RecvState} = CliState) -> | |||
receive | |||
{tcp, Socket, Data} -> | |||
try agHttpProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of | |||
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} -> | |||
agAgencyUtils:agencyReply(CurInfo, {StatusCode, Body, Headers}), | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}}; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}); | |||
[MiRequest | Outs] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}) | |||
end; | |||
{ok, NewRecvState} -> | |||
overReceiveTcpData(SrvState, CliState#cliState{recvState = NewRecvState}); | |||
{error, Reason} -> | |||
?WARN(overReceiveTcpData, "handle tcp data error: ~p ~n", [Reason]), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcpDataError, Reason}}) | |||
catch | |||
E:R:S -> | |||
?WARN(overReceiveTcpData, "handle tcp data crash: ~p:~p~n~p ~n ", [E, R, S]), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcp_error, handledataError}}) | |||
end; | |||
{timeout, TimerRef, waiting_over} -> | |||
case CurInfo of | |||
{_PidForm, _RequestId, TimerRef} -> | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:agencyReply(CurInfo, {error, timeout}), | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}}; | |||
[MiRequest] -> | |||
case ?agBeamPool:getv(PoolName) of | |||
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} -> | |||
case dealConnect(ServerName, HostName, Port, SocketOpts) of | |||
{ok, NewSocket} -> | |||
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsIns = [], status = leisure, curInfo = undefined, recvState = undefined}); | |||
{error, _Reason} -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}}) | |||
end; | |||
_Ret -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}}) | |||
end; | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
case ?agBeamPool:getv(PoolName) of | |||
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} -> | |||
case dealConnect(ServerName, HostName, Port, SocketOpts) of | |||
{ok, NewSocket} -> | |||
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsIns = [], requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined}); | |||
{error, _Reason} -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}}) | |||
end; | |||
_Ret -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}}) | |||
end | |||
end; | |||
[MiRequest] -> | |||
case ?agBeamPool:getv(PoolName) of | |||
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} -> | |||
case dealConnect(ServerName, HostName, Port, SocketOpts) of | |||
{ok, NewSocket} -> | |||
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsOuts = [], status = leisure, curInfo = undefined, recvState = undefined}); | |||
{error, _Reason} -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}}) | |||
end; | |||
_Ret -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}}) | |||
end; | |||
[MiRequest | Outs] -> | |||
case ?agBeamPool:getv(PoolName) of | |||
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} -> | |||
case dealConnect(ServerName, HostName, Port, SocketOpts) of | |||
{ok, NewSocket} -> | |||
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined}); | |||
{error, _Reason} -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}}) | |||
end; | |||
_Ret -> | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}}) | |||
end | |||
end; | |||
_ -> | |||
?WARN(overReceiveTcpData, "receive waiting_over TimerRef not match: ~p~n", [TimerRef]), | |||
overReceiveTcpData(SrvState, CliState) | |||
end; | |||
{tcp_closed, Socket} -> | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, tcp_closed}); | |||
{tcp_error, Socket, Reason} -> | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcp_error, Reason}}); | |||
#miRequest{} = MiRequest -> | |||
overReceiveTcpData(SrvState, CliState#cliState{requestsIns = [MiRequest | RequestsIns], backlogNum = BacklogNum + 1}); | |||
_Msg -> | |||
?WARN(overReceiveTcpData, "receive unexpect msg: ~p~n", [_Msg]), | |||
overReceiveTcpData(SrvState, CliState) | |||
end. | |||
-spec dealConnect(atom(), hostName(), port(), socketOpts()) -> {ok, socket()} | {error, term()}. | |||
dealConnect(ServerName, HostName, Port, SocketOptions) -> | |||
case inet:getaddrs(HostName, inet) of | |||
{ok, IPList} -> | |||
Ip = agMiscUtils:randomElement(IPList), | |||
case gen_tcp:connect(Ip, Port, SocketOptions, ?DEFAULT_CONNECT_TIMEOUT) of | |||
{ok, Socket} -> | |||
{ok, Socket}; | |||
{error, Reason} -> | |||
?WARN(ServerName, "connect error: ~p~n", [Reason]), | |||
{error, Reason} | |||
end; | |||
{error, Reason} -> | |||
?WARN(ServerName, "getaddrs error: ~p~n", [Reason]), | |||
{error, Reason} | |||
end. | |||
-spec dealQueueRequest(miRequest(), srvState(), cliState()) -> {ok, srvState(), cliState()}. | |||
dealQueueRequest(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem}, | |||
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState, | |||
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, backlogNum = BacklogNum} = CliState) -> | |||
case erlang:monotonic_time(millisecond) > OverTime of | |||
true -> | |||
%% 超时了 | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}), | |||
case RequestsOuts of | |||
[] -> | |||
case RequestsIns of | |||
[] -> | |||
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1}}; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1}); | |||
MiRLists -> | |||
[MiRequest | Outs] = lists:reverse(MiRLists), | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1}) | |||
end; | |||
[MiRequest] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1}); | |||
[MiRequest | Outs] -> | |||
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1}) | |||
end; | |||
_ -> | |||
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]), | |||
case gen_tcp:send(Socket, Request) of | |||
ok -> | |||
TimerRef = | |||
case OverTime of | |||
infinity -> | |||
undefined; | |||
_ -> | |||
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}]) | |||
end, | |||
{ok, SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, curInfo = {FromPid, RequestId, TimerRef}}}; | |||
{error, Reason} -> | |||
?WARN(ServerName, ":send error: ~p~n", [Reason]), | |||
gen_tcp:close(Socket), | |||
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, socketSendError}), | |||
agAgencyUtils:dealClose(SrvState, CliState, {error, socketSendError}) | |||
end | |||
end. | |||
@ -0,0 +1,438 @@ | |||
-module(vst). | |||
-export([ | |||
authorize/1, | |||
connect/2, | |||
request/2, | |||
vst_maxsize/0 | |||
]). | |||
assign_map(_acc@1, _key@1, _value@1) -> | |||
case _acc@1 of | |||
#{_key@1 := _} -> _acc@1; | |||
#{} -> _acc@1#{_key@1 => _value@1}; | |||
_ -> #{_key@1 => _value@1} | |||
end. | |||
assign_split([<<>>, _rest@1], _value@1, _acc@1, _pattern@1) -> | |||
_parts@1 = binary:split(_rest@1, _pattern@1), | |||
case _acc@1 of | |||
[_ | _] -> | |||
[assign_split(_parts@1, _value@1, none, _pattern@1) | _acc@1]; | |||
none -> | |||
[assign_split(_parts@1, _value@1, none, _pattern@1)]; | |||
_ -> _acc@1 | |||
end; | |||
assign_split([_key@1, _rest@1], _value@1, _acc@1, _pattern@1) -> | |||
_parts@1 = binary:split(_rest@1, _pattern@1), | |||
case _acc@1 of | |||
#{_key@1 := _current@1} -> | |||
_acc@1#{_key@1 => | |||
assign_split(_parts@1, _value@1, _current@1, _pattern@1)}; | |||
#{} -> | |||
_acc@1#{_key@1 => assign_split(_parts@1, _value@1, none, _pattern@1)}; | |||
_ -> | |||
#{_key@1 => assign_split(_parts@1, _value@1, none, _pattern@1)} | |||
end; | |||
assign_split([<<>>], nil, _acc@1, __pattern@1) -> | |||
case _acc@1 of | |||
[_ | _] -> _acc@1; | |||
_ -> [] | |||
end; | |||
assign_split([<<>>], _value@1, _acc@1, __pattern@1) -> | |||
case _acc@1 of | |||
[_ | _] -> [_value@1 | _acc@1]; | |||
none -> [_value@1]; | |||
_ -> _acc@1 | |||
end; | |||
assign_split([_key@1], _value@1, _acc@1, __pattern@1) -> | |||
assign_map(_acc@1, _key@1, _value@1). | |||
authorize(#{socket := _socket@1, username := _un@1, password := _pw@1} = _state@1) -> | |||
case eVPack:encode([1, 1000, <<"plain">>, _un@1, _pw@1]) of | |||
{ok, _auth@1} -> | |||
case send_stream(_socket@1, build_stream(_auth@1)) of | |||
ok -> | |||
case recv_header(_socket@1) of | |||
{ok, _header@1} -> | |||
case recv_stream(_socket@1, _header@1) of | |||
{ok, _stream@1} -> | |||
case decode_stream(_stream@1) of | |||
{ok, [[1, 2, 200, __headers@2] | __body@1]} -> | |||
ok; | |||
_@1 -> | |||
case _@1 of | |||
{ok, [[1, 2, _status@1, __headers@1], _body@1 | _]} -> | |||
{error, #{ | |||
'__exception__' => true, | |||
error_num => nil, | |||
status => _status@1, | |||
message => proplists:get_value(<<"errorMessage">>, _body@1), | |||
endpoint => | |||
case _state@1 of | |||
#{endpoint := _@3} -> | |||
_@3; | |||
_@3 when erlang:is_map(_@3) -> | |||
erlang:error({badkey, endpoint, _@3}); | |||
_@3 -> | |||
_@3:endpoint() | |||
end}}; | |||
{error, _reason@1} -> | |||
{error, _reason@1}; | |||
_@2 -> | |||
erlang:error({with_clause, _@2}) | |||
end | |||
end; | |||
_@1 -> | |||
case _@1 of | |||
{ok, | |||
[[1, 2, _status@1, __headers@1], _body@1 | _]} -> | |||
{error, #{ | |||
'__exception__' => true, | |||
error_num => nil, | |||
status => _status@1, | |||
message => proplists:get_value(<<"errorMessage">>, _body@1), | |||
endpoint => | |||
case _state@1 of | |||
#{endpoint := _@3} -> | |||
_@3; | |||
_@3 when erlang:is_map(_@3) -> | |||
erlang:error({badkey, endpoint, _@3}); | |||
_@3 -> _@3:endpoint() | |||
end}}; | |||
{error, _reason@1} -> | |||
{error, _reason@1}; | |||
_@2 -> erlang:error({with_clause, _@2}) | |||
end | |||
end; | |||
_@1 -> | |||
case _@1 of | |||
{ok, | |||
[[1, 2, _status@1, __headers@1], _body@1 | |||
| _]} -> | |||
{error, #{ | |||
'__exception__' => true, | |||
error_num => nil, status => _status@1, | |||
message => proplists:get_value(<<"errorMessage">>, _body@1), | |||
endpoint => | |||
case _state@1 of | |||
#{endpoint := _@3} -> _@3; | |||
_@3 when erlang:is_map(_@3) -> | |||
erlang:error({badkey, | |||
endpoint, | |||
_@3}); | |||
_@3 -> _@3:endpoint() | |||
end}}; | |||
{error, _reason@1} -> {error, _reason@1}; | |||
_@2 -> erlang:error({with_clause, _@2}) | |||
end | |||
end; | |||
_@1 -> | |||
case _@1 of | |||
{ok, [[1, 2, _status@1, __headers@1], _body@1 | _]} -> | |||
{error, | |||
#{ | |||
'__exception__' => true, error_num => nil, | |||
status => _status@1, | |||
message => proplists:get_value(<<"errorMessage">>, _body@1), | |||
endpoint => | |||
case _state@1 of | |||
#{endpoint := _@3} -> _@3; | |||
_@3 when erlang:is_map(_@3) -> | |||
erlang:error({badkey, | |||
endpoint, | |||
_@3}); | |||
_@3 -> _@3:endpoint() | |||
end}}; | |||
{error, _reason@1} -> {error, _reason@1}; | |||
_@2 -> erlang:error({with_clause, _@2}) | |||
end | |||
end; | |||
_@1 -> | |||
case _@1 of | |||
{ok, [[1, 2, _status@1, __headers@1], _body@1 | _]} -> | |||
{error, #{ | |||
'__exception__' => true, | |||
error_num => nil, | |||
status => _status@1, | |||
message => proplists:get_value(<<"errorMessage">>, _body@1), | |||
endpoint => | |||
case _state@1 of | |||
#{endpoint := _@3} -> _@3; | |||
_@3 when erlang:is_map(_@3) -> | |||
erlang:error({badkey, endpoint, _@3}); | |||
_@3 -> _@3:endpoint() | |||
end}}; | |||
{error, _reason@1} -> {error, _reason@1}; | |||
_@2 -> erlang:error({with_clause, _@2}) | |||
end | |||
end. | |||
body_for(<<>>) -> {ok, <<>>}; | |||
body_for(_body@1) -> | |||
eVPack:encode(_body@1). | |||
body_from([]) -> nil; | |||
body_from([_body@1]) -> _body@1; | |||
body_from(_body@1) -> _body@1. | |||
build_stream(_message@1) -> | |||
case chunk_every(_message@1, 30696) of | |||
[_first_chunk@1 | _rest_chunks@1] -> | |||
_n_chunks@1 = erlang:length([_first_chunk@1 | |||
| _rest_chunks@1]), | |||
_msg_length@1 = erlang:byte_size(_message@1) + | |||
_n_chunks@1 * 24, | |||
_rest_chunks@2 = | |||
lists:reverse(lists:flodl( | |||
fun(_n@1, _@1) -> | |||
case _rest_chunks@1 /= [] of | |||
true -> | |||
[prepend_chunk(lists:nth(_n@1, _rest_chunks@1), _n@1, 0, 0, _msg_length@1) | _@1]; | |||
false -> _@1 | |||
end | |||
end, [], lists:seq(1, erlang:length(_rest_chunks@1)))), | |||
[prepend_chunk(_first_chunk@1, _n_chunks@1, 1, 0, _msg_length@1) | _rest_chunks@2]; | |||
_only_chunk@1 -> | |||
prepend_chunk(_only_chunk@1, 1, 1, 0, erlang:byte_size(_message@1) + 24) | |||
end. | |||
chunk_every(_bytes@1, _size@1) when erlang:byte_size(_bytes@1) =< _size@1 -> | |||
_bytes@1; | |||
chunk_every(_bytes@1, _size@1) -> | |||
<<_chunk@1:_size@1/binary, _rest@1/binary>> = _bytes@1, | |||
[_chunk@1 | (chunk_every(_rest@1, _size@1))]. | |||
connect(#{addr := _addr@1, 'ssl?' := _ssl}, _opts@1) -> | |||
_mod@1 = case _ssl of | |||
_@1 when _@1 =:= false orelse _@1 =:= nil -> gen_tcp; | |||
_ -> ssl | |||
end, | |||
_transport_opts@1 = case _ssl of | |||
_@2 when _@2 =:= false orelse _@2 =:= nil -> | |||
tcp_opts; | |||
_ -> ssl_opts | |||
end, | |||
_transport_opts@2 = proplists:get_value(_transport_opts@1, _opts@1, []), | |||
_connect_timeout@1 = proplists:get(connect_timeout, _opts@1, 5000), | |||
_options@1 = lists:merge(_transport_opts@2, [{packet, raw}, {mode, binary}, {active, false}]), | |||
case _mod@1:connect(addr_for(_addr@1), port_for(_addr@1), _options@1, _connect_timeout@1) of | |||
{ok, _port@1} -> | |||
case _mod@1:send(_port@1, <<"VST/1.1\r\n\r\n">>) of | |||
ok -> {ok, {_mod@1, _port@1}}; | |||
_@3 -> _@3 | |||
end; | |||
_@3 -> _@3 | |||
end. | |||
decode_pair({_key@1, _value@1}, _acc@1) -> | |||
case case _key@1 /= <<>> of | |||
false -> false; | |||
true -> binary:last(_key@1) == 93 | |||
end | |||
of | |||
false -> assign_map(_acc@1, _key@1, _value@1); | |||
true -> | |||
_subkey@1 = binary:part(_key@1, | |||
0, | |||
erlang:byte_size(_key@1) - 1), | |||
assign_split(binary:split(_subkey@1, <<"[">>), | |||
_value@1, | |||
_acc@1, | |||
binary:compile_pattern(<<"][">>)) | |||
end. | |||
decode_stream(_@1) -> decode_stream(_@1, []). | |||
decode_stream(<<>>, _acc@1) -> {ok, _acc@1}; | |||
decode_stream(_stream@1, _acc@1) -> | |||
case eVPack:decode(_stream@1) of | |||
{ok, {_term@1, _rest@1}} -> | |||
decode_stream(_rest@1, erlang:'++'(_acc@1, [_term@1])); | |||
{ok, _term@2} -> {ok, erlang:'++'(_acc@1, [_term@2])}; | |||
{error, _reason@1} -> {error, _reason@1} | |||
end. | |||
headers_for(#{} = _headers@1) -> _headers@1; | |||
headers_for(_headers@1) | |||
when erlang:is_list(_headers@1) -> | |||
maps:from_list(_headers@1). | |||
method_for(delete) -> 0; | |||
method_for(get) -> 1; | |||
method_for(post) -> 2; | |||
method_for(put) -> 3; | |||
method_for(head) -> 4; | |||
method_for(patch) -> 5; | |||
method_for(options) -> 6; | |||
method_for(_) -> -1. | |||
port_for({unix, __path@1}) -> 0; | |||
port_for({tcp, __host@1, _port@1}) -> _port@1. | |||
prepend_chunk(_chunk@1, _chunk_n@1, _is_first@1, | |||
_msg_id@1, _msg_length@1) -> | |||
<<(24 + erlang:byte_size(_chunk@1)):32/integer-little, | |||
(binary:decode_unsigned(<<_chunk_n@1:31/integer, | |||
_is_first@1:1/integer>>, | |||
little)):32/integer, | |||
_msg_id@1:64/integer-little, | |||
_msg_length@1:64/integer-little, _chunk@1/binary>>. | |||
query_for(nil) -> #{}. | |||
recv_chunk({_mod@1, _port@1}, _chunk_length@1) -> | |||
_mod@1:recv(_port@1, _chunk_length@1 - 24). | |||
recv_header({_mod@1, _port@1}) -> | |||
case _mod@1:recv(_port@1, 24) of | |||
{ok, | |||
<<_chunk_length@1:32/integer-little, | |||
_chunk_x@1:32/integer, _msg_id@1:64/integer-little, | |||
_msg_length@1:64/integer-little>>} -> | |||
<<_chunk_n@1:31/integer, _is_first@1:1/integer>> = | |||
<<_chunk_x@1:32/integer-little>>, | |||
{ok, | |||
[_chunk_length@1, | |||
_chunk_n@1, | |||
_is_first@1, | |||
_msg_id@1, | |||
_msg_length@1]}; | |||
{error, _reason@1} -> {error, _reason@1} | |||
end. | |||
recv_stream(_socket@1, | |||
[_chunk_length@1, 1, 1, __msg_id@1, __msg_length@1]) -> | |||
recv_chunk(_socket@1, _chunk_length@1); | |||
recv_stream(_socket@1, | |||
[_chunk_length@1, | |||
_n_chunks@1, | |||
1, | |||
__msg_id@1, | |||
__msg_length@1]) -> | |||
case recv_chunk(_socket@1, _chunk_length@1) of | |||
{ok, _buffer@1} -> | |||
case recv_stream(_socket@1, _n_chunks@1, _buffer@1) of | |||
{ok, _stream@1} -> {ok, _stream@1}; | |||
_@1 -> _@1 | |||
end; | |||
_@1 -> _@1 | |||
end. | |||
recv_stream(_socket@1, _n_chunks@1, _buffer@1) -> | |||
lists:reduce_while(lists:seq(1, _n_chunks@1 - 1), | |||
_buffer@1, | |||
fun(_n@1, _buffer@2) -> | |||
case recv_header(_socket@1) of | |||
{ok, [_chunk_length@1, _, _, _, _]} -> | |||
case recv_chunk(_socket@1, _chunk_length@1) of | |||
{ok, _chunk@1} -> | |||
case _n@1 == _n_chunks@1 - 1 of | |||
false -> | |||
{cont, <<_buffer@2/binary, _chunk@1/binary>>}; | |||
true -> | |||
{halt, {ok, <<_buffer@2/binary, _chunk@1/binary>>}} | |||
end; | |||
_@1 -> | |||
case _@1 of | |||
{error, _reason@1} -> | |||
{halt, {error, _reason@1}}; | |||
_@2 -> | |||
erlang:error({with_clause, _@2}) | |||
end | |||
end; | |||
_@1 -> | |||
case _@1 of | |||
{error, _reason@1} -> | |||
{halt, {error, _reason@1}}; | |||
_@2 -> | |||
erlang:error({with_clause, _@2}) | |||
end | |||
end | |||
end). | |||
request(#{method := _method@1, path := _path@1, headers := _headers@1, body := _body@1}, #{socket := _socket@1, database := _database@1} = _state@1) -> | |||
#{path := _path@2, query := _query@1} = http_uri:parse(_path@1), | |||
{_database@3, _path@4} = | |||
case _path@2 of | |||
<<"/_db/", _rest@1/binary>> -> | |||
[_database@2, _path@3] = binary:split(_rest@1, <<"/">>), | |||
{_database@2, <<"/", _path@3/binary>>}; | |||
_ -> | |||
{case _database@1 of | |||
_@1 when _@1 =:= false orelse _@1 =:= nil -> | |||
<<>>; | |||
_@2 -> _@2 | |||
end, _path@2} | |||
end, | |||
_request@1 = [1, 1, _database@3, method_for(_method@1), _path@4, query_for(_query@1), headers_for(_headers@1)], | |||
case eVPack:encode(_request@1) of | |||
{ok, _request@2} -> | |||
case body_for(_body@1) of | |||
{ok, _body@2} -> | |||
case send_stream(_socket@1, build_stream(<<_request@2/binary, _body@2/binary>>)) | |||
of | |||
ok -> | |||
case recv_header(_socket@1) of | |||
{ok, _header@1} -> | |||
case recv_stream(_socket@1, _header@1) of | |||
{ok, _stream@1} -> | |||
case decode_stream(_stream@1) of | |||
{ok, [[1, 2, _status@1, _headers@2] | _body@3]} -> | |||
{ok, #{status => _status@1, headers => _headers@2, body => body_from(_body@3)}, _state@1}; | |||
_@3 -> | |||
case _@3 of | |||
{error, closed} -> | |||
{error, noproc, _state@1}; | |||
{error, _reason@1} -> | |||
{error, _reason@1, _state@1}; | |||
_@4 -> | |||
erlang:error({with_clause, _@4}) | |||
end | |||
end; | |||
_@3 -> | |||
case _@3 of | |||
{error, closed} -> | |||
{error, noproc, _state@1}; | |||
{error, _reason@1} -> | |||
{error, _reason@1, _state@1}; | |||
_@4 -> | |||
erlang:error({with_clause, _@4}) | |||
end | |||
end; | |||
_@3 -> | |||
case _@3 of | |||
{error, closed} -> | |||
{error, noproc, _state@1}; | |||
{error, _reason@1} -> | |||
{error, _reason@1, _state@1}; | |||
_@4 -> erlang:error({with_clause, _@4}) | |||
end | |||
end; | |||
_@3 -> | |||
case _@3 of | |||
{error, closed} -> {error, noproc, _state@1}; | |||
{error, _reason@1} -> | |||
{error, _reason@1, _state@1}; | |||
_@4 -> erlang:error({with_clause, _@4}) | |||
end | |||
end; | |||
_@3 -> | |||
case _@3 of | |||
{error, closed} -> {error, noproc, _state@1}; | |||
{error, _reason@1} -> {error, _reason@1, _state@1}; | |||
_@4 -> erlang:error({with_clause, _@4}) | |||
end | |||
end; | |||
_@3 -> | |||
case _@3 of | |||
{error, closed} -> {error, noproc, _state@1}; | |||
{error, _reason@1} -> {error, _reason@1, _state@1}; | |||
_@4 -> erlang:error({with_clause, _@4}) | |||
end | |||
end. | |||
send_stream(Socket, IoData) -> | |||
tcp:send(Socket, IoData). | |||
vst_maxsize() -> 30720. |
@ -0,0 +1,11 @@ | |||
{application, erlArango, | |||
[{description, "An OTP application"}, | |||
{vsn, "0.1.0"}, | |||
{registered, []}, | |||
{mod, {erlArango_app, []}}, | |||
{applications, [kernel, stdlib, jiffy]}, | |||
{env, []}, | |||
{modules, []}, | |||
{licenses, ["MIT License"]}, | |||
{links, []} | |||
]}. |
@ -0,0 +1,11 @@ | |||
-module(erlArango_app). | |||
-behaviour(application). | |||
-export([start/2, stop/1]). | |||
start(_StartType, _StartArgs) -> | |||
erlArango_sup:start_link(). | |||
stop(_State) -> | |||
ok. |
@ -0,0 +1,30 @@ | |||
-module(erlArango_sup). | |||
-include("agHttpCli.hrl"). | |||
-include("erlArango.hrl"). | |||
-behaviour(supervisor). | |||
-export([start_link/0]). | |||
-export([init/1]). | |||
-define(SERVER, ?MODULE). | |||
start_link() -> | |||
supervisor:start_link({local, ?SERVER}, ?MODULE, []). | |||
%% sup_flags() = #{strategy => strategy(), % optional | |||
%% intensity => non_neg_integer(), % optional | |||
%% period => pos_integer()} % optional | |||
%% child_spec() = #{id => child_id(), % mandatory | |||
%% start => mfargs(), % mandatory | |||
%% restart => restart(), % optional | |||
%% shutdown => shutdown(), % optional | |||
%% type => worker(), % optional | |||
%% modules => modules()} % optional | |||
init([]) -> | |||
SupFlags = #{strategy => one_for_one, intensity => 100, period => 3600}, | |||
PoolMgrSpec = #{id => agAgencyPoolMgrExm, start => {agAgencyPoolMgrExm, start_link, [?agAgencyPoolMgr, [], []]}, restart => permanent, shutdown => infinity, type => worker, modules => [agAgencyPoolMgrExm]}, | |||
HttpCliSupSpec = #{id => agAgencyPool_sup, start => {agAgencyPool_sup, start_link, []}, restart => permanent, shutdown => infinity, type => supervisor, modules => [agAgencyPool_sup]}, | |||
{ok, {SupFlags, [PoolMgrSpec, HttpCliSupSpec]}}. | |||
@ -0,0 +1,155 @@ | |||
-module(user_default). | |||
-include("agHttpCli.hrl"). | |||
-compile([export_all, nowarn_export_all]). | |||
start() -> | |||
erlSync:run(), | |||
application:ensure_all_started(erlArango), | |||
agHttpCli:startPool(tt, [{poolSize, 10}], []). | |||
tt(C, N) -> | |||
application:ensure_all_started(erlArango), | |||
agHttpCli:startPool(tt, [{poolSize, 16}], []), | |||
StartTime = erlang:system_time(millisecond), | |||
io:format("IMY********************** started~n"), | |||
[spawn(?MODULE, test, [N, StartTime]) || _Idx <- lists:seq(1, C)]. | |||
%%test(N, Request). | |||
%% /_api/database | |||
test(0, StartTime) -> | |||
agMiscFuns:curDbTime(tt), | |||
io:format("IMY******test over use time ~pms~n", [erlang:system_time(millisecond) - StartTime]); | |||
test(N, StartTime) -> | |||
agMiscFuns:curDbTime(tt), | |||
test(N - 1, StartTime). | |||
%% tt(C, N) -> | |||
%% application:start(erlArango), | |||
%% agHttpCli:startPool(tt, [{poolSize, 1}, {baseUrl, <<"http://localhost:8181">>}], []), | |||
%% Request = {<<"GET">>, <<"/_api/database/current">>, [], []}, | |||
%% io:format("IMY********************** start time ~p~n",[erlang:system_time(millisecond)]), | |||
%% [spawn(test, test, [N, Request]) || _Idx <- lists:seq(1, C)]. | |||
%% %%test(N, Request). | |||
%% | |||
%% %% /_api/database | |||
%% | |||
%% test(0, Request) -> | |||
%% R1 = {<<"POST">>, <<"/echo_body">>, [], []}, | |||
%% agHttpCli:callAgency(tt, {<<"GET">>, <<"/ibrowse_stream_once_chunk_pipeline_test">>, [], []}, infinity), | |||
%% agHttpCli:callAgency(tt, {<<"POST">>, <<"/echo_body">>, [], []}, infinity), | |||
%% io:format("IMY********************** test over ~p~n",[erlang:system_time(millisecond)]); | |||
%% test(N, Request) -> | |||
%% erlang:put(cnt, N), | |||
%% agHttpCli:callAgency(tt, Request, 5000), | |||
%% test(N - 1, Request). | |||
-define(HeadBin, <<"X-Content-Type-Options: nosniff\r\nEtag: \"_aKwJ_tm--E\"\r\nServer: ArangoDB\r\nConnection: Keep-Alive\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: 178">>). | |||
th1(0, Fun, Rn) -> | |||
?MODULE:Fun(?HeadBin, Rn); | |||
th1(N, Fun, Rn) -> | |||
?MODULE:Fun(?HeadBin, Rn), | |||
th1(N - 1, Fun, Rn). | |||
th2(0, Fun, Cl, Rn) -> | |||
?MODULE:Fun(?HeadBin, Cl, Rn); | |||
th2(N, Fun, Cl, Rn) -> | |||
?MODULE:Fun(?HeadBin, Cl, Rn), | |||
th2(N - 1, Fun, Cl, Rn). | |||
head1(Headers, Rn) -> | |||
HeadersList = binary:split(Headers, Rn, [global]), | |||
contentLength(HeadersList). | |||
head2(Headers, _Rn) -> | |||
HeadersList = binary:split(Headers, <<"\r\n">>, [global]), | |||
contentLength(HeadersList). | |||
head3(Headers, CL, Rn) -> | |||
case binary:split(Headers, CL) of | |||
[_, Rest1] -> | |||
case binary:split(Rest1, Rn) of | |||
[InBin, _Rest2] -> | |||
binary_to_integer(InBin); | |||
[InBin] -> | |||
binary_to_integer(InBin) | |||
end; | |||
_ -> | |||
0 | |||
end. | |||
%% binary:compile_pattern(<<"\r\n">>) | |||
%% binary:compile_pattern(<<"Content-Length: ">>) | |||
head4(Headers, _CL, _Rn) -> | |||
case binary:split(Headers, <<"Content-Length: ">>) of | |||
[_, Rest1] -> | |||
case binary:split(Rest1, <<"\r\n">>) of | |||
[InBin, _Rest2] -> | |||
binary_to_integer(InBin); | |||
[InBin] -> | |||
binary_to_integer(InBin) | |||
end; | |||
_ -> | |||
0 | |||
end. | |||
contentLength([]) -> | |||
undefined; | |||
contentLength([<<"Content-Length: ", Rest/binary>> | _T]) -> | |||
binary_to_integer(Rest); | |||
contentLength([<<"content-length: ", Rest/binary>> | _T]) -> | |||
binary_to_integer(Rest); | |||
contentLength([<<"Transfer-Encoding: chunked">> | _T]) -> | |||
chunked; | |||
contentLength([<<"transfer-encoding: chunked">> | _T]) -> | |||
chunked; | |||
contentLength([_ | T]) -> | |||
contentLength(T). | |||
%% 测试 jiffy 与 jsx 编码解码性能 | |||
tcjf(0, _Args1) -> | |||
Args = #{name => ffd, tet => "fdsff", <<"dfdf">> => 131245435346}, | |||
jiffy:encode(Args); | |||
tcjf(N, Args1) -> | |||
Args = #{name => ffd, tet => "fdsff", <<"dfdf">> => 131245435346}, | |||
jiffy:encode(Args), | |||
tcjf(N - 1, Args1). | |||
tcjx(0, _Args1) -> | |||
Args = {[{name, ffd}, {tet, "fdsff"}, {<<"dfdf">>, 131245435346}]}, | |||
jiffy:encode(Args); | |||
tcjx(N, Args1) -> | |||
Args = {[{name, ffd}, {tet, "fdsff"}, {<<"dfdf">>, 131245435346}]}, | |||
jiffy:encode(Args), | |||
tcjx(N - 1, Args1). | |||
-define(BodyBin1, <<"{\"_key\":\"01J\",\"_id\":\"airports/01J\",\"_rev\":\"_aKwJ_tm--E\",\"name\":\"Hilliard Airpark\",\"city\":\"Hilliard\",\"state\":\"FL\",\"country\":\"USA\",\"lat\":30.6880125,\"long\":-81.90594389,\"vip\":false}">>). | |||
-define(BodyBin2, <<"{\"_key\":\"01J\",\"_id\":\"airports/01J\",\"_rev\":\"_aPaBl7O--_\",\"name\":\"Hilliard Airpark\",\"city\":\"Hilliardfdfsdfdsffffffffffffffffffffffffffffffffffffffffffffffffffffffffafdsfasdfdafsdafdsfsdafdsafdsfdsfdsafdsfdsfdsfhghfghfghgfhsdsdfdsfdsfdsffdfddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddggggggggggggggggggggggggggggggggggggggggg\",\"state\":\"FL\",\"country\":\"USAjjkjkjkfgjkgjfkdjgldgjldjglfdjglfjdljljrlejtrltjewltjrelwtjrletjrletrletjlrejtjtrlwjrejwlrjjreljtljelwjrtlwjtreljrlewjrlwjrlwejrlejltkdfsafd\",\"lat\":30.6880125,\"long\":-81.90594389,\"vip\":false}">>). | |||
jd1(0, Fun) -> | |||
?MODULE:Fun(?BodyBin1); | |||
jd1(N, Fun) -> | |||
?MODULE:Fun(?BodyBin1), | |||
jd1(N - 1, Fun). | |||
jd2(0, Fun) -> | |||
?MODULE:Fun(?BodyBin2); | |||
jd2(N, Fun) -> | |||
?MODULE:Fun(?BodyBin2), | |||
jd2(N - 1, Fun). | |||
decodeJy1(Bin) -> | |||
jiffy:decode(Bin, [return_maps]). | |||
decodeJy2(Bin) -> | |||
jiffy:decode(Bin, [return_maps, copy_strings]). | |||
decodeJx1(Bin) -> | |||
jsx:decode(Bin, [return_maps]). | |||
decodeJx2(Bin) -> | |||
jsx:decode(Bin, []). |
@ -0,0 +1 @@ | |||
erl -sname arango -setcookie 123 -pa ./ebin -pa ./deps/*/ebin |