You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2016/11/02 15:11:01 UTC
[1/4] tinkerpop git commit: added choose(predicate,
traversal) which is a map to choose(predicate, traversal,
identity()) and gives us if/then-semantics instead of always requiring
if/then/else-semantics. [Forced Update!]
Repository: tinkerpop
Updated Branches:
refs/heads/TINKERPOP-1490 3c541b2d6 -> 03e37e81f (forced update)
added choose(predicate,traversal) which is a map to choose(predicate,traversal,identity()) and gives us if/then-semantics instead of always requiring if/then/else-semantics.
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/659865bd
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/659865bd
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/659865bd
Branch: refs/heads/TINKERPOP-1490
Commit: 659865bdb1a5e2db9e01847eb641101cab3d090a
Parents: 1eac35b
Author: Marko A. Rodriguez <ok...@gmail.com>
Authored: Mon Oct 31 13:23:42 2016 -0600
Committer: Marko A. Rodriguez <ok...@gmail.com>
Committed: Mon Oct 31 13:23:42 2016 -0600
----------------------------------------------------------------------
CHANGELOG.asciidoc | 1 +
docs/src/reference/the-traversal.asciidoc | 15 +++++++++++++--
docs/src/upgrade/release-3.2.x-incubating.asciidoc | 17 +++++++++++++++++
.../traversal/dsl/graph/GraphTraversal.java | 12 ++++++++++++
.../gremlin/process/traversal/dsl/graph/__.java | 14 ++++++++++++++
.../process/traversal/step/branch/BranchStep.java | 11 +++--------
.../traversal/step/branch/ChooseStepTest.java | 11 +++++++++--
.../traversal/step/branch/GroovyChooseTest.groovy | 5 +++++
.../process/traversal/step/branch/ChooseTest.java | 16 ++++++++++++++++
9 files changed, 90 insertions(+), 12 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/659865bd/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index cf8c007..328bc9d 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -26,6 +26,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
TinkerPop 3.2.4 (Release Date: NOT OFFICIALLY RELEASED YET)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* Added `choose(predicate,traversal)` and `choose(traversal,traversal)` to effect if/then-semantics (no else). Equivalent to `choose(x,y,identity())`.
* `SparkGraphComputer` no longer starts a worker iteration if the worker's partition is empty.
* Added `ProjectStep.getProjectKeys()` for strategies that rely on such information.
* Added `VertexFeatures.supportsDuplicateMultiProperties()` for graphs that only support unique values in multi-properties.
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/659865bd/docs/src/reference/the-traversal.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc
index c5c30a3..bbffb0e 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -449,7 +449,7 @@ Choose Step
image::choose-step.png[width=700]
The `choose()`-step (*branch*) routes the current traverser to a particular traversal branch option. With `choose()`,
-it is possible to implement if/else-based semantics as well as more complicated selections.
+it is possible to implement if/then/else-semantics as well as more complicated selections.
[gremlin-groovy,modern]
----
@@ -466,7 +466,18 @@ g.V().hasLabel('person').
<1> If the traversal yields an element, then do `in`, else do `out` (i.e. true/false-based option selection).
<2> Use the result of the traversal as a key to the map of traversal options (i.e. value-based option selection).
-However, note that `choose()` can have an arbitrary number of options and moreover, can take an anonymous traversal as its choice function.
+If the "false"-branch is not provided, then if/then-semantics are implemented.
+
+[gremlin-groovy,modern]
+----
+g.V().choose(hasLabel('person'), out('created')).values('name') <1>
+g.V().choose(hasLabel('person'), out('created'), identity()).values('name') <2>
+----
+
+<1> If the vertex is a person, emit the vertices they created, else emit the vertex.
+<2> If/then/else with an `identity()` on the false-branch is equivalent to if/then with no false-branch.
+
+Note that `choose()` can have an arbitrary number of options and moreover, can take an anonymous traversal as its choice function.
[gremlin-groovy,modern]
----
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/659865bd/docs/src/upgrade/release-3.2.x-incubating.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/upgrade/release-3.2.x-incubating.asciidoc b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
index 30daae1..f121aa4 100644
--- a/docs/src/upgrade/release-3.2.x-incubating.asciidoc
+++ b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
@@ -29,6 +29,23 @@ TinkerPop 3.2.4
Please see the link:https://github.com/apache/tinkerpop/blob/3.2.4/CHANGELOG.asciidoc#release-3-2-4[changelog] for a complete list of all the modifications that are part of this release.
+Upgrading for Users
+~~~~~~~~~~~~~~~~~~~
+
+If/Then-Semantics with Choose Step
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Gremlin's `choose()`-step supports if/then/else-semantics. Thus, to effect if/then-semantics, `identity()` was required.
+Thus, the following two traversals below are equivalent with the later being possible in this release.
+
+[source,groovy]
+----
+g.V().choose(hasLabel('person'),out('created'),identity())
+g.V().choose(hasLabel('person'),out('created'))
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1508[TINKERPOP-1508]
+
Upgrading for Providers
~~~~~~~~~~~~~~~~~~~~~~~
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/659865bd/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
index c8af8ca..088a4de 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/GraphTraversal.java
@@ -1293,6 +1293,12 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
return this.asAdmin().addStep(new ChooseStep<E, E2, Boolean>(this.asAdmin(), (Traversal.Admin<E, ?>) traversalPredicate, (Traversal.Admin<E, E2>) trueChoice, (Traversal.Admin<E, E2>) falseChoice));
}
+ public default <E2> GraphTraversal<S, E2> choose(final Traversal<?, ?> traversalPredicate,
+ final Traversal<?, E2> trueChoice) {
+ this.asAdmin().getBytecode().addStep(Symbols.choose, traversalPredicate, trueChoice);
+ return this.asAdmin().addStep(new ChooseStep<E, E2, Boolean>(this.asAdmin(), (Traversal.Admin<E, ?>) traversalPredicate, (Traversal.Admin<E, E2>) trueChoice, (Traversal.Admin<E, E2>) __.identity()));
+ }
+
public default <M, E2> GraphTraversal<S, E2> choose(final Function<E, M> choiceFunction) {
this.asAdmin().getBytecode().addStep(Symbols.choose, choiceFunction);
return this.asAdmin().addStep(new ChooseStep<>(this.asAdmin(), (Traversal.Admin<E, M>) __.map(new FunctionTraverser<>(choiceFunction))));
@@ -1304,6 +1310,12 @@ public interface GraphTraversal<S, E> extends Traversal<S, E> {
return this.asAdmin().addStep(new ChooseStep<E, E2, Boolean>(this.asAdmin(), (Traversal.Admin<E, ?>) __.filter(new PredicateTraverser<>(choosePredicate)), (Traversal.Admin<E, E2>) trueChoice, (Traversal.Admin<E, E2>) falseChoice));
}
+ public default <E2> GraphTraversal<S, E2> choose(final Predicate<E> choosePredicate,
+ final Traversal<?, E2> trueChoice) {
+ this.asAdmin().getBytecode().addStep(Symbols.choose, choosePredicate, trueChoice);
+ return this.asAdmin().addStep(new ChooseStep<E, E2, Boolean>(this.asAdmin(), (Traversal.Admin<E, ?>) __.filter(new PredicateTraverser<>(choosePredicate)), (Traversal.Admin<E, E2>) trueChoice, (Traversal.Admin<E, E2>) __.identity()));
+ }
+
public default <E2> GraphTraversal<S, E2> optional(final Traversal<?, E2> optionalTraversal) {
this.asAdmin().getBytecode().addStep(Symbols.optional, optionalTraversal);
return this.asAdmin().addStep(new ChooseStep<>(this.asAdmin(), (Traversal.Admin<E, ?>) optionalTraversal, (Traversal.Admin<E, E2>) optionalTraversal.asAdmin().clone(), (Traversal.Admin<E, E2>) __.<E2>identity()));
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/659865bd/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
index 1349633..83739d8 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/dsl/graph/__.java
@@ -968,6 +968,13 @@ public class __ {
}
/**
+ * @see GraphTraversal#choose(Predicate, Traversal)
+ */
+ public static <A, B> GraphTraversal<A, B> choose(final Predicate<A> choosePredicate, final Traversal<?, B> trueChoice) {
+ return __.<A>start().choose(choosePredicate, trueChoice);
+ }
+
+ /**
* @see GraphTraversal#choose(Function)
*/
public static <A, M, B> GraphTraversal<A, B> choose(final Function<A, M> choiceFunction) {
@@ -989,6 +996,13 @@ public class __ {
}
/**
+ * @see GraphTraversal#choose(Traversal, Traversal)
+ */
+ public static <A, M, B> GraphTraversal<A, B> choose(final Traversal<?, M> traversalPredicate, final Traversal<?, B> trueChoice) {
+ return __.<A>start().choose(traversalPredicate, trueChoice);
+ }
+
+ /**
* @see GraphTraversal#optional(Traversal)
*/
public static <A> GraphTraversal<A, A> optional(final Traversal<?, A> optionalTraversal) {
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/659865bd/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStep.java
index 19df2a6..778722d 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/BranchStep.java
@@ -113,17 +113,12 @@ public class BranchStep<S, E, M> extends ComputerAwareStep<S, E> implements Trav
private final void handleStart(final Traverser.Admin<S> start) {
final M choice = TraversalUtil.apply(start, this.branchTraversal);
final List<Traversal.Admin<S, E>> branch = this.traversalOptions.containsKey(choice) ? this.traversalOptions.get(choice) : this.traversalOptions.get(Pick.none);
- if (null != branch) {
- branch.forEach(traversal -> {
- traversal.addStart(start.split());
- });
- }
+ if (null != branch)
+ branch.forEach(traversal -> traversal.addStart(start.split()));
if (choice != Pick.any) {
final List<Traversal.Admin<S, E>> anyBranch = this.traversalOptions.get(Pick.any);
if (null != anyBranch)
- anyBranch.forEach(traversal -> {
- traversal.addStart(start.split());
- });
+ anyBranch.forEach(traversal -> traversal.addStart(start.split()));
}
}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/659865bd/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStepTest.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStepTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStepTest.java
index ad2c8a8..885da77 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStepTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseStepTest.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step.branch;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest;
@@ -25,11 +26,14 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.StepTest;
import java.util.Arrays;
import java.util.List;
-import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.*;
+import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.in;
+import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.out;
+import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.values;
import static org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent.Pick.none;
/**
* @author Daniel Kuppitz (http://gremlin.guru)
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
*/
public class ChooseStepTest extends StepTest {
@@ -38,7 +42,10 @@ public class ChooseStepTest extends StepTest {
return Arrays.asList(
__.choose(values("name")).option("marko", out()).option(none, in()),
__.choose(values("name")).option("marko", in()).option(none, out()),
- __.choose(values("name")).option("josh", out()).option(none, in())
+ __.choose(values("name")).option("josh", out()).option(none, in()),
+ __.choose(out("knows").is(P.gt(0)), out("knows"), out("knows")),
+ __.choose(out("knows").is(P.gt(0)), out("knows"), out("created")),
+ __.choose(out("knows").is(P.gt(0)), out("knows"))
);
}
}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/659865bd/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/branch/GroovyChooseTest.groovy
----------------------------------------------------------------------
diff --git a/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/branch/GroovyChooseTest.groovy b/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/branch/GroovyChooseTest.groovy
index 99bb31b..6c6a4ac 100644
--- a/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/branch/GroovyChooseTest.groovy
+++ b/gremlin-groovy-test/src/main/groovy/org/apache/tinkerpop/gremlin/process/traversal/step/branch/GroovyChooseTest.groovy
@@ -53,5 +53,10 @@ public abstract class GroovyChooseTest {
.option(none, identity()).name
""")
}
+
+ @Override
+ public Traversal<Vertex, String> get_g_V_chooseXoutXknowsX_count_isXgtX0XX__outXknowsXX_name() {
+ new ScriptTraversal<>(g, "gremlin-groovy", "g.V.choose(out('knows').count.is(gt(0)), out('knows')).name")
+ }
}
}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/659865bd/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseTest.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseTest.java
index ce12daf..8938bec 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/branch/ChooseTest.java
@@ -21,6 +21,7 @@ package org.apache.tinkerpop.gremlin.process.traversal.step.branch;
import org.apache.tinkerpop.gremlin.LoadGraphWith;
import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest;
import org.apache.tinkerpop.gremlin.process.GremlinProcessRunner;
+import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.MapHelper;
@@ -58,6 +59,8 @@ public abstract class ChooseTest extends AbstractGremlinProcessTest {
public abstract Traversal<Vertex, String> get_g_V_chooseXlabelX_optionXblah__outXknowsXX_optionXbleep__outXcreatedXX_optionXnone__identityX_name();
+ public abstract Traversal<Vertex, String> get_g_V_chooseXoutXknowsX_count_isXgtX0XX__outXknowsXX_name();
+
@Test
@LoadGraphWith(MODERN)
public void g_V_chooseXout_countX_optionX2L__nameX_optionX3L__valueMapX() {
@@ -100,6 +103,14 @@ public abstract class ChooseTest extends AbstractGremlinProcessTest {
checkResults(Arrays.asList("marko", "vadas", "peter", "josh", "lop", "ripple"), traversal);
}
+ @Test
+ @LoadGraphWith(MODERN)
+ public void g_V_chooseXoutXknowsX_count_isXgtX0XX__outXknowsXX_name() {
+ final Traversal<Vertex, String> traversal = get_g_V_chooseXoutXknowsX_count_isXgtX0XX__outXknowsXX_name();
+ printTraversalForm(traversal);
+ checkResults(Arrays.asList("vadas", "josh", "vadas", "josh", "peter", "lop", "ripple"), traversal);
+ }
+
public static class Traversals extends ChooseTest {
@Override
@@ -126,5 +137,10 @@ public abstract class ChooseTest extends AbstractGremlinProcessTest {
.option("bleep", out("created"))
.option(TraversalOptionParent.Pick.none, identity()).values("name");
}
+
+ @Override
+ public Traversal<Vertex, String> get_g_V_chooseXoutXknowsX_count_isXgtX0XX__outXknowsXX_name() {
+ return g.V().choose(out("knows").count().is(P.gt(0)), out("knows")).values("name");
+ }
}
}
\ No newline at end of file
[3/4] tinkerpop git commit: fixed the filter latex in step-types.png.
CTR.
Posted by sp...@apache.org.
fixed the filter latex in step-types.png. CTR.
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/d7fb966b
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/d7fb966b
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/d7fb966b
Branch: refs/heads/TINKERPOP-1490
Commit: d7fb966b48aefa498b71b900db4a3367ef4d52f0
Parents: f55cb35
Author: Marko A. Rodriguez <ok...@gmail.com>
Authored: Wed Nov 2 08:08:47 2016 -0600
Committer: Marko A. Rodriguez <ok...@gmail.com>
Committed: Wed Nov 2 08:08:47 2016 -0600
----------------------------------------------------------------------
docs/static/images/step-types.png | Bin 129207 -> 63509 bytes
docs/static/images/tinkerpop3.graffle | 667 +++++++++++++++++++++--------
2 files changed, 487 insertions(+), 180 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/d7fb966b/docs/static/images/step-types.png
----------------------------------------------------------------------
diff --git a/docs/static/images/step-types.png b/docs/static/images/step-types.png
index c65fe19..03bf8ee 100644
Binary files a/docs/static/images/step-types.png and b/docs/static/images/step-types.png differ
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/d7fb966b/docs/static/images/tinkerpop3.graffle
----------------------------------------------------------------------
diff --git a/docs/static/images/tinkerpop3.graffle b/docs/static/images/tinkerpop3.graffle
index 226ddb5..ac38a3c 100644
--- a/docs/static/images/tinkerpop3.graffle
+++ b/docs/static/images/tinkerpop3.graffle
@@ -20,11 +20,467 @@
<key>GuidesVisible</key>
<string>YES</string>
<key>ImageCounter</key>
- <integer>134</integer>
+ <integer>135</integer>
<key>Images</key>
<array>
<dict>
<key>Extension</key>
+ <string>pdf</string>
+ <key>ID</key>
+ <integer>134</integer>
+ <key>RawData</key>
+ <data>
+ JVBERi0xLjQKJdDUxdgKNCAwIG9iaiA8PAovTGVuZ3RoIDM4ICAg
+ ICAgICAKL0ZpbHRlciAvRmxhdGVEZWNvZGUKPj4Kc3RyZWFtCnja
+ K+QyVDAAQkMFXSMLIGFmYqyQnMul75lrqOCSzxXIBQBs2wa4CmVu
+ ZHN0cmVhbQplbmRvYmoKMyAwIG9iaiA8PAovVHlwZSAvUGFnZQov
+ Q29udGVudHMgNCAwIFIKL1Jlc291cmNlcyAyIDAgUgovTWVkaWFC
+ b3ggWzAgMCA1MCAxMF0KL1BhcmVudCA1IDAgUgo+PiBlbmRvYmoK
+ MSAwIG9iaiA8PAovVHlwZSAvWE9iamVjdAovU3VidHlwZSAvRm9y
+ bQovRm9ybVR5cGUgMQovUFRFWC5GaWxlTmFtZSAoLi93ZHY1cTMt
+ Mi5wZGYpCi9QVEVYLlBhZ2VOdW1iZXIgMQovUFRFWC5JbmZvRGlj
+ dCA2IDAgUgovQkJveCBbMCAwIDYxMiA3OTJdCi9SZXNvdXJjZXMg
+ PDwKL0ZvbnQgPDwgL0YxMSA3IDAgUi9GMTQgOCAwIFI+PgovUHJv
+ Y1NldCBbIC9QREYgL1RleHQgXQo+PgovTGVuZ3RoIDExOAovRmls
+ dGVyIC9GbGF0ZURlY29kZQo+PgpzdHJlYW0KeNptzzEOwjAMheE9
+ p3jdYHFtk5hkRSpIrHij3ar2/jcgqmhVQScvn349M2YwHoF/7s1D
+ exdBoWJq8AmamVI2WEykGT7ifXqdB39WF3euUExSGwtovmAfEqXr
+ ZQXdQUGYrNgqelY9qtQ1sqFlyN8TnYcP5HMwCwplbmRzdHJlYW0K
+ ZW5kb2JqCjYgMCBvYmoKPDwKL1Byb2R1Y2VyIChwZGZUZVgtMS40
+ MC4xNCkKL0NyZWF0b3IgKFRlWCkKL0NyZWF0aW9uRGF0ZSAoRDoy
+ MDE2MTEwMjE1MDExMCswMScwMCcpCi9Nb2REYXRlIChEOjIwMTYx
+ MTAyMTUwMTEwKzAxJzAwJykKL1RyYXBwZWQgL0ZhbHNlCi9QVEVY
+ LkZ1bGxiYW5uZXIgKFRoaXMgaXMgcGRmVGVYLCBWZXJzaW9uIDMu
+ MTQxNTkyNi0yLjUtMS40MC4xNCBcKFRlWCBMaXZlIDIwMTMvRGVi
+ aWFuXCkga3BhdGhzZWEgdmVyc2lvbiA2LjEuMSkKPj4KZW5kb2Jq
+ CjcgMCBvYmoKPDwKL1R5cGUgL0ZvbnQKL1N1YnR5cGUgL1R5cGUx
+ Ci9CYXNlRm9udCAvTE9CR01YK0NNTUkxMAovRm9udERlc2NyaXB0
+ b3IgOSAwIFIKL0ZpcnN0Q2hhciA2OQovTGFzdENoYXIgODMKL1dp
+ ZHRocyAxMCAwIFIKPj4KZW5kb2JqCjggMCBvYmoKPDwKL1R5cGUg
+ L0ZvbnQKL1N1YnR5cGUgL1R5cGUxCi9CYXNlRm9udCAvQ0xYUllJ
+ K0NNU1kxMAovRm9udERlc2NyaXB0b3IgMTEgMCBSCi9GaXJzdENo
+ YXIgMTgKL0xhc3RDaGFyIDMzCi9XaWR0aHMgMTIgMCBSCj4+CmVu
+ ZG9iago5IDAgb2JqCjw8Ci9UeXBlIC9Gb250RGVzY3JpcHRvcgov
+ Rm9udE5hbWUgL0xPQkdNWCtDTU1JMTAKL0ZsYWdzIDQKL0ZvbnRC
+ Qm94IFsgLTMyIC0yNTAgMTA0OCA3NTBdCi9Bc2NlbnQgNjk0Ci9D
+ YXBIZWlnaHQgNjgzCi9EZXNjZW50IC0xOTQKL0l0YWxpY0FuZ2xl
+ IC0xNAovU3RlbVYgNzIKL1hIZWlnaHQgNDMxCi9DaGFyU2V0ICgv
+ RS9TKQovRm9udEZpbGUgMTMgMCBSCj4+CmVuZG9iagoxMCAwIG9i
+ agpbIDczOC4yIDY0My4xIDc4Ni4zIDgzMS4zIDQzOS42IDU1NC41
+ IDg0OS4zIDY4MC42IDk3MC4xIDgwMy41IDc2Mi44IDY0MiA3OTAu
+ NiA3NTkuMyA2MTMuMl0KZW5kb2JqCjExIDAgb2JqCjw8Ci9UeXBl
+ IC9Gb250RGVzY3JpcHRvcgovRm9udE5hbWUgL0NMWFJZSStDTVNZ
+ MTAKL0ZsYWdzIDQKL0ZvbnRCQm94IFsgLTI5IC05NjAgMTExNiA3
+ NzVdCi9Bc2NlbnQgNzUwCi9DYXBIZWlnaHQgNjgzCi9EZXNjZW50
+ IC0xOTQKL0l0YWxpY0FuZ2xlIC0xNAovU3RlbVYgNDAKL1hIZWln
+ aHQgNDMxCi9DaGFyU2V0ICgvYXJyb3dyaWdodC9yZWZsZXhzdWJz
+ ZXQpCi9Gb250RmlsZSAxNCAwIFIKPj4KZW5kb2JqCjEyIDAgb2Jq
+ ClsgNzc3LjggNzc3LjggNzc3LjggNzc3LjggNzc3LjggNzc3Ljgg
+ Nzc3LjggNzc3LjggNzc3LjggNzc3LjggMTAwMCAxMDAwIDc3Ny44
+ IDc3Ny44IDEwMDAgMTAwMF0KZW5kb2JqCjEzIDAgb2JqCjw8Ci9M
+ ZW5ndGgxIDE0MjkKL0xlbmd0aDIgNjMyNgovTGVuZ3RoMyAwCi9M
+ ZW5ndGggNzI5MAovRmlsdGVyIC9GbGF0ZURlY29kZQo+PgpzdHJl
+ YW0KeNqNdwVU033bP1LCEEmRZiIgvY2S7k5pVBDGGDBiG9ukQZAu
+ pbukpbtEQEkBUVppJCRUlA6Bd3p7P89zP///Oe97ds72u/r6fK/P
+ 9T2/cd+4YyykZIeyhaujkDghiDBYGqiip6cFAQPBYFFhMFgEwM1t
+ gsC5wP/WA7jN4BgsAoWU/g8PFQwcisPrVKE4vKMeCgnUfugChIgC
+ IRLSkNvSYDBQBAyW+tsRhZEGqkLdEXZAPWGgNgoJxwK4VVBoLwzC
+ wRGHr/P3I5AXxgeESEndFvwdDlRyhWMQMCgSqAfFOcJd8RVhUBeg
+ MQqGgOO8/pGCV9YRh0NLg0AeHh7CUFesMArjIM8nCPRA4ByBRnAs
+ HOMOtwP+ggzUh7rC/0ATBnADTRwR2L8Mxih7nAcUAwfiFS4IGByJ
+ xYc8RNrBMUB8daCxli7QAA1H/uWs+5eDIPDP4QAhwpB/pfsT/SsR
+ Avk7GAqDoVzRUKQXAukAtEe4wIEG6rrCOE+cIBCKtPvlCHXBovDx
+ UHcowgVqi3f43ToUqK5kCITiEf7Bh4VhEGgcVhiLcPmFEfQrDf6Y
+ 1ZB2KihXVzgShwX86k8VgYHD8OfuBfozXGckygPp87dkj0Da2f+C
+ YfcQDTJFItwewrVU//jgVYB/6xzgOKA4WPK2qKQ4EO4GhHvCHEG/
+ Cph4oeG/jZBfajwGPx80Cg20x8OA+yHs4fgfgA8W6g4H4jAP4X4+
+ /2n4pwSAQIB2CBgOaAt3QCAB/86OV8Pt/5Lx88cgPIH3wXj6QYDg
+ X59/PVnhGWaHQrp4/dv994hBugbKGnoWAn8g/8uorIzyBPoIiYoA
+ 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
+ U6O1Ye6X9X18trinhe5HL2cLpQrktNAKoxRZZ0ocVeBqUR94Aq5R
+ rmxmSrr22iOhCnpLUe/UFIcf2Euj5NaRT4+xUmRWk6AuDrHRzRey
+ 2gdqN3cfXZ+gVCJWI9UIPGuOieMEmF5cT3Xjeasz1T22H++q2Tg7
+ O20GkrpRiGitSm0qdOLLS21WqoeF4tilVEkXo+fF7F9Y0KlXNCZp
+ gL6xXj2gfBckple907X1KVreBT3BBMuqFy0d+iRsMOH85IqHK2pJ
+ Jis6+0S9gTxgcZ3T613WrtLkFkexBIKm9+HdXhx4RWqWYVo/y4bI
+ +KPPgdrVSpDEJeHY7CjDE/14QTVbI0XkmbqHVP9Tu5T3U21wsjmP
+ JJ32KmKAU0GtR9AEO7ke/xnO56HN7uKxx/RQoF9kQKlpgOoB8yMv
+ 5vhHk58wfVpjWgxmoh+15Nkp2Z3kRJtjSls9zRnojp69qc6pJeNP
+ hdzkhO0omqxHK0gRrJ9X4Tr5+um3o4q87Sk1VEaSRIQRSi47apGi
+ 8ICV3rKBq0Fbss49gDdrLiaMzjPzgXxBNkPzZ1cHVMbm+BADtul5
+ VM7jJa9v9cPzvuQSvLl3rLhZVsKQtcmTPhfxOokBNZ+CEeyxGooM
+ eMjEuTcx4vTiUM7vBPglwewkc70jd4q7B54d1HydZXO8jjxJ2x+n
+ fYU4Vj8xd9CumXpyJ9Ni1jKEyn98N185OhBuvW45D5Bdw/kpgOPQ
+ eQbQcCe5wivGUzwzuU5GBSf5iDKpzeenyRLqXJJaAfFq3DktNRvL
+ ufesZY2NKoMt7FPQj1hRfgWfp/WvP80Cd92y8Gp2j7jHQM/aTZcs
+ fEg1Mykmxsq6UChP6JWmvL8PpvFVcn77NPDmIDcM877qfwDGtClf
+ CmVuZHN0cmVhbQplbmRvYmoKMTQgMCBvYmoKPDwKL0xlbmd0aDEg
+ MTQzMgovTGVuZ3RoMiA2MTI5Ci9MZW5ndGgzIDAKL0xlbmd0aCA3
+ MTA1Ci9GaWx0ZXIgL0ZsYXRlRGVjb2RlCj4+CnN0cmVhbQp42o14
+ B1ST29I2AgIiHQHpAek1AenSe+9NpIYEAiFBEjpIr9IRpAmC9CIg
+ RTpKld6kK0iTXgQERMoX9Zx777n/v9b3rayVvHvmmZn97HlmJyvs
+ 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
+ QGHBfAcXQSiDB3QSGcILHbmbpb29y1zjfwqXZQDzLESy7lVYBRiU
+ f7UsdQlfJfsIKwrVLNXtG49jUb8d82bizc/1u98NyK+s0ROmUunt
+ g9/Set3luO6VonkGiygnbhu/zPL1W/n+Xd/fVnhNpRiOk9J2JO6F
+ W06Pnz+OTlIzlS/uqFiWqmGZM1kYvSJWbOq/E2KCrHk+79GUvZMk
+ 21HzjFnqWVa19MAnGb7QnpWisrZHrGU/6lMyaMYMdm96GCimzBTg
+ ZukrORLSkkzIWfNl91IPZJG2OlNQx9pB4evy6md6VtilNemesxmU
+ hfbqfWo+zlzgU+EQr58JTNF9QTuG/cZ66jVQX83hkiEUXRjR3pB5
+ ZUvuruCOx/v4EYvcxp8zTA1a7Lq5ctvpaiHhyRQPFvQ44zReDcwH
+ eCRO8vWw1bI6Ghdpyx7XbMsuJbvyTIWKeZBM00jP64FQ/iuqpMly
+ +TpJIlYHwilRRms14tCmcACdt35hREijLndoLXE5UGzi6zg7Sdit
+ UFWi/PMkA59XlEdH0E3fiFErciXHT8cPVvN809OXFVIGtqb8z9/1
+ zGajhKCcikfeC8tV1r2vJ17LWYKZxJOrEXRijzlDaL/e0Iy8uO+s
+ K02ze1UmN2UMSCr+BOf7rIdUfrpA89UZ+2M6D4kYwWMcizkWvJ3T
+ 87fDMxsTj5DNwVbTbG85YtaODi3JxeEp8atvUY0coaHvBROWl624
+ M1iM1roCbYmPsJfK92ZHa5aog1oNWXOl4jdSLtytsAyjCOVFDtNF
+ FaTJ5/M8EPPpRisEFenx1SMtrGFjpyWUloJ4Q/wB/r6RYz19cjkT
+ qbxPGTJgJDcuS8C2w66IvvmV2okvk76lxDolwVj7juyoH6WbB9kA
+ rMbpJZ186uDVtXKTudXh3pB225IMy+MN2Uk+IuzdQzt0Ljw5zZFM
+ NPFaN9Kjw7oIz2BzsEYiwRBghaO3y6LvenrrfQL9aaOb6GnLe6mC
+ moepG+09qx4zTBLORN7kcdR1aknerlJ0Sn3TK4BkJ9pdUNao5IFB
+ qGX550w7H1VFWVb2YD/I5wJPlo3q1CHR2IECA70Ntq9vGyWvWz9y
+ 97XGKd+b2Siw5yzvXGun52+sJ/oghCVI3w1g/xbnHzHbTh8z84mG
+ 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
+ I2BeTfPsHt4P2L5OYJg+j7wfVAd9CYcTwXmFCJtGPvhbpJhwFMmq
+ M4U3amvsJEKRLQYTg19jOdiF2xd/6Plv/WSeqcvRm5JTNt15/aZC
+ a5OuwGfGVtldIF5dzY2WmvUVyUmCSULdQrzHdOaol+Wbt8qATzMH
+ jZlzj0VHdcVCed00I/fI84lkt17aVC11SxSbVaTV3+d1MGYp+R+X
+ ptITCmVuZHN0cmVhbQplbmRvYmoKMiAwIG9iaiA8PAovWE9iamVj
+ dCA8PCAvSW0xIDEgMCBSID4+Ci9Qcm9jU2V0IFsgL1BERiBdCj4+
+ IGVuZG9iago1IDAgb2JqIDw8Ci9UeXBlIC9QYWdlcwovQ291bnQg
+ MQovS2lkcyBbMyAwIFJdCj4+IGVuZG9iagoxNSAwIG9iaiA8PAov
+ VHlwZSAvQ2F0YWxvZwovUGFnZXMgNSAwIFIKPj4gZW5kb2JqCjE2
+ IDAgb2JqIDw8Ci9Qcm9kdWNlciAocGRmVGVYLTEuNDAuMTQpCi9D
+ cmVhdG9yIChUZVgpCi9DcmVhdGlvbkRhdGUgKEQ6MjAxNjExMDIx
+ NTAxMTArMDEnMDAnKQovTW9kRGF0ZSAoRDoyMDE2MTEwMjE1MDEx
+ MCswMScwMCcpCi9UcmFwcGVkIC9GYWxzZQovUFRFWC5GdWxsYmFu
+ bmVyIChUaGlzIGlzIHBkZlRlWCwgVmVyc2lvbiAzLjE0MTU5MjYt
+ Mi41LTEuNDAuMTQgKFRlWCBMaXZlIDIwMTMvRGViaWFuKSBrcGF0
+ aHNlYSB2ZXJzaW9uIDYuMS4xKQo+PiBlbmRvYmoKeHJlZgowIDE3
+ CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDIzNCAwMDAwMCBu
+ IAowMDAwMDE2NDc5IDAwMDAwIG4gCjAwMDAwMDAxMzIgMDAwMDAg
+ biAKMDAwMDAwMDAxNSAwMDAwMCBuIAowMDAwMDE2NTQ0IDAwMDAw
+ IG4gCjAwMDAwMDA2MzEgMDAwMDAgbiAKMDAwMDAwMDg5OCAwMDAw
+ MCBuIAowMDAwMDAxMDM2IDAwMDAwIG4gCjAwMDAwMDExNzUgMDAw
+ MDAgbiAKMDAwMDAwMTM5NyAwMDAwMCBuIAowMDAwMDAxNTA0IDAw
+ MDAwIG4gCjAwMDAwMDE3NDcgMDAwMDAgbiAKMDAwMDAwMTg1OCAw
+ MDAwMCBuIAowMDAwMDA5MjYxIDAwMDAwIG4gCjAwMDAwMTY2MDEg
+ MDAwMDAgbiAKMDAwMDAxNjY1MSAwMDAwMCBuIAp0cmFpbGVyCjw8
+ IC9TaXplIDE3Ci9Sb290IDE1IDAgUgovSW5mbyAxNiAwIFIKL0lE
+ IFs8ODA0RTdFMUYyMzM2NTRGMjg5MTZFRjI3QkIzREY2Mjc+IDw4
+ MDRFN0UxRjIzMzY1NEYyODkxNkVGMjdCQjNERjYyNz5dID4+CnN0
+ YXJ0eHJlZgoxNjkxNwolJUVPRgo=
+ </data>
+ </dict>
+ <dict>
+ <key>Extension</key>
<string>png</string>
<key>ID</key>
<integer>133</integer>
@@ -67584,155 +68040,6 @@
<key>Extension</key>
<string>tiff</string>
<key>ID</key>
- <integer>62</integer>
- <key>RawData</key>
- <data>
- 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
- SmN9RMNlfrRi5SuNSC43JRjmXqE4NHgRXF0nZmTVqrx0WIvY30nI
- kQFTIjGRGwUDIGpC8OSSY5YfVcTHZX2n5CSHGCb9+oRY4l3mvLyJ
- i9w1ABIiBpAhD4JJEXsPObappJzwWO4RBMCkRt28KBx4qTAAFVge
- d9fCPxkQQtlJUi8x1cIEuLCilYNJ60Xpct1bpokc0hKyjYANJypT
- bcwzkRRmVVITOwgYNMQzHdZWlCeLbh5rIgZk7xmcZnZeTNGQkq6W
- ECT8iRNCMDwHgL3rKo+tLR150qWOe91FKMbxXsTLacdknC2ZrHWb
- Cb3M+MywDLhEsqEXOobnJSE8gXtYieBVZzn41pqWQ5PzAKLEMVWn
- oxZFDwIcTRqjaZZzFnuT1u2Hi3cskQ36dsivAY78Efsn5Fezs5kT
- PPq04GAlgXwScqXOj5WI53pu8gg3FCL6wIFO8dxwuVcF5kT0zfJy
- OcsABy7mHMyGF75i4ukNyiC8+550Xo3R+kEldDYDpPTendP6hqKI
- pubOE4ICAA8BAAADAAAAAQDcAAABAQADAAAAAQArAAABAgADAAAA
- BAAACPoBAwADAAAAAQAFAAABBgADAAAAAQACAAABEQAEAAAAAQAA
- AAgBEgADAAAAAQABAAABFQADAAAAAQAEAAABFgADAAAAAQArAAAB
- FwAEAAAAAQAACDgBHAADAAAAAQABAAABPQADAAAAAQACAAABUgAD
- AAAAAQABAAABUwADAAAABAAACQKHcwAHAAAMSAAACQoAAAAAAAgA
- CAAIAAgAAQABAAEAAQAADEhMaW5vAhAAAG1udHJSR0IgWFlaIAfO
- AAIACQAGADEAAGFjc3BNU0ZUAAAAAElFQyBzUkdCAAAAAAAAAAAA
- AAAAAAD21gABAAAAANMtSFAgIAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEWNwcnQAAAFQAAAA
- M2Rlc2MAAAGEAAAAbHd0cHQAAAHwAAAAFGJrcHQAAAIEAAAAFHJY
- WVoAAAIYAAAAFGdYWVoAAAIsAAAAFGJYWVoAAAJAAAAAFGRtbmQA
- AAJUAAAAcGRtZGQAAALEAAAAiHZ1ZWQAAANMAAAAhnZpZXcAAAPU
- AAAAJGx1bWkAAAP4AAAAFG1lYXMAAAQMAAAAJHRlY2gAAAQwAAAA
- DHJUUkMAAAQ8AAAIDGdUUkMAAAQ8AAAIDGJUUkMAAAQ8AAAIDHRl
- eHQAAAAAQ29weXJpZ2h0IChjKSAxOTk4IEhld2xldHQtUGFja2Fy
- ZCBDb21wYW55AABkZXNjAAAAAAAAABJzUkdCIElFQzYxOTY2LTIu
- MQAAAAAAAAAAAAAAEnNSR0IgSUVDNjE5NjYtMi4xAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAAAAAAAAAAAA
- AAAAWFlaIAAAAAAAAG+iAAA49QAAA5BYWVogAAAAAAAAYpkAALeF
- AAAY2lhZWiAAAAAAAAAkoAAAD4QAALbPZGVzYwAAAAAAAAAWSUVD
- IGh0dHA6Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAWSUVDIGh0dHA6
- Ly93d3cuaWVjLmNoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAAGRlc2MAAAAAAAAALklFQyA2MTk2
- Ni0yLjEgRGVmYXVsdCBSR0IgY29sb3VyIHNwYWNlIC0gc1JHQgAA
- AAAAAAAAAAAALklFQyA2MTk2Ni0yLjEgRGVmYXVsdCBSR0IgY29s
- b3VyIHNwYWNlIC0gc1JHQgAAAAAAAAAAAAAAAAAAAAAAAAAAAABk
- ZXNjAAAAAAAAACxSZWZlcmVuY2UgVmlld2luZyBDb25kaXRpb24g
- aW4gSUVDNjE5NjYtMi4xAAAAAAAAAAAAAAAsUmVmZXJlbmNlIFZp
- ZXdpbmcgQ29uZGl0aW9uIGluIElFQzYxOTY2LTIuMQAAAAAAAAAA
- AAAAAAAAAAAAAAAAAAAAAAAAdmlldwAAAAAAE6T+ABRfLgAQzxQA
- A+3MAAQTCwADXJ4AAAABWFlaIAAAAAAATAlWAFAAAABXH+dtZWFz
- AAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAACjwAAAAJzaWcgAAAA
- AENSVCBjdXJ2AAAAAAAABAAAAAAFAAoADwAUABkAHgAjACgALQAy
- ADcAOwBAAEUASgBPAFQAWQBeAGMAaABtAHIAdwB8AIEAhgCLAJAA
- lQCaAJ8ApACpAK4AsgC3ALwAwQDGAMsA0ADVANsA4ADlAOsA8AD2
- APsBAQEHAQ0BEwEZAR8BJQErATIBOAE+AUUBTAFSAVkBYAFnAW4B
- dQF8AYMBiwGSAZoBoQGpAbEBuQHBAckB0QHZAeEB6QHyAfoCAwIM
- AhQCHQImAi8COAJBAksCVAJdAmcCcQJ6AoQCjgKYAqICrAK2AsEC
- ywLVAuAC6wL1AwADCwMWAyEDLQM4A0MDTwNaA2YDcgN+A4oDlgOi
- A64DugPHA9MD4APsA/kEBgQTBCAELQQ7BEgEVQRjBHEEfgSMBJoE
- qAS2BMQE0wThBPAE/gUNBRwFKwU6BUkFWAVnBXcFhgWWBaYFtQXF
- BdUF5QX2BgYGFgYnBjcGSAZZBmoGewaMBp0GrwbABtEG4wb1BwcH
- GQcrBz0HTwdhB3QHhgeZB6wHvwfSB+UH+AgLCB8IMghGCFoIbgiC
- CJYIqgi+CNII5wj7CRAJJQk6CU8JZAl5CY8JpAm6Cc8J5Qn7ChEK
- Jwo9ClQKagqBCpgKrgrFCtwK8wsLCyILOQtRC2kLgAuYC7ALyAvh
- C/kMEgwqDEMMXAx1DI4MpwzADNkM8w0NDSYNQA1aDXQNjg2pDcMN
- 3g34DhMOLg5JDmQOfw6bDrYO0g7uDwkPJQ9BD14Peg+WD7MPzw/s
- EAkQJhBDEGEQfhCbELkQ1xD1ERMRMRFPEW0RjBGqEckR6BIHEiYS
- RRJkEoQSoxLDEuMTAxMjE0MTYxODE6QTxRPlFAYUJxRJFGoUixSt
- FM4U8BUSFTQVVhV4FZsVvRXgFgMWJhZJFmwWjxayFtYW+hcdF0EX
- ZReJF64X0hf3GBsYQBhlGIoYrxjVGPoZIBlFGWsZkRm3Gd0aBBoq
- GlEadxqeGsUa7BsUGzsbYxuKG7Ib2hwCHCocUhx7HKMczBz1HR4d
- Rx1wHZkdwx3sHhYeQB5qHpQevh7pHxMfPh9pH5Qfvx/qIBUgQSBs
- IJggxCDwIRwhSCF1IaEhziH7IiciVSKCIq8i3SMKIzgjZiOUI8Ij
- 8CQfJE0kfCSrJNolCSU4JWgllyXHJfcmJyZXJocmtyboJxgnSSd6
- J6sn3CgNKD8ocSiiKNQpBik4KWspnSnQKgIqNSpoKpsqzysCKzYr
- aSudK9EsBSw5LG4soizXLQwtQS12Last4S4WLkwugi63Lu4vJC9a
- L5Evxy/+MDUwbDCkMNsxEjFKMYIxujHyMioyYzKbMtQzDTNGM38z
- uDPxNCs0ZTSeNNg1EzVNNYc1wjX9Njc2cjauNuk3JDdgN5w31zgU
- OFA4jDjIOQU5Qjl/Obw5+To2OnQ6sjrvOy07azuqO+g8JzxlPKQ8
- 4z0iPWE9oT3gPiA+YD6gPuA/IT9hP6I/4kAjQGRApkDnQSlBakGs
- Qe5CMEJyQrVC90M6Q31DwEQDREdEikTORRJFVUWaRd5GIkZnRqtG
- 8Ec1R3tHwEgFSEtIkUjXSR1JY0mpSfBKN0p9SsRLDEtTS5pL4kwq
- THJMuk0CTUpNk03cTiVObk63TwBPSU+TT91QJ1BxULtRBlFQUZtR
- 5lIxUnxSx1MTU19TqlP2VEJUj1TbVShVdVXCVg9WXFapVvdXRFeS
- V+BYL1h9WMtZGllpWbhaB1pWWqZa9VtFW5Vb5Vw1XIZc1l0nXXhd
- yV4aXmxevV8PX2Ffs2AFYFdgqmD8YU9homH1YklinGLwY0Njl2Pr
- ZEBklGTpZT1lkmXnZj1mkmboZz1nk2fpaD9olmjsaUNpmmnxakhq
- n2r3a09rp2v/bFdsr20IbWBtuW4SbmtuxG8eb3hv0XArcIZw4HE6
- cZVx8HJLcqZzAXNdc7h0FHRwdMx1KHWFdeF2Pnabdvh3VnezeBF4
- bnjMeSp5iXnnekZ6pXsEe2N7wnwhfIF84X1BfaF+AX5ifsJ/I3+E
- f+WAR4CogQqBa4HNgjCCkoL0g1eDuoQdhICE44VHhauGDoZyhteH
- O4efiASIaYjOiTOJmYn+imSKyoswi5aL/IxjjMqNMY2Yjf+OZo7O
- jzaPnpAGkG6Q1pE/kaiSEZJ6kuOTTZO2lCCUipT0lV+VyZY0lp+X
- Cpd1l+CYTJi4mSSZkJn8mmia1ZtCm6+cHJyJnPedZJ3SnkCerp8d
- n4uf+qBpoNihR6G2oiailqMGo3aj5qRWpMelOKWpphqmi6b9p26n
- 4KhSqMSpN6mpqhyqj6sCq3Wr6axcrNCtRK24ri2uoa8Wr4uwALB1
- sOqxYLHWskuywrM4s660JbSctRO1irYBtnm28Ldot+C4WbjRuUq5
- wro7urW7LrunvCG8m70VvY++Cr6Evv+/er/1wHDA7MFnwePCX8Lb
- w1jD1MRRxM7FS8XIxkbGw8dBx7/IPci8yTrJuco4yrfLNsu2zDXM
- tc01zbXONs62zzfPuNA50LrRPNG+0j/SwdNE08bUSdTL1U7V0dZV
- 1tjXXNfg2GTY6Nls2fHadtr724DcBdyK3RDdlt4c3qLfKd+v4Dbg
- veFE4cziU+Lb42Pj6+Rz5PzlhOYN5pbnH+ep6DLovOlG6dDqW+rl
- 63Dr++yG7RHtnO4o7rTvQO/M8Fjw5fFy8f/yjPMZ86f0NPTC9VD1
- 3vZt9vv3ivgZ+Kj5OPnH+lf65/t3/Af8mP0p/br+S/7c/23//w==
- </data>
- </dict>
- <dict>
- <key>Extension</key>
- <string>tiff</string>
- <key>ID</key>
<integer>61</integer>
<key>RawData</key>
<data>
@@ -198810,7 +199117,7 @@
<key>MasterSheets</key>
<array/>
<key>ModificationDate</key>
- <string>2016-09-14 16:16:41 +0000</string>
+ <string>2016-11-02 14:07:28 +0000</string>
<key>Modifier</key>
<string>Marko Rodriguez</string>
<key>NotesVisible</key>
@@ -280644,6 +280951,34 @@ endTime:2001}</string>
<array>
<dict>
<key>Bounds</key>
+ <string>{{26.681371487677097, 75.237825457955836}, {94.197063848376274, 18.839412769675256}}</string>
+ <key>Class</key>
+ <string>ShapedGraphic</string>
+ <key>ID</key>
+ <integer>99</integer>
+ <key>ImageID</key>
+ <integer>134</integer>
+ <key>Style</key>
+ <dict>
+ <key>fill</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>shadow</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ <key>stroke</key>
+ <dict>
+ <key>Draws</key>
+ <string>NO</string>
+ </dict>
+ </dict>
+ </dict>
+ <dict>
+ <key>Bounds</key>
<string>{{309.073974609375, 148.41815948486328}, {136.21464538574219, 48.265029907226562}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
@@ -281173,34 +281508,6 @@ endTime:2001}</string>
</dict>
<dict>
<key>Bounds</key>
- <string>{{32.7799072265625, 75.472727272727298}, {82, 16.027272727272727}}</string>
- <key>Class</key>
- <string>ShapedGraphic</string>
- <key>ID</key>
- <integer>83</integer>
- <key>ImageID</key>
- <integer>62</integer>
- <key>Style</key>
- <dict>
- <key>fill</key>
- <dict>
- <key>Draws</key>
- <string>NO</string>
- </dict>
- <key>shadow</key>
- <dict>
- <key>Draws</key>
- <string>NO</string>
- </dict>
- <key>stroke</key>
- <dict>
- <key>Draws</key>
- <string>NO</string>
- </dict>
- </dict>
- </dict>
- <dict>
- <key>Bounds</key>
<string>{{138.5596923828125, 174.35746765136719}, {54, 20.25}}</string>
<key>Class</key>
<string>ShapedGraphic</string>
@@ -309556,7 +309863,7 @@ Database}</string>
<key>WindowInfo</key>
<dict>
<key>CurrentSheet</key>
- <integer>75</integer>
+ <integer>42</integer>
<key>Expanded_Canvases</key>
<array/>
<key>Frame</key>
[4/4] tinkerpop git commit: TINKERPOP-1490 Implemented promise API
for Traversal
Posted by sp...@apache.org.
TINKERPOP-1490 Implemented promise API for Traversal
Added two promise() methods that return CompletableFuture on Traversal. Provided an override on DefaultTraversal for those methods because the function that transforms the Traversal is executed in a different thread and therefore requires Graph transaction management (or else we would orphan transactions). Did not update gremlin-python with the promise API because it seemed to beg discussion on the "right" way to do that (i.e. what library to use to support promises?, just use futures from the core lib?, etc).
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/03e37e81
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/03e37e81
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/03e37e81
Branch: refs/heads/TINKERPOP-1490
Commit: 03e37e81fde3291f155d4ac7cf5d6fb42f76d6d0
Parents: d7fb966
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Tue Nov 1 09:30:28 2016 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Wed Nov 2 11:10:49 2016 -0400
----------------------------------------------------------------------
CHANGELOG.asciidoc | 1 +
.../upgrade/release-3.2.x-incubating.asciidoc | 20 +
gremlin-core/pom.xml | 5 +
.../gremlin/process/traversal/Traversal.java | 50 ++
.../traversal/util/DefaultTraversal.java | 38 ++
.../process/traversal/TraversalTest.java | 473 +++++++++++++++++++
gremlin-groovy/pom.xml | 5 -
.../gremlin/python/jsr223/PythonProvider.java | 1 +
.../process/traversal/CoreTraversalTest.java | 42 ++
.../TinkerGraphGroovyTranslatorProvider.java | 1 +
.../TinkerGraphJavaTranslatorProvider.java | 1 +
11 files changed, 632 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/03e37e81/CHANGELOG.asciidoc
----------------------------------------------------------------------
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 328bc9d..23c49ca 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -26,6 +26,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
TinkerPop 3.2.4 (Release Date: NOT OFFICIALLY RELEASED YET)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+* Added `Traversal.promise()` methods to allow for asynchronous traversal processing.
* Added `choose(predicate,traversal)` and `choose(traversal,traversal)` to effect if/then-semantics (no else). Equivalent to `choose(x,y,identity())`.
* `SparkGraphComputer` no longer starts a worker iteration if the worker's partition is empty.
* Added `ProjectStep.getProjectKeys()` for strategies that rely on such information.
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/03e37e81/docs/src/upgrade/release-3.2.x-incubating.asciidoc
----------------------------------------------------------------------
diff --git a/docs/src/upgrade/release-3.2.x-incubating.asciidoc b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
index f121aa4..acaa83b 100644
--- a/docs/src/upgrade/release-3.2.x-incubating.asciidoc
+++ b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
@@ -32,6 +32,26 @@ Please see the link:https://github.com/apache/tinkerpop/blob/3.2.4/CHANGELOG.asc
Upgrading for Users
~~~~~~~~~~~~~~~~~~~
+Traversal Promises
+^^^^^^^^^^^^^^^^^^
+
+The `Traversal` API now has two `promise()` method overloads. These methods return a promise in the form of a
+`CompleteableFuture`. Usage is as follows:
+
+[source,groovy]
+----
+gremlin> promise = g.V().out().promise{it.next()}
+==>java.util.concurrent.CompletableFuture@4aa3d36[Completed normally]
+gremlin> promise.join()
+==>v[3]
+gremlin> promise.isDone()
+==>true
+gremlin> g.V().out().promise{it.toList()}.thenApply{it.size()}.get()
+==>6
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-1490[TINKERPOP-1490]
+
If/Then-Semantics with Choose Step
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/03e37e81/gremlin-core/pom.xml
----------------------------------------------------------------------
diff --git a/gremlin-core/pom.xml b/gremlin-core/pom.xml
index e8f3a34..0594448 100644
--- a/gremlin-core/pom.xml
+++ b/gremlin-core/pom.xml
@@ -61,6 +61,11 @@ limitations under the License.
</exclusion>
</exclusions>
</dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.3.1</version>
+ </dependency>
<!-- LOGGING -->
<dependency>
<groupId>org.slf4j</groupId>
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/03e37e81/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java
index edc18e0..e4ba5a6 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Traversal.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal;
+import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.apache.tinkerpop.gremlin.process.computer.GraphComputer;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
@@ -42,7 +43,13 @@ import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import java.util.function.Consumer;
+import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@@ -140,6 +147,43 @@ public interface Traversal<S, E> extends Iterator<E>, Serializable, Cloneable, A
}
/**
+ * Starts a promise to execute a function on the current {@code Traversal} that will be completed in the future.
+ * This implementation uses {@link Admin#traversalExecutorService} to execute the supplied
+ * {@code traversalFunction}.
+ */
+ public default <T> CompletableFuture<T> promise(final Function<Traversal, T> traversalFunction) {
+ return promise(traversalFunction, Admin.traversalExecutorService);
+ }
+
+ /**
+ * Starts a promise to execute a function on the current {@code Traversal} that will be completed in the future.
+ * This implementation uses the caller supplied {@code ExecutorService} to execute the {@code traversalFunction}.
+ */
+ public default <T> CompletableFuture<T> promise(final Function<Traversal, T> traversalFunction, final ExecutorService service) {
+ final CompletableFuture<T> promise = new CompletableFuture<>();
+ final Future iterationFuture = service.submit(() -> {
+ try {
+ promise.complete(traversalFunction.apply(this));
+ } catch (Exception ex) {
+ // the promise may have been cancelled by the caller, in which case, there is no need to attempt
+ // another write on completion
+ if (!promise.isDone()) promise.completeExceptionally(ex);
+ }
+ });
+
+ // if the user cancels the promise then attempt to kill the iteration.
+ promise.exceptionally(t -> {
+ if (t instanceof CancellationException) {
+ iterationFuture.cancel(true);
+ }
+
+ return null;
+ });
+
+ return promise;
+ }
+
+ /**
* Add all the results of the traversal to the provided collection.
*
* @param collection the collection to fill
@@ -253,6 +297,12 @@ public interface Traversal<S, E> extends Iterator<E>, Serializable, Cloneable, A
public interface Admin<S, E> extends Traversal<S, E> {
/**
+ * Service that handles promises.
+ */
+ static final ExecutorService traversalExecutorService = Executors.newCachedThreadPool(
+ new BasicThreadFactory.Builder().namingPattern("traversal-executor-%d").build());
+
+ /**
* Get the {@link Bytecode} associated with the construction of this traversal.
*
* @return the byte code representation of the traversal
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/03e37e81/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversal.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversal.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversal.java
index 5bd01da..a81f34e 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversal.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/DefaultTraversal.java
@@ -42,6 +42,10 @@ import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ForkJoinPool;
+import java.util.function.Function;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
@@ -316,6 +320,40 @@ public class DefaultTraversal<S, E> implements Traversal.Admin<S, E> {
this.graph = graph;
}
+ /**
+ * Override of {@link Traversal#promise(Function)} that is aware of graph transactions.
+ */
+ @Override
+ public <T2> CompletableFuture<T2> promise(final Function<Traversal, T2> traversalFunction) {
+ return this.promise(traversalFunction, Traversal.Admin.traversalExecutorService);
+ }
+
+ /**
+ * Override of {@link Traversal#promise(Function)} that is aware of graph transactions. In a transactional graph
+ * a promise represents the full scope of a transaction, even if the graph is only partially iterated.
+ */
+ @Override
+ public <T2> CompletableFuture<T2> promise(final Function<Traversal, T2> traversalFunction, final ExecutorService service) {
+ if (graph != null && graph.features().graph().supportsTransactions()) {
+ final Function<Traversal, T2> transactionAware = traversal -> {
+
+ try {
+ if (graph.tx().isOpen()) graph.tx().rollback();
+ final T2 obj = traversalFunction.apply(traversal);
+ if (graph.tx().isOpen()) graph.tx().commit();
+ return obj;
+ } catch (Exception ex) {
+ if (graph.tx().isOpen()) graph.tx().rollback();
+ throw ex;
+ }
+ };
+
+ return Traversal.Admin.super.promise(transactionAware, service);
+ } else {
+ return Traversal.Admin.super.promise(traversalFunction, service);
+ }
+ }
+
@Override
public boolean equals(final Object other) {
return other != null && other.getClass().equals(this.getClass()) && this.equals(((Traversal.Admin) other));
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/03e37e81/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalTest.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalTest.java
new file mode 100644
index 0000000..aa1b99b
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalTest.java
@@ -0,0 +1,473 @@
+/*
+ * 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.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal;
+
+import org.apache.tinkerpop.gremlin.process.remote.traversal.DefaultRemoteTraverser;
+import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalInterruptedException;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import static org.hamcrest.core.Is.is;
+import static org.hamcrest.core.IsCollectionContaining.hasItems;
+import static org.hamcrest.core.IsInstanceOf.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Stephen Mallette (http://stephen.genoprime.com)
+ */
+public class TraversalTest {
+
+ private final ExecutorService service = Executors.newFixedThreadPool(2);
+
+ @Test
+ public void shouldTryNext() {
+ final MockTraversal<Integer> t = new MockTraversal<>(1, 2, 3);
+ final Optional<Integer> optFirst = t.tryNext();
+ assertEquals(1, optFirst.get().intValue());
+ final Optional<Integer> optSecond = t.tryNext();
+ assertEquals(2, optSecond.get().intValue());
+ final Optional<Integer> optThird = t.tryNext();
+ assertEquals(3, optThird.get().intValue());
+
+ IntStream.range(0, 100).forEach(i -> {
+ assertThat(t.tryNext().isPresent(), is(false));
+ });
+ }
+
+ @Test
+ public void shouldGetTwoAtATime() {
+ final MockTraversal<Integer> t = new MockTraversal<>(1, 2, 3, 4, 5, 6, 7);
+ final List<Integer> batchOne = t.next(2);
+ assertEquals(2, batchOne.size());
+ assertThat(batchOne, hasItems(1 ,2));
+
+ final List<Integer> batchTwo = t.next(2);
+ assertEquals(2, batchTwo.size());
+ assertThat(batchTwo, hasItems(3 ,4));
+
+ final List<Integer> batchThree = t.next(2);
+ assertEquals(2, batchThree.size());
+ assertThat(batchThree, hasItems(5, 6));
+
+ final List<Integer> batchFour = t.next(2);
+ assertEquals(1, batchFour.size());
+ assertThat(batchFour, hasItems(7));
+
+ final List<Integer> batchFive = t.next(2);
+ assertEquals(0, batchFive.size());
+ }
+
+ @Test
+ public void shouldFillList() {
+ final MockTraversal<Integer> t = new MockTraversal<>(1, 2, 3, 4, 5, 6, 7);
+ final List<Integer> listToFill = new ArrayList<>();
+ final List<Integer> batch = t.fill(listToFill);
+ assertEquals(7, batch.size());
+ assertThat(batch, hasItems(1 ,2, 3, 4, 5, 6, 7));
+ assertThat(t.hasNext(), is(false));
+ assertSame(listToFill, batch);
+ }
+
+ @Test
+ public void shouldStream() {
+ final MockTraversal<Integer> t = new MockTraversal<>(1, 2, 3, 4, 5, 6, 7);
+ final List<Integer> batch = t.toStream().collect(Collectors.toList());
+ assertEquals(7, batch.size());
+ assertThat(batch, hasItems(1 ,2, 3, 4, 5, 6, 7));
+ assertThat(t.hasNext(), is(false));
+ }
+
+ @Test
+ public void shouldPromiseNextThreeUsingForkJoin() throws Exception {
+ final MockTraversal<Integer> t = new MockTraversal<>(1, 2, 3, 4, 5, 6, 7);
+ final CompletableFuture<List<Integer>> promiseFirst = t.promise(traversal -> traversal.next(3));
+ final List<Integer> listFirst = promiseFirst.get();
+ assertEquals(3, listFirst.size());
+ assertThat(listFirst, hasItems(1 ,2, 3));
+ assertThat(t.hasNext(), is(true));
+ assertThat(promiseFirst.isDone(), is(true));
+
+ final CompletableFuture<List<Integer>> promiseSecond = t.promise(traversal -> traversal.next(3));
+ final List<Integer> listSecond = promiseSecond.get();
+ assertEquals(3, listSecond.size());
+ assertThat(listSecond, hasItems(4, 5, 6));
+ assertThat(t.hasNext(), is(true));
+ assertThat(promiseSecond.isDone(), is(true));
+
+ final CompletableFuture<List<Integer>> promiseThird = t.promise(traversal -> traversal.next(3));
+ final List<Integer> listThird = promiseThird.get();
+ assertEquals(1, listThird.size());
+ assertThat(listThird, hasItems(7));
+ assertThat(t.hasNext(), is(false));
+ assertThat(promiseThird.isDone(), is(true));
+
+ final CompletableFuture<Integer> promiseDead = t.promise(traversal -> (Integer) traversal.next());
+ final AtomicBoolean dead = new AtomicBoolean(false);
+ promiseDead.exceptionally(tossed -> {
+ dead.set(tossed instanceof NoSuchElementException);
+ return null;
+ });
+
+ try {
+ promiseDead.get(10000, TimeUnit.MILLISECONDS);
+ fail("Should have gotten an exception");
+ } catch (Exception ex) {
+ if (ex instanceof TimeoutException) {
+ fail("This should not have timed out but should have gotten an exception caught above in the exceptionally() clause");
+ }
+
+ assertThat(ex.getCause(), instanceOf(NoSuchElementException.class));
+ }
+
+ assertThat(dead.get(), is(true));
+ assertThat(t.hasNext(), is(false));
+ assertThat(promiseDead.isDone(), is(true));
+ }
+
+ @Test
+ public void shouldPromiseNextThreeUsingSpecificExecutor() throws Exception {
+ final MockTraversal<Integer> t = new MockTraversal<>(1, 2, 3, 4, 5, 6, 7);
+ final CompletableFuture<List<Integer>> promiseFirst = t.promise(traversal -> traversal.next(3), service);
+ final List<Integer> listFirst = promiseFirst.get();
+ assertEquals(3, listFirst.size());
+ assertThat(listFirst, hasItems(1 ,2, 3));
+ assertThat(t.hasNext(), is(true));
+ assertThat(promiseFirst.isDone(), is(true));
+
+ final CompletableFuture<List<Integer>> promiseSecond = t.promise(traversal -> traversal.next(3), service);
+ final List<Integer> listSecond = promiseSecond.get();
+ assertEquals(3, listSecond.size());
+ assertThat(listSecond, hasItems(4, 5, 6));
+ assertThat(t.hasNext(), is(true));
+ assertThat(promiseSecond.isDone(), is(true));
+
+ final CompletableFuture<List<Integer>> promiseThird = t.promise(traversal -> traversal.next(3), service);
+ final List<Integer> listThird = promiseThird.get();
+ assertEquals(1, listThird.size());
+ assertThat(listThird, hasItems(7));
+ assertThat(t.hasNext(), is(false));
+ assertThat(promiseThird.isDone(), is(true));
+
+ final CompletableFuture<Integer> promiseDead = t.promise(traversal -> (Integer) traversal.next(), service);
+ final AtomicBoolean dead = new AtomicBoolean(false);
+ promiseDead.exceptionally(tossed -> {
+ dead.set(tossed instanceof NoSuchElementException);
+ return null;
+ });
+
+ try {
+ promiseDead.get(10000, TimeUnit.MILLISECONDS);
+ fail("Should have gotten an exception");
+ } catch (Exception ex) {
+ if (ex instanceof TimeoutException) {
+ fail("This should not have timed out but should have gotten an exception caught above in the exceptionally() clause");
+ }
+
+ assertThat(ex.getCause(), instanceOf(NoSuchElementException.class));
+ }
+
+ assertThat(dead.get(), is(true));
+ assertThat(t.hasNext(), is(false));
+ assertThat(promiseDead.isDone(), is(true));
+ }
+
+ @Test
+ public void shouldInterruptTraversalFunction() throws Exception {
+ final Random rand = new Random(1234567890);
+
+ // infinite traversal
+ final MockTraversal<Integer> t = new MockTraversal<>(IntStream.generate(rand::nextInt).iterator());
+
+ // iterate a bunch of it
+ final CompletableFuture<List<Integer>> promise10 = t.promise(traversal -> traversal.next(10), service);
+ assertEquals(10, promise10.get(10000, TimeUnit.MILLISECONDS).size());
+ final CompletableFuture<List<Integer>> promise100 = t.promise(traversal -> traversal.next(100), service);
+ assertEquals(100, promise100.get(10000, TimeUnit.MILLISECONDS).size());
+ final CompletableFuture<List<Integer>> promise1000 = t.promise(traversal -> traversal.next(1000), service);
+ assertEquals(1000, promise1000.get(10000, TimeUnit.MILLISECONDS).size());
+
+ // this is endless, so let's cancel
+ final CompletableFuture<List<Integer>> promiseForevers = t.promise(traversal -> traversal.next(Integer.MAX_VALUE), service);
+
+ // specify what to do on exception
+ final AtomicBoolean failed = new AtomicBoolean(false);
+ promiseForevers.exceptionally(ex -> {
+ failed.set(true);
+ return null;
+ });
+
+ try {
+ // let it actually iterate a moment
+ promiseForevers.get(500, TimeUnit.MILLISECONDS);
+ fail("This should have timed out because the traversal has infinite items in it");
+ } catch (TimeoutException tex) {
+
+ }
+
+ assertThat(promiseForevers.isDone(), is(false));
+ promiseForevers.cancel(true);
+ assertThat(failed.get(), is(true));
+ assertThat(promiseForevers.isDone(), is(true));
+ }
+
+ @Test
+ public void shouldIterate() {
+ final MockTraversal<Integer> t = new MockTraversal<>(1, 2, 3, 4, 5, 6, 7);
+ assertThat(t.hasNext(), is(true));
+ t.iterate();
+ assertThat(t.hasNext(), is(false));
+ }
+
+ private static class MockStep<E> implements Step<E,E> {
+
+ private final Iterator<E> itty;
+
+ MockStep(final Iterator<E> itty) {
+ this.itty = itty;
+ }
+
+ @Override
+ public void addStarts(final Iterator starts) {
+
+ }
+
+ @Override
+ public void addStart(final Traverser.Admin start) {
+
+ }
+
+ @Override
+ public void setPreviousStep(final Step step) {
+
+ }
+
+ @Override
+ public Step getPreviousStep() {
+ return null;
+ }
+
+ @Override
+ public void setNextStep(final Step step) {
+
+ }
+
+ @Override
+ public Step getNextStep() {
+ return null;
+ }
+
+ @Override
+ public Traversal.Admin getTraversal() {
+ return null;
+ }
+
+ @Override
+ public void setTraversal(final Traversal.Admin traversal) {
+
+ }
+
+ @Override
+ public void reset() {
+
+ }
+
+ @Override
+ public Step clone() {
+ return null;
+ }
+
+ @Override
+ public Set<String> getLabels() {
+ return null;
+ }
+
+ @Override
+ public void addLabel(final String label) {
+
+ }
+
+ @Override
+ public void removeLabel(final String label) {
+
+ }
+
+ @Override
+ public void setId(final String id) {
+
+ }
+
+ @Override
+ public String getId() {
+ return null;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return itty.hasNext();
+ }
+
+ @Override
+ public Traverser.Admin<E> next() {
+ return new DefaultRemoteTraverser<>(itty.next(), 1L);
+ }
+ }
+
+
+ private static class MockTraversal<T> implements Traversal.Admin<T,T> {
+
+ private Iterator<T> itty;
+
+ private Step mockEndStep;
+
+ private List<Step> steps;
+
+ MockTraversal(final T... objects) {
+ this(Arrays.asList(objects));
+ }
+
+ MockTraversal(final List<T> list) {
+ this(list.iterator());
+ }
+
+ MockTraversal(final Iterator<T> itty) {
+ this.itty = itty;
+ mockEndStep = new MockStep<>(itty);
+ steps = Collections.singletonList(mockEndStep);
+ }
+
+ @Override
+ public Bytecode getBytecode() {
+ return null;
+ }
+
+ @Override
+ public List<Step> getSteps() {
+ return steps;
+ }
+
+ @Override
+ public <S2, E2> Admin<S2, E2> addStep(final int index, final Step<?, ?> step) throws IllegalStateException {
+ return null;
+ }
+
+ @Override
+ public <S2, E2> Admin<S2, E2> removeStep(final int index) throws IllegalStateException {
+ return null;
+ }
+
+ @Override
+ public void applyStrategies() throws IllegalStateException {
+
+ }
+
+ @Override
+ public TraverserGenerator getTraverserGenerator() {
+ return null;
+ }
+
+ @Override
+ public Set<TraverserRequirement> getTraverserRequirements() {
+ return null;
+ }
+
+ @Override
+ public void setSideEffects(final TraversalSideEffects sideEffects) {
+
+ }
+
+ @Override
+ public TraversalSideEffects getSideEffects() {
+ return null;
+ }
+
+ @Override
+ public void setStrategies(final TraversalStrategies strategies) {
+
+ }
+
+ @Override
+ public TraversalStrategies getStrategies() {
+ return null;
+ }
+
+ @Override
+ public void setParent(final TraversalParent step) {
+
+ }
+
+ @Override
+ public TraversalParent getParent() {
+ return null;
+ }
+
+ @Override
+ public Admin<T, T> clone() {
+ return null;
+ }
+
+ @Override
+ public boolean isLocked() {
+ return false;
+ }
+
+ @Override
+ public Optional<Graph> getGraph() {
+ return null;
+ }
+
+ @Override
+ public void setGraph(final Graph graph) {
+
+ }
+
+ @Override
+ public boolean hasNext() {
+ return itty.hasNext();
+ }
+
+ @Override
+ public T next() {
+ if (Thread.interrupted()) throw new TraversalInterruptedException();
+ return itty.next();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/03e37e81/gremlin-groovy/pom.xml
----------------------------------------------------------------------
diff --git a/gremlin-groovy/pom.xml b/gremlin-groovy/pom.xml
index acc51b7..dae5e8a 100644
--- a/gremlin-groovy/pom.xml
+++ b/gremlin-groovy/pom.xml
@@ -61,11 +61,6 @@ limitations under the License.
<classifier>indy</classifier>
</dependency>
<dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- <version>3.3.1</version>
- </dependency>
- <dependency>
<groupId>com.github.jeremyh</groupId>
<artifactId>jBCrypt</artifactId>
<version>jbcrypt-0.4</version>
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/03e37e81/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonProvider.java
----------------------------------------------------------------------
diff --git a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonProvider.java b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonProvider.java
index fe04156..3bf4239 100644
--- a/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonProvider.java
+++ b/gremlin-python/src/test/java/org/apache/tinkerpop/gremlin/python/jsr223/PythonProvider.java
@@ -72,6 +72,7 @@ public class PythonProvider extends AbstractGraphProvider {
"shouldNeverPropagateANullValuedTraverser",
"shouldHidePartitionKeyForValues",
"g_withSackXBigInteger_TEN_powX1000X_assignX_V_localXoutXknowsX_barrierXnormSackXX_inXknowsX_barrier_sack",
+ "shouldUsePromiseAndControlTransactionsIfAvailable",
//
ProgramTest.Traversals.class.getCanonicalName(),
TraversalInterruptionTest.class.getCanonicalName(),
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/03e37e81/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/CoreTraversalTest.java
----------------------------------------------------------------------
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/CoreTraversalTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/CoreTraversalTest.java
index bf3acf3..e68d872 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/CoreTraversalTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/CoreTraversalTest.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.process.traversal;
import org.apache.tinkerpop.gremlin.ExceptionCoverage;
import org.apache.tinkerpop.gremlin.FeatureRequirement;
+import org.apache.tinkerpop.gremlin.FeatureRequirementSet;
import org.apache.tinkerpop.gremlin.LoadGraphWith;
import org.apache.tinkerpop.gremlin.process.AbstractGremlinProcessTest;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal;
@@ -37,6 +38,9 @@ import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN;
@@ -272,4 +276,42 @@ public class CoreTraversalTest extends AbstractGremlinProcessTest {
assertEquals(1, IteratorUtils.count(t));
g.tx().rollback();
}
+
+ @Test
+ @FeatureRequirementSet(FeatureRequirementSet.Package.SIMPLE)
+ public void shouldUsePromiseAndControlTransactionsIfAvailable() throws Exception {
+ // this test will validate that transactional graphs can properly open/close transactions within a promise.
+ // as there is a feature check, non-transactional graphs can use this to simply exercise the promise API
+ final Vertex vAdded = g.addV("person").property("name", "stephen").promise(t -> (Vertex) t.next()).get(10000, TimeUnit.MILLISECONDS);
+ final Vertex vRead = g.V().has("name", "stephen").next();
+ assertEquals(vAdded.id(), vRead.id());
+
+ // transaction should have been committed at this point so test the count in this thread to validate persistence
+ assertVertexEdgeCounts(graph, 1, 0);
+
+ // cancel a promise and ensure the transaction ended in failure. hold the traversal in park until it can be
+ // interrupted, then the promise will have to rollback the transaction.
+ final CompletableFuture promiseToCancel = g.addV("person").property("name", "marko").sideEffect(traverser -> {
+ try {
+ Thread.sleep(100000);
+ } catch (Exception ignored) {
+
+ }
+ }).promise(t -> (Vertex) t.next());
+
+ try {
+ promiseToCancel.get(500, TimeUnit.MILLISECONDS);
+ fail("Should have timed out");
+ } catch (TimeoutException te) {
+
+ }
+
+ promiseToCancel.cancel(true);
+
+ // graphs that support transactions will rollback the transaction
+ if (graph.features().graph().supportsTransactions())
+ assertVertexEdgeCounts(graph, 1, 0);
+ else
+ assertVertexEdgeCounts(graph, 2, 0);
+ }
}
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/03e37e81/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/groovy/jsr223/TinkerGraphGroovyTranslatorProvider.java
----------------------------------------------------------------------
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/groovy/jsr223/TinkerGraphGroovyTranslatorProvider.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/groovy/jsr223/TinkerGraphGroovyTranslatorProvider.java
index dd118d7..a595c34 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/groovy/jsr223/TinkerGraphGroovyTranslatorProvider.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/groovy/jsr223/TinkerGraphGroovyTranslatorProvider.java
@@ -51,6 +51,7 @@ public class TinkerGraphGroovyTranslatorProvider extends TinkerGraphProvider {
"g_VX1X_out_injectXv2X_name",
"shouldNeverPropagateANoBulkTraverser",
"shouldNeverPropagateANullValuedTraverser",
+ "shouldUsePromiseAndControlTransactionsIfAvailable",
GraphComputerTest.class.getCanonicalName(),
ProgramTest.Traversals.class.getCanonicalName(),
TraversalInterruptionTest.class.getCanonicalName(),
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/03e37e81/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/jsr223/TinkerGraphJavaTranslatorProvider.java
----------------------------------------------------------------------
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/jsr223/TinkerGraphJavaTranslatorProvider.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/jsr223/TinkerGraphJavaTranslatorProvider.java
index f40a8c7..15a6c0b 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/jsr223/TinkerGraphJavaTranslatorProvider.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/jsr223/TinkerGraphJavaTranslatorProvider.java
@@ -49,6 +49,7 @@ public class TinkerGraphJavaTranslatorProvider extends TinkerGraphProvider {
TraversalInterruptionComputerTest.class.getCanonicalName(),
"shouldNeverPropagateANoBulkTraverser",
"shouldNeverPropagateANullValuedTraverser",
+ "shouldUsePromiseAndControlTransactionsIfAvailable",
ElementIdStrategyProcessTest.class.getCanonicalName(),
EventStrategyProcessTest.class.getCanonicalName(),
ProgramTest.Traversals.class.getCanonicalName()));
[2/4] tinkerpop git commit: Merge branch 'TINKERPOP-1508' into tp32
Posted by sp...@apache.org.
Merge branch 'TINKERPOP-1508' into tp32
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/f55cb359
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/f55cb359
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/f55cb359
Branch: refs/heads/TINKERPOP-1490
Commit: f55cb359363c61727a1c666dc4dbd2331bf6fd87
Parents: eb1dfb8 659865b
Author: Marko A. Rodriguez <ok...@gmail.com>
Authored: Wed Nov 2 06:45:57 2016 -0600
Committer: Marko A. Rodriguez <ok...@gmail.com>
Committed: Wed Nov 2 06:45:57 2016 -0600
----------------------------------------------------------------------
CHANGELOG.asciidoc | 1 +
docs/src/reference/the-traversal.asciidoc | 15 +++++++++++++--
docs/src/upgrade/release-3.2.x-incubating.asciidoc | 17 +++++++++++++++++
.../traversal/dsl/graph/GraphTraversal.java | 12 ++++++++++++
.../gremlin/process/traversal/dsl/graph/__.java | 14 ++++++++++++++
.../process/traversal/step/branch/BranchStep.java | 11 +++--------
.../traversal/step/branch/ChooseStepTest.java | 11 +++++++++--
.../traversal/step/branch/GroovyChooseTest.groovy | 5 +++++
.../process/traversal/step/branch/ChooseTest.java | 16 ++++++++++++++++
9 files changed, 90 insertions(+), 12 deletions(-)
----------------------------------------------------------------------