I'm migrating from the deprecated gremlin-javascript to the new Tinkerpop gremlin.
gremlin-javascript supported an execute method that would take an arbitrary string as a traversal. We could dynamically create and pass this string, such as chaining an arbitrary number of property traversals on a vertex.
Is there a way to dynamically build traversals in the gremlin js client?
For all language variants of Gremlin (Java, JS, Python, etc), you write Gremlin by constructing a Traversal object. You have a g which is a GraphTraversalSource which spawns those Traversal objects and thus:
var t = g.V().values('names');
does not yield a result (i.e. a list of "name" values) in t but a Traversal object. To get the result you need to iterate the traversal as for example:
t.toList().then(names => console.log(names));
So, if you have a Traversal object that is not yet iterated you can continue to add to it:
var t = g.V().values('names');
t = t.limit(1);
t.next().then(...)
Related
I use Gremlin API in Java.
Assume we have a traversal to persons and another traversal to locations that is quite long and dependent on the first:
GraphTraversal<?, Vertex> persons = g.V().has("prop", "value");
GraphTraversal<?, Vertex> locations = persons.out("place").has(..)..;
Now I want to link each person to the locations that correspond to that persons with a direct link, considering that some of these edges are already in place.
Which strategy would be good to do such links using Gremlin API in Java?
I couldn't find an easy way to link two streams of vertices with many to many relationship. But getting the set of objects and creating edge in loop as usually for one to many works for me:
Set<Object> personVertexIds = persons.id().toSet();
personVertexIds.forEach(id -> {
GraphTraversal<Vertex, Vertex> person = g.V(id).as("p");
GraphTraversal<?, Vertex> locations = persons.out("place").has(..)..;
locations.coalesce(inE("link").where(outV().where(P.eq("p"))),
addE("link").from("p")).property("prop", value);
});
I'm working on a gremlin query that navigates along several edges and eventually produces a String. Depending on the graph content, this traversal may be empty. In case that the traversal ends up being empty, I want to return a default value instead.
Here's what I am currently doing:
GraphTraversal<?, ?> traversal = g.traversal().V().
// ... fairly complex navigation here...
// eventually, we arrive at the target vertex and use its name
.values("name")
// as we don't know if the target vertex is present, lets add a default
.union(
identity(), // if we found something we want to keep it
constant("") // empty string is our default
)
// to make sure that we do not use the default if we have a value...
.order().by(s -> ((String)s).length(), Order.decr)
.limit(1)
This query works, but it is fairly convoluted - all I want is a default if the traversal ends up not finding anything.
Does anybody have a better proposal? My only restriction is that it has to be done within gremlin itself, i.e. the result must be of type GraphTraversal.
You can probably use coalesce() in some way:
gremlin> g.V().has('person','name','marko').coalesce(has('person','age',29),constant('nope'))
==>v[1]
gremlin> g.V().has('person','name','marko').coalesce(has('person','age',231),constant('nope'))
==>nope
If you have more complex logic in mind for determining if something is found or not then consider choose() step.
I am trying to build complex traversals using Java client to a remote JanusGraph server.
The following traversal returns the ReferenceVertex elements identified by the label "mylabel":
GraphTraversal<Vertex, Vertex> t = g.V().hasLabel("mylabel");
List<Vertex> r = t.toList();
For more complex queries I need to concatenate multiple traversals that form a part of the whole queries. The following code illustrates the concept:
GraphTraversal<Vertex, Vertex> addMe = __.hasLabel("mylabel");
GraphTraversal<Vertex, Vertex> t = g.V();
for (Step<?, ?> step : addMe.asAdmin().getSteps()) {
t.asAdmin().addStep(step);
}
List<Vertex> r = t.toList();
For local access this works. For remote access however, it returns all vertices available on the server, not the ones identified by the label.
In both cases, t.toString() returns
[GraphStep(vertex,[]), HasStep([~label.eq(mylabel)])]
What am I doing wrong?
I don't think you need to get into any of the asAdmin() methods. Rather than build anonymous traversals to try to append to a parent traversal, I think it would be better to just pass around the parent GraphTraversal instance and simply add steps to that as needed:
private static GraphTraversal addFilters(GraphTraversal t) {
return t.hasLabel("mylabel");
}
...
GraphTraversal<Vertex, Vertex> t = g.V();
t = addFilters(t);
List<Vertex> r = t.toList();
There is a reason why your approach doesn't work with remote traversals and it has to do with how Gremlin bytecode is constructed behind the scenes. Using the asAdmin() methods bypasses some inner workings and those portions of the traversal are not sent to the server - that's a simple way of explaining it anyway. If you absolutely must construct anonymous portions of a traversal that way and then append them in that exact fashion then I guess I would do:
GraphTraversal<Vertex, Vertex> addMe = __.hasLabel("mylabel");
GraphTraversal<Vertex, Vertex> t = g.V();
List<Vertex> r = t.filter(addMe).toList();
I don't particularly like that approach because depending on what you're doing you could trick out JanusGraph traversal strategies that optimize your traversals and you'll lose some performance optimizations. I also don't like the style as much - it just seems more natural to pass around the GraphTraversal to the functions that need to mutate it with new steps. You might also find this information about traversal re-use helpful.
I am playing with TinkerGraph and gremlin-scala and I see that it is capable of persisting complex objects:
case class InnerObj(a: Int, b: String)
case class ComplexObj(a: Int, b: InnerObj)
case class SuperComplexObj(a : String, b: ComplexObj)
class GremlinQueriesSpec extends FlatSpec
with ScalaFutures with MustMatchers {
behavior of "Gremlin queries"
it must "be able to persist complex objects containing collections" taggedAs Integration in {
val g = TinkerGraph.open()
implicit val graph = g.asScala
val user = RandomData.randomUserDataAggregate
graph + user
graph.V().toCC[UserDataAggregate].toList() must be eq List(user)
}
}
However, docs are not completely clear to me. On еру one hand there's not much structure available for property values besides lists, sets, and metaproperties. On the other hand docs say:
A Property denotes a key/value pair associated with an Edge. A
property is much like a Java8 Optional in that a property can be not
present (i.e. empty). The key of a property is always a String and the
value of a property is an arbitrary Java object. Each underlying graph
engine will typically have constraints on what Java objects are
allowed to be used as values.
Ok, it looks like it depends on the implementation. But is possible to work with nested objects in Gremlin queries?
It is indeed dependent on the implementation. You are using TinkerGraph which can in turn store any Java object so you're free to put whatever you like in there:
gremlin> g.addV().property('function',{it.length()})
==>v[2]
gremlin> g.V().sideEffect{println(it.get().value('function')("four"))}
4
==>v[2]
crazy right? Of course, you will need to consider issues like serialization and such if you start sticking random odds/ends in there and need to persist those objects or push them over the wire (like through Gremlin Server).
There is no problem with a nested object as a value for a TinkerGraph property. Just be careful. Really stop to think about your schema before going to deep down the path of storing complex Java objects as properties. Perhaps it would be better to just model those objects as elements of the graph as first class citizens to enable Gremlin traversals to work over them directly.
Couldn't figure from the documentation and the source code what do they represent?
http://tinkerpop.apache.org/javadocs/3.2.5/full/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/DefaultGraphTraversal.html
In Apache TinkerPop/Gremlin a Traversal is analogous to a query and a query can return more than just vertices.
<S,E> are type variables that form the Java generic definition for the DefaultGraphTraversal class. They aren't specific classes, but represent the "start" (for S) and "end" (for E) types that will go into and come out of the traversal respectively. In a sense, those types become defined when you form the traversal you want to execute. g.V().count() returns a GraphTraversal<Vertex,Long> where the S is defined as Vertex and the E is defined as a Long - the start to the traversal is a Vertex and the end to the traversal is a Long.