Purely functional graph traversal combinators written in Scala.
trails is applying the idea of parser combinators to graph traversals. The following combinators are supported:
t1 ~ t2 // Sequence
t1 | t2 // Choice
t.? // Optionality
t.* // Repetition 0..n
t.+ // Repetition 1..n
Find a more comprehensive description in our paper. Slides from the talk are available as well.
- Purely functional: No mutable state, no surprises.
- Lazy: Compute only as much information as required.
- Cycle aware: Avoid spinning in a circle.
- Labeling: Name path snippets.
- Supports different graph APIs (blueprints and neo4j are already included)
test("Example") {
val graph = new TinkerGraph()
val v0 = graph.addVertex("v0")
val v1 = graph.addVertex("v1")
val v2 = graph.addVertex("v2")
val e0 = graph.addEdge("e0", v0, v1, "e")
val e1 = graph.addEdge("e1", v0, v2, "f")
val t = V("v0") ~ (outE("e")|outE("f")) ~ inV
val res = Traverser.run(t, graph)
assert(res.size === 2)
val (List(`v0`, `e0`, `v1`), `v0` ~ `e0` ~ `v1`) = res.head
val (List(`v0`, `e1`, `v2`), `v0` ~ `e1` ~ `v2`) = res.tail.head
}
test("Cycles") {
val graph = new TinkerGraph()
val v0 = graph.addVertex("v0")
val e0 = graph.addEdge("e0", v0, v0, "e")
val f0 = graph.addEdge("f0", v0, v0, "f")
val f1 = graph.addEdge("f1", v0, v0, "f")
val expr0 = V("v0") ~ (out("e").+ ~ out("f")).+
val paths = Traverser.run(expr0, graph).map(_._1)
assert(paths.size === 4)
assert(paths.toSet === Set(
List(v0, e0, v0, f1, v0),
List(v0, e0, v0, f1, v0, e0, v0, f0, v0),
List(v0, e0, v0, f0, v0),
List(v0, e0, v0, f0, v0, e0, v0, f1, v0)
))
}
trails is licensed under the MIT License.
trails adapted many ideas (especially method names) from gremlin.