The Tables view allows for a familiar tabular look at the contents of a workspace, and also lets you select some relational columns like "path", "parent", and incoming/outgoing references. However, things get complicated if you want a gremlin query to produce a similar result, either to serve as a starting point or in order to export data into an excel-file.
By defining some reusable functions, you can easily create a query that lets you emulate a table view, and then expand upon it to create more complex tabular outputs:
Note: There are some limitations with this approach to be aware of:
Workspace names cannot be seen in the graph, so the “path”-column will not be an exact replica.
If you add more than nine columns, the column order becomes unpredictable due to data optimizations in Gremlin.
Helper Functions
Reference-columns
Perhaps the most interesting helper function to add is the “relational” columns that show referenced components:
def outgoing = { out(it).values('name').fold().map{it.get().join(', ')}}def incoming = { __.in(it).values('name').fold().map{it.get().join(', ')} }
This creates a comma-separated list of referenced components (by name). Note that the incoming-function starts with “__.in”. This is because “in” is a reserved keyword in Groovy, and would cause an error if you tried to invoke it in a “sub-traversal”.
Parent column
You can also find the parent by reusing the “outgoing”-function, or create a separate function for readability:
def parent = { coalesce(out('ardoq_parent').values('name'), constant('')) }
Field columns
Next, add a function that safely returns a field value even if the field is missing:
def v = { __.coalesce(values(it), constant(''))}// For numbers, so they get a default of 0 instead:def n = { __.coalesce(values(it), constant(''))}
This works for most fields, but there are some exceptions.
There is the date-range field which is two data-entries grouped together into one column:
def dateRange = { dateStart = it + '_start_date'; dateEnd = it + '_end_date'; return { it.get().property(dateStart).orElse('') + ' - ' + it.get().property(dateEnd).orElse('') }}
There is the SelectMultipleList-field, which in Gremlin is encoded as a Stringified list that should be broken apart. Here’s a regex-method:
def multiSelect = { fieldName = it return { (it.property(fieldName).orElse('') =~ /"(.*?)"/).findAll().collect{it[1]}.join(', ') }}
Path column
There is the “path”, which is a list of the parent components separated by an arrow. (NB: Workspace name is not available in the graph)
def path = { emit().repeat(out('ardoq_parent')). values('name').fold(). map{it.get().reverse().join(' > ')}}
Tags
You can also include tags. Since a component can have multiple tags, you'll need to make a slight adjustment to the “v”-function:
def tags = { values('tags').fold().map{it.get().join(', ')}}
Putting It All Together
With helper functions defined, you can now put it all together to create a tabular output. Here’s an extreme case that includes all the functions in our toolkit:
def outgoing = {
out(it).values('name').fold().map{it.get().join(', ')}
}
def incoming = {
__.in(it).values('name').fold().map{it.get().join(', ')}
}
def dateRange = {
dateStart = it + '_start_date';
dateEnd = it + '_end_date';
return {
it.property(dateStart).orElse('') + ' - ' + it.property(dateEnd).orElse('')
}
}
def multiSelect = {
fieldName = it
return {
it.property(fieldName).orElse('')//.substring(2)
}
}
def v = {
__.coalesce(values(it), constant(''))
}
def parent = {
coalesce(out('ardoq_parent').values('name'), constant(''))
}
def path = {
emit().repeat(out('ardoq_parent')).
values('name').fold().map{it.get().reverse().join(' > ')}
}
def tags = {
values('tags').fold().map{it.get().join(', ')}
}
g.V().hasLabel('Application').
project('Ardoq Id', 'name', '-> Is integrated with', '<- Is Realized By', '<- Owns', 'Live', 'Active?', 'Use case', 'Missing value', 'Parent', 'Path', 'Tags', 'Component type').
by('component-key').
by('name').
by(outgoing('Is Integrated With')).
by(incoming('Is Realized By')).
by(incoming('Owns')).
by(dateRange('live')).
by(v('active')).
by(multiSelect('use_case')).
by(v('missing')).
by(parent()).
by(path()).
by(tags()).
by(label)
What About The Reference Table View?
Replicating a reference table view is also pretty straight forward, but does require some modifications.
First, instead of selecting components (vertices), we instead select references (edges):
g.E()...
Then introduce two new helper functions for selecting the incoming and outgoing component:
def source = { outV().values('name')}def target = { inV().values('name')}
Also, tags are stores slightly different on references due to a gremlin limitation ("multi-properties" are not available for edges), so you can rewrite the tags-helper:
def tags = { coalesce(values('tags'), constant([])).map{it.get().join(', ')}}
These changes now show a "reference table"-style output: