blob: 2b65644c4702adeaf5e435b5eef11483f4af2b85 [file] [log] [blame]
////
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
////
[[traversal-component-reuse]]
Traversal Component Reuse
-------------------------
Good software development practices require reuse to keep software maintainable. In Gremlin, there are often bits of
traversal logic that could be represented as components that might be tested independently and utilized
as part of other traversals. One approach to doing this would be to extract such logic into an anonymous traversal
and provide it to a parent traversal through `flatMap()` step.
Using the modern toy graph as an example, assume that there are number of traversals that are interested in filtering
on edges where the "weight" property is greater than "0.5". A query like that might look like this:
[gremlin-groovy,modern]
----
g.V(1).outE("knows").has('weight', P.gt(0.5d)).inV().both()
----
Repeatedly requiring that filter on "weight" could lead to a lot of duplicate code, which becomes difficult to
maintain. It would be nice to extract that logic so as to centralize it for reuse in all places where needed. An
anonymous traversal allows that to happen and can be created as follows.
[gremlin-groovy,modern]
----
weightFilter = outE("knows").has('weight', P.gt(0.5d)).inV();[]
g.V(1).flatMap(weightFilter).both()
----
The `weightFilter` is an anonymous traversal and it is created by way `__` class. The `__` is omitted above from
initalization of `weightFilter` because it is statically imported to the Gremlin Console. The `weightFilter` gets
passed to the "full" traversal by way for `flatMap()` step and the results are the same. Of course, there is a problem.
If there is an attempt to use that `weightFilter` a second time, the traversal with thrown an exception because both
the `weightFilter` and parent traversal have been "compiled" which prevents their re-use. A simple fix to this would
be to clone the `weightFilter`.
[gremlin-groovy,modern]
----
weightFilter = outE("knows").has('weight', P.gt(0.5d)).inV();[]
g.V(1).flatMap(weightFilter.clone()).both()
g.V(1).flatMap(weightFilter.clone()).bothE().otherV()
g.V(1).flatMap(weightFilter.clone()).groupCount()
----
Now the `weightFilter` can be reused over and over again. Remembering to `clone()` might lead to yet another maintenance
issue in that failing to recall that step would likely result in a bug. One option might be to wrap the `weightFilter`
creation in a function that returns the clone. Another approach might be to parameterize that function to construct
a new anonymous traversal each time with the idea being that this might gain even more flexibility in parameterizing
the anonymous traversal itself.
[gremlin-groovy,modern]
----
weightFilter = { w -> outE("knows").has('weight', P.gt(w)).inV() }
g.V(1).flatMap(weightFilter(0.5d)).both()
g.V(1).flatMap(weightFilter(0.5d)).bothE().otherV()
g.V(1).flatMap(weightFilter(0.5d)).groupCount()
----