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.
Related
i want to write a gremlin script,like this:
g.V().out().as('a').[many code].as('b')
.where('a',eq('b').or(eq('a_constant_string'))).by('name')
but a_constant_string is a string constant ,not a vertex or edge
if writing like this,will thrown Exception:
org.apache.tinkerpop.gremlin.driver.exception.ResponseException: Neither the sideEffects, map, nor path has a a_constant_string-key
how to write
When you use where() that way you put the predicate eq() into a mode where it is dealing with labels only so you can't inject a constant in there. In this case, I'd think the easiest thing to do would be to move the "or" outside of the where():
g.V().
out().as('a').as('b').
or(where('a', eq('b')).by('name'),
has('name', 'a_constant_string'))
I am building a query based on passed parameters. For example, if pass p1='a' and p2='b', my query will look something like this:
g.V()
.has("p1","a")
.has("p2","b")
If let's say p2 is not passed, then I won't have a second check:
g.V()
.has("p1","a")
Is it possible to perform parameter check inside the query instead of creating conditional checks for parameters before creating query?
Edit: Use case is based RESTful web service where I have something like, /server/myEndpoint?p1=a and endpoint implementation would build gremlin query with .has() steps solely based on presence of p1 or p2, so if p2 is not passed, query would look like in second snippet, and if passed would look like the one in first.
One possible approach is to build GraphTraversal until it's not executed:
Map<String, String> map = new HashMap<>();
map.put("p1", "a");
map.put("p2", null);
final GraphTraversal<Vertex, Vertex> v = g.V();
map.forEach((k,v) -> {
if(v != null) {
v.has(k,v);
}
});
return v.toStream().collect(Collectors.toList());
As a suggested improvement where you are creating the search, the Gremlin-optimized way to approach this would be through a parameterized script. In theory this would mean that you are building the query in a way which is looking for each parameter.
However, I don't believe there is an inherent "if" statement, per-say, so if you wanted to use the query to optionally handle your "Has" cases, it could be done with an "Or Step", an "And Step", or a "Choose Step". I would discourage it as a pattern, though. It is similar to how MySQL has the options to handle cases in Select queries: it would almost always be more performant and a better separation of concerns to bind the parameters right in the first place than to have the query builder sort out the programmatic logic.
This query works:
choose(V().hasLabel("user"), V().hasLabel("user").drop())
But is searching 2 times for the vertex "user", I want to optimize it to 1 time, so I changed it for this one:
choose(V().hasLabel("user").fold(), unfold().drop())
If I do that trick multiple times in my query it throws:
Error: ResponseError: Server error: Vertex with id 70 was removed. (500)
I don't understand what that means, maybe fold() does not overwrite previous fold() calls, I don't know.
Is there an alternative to fold() unfold() for this use case? I've tried:
choose(V().hasLabel("user").as("u"), select("u").drop())
but that does not work, it seems .as() don't save anything when is called inside choose()
I also tried:
choose(V().hasLabel("user").store("u"), select("u").drop())
But throws another error I don't understand: The incoming object is not removable
Your first attempt with:
choose(V().hasLabel("user"), V().hasLabel("user").drop())
is not as expensive as you think. The if portion of choose() (i.e. the first child traversal) does not iterate all users. It immediately returns true if one "user" is found". Your then child traversal will iterate all "user" vertices. Also, you don't provide a false value to choose() so in that case, I believe that you will end up calling the drop() traversal for either situation:
gremlin> g.inject(1).choose(V().hasLabel('no-exist'),constant(1))
==>1
gremlin> g.inject(1).choose(V().hasLabel('no-exist'),constant(1),constant(2))
==>2
Of course, I'd wonder why you need to do an if/then here at all because calling V().hasLabel("user").drop() without choose() will remove all "user" vertices found, or if none are found, just do nothing.
For this traversal:
choose(V().hasLabel("user").fold(), unfold().drop())
note that V().hasLabel("user").fold() will always return "true" because you fold() which is a reducing step which will return a List of items in the stream. If the stream is empty you get an empty List and thus choose() will use the true path there. In any event, your unfold() is not unrolling what is returned from the first choose() parameter - it unfolds the same Traverser object handed to the choose() step. You don't have what comes before the choose() so I can't say what that is.
I'm not completely sure but based on your remaining traversal examples, I think you might be misusing choose() in general. You don't seem to have need for an if/then or switch style operation. With Gremlin you don't need to check if something is present in order to remove it and it is in fact cheaper not to do so as mentioned earlier.
g.V() is usually used to start a traversal. But since this a startup action, why its begin node is a Vertex?
g is the TraversalSource, by specifying V() you're saying that you are going to start at a set of elements that are of type Vertex, just like by specifying E() would specify starting at a set of elements that are of type Edge http://tinkerpop.apache.org/docs/current/reference/#the-graph-process and http://tinkerpop.apache.org/docs/current/reference/#traversal are pretty clear on how traversals are built up.
I am trying to find vertices except for some specific ones and this query below doesnt work:
g.V()
.has('someId')
.except([g.V().has('someId').has('some_other_filter')])
Is this usage of 'except' where I provide it a query within it not correct?
Thanks
You need to iterate your pipe:
g.V()
.has('someId')
.except(g.V().has('someId').has('some_other_filter').toList())
without dumping what's in your except to a list you are actually just passing a pipeline to except which won't ever evaluate against a vertex as true.