ecal.md 10 KB

EliasDB Event Condition Action Language

EliasDB supports a scripting language called Event Condition Action Language (ECAL) to enable rule based scripting functionality. ECAL provides database trigger functionality for EliasDB.

ECAL was added for the following use-cases:

  • Providing a way to manipulate data in response to events
  • Enforce certain aspects of a database schema
  • Providing back-end logic for web applications using EliasDB

The source of EliasDB comes with a chat example containing a simple ECAL script which adds a timestamp to nodes and a game example which demonstrates a more complex application of ECAL.

ECAL related config values:

These ECAL related config options are available in eliasdb.config.json:

Configuration Option Description
EnableECALScripts Enable ECAL scripting.
ECALScriptFolder Scripting folder for ECAL scripts.
ECALEntryScript Entry script in the script folder.
ECALLogFile File in which the logs should be written (use an empty string for stdout).
ECALLogLevel Log level for the printed logs.
EnableECALDebugServer Enable debugging and start the ECAL debug server. Note: Activating debugging will slow down the interpreter speed significantly!
ECALDebugServerHost Host for the debug server.
ECALDebugServerPort Port for the debug server.
ECALWorkerCount Number of worker threads in the ECA engine's thread pool.

ECAL Debugging

If the debug server is enabled in the config file then it is possible to debug ECAL scripts with VSCode. The debugger supports break points and thread state inspection. It is also possible to restart and reload the scripts.

Using the -ecal-console parameter it is possible to open an interactive console into the server process. If used together with the debug server additional debug commands are available also there. Enter ? to see the build-in documentation.

EliasDB specific events which can be handled:

The ECAL interpreter in EliasDB receives the following events:

Web Request ECAL event kind Event state contents Description
/db/api/ db.web.api bodyJSON, bodyString, header, method, path, pathList, query Any web request to /db/api/... These endpoints are public and never require authentication.
/db/ecal/ db.web.ecal bodyJSON, bodyString, header, method, path, pathList, query Any web request to /db/ecal/... These endpoints are considered internal and require authentication if access control is enabled.
/db/sock/ db.web.sock bodyJSON, bodyString, commID, header, method, path, pathList, query Any web request to /db/sock/... These endpoints are used to initiate websocket connections.
- db.web.sock.data commID, data, header, method, path, pathList, query An existing websocket connection received some JSON object data. If the close attribute of the object is set to true then the websocket connection is closed.
EliasDB Graph Event ECAL event kind Event state contents Description
graph.EventNodeCreated db.node.created part, trans, node A node was created.
graph.EventNodeUpdated db.node.updated part, trans, node, old_node A node was updated.
graph.EventNodeDeleted db.node.deleted part, trans, node A node was deleted.
graph.EventEdgeCreated db.edge.created part, trans, edge An edge was created.
graph.EventEdgeUpdated db.edge.updated part, trans, edge, old_edge An edge was updated.
graph.EventEdgeDeleted db.edge.deleted part, trans, edge An edge was deleted.
graph.EventNodeStore db.node.store part, trans, node A node is about to be stored (always overwriting existing values).
graph.EventNodeUpdate db.node.update part, trans, node A node is about to be updated.
graph.EventNodeDelete db.node.delete part, trans, key, kind A node is about to be deleted.
graph.EventEdgeStore db.edge.store part, trans, edge An edge is about to be stored.
graph.EventEdgeDelete db.edge.delete part, trans, key, kind An edge is about to be deleted.

Note: EliasDB will wait for the event cascade to be finished before performing the actual operation (e.g. inserting a node). If the event handling requires a time consuming operation then a new parallel event cascade can be started using addEvent with a scope:

EliasDB can receive the following events from the ECAL interpreter:

ECAL event kind Event state contents Description
db.web.sock.msg commID, payload, close The payload is send to a client with an open websocket identified by the commID.
addEvent("request", "foo.bar.xxx", {
   "payload" : 123
}, {
   "": true  # This scope allows all events
})

EliasDB specific functions:

The ECAL interpreter in EliasDB supports the following EliasDB specific functions:

db.storeNode(partition, nodeMap, [transaction])

Inserts or updates a node in EliasDB.

Parameter | Description -|- partition | Partition of the node nodeMap | Node object as a map with at least a key and a kind attribute transaction | Optional a transaction to group a set of changes

Example:

db.storeNode("main", {
  "key" : "foo",
  "kind" : "bar",
  "data" : 123,
})

db.updateNode(partition, nodeMap, [transaction])

Updates a node in EliasDB (only update the given values of the node).

Parameter | Description -|- partition | Partition of the node nodeMap | Node object as a map with at least a key and a kind attribute transaction | Optional a transaction to group a set of changes

Example:

db.updateNode("main", {
  "key" : "foo",
  "kind" : "bar",
  "data" : 123,
})

db.removeNode(partition, nodeKey, nodeKind, [transaction])

Removes a node in EliasDB.

Parameter | Description -|- partition | Partition of the node nodeKey | Key attribute of the node to remove nodeKind | Kind attribute of the node to remove transaction | Optional a transaction to group a set of changes

Example:

db.removeNode("main", "foo", "bar")

db.fetchNode(partition, nodeKey, nodeKind)

Fetches a node in EliasDB.

Parameter | Description -|- partition | Partition of the node nodeKey | Key attribute of the node to fetch nodeKind | Kind attribute of the node to fetch

Example:

db.fetchNode("main", "foo", "bar")

db.storeEdge(partition, edgeMap, [transaction])

Inserts or updates an edge in EliasDB.

Parameter | Description -|- partition | Partition of the edge edgeMap | Edge object as a map with at least the main attributes: key, kind, end1cascading, end1key, end1kind, end1role, end2cascading, end2key, end2kind, end2role transaction | Optional a transaction to group a set of changes

Example:

db.storeEdge("main", {
  "key":           "123",
  "kind":          "myedges",
  "end1cascading": true,
  "end1key":       "foo",
  "end1kind":      "bar",
  "end1role":      "role1",
  "end2cascading": false,
  "end2key":       "key2",
  "end2kind":      "kind2",
  "end2role":      "role2",
})

db.removeEdge(partition, edgeKey, edgeKind, [transaction])

Removes an edge in EliasDB.

Parameter | Description -|- partition | Partition of the edge edgeKey | Key attribute of the edge to remove edgeKind | Kind attribute of the edge to remove transaction | Optional a transaction to group a set of changes

Example:

db.removeEdge("main", "123", "myedges")

db.fetchEdge(partition, edgeKey, edgeKind)

Fetches an edge in EliasDB.

Parameter | Description -|- partition | Partition of the edge edgeKey | Key attribute of the edge to fetch edgeKind | Kind attribute of the edge to fetch

Example:

db.fetchEdge("main", "123", "myedges")

db.traverse(partition, nodeKey, nodeKind, traversalSpec)

Traverses an edge in EliasDB from a given node. Returns a list of nodes which were reached and a list of edges which were followed.

Parameter | Description -|- partition | Partition of the node nodeKey | Key attribute of the node to traverse from nodeKind | Kind attribute of the node to traverse from traversalSpec | Traversal spec

Example:

[nodes, edges] := db.traverse("main", "foo", "bar", "role1:myedges:role2:kind2")

db.newTrans()

Creates a new transaction for EliasDB.

Example:

trans := db.newTrans()

db.newRollingTrans(n)

Creates a new rolling transaction for EliasDB. A rolling transaction commits after n entries.

Parameter | Description -|- n | Rolling threshold (number of operations before rolling)

Example:

trans := db.newRollingTrans(5)

db.commit(transaction)

Commits an existing transaction for EliasDB.

Parameter | Description -|- transaction | Transaction to execute

Example:

db.commit(trans)

db.query(partition, query)

Run an EQL query.

Parameter | Description -|- partition | Partition to query query | Query to execute

Example:

db.commit("main", "get bar")

db.graphQL(partition, query, [variables], [operationName])

Run a GraphQL query.

Parameter | Description -|- partition | Partition to query query | Query to execute variables | Map of variables for the query operationName | Operation to execute (useful if the query defines more than a single operation)

Example:

db.graphQL("main", "query myquery($x: string) { bar(key:$x) { data }}", {
  "x": "foo",  
}, "myquery")

db.raiseGraphEventHandled()

When handling a graph event, notify the GraphManager of EliasDB that no further action is necessary. This creates a special error object and should not be used inside a try block. When using a try block this can be used inside an except or otherwise block.

Example:

sink mysink
  kindmatch [ "db.*.*" ],
{
  db.raiseGraphEventHandled()
}

db.raiseWebEventHandled()

When handling a web event, notify the web API of EliasDB that the web request was handled. This creates a special error object and should not be used inside a try block. When using a try block this can be used inside an except or otherwise block.

Example:

sink mysink
  kindmatch [ "web.*.*" ],
{
  db.raiseWebEventHandled({
    "status" : 200,
    "headers" : {
      "Date": "today"
    },
    "body" : {
      "mydata" : [1,2,3]
    }
  })
}