Writing and modifying data in the system

The write API has a single data/write resource that takes a batch of "commands" to execute. The commands are executed in order. The return contains a result corresponding to each input command, in the same order.

Warning

The data/write API allows you to modify almost all of the data in the BMC Helix Discovery datastore. Some changes can violate the system’s expectations about the contents of nodes and relationships, and lead to errors in the user interface or in system behavior. For this reason you should avoid using the API to modify data maintained by the core system or by patterns. In general, the API should only be used to:

  • Add new nodes and relationships that are separate from those maintained by the system
  • Augment nodes that are maintained by the system by adding new attributes and relationships to them, while leaving their existing attributes and relationships unchanged

Because the API is intended for high volume data manipulation use cases, use of the API does not create Audit records. The api/datastore/write permission should only be given to users that have a specific need for it.

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:

CodeMeaning
200

Success with results for the command

204Success with no results for the command
400Invalid request parameters
403Forbidden 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.
404Not 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 Helix 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:

TypeValue
date-timeAn 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 Helix Discovery does not have a distinguished date type, so it will be stored as a date-time with time 00:00:00)

byteBase64 encoded binary data
imageBase64 encoded binary data that represents a graphical image like a JPEG or PNG
floatA 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:

#idNode idVariable length, at least 26 hex digits
#partitionPartition idFixed 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.

RequestSuccess 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
the attribute, with the value null. Instead, list it in a "void" list. In that case, if there
is no state that is also being set, the "state" object can be missing:

{
	"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

RequestSuccess 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
possible to use a single batch of commands to creates some nodes and
relationships between them. These temporary node ids are only valid for
the duration of this batch of commands. Subsequent batches must
use the real node ids returned from the operation.


{
	"cmd":       "create_node",
	"code":      200,
	"id":        "node_id",
	"partition": "partition_id"
}
Command

create_rel - Creates a new relationship between two nodes

RequestSuccess 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
that already exist in the database, or they can be temporary ids for
nodes / relationships created earlier in the batch.


{
	"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.

RequestSuccess 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.
RequestSuccess 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.

RequestSuccess 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.

RequestSuccess 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.

RequestSuccess response


{
    "cmd":       "confirm",
    "id":        "node_id",
    "partition": "partition_id"
}


{
    "cmd":  "confirm",
    "code": 204
}
Was this page helpful? Yes No Submitting... Thank you

Comments