Writing and modifying data in the system
Request structure
POST /data/write
The body should contain a JSON array of objects:
{
"cmd": "command_type",
// ... command-specific parameters
},
{
"cmd": "command_type",
// ... second command parameters
},
// ...
]
The result is a matching array of JSON objects:
{
"cmd": "command_type",
"code": 200// HTTP status code
// ... command-specific results
},
{
"cmd": "command_type",
"code": //...
"message": "error message"// Non-success results have a message
// ... second command results
},
// ...
]
Result and error codes
Result codes use a subset of HTTP status codes. The server attempts to execute all the commands, even if some fail, meaning that commands after a failure will execute, unless they are somehow dependent on an earlier failed command.
The following codes are in use:
Code | Meaning |
---|---|
200 | Success with results for the command |
204 | Success with no results for the command |
400 | Invalid request parameters |
403 | Forbidden to perform this specific command. If the user has no permission to write to the database at all, the overall HTTP request will fail with a 403 error. |
404 | Not found – an operation referred to a node, relationship, or partition that did not exist |
If a request contains only a single command, errors are returned as the status of the overall HTTP request. For example, a single command that tries to set the state of a non-existent node will receive a 404 HTTP response, rather than a 200 where the returned body contains the 404 status.
Naming conventions
Make sure you adhere to the Naming Conventions. In particular, attribute names that we choose should be lower_case_with_underscores.
Attribute data types
Nodes and relationships have attributes. Attributes are dynamically typed, and for most purposes, the range of JSON data types is sufficient. However, there are some types available in BMC Discovery that cannot be automatically inferred from the JSON data. For these cases, the caller can specify types (based on type names from Swagger) for any where the defaults are not sufficient:
Type | Value |
---|---|
date-time | An ISO-8601 / RFC-3339 date-time string, For example, "2020-12-10T16:53:12Z" |
date | An ISO-8601 / RFC-3339 date string, For example, "2020-12-10". (BMC Discovery does not have a distinguished date type, so it will be stored as a date-time with time 00:00:00) |
byte | Base64 encoded binary data |
image | Base64 encoded binary data that represents a graphical image like a JPEG or PNG |
float | A JSON number that is interpreted as double-precision floating point, even if it has an integral value |
Node and partition ids
Every node and relationship in the database has a node id, and exists in a partition, which also has an id. Generally the API requires specifying the partition id for all requests, and requires the node id when referring to existing nodes and relationships.
To get the node and partition ids for existing nodes and relationships, use special key expressions:
#id | Node id | Variable length, at least 26 hex digits |
#partition | Partition id | Fixed length, 24 hex digits |
To create new nodes or relationships, look up the partitions with the /data/partitions endpoint.
Available commands
In this table, the request and response are part of the request and response lists to /data/write as described above.
Command | |
---|---|
set - Sets the state of a node or relationship. | |
Request | Success response |
{ "cmd": "set", "id": "node_id", "partition": "partition_id", "state": { "attr1": "value1", ... } } to specify non-default types, add an additional "types" object: { "cmd": "set", "id": "node_id", "partition": "partition_id", "state": { "one": "2021-01-01", "two": "2021-01-01" ... }, "types": { "two": "date" } } Here, attribute "one" contains just a string, whereas "two" is interpreted as a date. To remove an attribute that already exists, do not set it to null, because that keeps { "cmd": "set", "id": "node_id", "partition": "partition_id", "void": [ "one", // remove the "one" and "two" attributes from the node "two" ] } | { "cmd": "set", "code": 204 } |
Command | |
create_node - Creates a new node | |
Request | Success response |
{ "cmd": "create_node", "partition": "partition_id", "kind": "NodeKind", "id": "optional temporary id to refer to in later commands", "state" : { // ... } } The caller can specify a temporary node id for the node, meaning that it is | { "cmd": "create_node", "code": 200, "id": "node_id", "partition": "partition_id" } |
Command | |
create_rel - Creates a new relationship between two nodes | |
Request | Success response |
{ "cmd": "create_rel", "partition": "partition_id", "kind": "RelationshipKind", "id": "optional temporary id", "state" : { // state of relationship }, "role1" : { "role": "RoleName1", "id": "node_id", "partition": "partition_id" }, "role2" : { "role": "RoleName2", "id": "node_id", "partition": "partition_id" } } The node_ids used in the relationship roles can either be the ids of nodes | { "cmd": "create_rel", "code": 200, "id": "node_id", "partition": "partition_id" } |
Command | |
destroy - Remove the node or relationship from the database. Depending on the | |
Request | Success response |
{ "cmd": "destroy", "id": "node or rel id", "partition": "partition_id", "cascade": true,// optional "async": false// optional } "cascade" defaults to true, meaning that any relationships to the "async" defaults to false. If set to true, the node destruction is | { "cmd": "destroy", "code": 204 } |
Command | |
find_or_create_node - Atomically finds an existing node or creates a new one. | |
Request | Success response |
{ "cmd": "find_or_create_node", "partition": "partition_id", "id": "optional temporary id", "kind": "NodeKind", "state" : { "key": "My key value", "attr": "Something" }, "match_keys" : [ "key" ], "update" : true // optional } Performs a find operation for the subset of state specified by "match_keys". If an existing node is not found, a new node is created with all the If "update" is true (the default) then if a node if found rather than It is a 400 error if multiple matching nodes are found. | { "cmd":"find_or_create_node", "code":200, "id":"node_id", "created":true // true if node was created, // false if found } |
Command | |
traverse_find_or_create_node - Atomically performs a graph traversal from If the node and relationship are created, they are created in the same partition as | |
Request | Success response |
{ "cmd": "traverse_find_or_create_node", "id": "optional temporary node id", "from": { // "from" node must exist "role": "FromRole", "id": "node_id", "partition": "partition_id" }, "rel" : { "kind" : "RelKind", "state" : { ... } }, "to": { // "to" node is matched as // in find_or_create_node "role": "ToRole", "kind": "NodeKind", "state": {} "match_keys" : [ ... ], }, "update" : true } | { "cmd": "traverse_find_or_create_node", "code": 200, "node": { "id": "node_id", "partition": "partition_id" }, "rel" { "id": "rel_id", "partition": "partition_id" }, "created": true // true if node was created } |
Command | |
find_or_create_rel - Atomically returns an existing relationship between | |
Request | Success response |
{ "cmd": "find_or_create_rel", "partition": "partition_id", "kind": "RelKind", "state" : { // ... }, "role1" : { "role": "RoleName1", "id": "node_id", "partition": "partition_id" }, "role2" : { "role": "RoleName2", "id": "node_id", "partition": "partition_id" }, "update" : true } | { "cmd": "find_or_create_rel", "code": 200, "id": "node_id", "partition": "partition_id", "created: true // true if relationship was created } |
Command | |
confirm - Does not change the node at all, but sends a "confirmed" event | |
Request | Success response |
{ "cmd": "confirm", "id": "node_id", "partition": "partition_id" } | { "cmd": "confirm", "code": 204 } |