# Actions Reference

# Anatomy of an Action

# Name and label

The name of the action represents the machine-readable name, and is generally used by implementers and referenced in the action editor.

The label of an action represents the human name the end-user will see in the visualisation.

For eg, fetch-all would be the name while Visualise All Elements would be the label of an action.

# Scope

The Action Scope defines the context in which the action can be triggered.

There are 3 scopes:

  • GLOBAL defines that the action can be triggered at any moment and doesn't require the user to select an element on the canvas. Globally scoped actions appear in the search bar.

  • LOCAL defines that the action can be triggered after the user has selected an element on the canvas, be it a node or a link. Locally scoped actions appear in the node or link context menu after right clicking on it.

  • MULTISELECTION defines that the action can be triggered after the user has selected more than one element on the canvas, be it nodes, links or a mix of them. They appear at the same place as locally scoped actions.

  • COMPUTED_ATTRIBUTE (beta) defines that the action will be triggered before results are returned to the canvas. The corresponding Cypher query is executed and its result is mapped to an attribute of a Schema class or relationship.

# Return Type

The Return Type of an action defines how the results will be presented to the user. There are 5 possible Return Types:

  • GRAPH the result will be mapped to nodes and links to be displayed on the canvas.

  • TABLE the result will be mapped to rows and columns and displayed in a dialogue as a table view.

  • PREVIEW the result will be displayed in a Graph Preview dialogue, allowing the user to select the elements to add to the canvas.

  • CHART the result will be displayed in a Chart format dialogue, useful for showing time series data for example.

# Resource

Available since : 2.9

An action can be performed on any Resource accessible to a user and it is not limited to a default Resource of the perspective. This allows you to choose any other Resource to run the action on. Selection parameters or input parameters work against the visualisation and therefore it allows you to save data from the visualisation into other DB. Note that the action respects Resource's read-only.

Limitations:
Returning results from another resource in the form of a graph has these consequences:

  • It may potentially override a node on the visualisation it happens to have the same internal Neo4j id as a node on the canvas.
  • Running an action based on the id of the node from the other resource on the default resource can lead to unexpected results.
  • If Data Refresh is enabled in the KG settings (visualisation section), refreshing the visualisation will lose nodes from other resources.

# Inclusion Rule

Inclusion Rules define, for local or multiselection scoped actions, on which node Classes or Relationship types an action can be triggered.

By default, actions are visible on all elements.

# Cypher Query

Actions are bound to a specified Cypher query.

The Cypher query itself will have to return results in a type that is in accordance with the Result Type of the action.

Cypher queries can read, write or even return virtual data back to the visualisation.

# Selection Parameters

Selection parameters indicate which parts of a selection should be sent as Cypher query parameters.

When only a single node or link is selected, the selection parameter contains the id of the selected item. When multiple nodes or links are selected, the ids of those items can be sent as a collection of ids. You can define the name of the Cypher query parameters that will be passed along with the Cypher query.

# Input parameters

Input parameters define a series of inputs that will have to be entered by the user when triggering the action. They will be displayed as a form in a dialogue.

For example, you can create an action that will find data between a start and end year, where the user will be able to specify those variables.


# Graph Actions

Graph Actions allow users to return data that will be displayed on the canvas.

# Use case

Actions with a Graph return type have generally one goal in common : to access quickly the right information without having to resort to a lot of graph exploration effort which is generally a limitation in standard graph visualisation products as the graph shape on the screen becomes more and more complex for the human brain to make sense of.

# Global Graph Action example

Let's setup a global graph action, always on the wine demo we used in the getting started guide.

Schema

Imagine we would have to answer the following question:

Find all wines of Variety Sauvignon Blanc that are produced in Italy

As you can see from the schema, we would have to expand a lot of nodes, for every expansion we can bring potentially hundreds or thousands of nodes which would make your graph visualisation unusable after two clicks.

The corresponding Cypher query would be the following :

MATCH p=(n:Variety)<-[:IS_OF]-(w)-[:PRODUCED_IN]->(region:Region)-->()-->(c:Country {name: "Italy"})
WHERE n.name CONTAINS "Sauvignon" AND NOT region.name = "Unknown"
RETURN p LIMIT 10

Let's make a global graph action out of it:

Schema

After saving it, the action is immediately available in the search bar:

Schema

Clicking on it, you see the response to the question immediately and in a much more processable way for human:

Schema

# Local Graph Action example

For showing the locally scoped Graph action, let's consider the following scenario :

For a given wine, I would like to return all wines that have at least 2 flavours in common

The corresponding Cypher query would be :

MATCH p=(n:Wine)-[:FLAVOUR]->(f)<-[:FLAVOUR]-(otherWine)
WHERE id(n) = $id
WITH otherWine, collect(p) AS commonFlavours
WHERE size(commonFlavours) >= 2
RETURN commonFlavours[..10]

As you can see, we need to pass the id of the wine we'll select in the visualisation, let's do that when creating the action :

Schema

The action is then visible in the context menu of a selected node:

Schema

Triggering the action returns now the results

Schema

# Multiselection Graph Action example

Multiselection graph actions can be best presented with a find paths between two nodes scenario.

So let's take two wines and see how they are related to each other, with the following Cypher query :

MATCH (n1:Wine) WHERE id(n1) = $ids[0]
MATCH (n2:Wine) WHERE id(n2) = $ids[1]
MATCH p=allShortestPaths((n1)-[*..5]-(n2))
RETURN p LIMIT 25

As you can see, we expect here a collection of ids to be passed as Cypher query parameters.

Here the corresponding Action:

Schema

After having selected at least two wines, the action will be visible in the context menu:

Schema

And no surprises, it returns the shortest paths between the two wines:

Schema

# Computed Attribute Action example

Computed Attribute actions are very handy when it comes to represent information that doesn't exist in the database but is computed instead.

An example is when you have a Product node having a STOCK_LOCATION relationship to a Warehouse and the relationship contains an attribute quantity. You would like in such scenario to reflect the sum of all the quantities in the warehouse onto a stock attribute on the product itself.

You receive the id of the node or relationship during the action execution as the $id parameter, knowing this your cypher query for the action will be the following :

MATCH (n)-[r:STOCK_LOCATION]->(w)
WHERE id(n) = $id
RETURN $id AS id, sum(r.quantity) AS stock

The semantics of the returned information from the query is the following :

  • The selected label of the computed attribute will make sure that the query is executed only when nodes with that label are returned from Neo4j.
  • You need to return the id of the node/relationship AS the id alias.
  • You need to return the value as the name of the attribute you want to map the result to ( in the case above it is stock).
  • The attribute needs to be present on the schema/perspective.

# Note about performance

Computed attributes are executed after results are returned from Neo4j (during search, expansion or action). The execution is made on the batch of nodes or relationships before returning them to the visualisation canvas, the generated query will be the following :

UNWIND $ids AS id
<your computed attribute cypher query>

It is important to keep such queries as efficient as possible as they have an impact to your overall graph visualisation experience.

Available since : 2.9
An advanced option to refer to the node or relationship is through the $node or $relationship parameter. The variable name is determined dynamically based on the inclusion type. This syntax is identical to $_selection for local actions. This construct is especially helpful when using computed attribute actions together with a different resource. You can even mix and match them together. An example:

MATCH (p:OtherProduct {code: $node.values.productCode}) 
RETURN $id AS id, p.stockAmount AS stock

# Use cases for Computed Attributes

  • Intelligence : Compute if an individual should be considered as criminal, returning criminal = true and have a style display an animated badge based on the value of that attribute

  • Supply Chain : Compute the stock based on neighbourhood information, such as the sum of all the stock locations of a particular product.

  • Impact Analysis : Determine if a Router is healthy based on his connection to gateways and their healtcheck.

  • Network Analysis : Return automatically in and out degrees of nodes so styling can be applied to size nodes based on their degrees.

# Returning element states from actions

In some scenarios it is very handy to be able to specify a visual effect on elements when the graph data from actions is returned.

For example, you might want to :

  • highlight nodes or relationships to indicate to the user some relevant part of the graph to focus on
  • remove nodes or relationships from the canvas when new data is returned
  • ping nodes or relationships
  • automatically select nodes or relationships

We offer Cypher actions to return such states with the following managed aliases from Cypher query results :

  • RETURN 1 AS __removeNode__ : remove from the canvas the node with id 1
  • RETURN 7 AS __removeRel__ : remove from the canvas the relationship with id 7
  • RETURN [1,9,34] AS __removeNodes__ : remove from the canvas the nodes with ids 1,9 and 34
  • RETURN [6, 72] AS __removeRels__ : remove from the canvas the relationships with ids 6 and 72

The same semantics apply for the following managed state aliases

  • __pingNode__
  • __pingNodes__
  • __pingRel__
  • __pingRels__
  • __highlightNode__
  • __highlightNodes__
  • __highlightRel__
  • __highlightRels__

The following multiselect local action cypher query will, used to merge 2 nodes together, will remove the original nodes from the canvas and return the new merged node :

MATCH (n1) WHERE id(n1) = $ids[0]
MATCH (n2) WHERE id(n2) = $ids[1]
MERGE (new:Person {name: n1.firstName + ' ' + n2.lastName})
RETURN new, [id(n1, id(n2))] AS __removeNodes__

# Table Actions

Some information would best be represented as a table rather than a graph structure. Hume allows you to display tables nicely in a dialogue and also has an export to XLSX and CSV capabilities.

# Table Action example

Continuing on our Wines graph, we have the following scenario :

For a given wine, find common wines by flavour and return a table with the wine name and the number of flavours in common

Showing frequency here would be hard to be done as a graph, hence we can return a table for this.

The Cypher query will be the following :

MATCH (n:Wine) 
WHERE id(n) = $id
MATCH (n)-[:FLAVOUR]->(f)<-[:FLAVOUR]-(other)
RETURN other.name AS name, count(*) AS flavoursInCommon

And here the action :

Schema

Since it is a local action, it is available in the context menu of a selected node :

Schema

And the result will look like this :

Schema

# Result columns mapping

As you can see, the returned aliases from the Cypher query are mapped to column headers in the table view.

There are cases, however, when you would like to have a key, value table style, and have one row where the key is the alias name and the value is the value.

Such a case is when you want to return the properties of a node as a key value table, having to write in the RETURN clause of the Cypher query all the properties as aliases would not be friendly, especially for nodes with large amount of properties.

To enable this scenario, there is a convential return alias that you can use and will transform a map as a list of key/value pairs.

Let's make a simple example, and show the details of a node as a table with the following Cypher query :

MATCH (n:Wine)
WHERE id(n) = $id
RETURN n{.*} AS __kvRow__

The action :

Schema

And the resulting table :

Schema

You can also return maps, instead of Cypher returned aliases, where keys will be column headers and values will be cell values.

To do so, you need to return with the special __row__ alias. It is useful for example when you want to return the content of a node with additional values in it.

For example:

RETURN n{.*, count: 13} AS __row__

# Preview Actions

Preview Actions allow users to first preview results in a dialogue before adding them to the current canvas.

Let's come back to our find similar wines having more than 2 flavours in common use case where we limited the result set to be the 10 first paths found.

We can return all of them and preview them before adding them to the visualisation.

Let's remove the collection subset from the Cypher query :

MATCH p=(n:Wine)-[:FLAVOUR]->(f)<-[:FLAVOUR]-(otherWine)
WHERE id(n) = $id
WITH otherWine, collect(p) AS paths
WHERE size(commonFlavours) >= 2
UNWIND paths AS path
RETURN path

And use the PREVIEW return type for the action:

Schema

As usual, local actions appear in the context menu of the selected node:

Schema

Here, the result is a bit different as a new canvas will be shown in a dialogue:

Schema

As you can see, there are 226 results (paths) that you can now filter, by using the relationship type or node class exclusion in the above menu.

You can select specific results to be added to the canvas:

Schema

Or you can also leverage grouping functions:

  • All nodes are equal will group results where all the nodes are the same (by their ids)
  • All classes are equal will group results where all the node classes are the same
Schema

After having selected results and clicked on Add to Canvas, they will be added to the canvas and highlighted:

Schema

# Chart Actions ( experimental )

Time series data are well represented as charts. Actions allow you to return from a Cypher query a map with some convention that will be rendered as a chart data.

The following action will return a dummy chart, but will show you the structure you will need to produce for returning a chart type action :

RETURN [{time: '2019-09', qty: 210}, {time: '2019-10', qty: 745}] AS values, 
'bar' as chartType, 
{x: {field: 'time', type: 'nominal'}, y: {field: 'qty', type: 'quantitative'}} AS options

The action will be the following :

Schema

And resulting as

Schema

# Input Parameters

Actions can define that the user should provide values for parameters that can be used in the Cypher query.

For example, in one of the previous actions we decided to find common wines having at least 2 flavours in common. But maybe the user would like to provide this value.

Let's modify the Cypher query for it:

MATCH p=(n:Wine)-[:FLAVOUR]->(f)<-[:FLAVOUR]-(otherWine)
WHERE id(n) = $id
WITH otherWine, collect(p) AS commonFlavours
WHERE size(commonFlavours) >= toInteger($minimum)
RETURN commonFlavours[..10]

Note that we did use a cast with toInteger, because input parameters do not support numbers yet.

And add an input parameter to the action:

Schema

Now, when the user will trigger the action, s/he will be prompted to input the value for the minimum flavours in common:

Schema

# Parameter Types

# STRING

String parameter type will render an input text field and accept a string value.

Schema

# DATE

Date parameter type will render a date picker. The value will be in the YYYY-mm-dd format ( eg: 2020-11-06 ).

Schema

# SELECT

Select parameter type will render a list of options the user can choose from, you will need to provide the list when configuring the action. The key of the selected element will be passed as Cypher query parameter.

Schema

# SELECT ASYNC

Select Async parameter type is useful when the amount of possible values for a selection is too large to be able to use with a Simple Select parameter type.

The most common use case is to have a globally scoped action where the user will be prompted to enter a value and options will be rendered while the user is typing.

Let's make such an action with our Wines graph assuming we would like the user to have a global action where s/he will be prompted to enter the name of a wine, and s/he can possibly make faults in the writing. We would then leverage the full text search index to return values s/he can choose from.

Schema

The Cypher query (Cypher Resolver) that will fetch the results for the input while the user is typing is the following:

CALL db.index.fulltext.queryNodes('Wine', $wine + '~')
YIELD node
RETURN id(node) AS id, node.name AS value

Once the user has made a selection and clicks on Submit, the id returned by the query will be passed as the Cypher parameter name to the action that is used to return the results themselves, in our case $wine:

MATCH (n) WHERE id(n) = $wine
MATCH p=(n)-[r]->(f)
RETURN p
Schema

# Optional Parameters handling

Before 2.8 , optional parameters that would not be filled by the user would not be sent at all as Cypher query parameter.

From 2.8 onwards, this behaviour has changed and now empty optional input parameters are sent with the null value as Cypher query parameter and can be handled with coalesce for eg :

MATCH (n:Node)
SET n.description = coalesce($description, 'no description entered')

# Cypher Parameters

By default, when you have selection parameters added in your action, there will be default Cypher parameters that are available in order to give you more flexibility when working with actions and Cypher.

# Selection Parameters

Selection parameters are available as the $_selection parameter in Cypher, it is a map that represents nodes and relationships selected, their class label and attribute values.

It can have two forms in case you have a single selection or a multiselection.

# Single Selection

The $_selection parameter will have the following shape :

For a node :

{
    "node": {
        "id": 123,
        "class": "Wine",
        "values": {
            "name": "Primitivo di Manduria (1997)",
            "description": "Simple and sweet, this Primitivo has jammy...",
            "price": 20.0,
            "points": 82
        }
    }
}

For a relationship :

{
    "relationship": {
        "id": 123,
        "type": "IS_OF",
        "values": {},
        "startClass": "Wine",
        "endClass": "Variety"
    }
}

If the selected element is a virtual node or virtual relationship, for example returned by APOC with the apoc.create.vNode() function, the node key would become vNode and its content would be the same as above.

An example action's Cypher query using the selection parameters would be :

MATCH (n:Wine)
WHERE id(n) = $_selection.node.id
....

From Hume 2.7, the start and end node ids of the relationship have been added to the selection :

{
    "relationship": {
        "id": 123,
        "type": "IS_OF",
        "values": {},
        "startClass": "Wine",
        "endClass": "Variety",
        "startNodeId": 2,
        "endNodeId": 3
    }
}

# Multiselection

For nodes :

{
    "nodes": [
        {
            "id": 123,
            "class": "Wine",
            "values": {
                "name": "Primitivo di Manduria (1997)",
                "description": "Simple and sweet, this Primitivo has jammy...",
                "price": 20.0,
                "points": 82
            }
        }
    ]
}

For relationships :

{
    "relationships": [
        {
            "id": 123,
            "type": "IS_OF",
            "values": {},
            "startClass": "Wine",
            "endClass": "Variety"
        }
    ]
}

As for single selection, in case virtual nodes or relationships would be present as part of the selection, they will be referenced via $vNodes and $vRelationships.

A mixed multiselection of nodes and relationships would be represented like this :

{
    "nodes": [
        {
            "id": 123,
            "class": "Wine",
            "values": {
                "name": "Primitivo di Manduria (1997)",
                "description": "Simple and sweet, this Primitivo has jammy...",
                "price": 20.0,
                "points": 82
            }
        },
        {
            "id": 456,
            "class": "Wine",
            "values": {
                "name": "Brutocao 2005 Contento Vineyard Primitivo (Mendocino)",
                "description": "Simple and sweet...",
                "price": 20.0,
                "points": 85
            }
        }
    ],
    "relationships" [
        {
            "id": 123,
            "type": "IS_OF",
            "values": {},
            "startClass": "Wine",
            "endClass": "Variety"
        },
        {
            "id": 654,
            "type": "IS_OF",
            "values": {},
            "startClass": "Wine",
            "endClass": "Variety"
        }
    ],
    "vNodes": [
        {
            "id": -2,
            "class": "Wine",
            "values": {
                "name": "Some virtual wine node",
                "description": "...",
                "price": 20.0,
                "points": 85
            }
        }
    ]
}

# Username parameter

The current username of the logged in user will be passed as Cypher query parameter under the _hume_principal reference.

For example, if your action would be Favorite this node and you would like to represent the current logged in user as a node in the graph, your action Cypher query can be the following :

MATCH (n:Wine)
WHERE id(n) = $id
MERGE (u:User {username: $_hume_principal})
MERGE (u)-[:FAVORITES]->(n)
RETURN n

if you would have Neo4j query logs enabled, one of our previous Table action would have been logged in the following manner :

2020-11-06 12:42:58.433+0000 INFO  Query started: id:218336 - 0 ms: bolt-session	bolt	neo4j-java/4.0.3-5762a3142513ab044cde323373d9c1440ae07b05		client/172.18.0.2:52036	server/172.18.0.20:7687>	wines - neo4j - MATCH (n:Wine)
WHERE id(n) = $id
RETURN n{.*} AS __kvRow__ - {_widgets: {}, id: 18335, _selection: {node: {class: 'Wine', id: 18335, values: {name: 'Brutocao 2005 Contento Vineyard Primitivo (Mendocino)', description: 'Simple and sweet, this Primitivo has jammy flavors of black raspberries, cherries and chocolate. It's a little heavy and soft.', price: 20.0, points: 82}}}, _hume_principal: 'christophe@graphaware.com'} - {hume: {principal: 'com.hume.api.rest.security.domain.ApplicationUser@8cb98fb'}}

# Managed nodes

Sometimes, you will want to be able to express that a map should be returned and be represented as a node on the canvas. A concrete situation is when you want to have an action that will compute some value that should be returned as an attribute on the node, but not written in the database as this value should be transient.

You will face in that case some limitations with the available tooling in Neo4j land :

  • Writing the value on the nodes will force you to delete it somehow afterwards, another side effect is that all write transactions will lead to the leader node in the cluster and you will not benefit from load balanced read queries against your full Neo4j cluster.
  • You cannot return a virtual node or relationship with APOC with a concrete neo4j internal id

Let's say for example a user would want to change the points score of a Wine node, so it would display in red in the graph visualisation based on some styling rules.

To do so, you can return the wine node from the database, but override its score value with some logic. But let's keep the logic to something fixed for our example :

MATCH (n:Node)
WHERE id(n) = $id
// some logic
RETURN n{.*, score: 192, __id: id(n), __labels: labels(n)} AS __node__

The convention for managed nodes is the following :

  • It must return a Map type
  • The result alias must be __node__
  • The map must contain the id of the node under the __id key
  • The map must contain the labels of the node under the __labels key

Note that you can actually return virtual nodes with a managed node, just use a negative id.

Since Hume 2.7, you can also return a collection of managed nodes, return simply an array of them aliased as __nodes__, eg :

UNWIND range(0, 10) AS i 
RETURN collect({__id: i, __labels:['Wine'], name: 'Wine ' + i}) AS __nodes__

# Managed Relationships

Similarly to the managed nodes, you can return managed relationships.

The convention for managed relationships is the following :

  • It must return a Map type
  • The result alias must be __rel__
  • The map must contain the id of the relationship under the __id key
  • The map must contain the type of the relationship under the __type key
  • The map must contain the start node id of the relationship under the __start key
  • The map must contain the end node id of the relationship under the __end key

Example :

MATCH (n)-[r]->(o)
WHERE id(n) = $id
RETURN n, o, 
r{.*, critical: false, __id: id(r), __type: type(r), 
  __start: id(startNode(r)), __end: id(endNode(r))} 
AS __rel__

Since Hume 2.7, you can also return a collection of managed relationships, return simply an array of them aliased as __rels__, eg :

MATCH (n1) WHERE id(n1) = 4529
MATCH (n2) WHERE id(n2) = 4527
MATCH paths=(n1)-[r*..3]-(n2)
RETURN nodes(paths) AS nodes, 
  [x IN relationships(paths) | 
      x{.*, flows: 1, __start: id(startNode(x)), __end: id(endNode(x)), 
      __type: type(x), __id: id(x)}
  ] AS __rels__