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 partition's configuration, this either marks the node / relationship as destroyed, or actually completely removes it. | |
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 node / relationship are automatically destroyed. If it it set to false, the node is only destroyed if it has no relationships. If it has any relationships, returns a 400 error with message "node has relationships". "async" defaults to false. If set to true, the node destruction is performed asynchronously in the background, in an order that is efficient for the database. |
{ "cmd": "destroy", "code": 204 } |
Command | |
find_or_create_node - Atomically finds an existing node or creates a new one. If multiple concurrent callers use this operation with the same criteria, it is guaranteed that only one node will be created. It is not atomic against concurrent calls to create_node. | |
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 attributes in "state". If "update" is true (the default) then if a node if found rather than created, all the attributes in "state" are set on the node. If "update" is false, the node is left unchanged. 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 the source node, looking for a related node matching the required state. Either returns an existing node and relationship, or creates a new node and relationship. If the node and relationship are created, they are created in the same partition as the source node; if existing ones are found, the expectation is that they are also in the same partition, but if they were created without using traverse_find_or_create, they may be in different partitions. | |
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 two nodes, or creates a new one. | |
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 into the reasoning engine, to indicate that a node that was previously created has been looked for and has been confirmed to exist. | |
Request | Success response |
{ "cmd": "confirm", "id": "node_id", "partition": "partition_id" } |
{ "cmd": "confirm", "code": 204 } |