1.1.1: OverflowDb Traversals #
This release introduces a major rearchitecture of the cpg query language (CPGQL). Most changes happened under the hood, however there are a few changes that are user facing. The migration should be straightforward, and in any case we’re here to help.
Background: CPGQL was previously based on the Gremlin graph traversal language (accessible via
.raw), and is now based on OverflowDb Traversal. The main drivers behind this change are: better performance, less complexity and fewer dependencies. OverflowDb Traversal is a Scala collection with additional graph steps, which means we now inherit many useful steps from the rich Scala standard collection library, where previously convertions between Traversal and Scala collections were needed.
At the same time, Traversal offers largely the same steps and semantics as its TinkerPop predecessor.
Here are the most important new parts - from our experience the swap of
where accounts for 90% of user-facing changes:
filter(A => Boolean): just like any other Scala collection, filter now simply takes a predicate (it used to take a Traversal).
where(trav: Traversal[A] => Traversal[_]): preserves elements if the provided traversal has at least one result. This is what filter used to be. Effectively,
filterOnEnd- this is now simply
repeatnow has a builder DSL to configure it’s behaviour, which is more specific and easier to understand than tinkerpop’s api, which relied on the order of modulators in the traversal. For example, in tinkerpop
.emit.repeat(Traversal)means “emit everything”, while
.repeat(Traversal).emitmeans “emit all but the first element”. In OverflowDb Traversal this behaviour is more explicit:
Some more examples for the migration:
repeatuses depth first search (DFS) by default and can be configured to use breadth first search (BFS) instead. Tinkerpop has a long standing issue that it claims to use DFS but actually uses BFS, and also cannot be configured to use one or the other.
.startstep only exists for
NewNode, not for other collections any more. Instead, use the standard scala collection mechanism
// still works - nothing changed: val someMethod = cpg.method.head someMethod.start.parameter //Traversal[MethodParameterIn] // using `.to(Traversal)` - standard scala collection mechanism val methodList: cpg.method.l methodList.to(Traversal) //Traversal[Method]
.clonedoesn’t exist any more - note that Traversals typically have Iterator semantics, i.e. they are consumed during iteration:
val source = cpg.identifier.name("foo") val sink = cpg.call.name("bar") sink.reachableByFlows(source).l //contains flows (if any) sink.reachableByFlows(source).l //always empty - both source and sink Traversals have been consumed!
If you want to execute your traversals multiple times, simply define them as a function (
def) rather than a value (
def source = cpg.identifier.name("foo") def sink = cpg.call.name("bar") sink.reachableByFlows(source).l //contains flows (if any) sink.reachableByFlows(source).l //same result: also contains flows (if any)
Please let us know if you need help with your migration, either by opening an issue or asking on gitter.
Bleeding edge / power users only: #
- If you used the underlying tinkerpop api and OverflowDb types (e.g. via
.raw), your scripts may be subject to additional changes, due to the removal of the Tinkerpop dependency and renames in OverflowDb types:
- all Tinkerpop types are gone: