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 2021/09/02 15:19:34 UTC
[tinkerpop] 09/10: TINKERPOP-2605 Improved null handling in
reducing math steps.
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch TINKERPOP-2605
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 89119c39a0e9a4d996559f2a3986bdfca41f29de
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Sun Aug 29 12:15:40 2021 -0400
TINKERPOP-2605 Improved null handling in reducing math steps.
---
CHANGELOG.asciidoc | 2 +
docs/src/dev/developer/for-committers.asciidoc | 3 +-
docs/src/reference/the-traversal.asciidoc | 78 ++++++++++--
docs/src/upgrade/release-3.5.x.asciidoc | 5 +-
.../process/traversal/step/map/MaxGlobalStep.java | 1 -
.../process/traversal/step/map/MeanGlobalStep.java | 22 +++-
.../process/traversal/step/map/MeanLocalStep.java | 18 ++-
.../process/traversal/step/map/MinGlobalStep.java | 1 -
.../process/traversal/step/map/SumGlobalStep.java | 16 ++-
.../process/traversal/step/map/SumLocalStep.java | 11 +-
.../traversal/step/util/ReducingBarrierStep.java | 59 ++++++---
.../gremlin/structure/io/gryo/GryoVersion.java | 10 +-
.../tinkerpop/gremlin/util/NumberHelper.java | 136 +++++++++++++++++++++
.../tinkerpop/gremlin/util/NumberHelperTest.java | 112 +++++++++++------
.../Gherkin/GherkinTestRunner.cs | 9 +-
.../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 20 +++
.../gremlin-javascript/test/cucumber/gremlin.js | 20 +++
gremlin-python/src/main/python/radish/gremlin.py | 20 +++
.../GraphBinaryRemoteGraphComputerProvider.java | 18 ++-
.../GraphSONRemoteGraphComputerProvider.java | 18 ++-
.../remote/GryoRemoteGraphComputerProvider.java | 18 ++-
...emoteGraphGroovyTranslatorComputerProvider.java | 18 ++-
gremlin-test/features/map/Max.feature | 44 +++++++
gremlin-test/features/map/Mean.feature | 73 +++++++++++
gremlin-test/features/map/Min.feature | 44 +++++++
gremlin-test/features/map/Sum.feature | 73 +++++++++++
.../gremlin/process/GremlinProcessRunner.java | 4 +-
.../process/traversal/step/map/MaxTest.java | 63 ++++++++++
.../process/traversal/step/map/MeanTest.java | 93 ++++++++++++++
.../process/traversal/step/map/MinTest.java | 63 ++++++++++
.../process/traversal/step/map/SumTest.java | 100 +++++++++++++++
.../spark/process/computer/SparkMemory.java | 1 -
.../tinkergraph/process/computer/TinkerMemory.java | 21 ++--
.../process/TinkerGraphComputerProvider.java | 3 +-
34 files changed, 1102 insertions(+), 95 deletions(-)
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 9762fca..6557c8b 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -31,6 +31,8 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
* Allowed `null` string values in the Gremlin grammar.
* Prevented exception with `hasLabel(null)` and instead filter away traversers as labels can't ever be null.
* Improved handling of `null` when passed to `P` predicates.
+* Handled `null` for mathematical reducing operations of `sum()`, `mean()`, `max()` and `min()`.
+* Allowed `null` values in `Memory` for `GraphComputer`.
* Refined `DotNetTranslator` to be more explicit with `null` arguments to ensure that the right overloads are called.
* Created `GremlinParser` to construct `Traversal` objects from `gremlin-language`.
diff --git a/docs/src/dev/developer/for-committers.asciidoc b/docs/src/dev/developer/for-committers.asciidoc
index 6abcd0f..e020b71 100644
--- a/docs/src/dev/developer/for-committers.asciidoc
+++ b/docs/src/dev/developer/for-committers.asciidoc
@@ -342,7 +342,8 @@ edge identifier or the `.sid` suffix which gets a string representation of the e
* Lambda - *c[_xxx_]* - The "xxx" should contain a lambda written in Groovy.
* List - *l[_xxx_,_yyy_,_zzz_,...]* - A comma separated collection of values that make up the list should be added to
between the square brackets. These values respect the type system thus allowing for creation of lists of vertices,
-edges, maps, and any other available type.
+edges, maps, and any other available type. Spaces are taken literally, therefore it is important to avoid spaces unless
+they are required for the test.
* Map - *m[_xxx_]* - The "xxx" should be replaced with a JSON string. Note that keys and values will be parsed using
the type notation system so that it is possible to have maps containing arbitrary keys and values.
* Numeric - *d[_xxx_]._y_* - The "xxx" should be replaced with a number. The suffix denoted by "y" should always be
diff --git a/docs/src/reference/the-traversal.asciidoc b/docs/src/reference/the-traversal.asciidoc
index 92f182c..1b662eb 100644
--- a/docs/src/reference/the-traversal.asciidoc
+++ b/docs/src/reference/the-traversal.asciidoc
@@ -2162,7 +2162,8 @@ link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gre
[[max-step]]
=== Max Step
-The `max()`-step (*map*) operates on a stream of comparable objects and determines which is the last object according to its natural order in the stream.
+The `max()`-step (*map*) operates on a stream of comparable objects and determines which is the last object according
+to its natural order in the stream.
[gremlin-groovy,modern]
----
@@ -2171,8 +2172,22 @@ g.V().repeat(both()).times(3).values('age').max()
g.V().values('name').max()
----
-IMPORTANT: `max(local)` determines the max of the current, local object (not the objects in the traversal stream).
-This works for `Collection` and `Comparable`-type objects.
+When called as `max(local)` it determines the maximum value of the current, local object (not the objects in the
+traversal stream). This works for `Collection` and `Comparable`-type objects.
+
+[gremlin-groovy,modern]
+----
+g.V().values('age').fold().max(local)
+----
+
+When there are `null` values being evaluated the `null` objects are ignored, but if all values are recognized as `null`
+the return value is `null`.
+
+[gremlin-groovy,modern]
+----
+g.inject(null,10, 9, null).max()
+g.inject([null,null,null]).max(local)
+----
*Additional References*
@@ -2195,8 +2210,22 @@ g.V().repeat(both()).times(3).values('age').dedup().mean()
<1> Realize that traversers are being bulked by `repeat()`. There may be more of a particular number than another,
thus altering the average.
-IMPORTANT: `mean(local)` determines the mean of the current, local object (not the objects in the traversal stream).
-This works for `Collection` and `Number`-type objects.
+When called as `mean(local)` it determines the mean of the current, local object (not the objects in the traversal
+stream). This works for `Collection` and `Number`-type objects.
+
+[gremlin-groovy,modern]
+----
+g.V().values('age').fold().mean()
+----
+
+If `mean()` encounters `null` values, they will be ignored (i.e. their traversers not counted toward toward the
+divisor). If all traversers are `null` then the stream will return `null`.
+
+[gremlin-groovy,modern]
+----
+g.inject(null,10, 9, null).mean()
+g.inject([null,null,null]).mean(local)
+----
*Additional References*
@@ -2207,7 +2236,8 @@ link:++https://tinkerpop.apache.org/javadocs/x.y.z/core/org/apache/tinkerpop/gre
[[min-step]]
=== Min Step
-The `min()`-step (*map*) operates on a stream of comparable objects and determines which is the first object according to its natural order in the stream.
+The `min()`-step (*map*) operates on a stream of comparable objects and determines which is the first object according
+to its natural order in the stream.
[gremlin-groovy,modern]
----
@@ -2216,8 +2246,22 @@ g.V().repeat(both()).times(3).values('age').min()
g.V().values('name').min()
----
-IMPORTANT: `min(local)` determines the min of the current, local object (not the objects in the traversal stream).
-This works for `Collection` and `Comparable`-type objects.
+When called as `min(local)` it determines the minimum value of the current, local object (not the objects in the
+traversal stream). This works for `Collection` and `Comparable`-type objects.
+
+[gremlin-groovy,modern]
+----
+g.V().values('age').fold().min(local)
+----
+
+When there are `null` values being evaluated the `null` objects are ignored, but if all values are recognized as `null`
+the return value is `null`.
+
+[gremlin-groovy,modern]
+----
+g.inject(null,10, 9, null).min()
+g.inject([null,null,null]).min(local)
+----
*Additional References*
@@ -3457,8 +3501,22 @@ g.V().values('age').sum()
g.V().repeat(both()).times(3).values('age').sum()
----
-IMPORTANT: `sum(local)` determines the sum of the current, local object (not the objects in the traversal stream).
-This works for `Collection`-type objects.
+When called as `sum(local)` it determines the sum of the current, local object (not the objects in the traversal
+stream). This works for `Collection`-type objects.
+
+[gremlin-groovy,modern]
+----
+g.V().values('age').fold().sum(local)
+----
+
+When there are `null` values being evaluated the `null` objects are ignored, but if all values are recognized as `null`
+the return value is `null`.
+
+[gremlin-groovy,modern]
+----
+g.inject(null,10, 9, null).sum()
+g.inject([null,null,null]).sum(local)
+----
*Additional References*
diff --git a/docs/src/upgrade/release-3.5.x.asciidoc b/docs/src/upgrade/release-3.5.x.asciidoc
index 5a0d66b..b22965b 100644
--- a/docs/src/upgrade/release-3.5.x.asciidoc
+++ b/docs/src/upgrade/release-3.5.x.asciidoc
@@ -37,8 +37,11 @@ been noted that supplying a `null` value as an argument to certain Gremlin steps
were either not clear or perhaps even unexpected. The following changes were made to help make these behaviors more
consistent and to further solidify the semantics of `null` usage in the Gremlin language:
-* `hasLabel(null)` and `has(T.label,null)` - Formerly, these methods threw general `NullPointerException` failures,
+* `hasLabel(null)` and `has(T.label,null)` - These methods used to throw general `NullPointerException` failures,
but now filter results. As a `T.label` can't be `null`, it will effectively filter all elements.
+* `sum()`, `mean()`, `max()` and `min()` - These methods used to throw general `NullPointerException` failures, but
+now ignore `null` values when other numbers are present. If all of the values in the stream are `null` then `hasNext()`
+return `false`.
See: link:https://issues.apache.org/jira/browse/TINKERPOP-2605[TINKERPOP-2605]
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MaxGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MaxGlobalStep.java
index 2873f55..1535a11 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MaxGlobalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MaxGlobalStep.java
@@ -36,7 +36,6 @@ public final class MaxGlobalStep<S extends Comparable> extends ReducingBarrierSt
public MaxGlobalStep(final Traversal.Admin traversal) {
super(traversal);
- this.setSeedSupplier(new ConstantSupplier<>((S) Double.valueOf(Double.NaN)));
this.setReducingBiOperator((BinaryOperator) Operator.max);
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanGlobalStep.java
index 5e90336..8f03ef5 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanGlobalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanGlobalStep.java
@@ -44,10 +44,23 @@ public final class MeanGlobalStep<S extends Number, E extends Number> extends Re
public MeanGlobalStep(final Traversal.Admin traversal) {
super(traversal);
- this.setSeedSupplier((Supplier) MeanNumberSupplier.instance());
this.setReducingBiOperator(MeanGlobalBiOperator.INSTANCE);
}
+ /**
+ * Advances the starts until a non-null value is found or simply returns {@code null}. In this way, an all
+ * {@code null} stream will result in {@code null}.
+ */
+ @Override
+ protected E generateSeedFromStarts() {
+ E e = null;
+ while (starts.hasNext() && null == e) {
+ e = projectTraverser(this.starts.next());
+ }
+
+ return e;
+ }
+
@Override
public void processAllStarts() {
if (this.starts.hasNext())
@@ -56,7 +69,7 @@ public final class MeanGlobalStep<S extends Number, E extends Number> extends Re
@Override
public E projectTraverser(final Traverser.Admin<S> traverser) {
- return (E) new MeanNumber(traverser.get(), traverser.bulk());
+ return null == traverser.get() ? null : (E) new MeanNumber(traverser.get(), traverser.bulk());
}
@Override
@@ -66,7 +79,8 @@ public final class MeanGlobalStep<S extends Number, E extends Number> extends Re
@Override
public E generateFinalResult(final E meanNumber) {
- return (E) ((MeanNumber) meanNumber).getFinal();
+ // if the meanNumber is null it means the whole stream was null
+ return null == meanNumber ? (E) null : (E) ((MeanNumber) meanNumber).getFinal();
}
/////
@@ -77,6 +91,8 @@ public final class MeanGlobalStep<S extends Number, E extends Number> extends Re
@Override
public S apply(final S mutatingSeed, final S number) {
+ if (null == mutatingSeed || null == number) return mutatingSeed;
+
if (mutatingSeed instanceof MeanNumber) {
return (number instanceof MeanNumber) ?
(S) ((MeanNumber) mutatingSeed).add((MeanNumber) number) :
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanLocalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanLocalStep.java
index 5ffb1a6..2ed1440 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanLocalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanLocalStep.java
@@ -42,17 +42,29 @@ public final class MeanLocalStep<E extends Number, S extends Iterable<E>> extend
protected Number map(final Traverser.Admin<S> traverser) {
final Iterator<E> iterator = traverser.get().iterator();
if (iterator.hasNext()) {
+ // forward the iterator to the first non-null or return null
+ E result = untilNonNull(iterator);
Long counter = 1L;
- E result = iterator.next();
while (iterator.hasNext()) {
- result = (E) NumberHelper.add(result, iterator.next());
- counter++;
+ final Number n = iterator.next();
+ if (n != null) {
+ result = (E) NumberHelper.add(result, n);
+ counter++;
+ }
}
return NumberHelper.div(result, counter, true);
}
throw FastNoSuchElementException.instance();
}
+ private E untilNonNull(final Iterator<E> itty) {
+ E result = null;
+ while (itty.hasNext() && null == result) {
+ result = itty.next();
+ }
+ return result;
+ }
+
@Override
public Set<TraverserRequirement> getRequirements() {
return Collections.singleton(TraverserRequirement.OBJECT);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MinGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MinGlobalStep.java
index 781e93d..db6d192 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MinGlobalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MinGlobalStep.java
@@ -36,7 +36,6 @@ public final class MinGlobalStep<S extends Comparable> extends ReducingBarrierSt
public MinGlobalStep(final Traversal.Admin traversal) {
super(traversal);
- this.setSeedSupplier(new ConstantSupplier<>((S) Double.valueOf(Double.NaN)));
this.setReducingBiOperator((BinaryOperator) Operator.min);
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumGlobalStep.java
index 3711cfe..21a6187 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumGlobalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumGlobalStep.java
@@ -43,10 +43,23 @@ public final class SumGlobalStep<S extends Number> extends ReducingBarrierStep<S
public SumGlobalStep(final Traversal.Admin traversal) {
super(traversal);
- this.setSeedSupplier(new ConstantSupplier<>((S) Integer.valueOf(0)));
this.setReducingBiOperator((BinaryOperator) Operator.sum);
}
+ /**
+ * Advances the starts until a non-null value is found or simply returns {@code null}. In this way, an all
+ * {@code null} stream will result in {@code null}.
+ */
+ @Override
+ protected S generateSeedFromStarts() {
+ S s = null;
+ while (starts.hasNext() && null == s) {
+ s = projectTraverser(this.starts.next());
+ }
+
+ return s;
+ }
+
@Override
public void processAllStarts() {
if (this.starts.hasNext())
@@ -58,7 +71,6 @@ public final class SumGlobalStep<S extends Number> extends ReducingBarrierStep<S
return (S) mul(traverser.get(), traverser.bulk());
}
-
@Override
public Set<TraverserRequirement> getRequirements() {
return REQUIREMENTS;
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumLocalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumLocalStep.java
index 3249e31..e3284e1 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumLocalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumLocalStep.java
@@ -42,7 +42,8 @@ public final class SumLocalStep<E extends Number, S extends Iterable<E>> extends
protected E map(final Traverser.Admin<S> traverser) {
final Iterator<E> iterator = traverser.get().iterator();
if (iterator.hasNext()) {
- Number result = iterator.next();
+ // forward the iterator to the first non-null or return null
+ Number result = untilNonNull(iterator);
while (iterator.hasNext()) {
result = NumberHelper.add(result, iterator.next());
}
@@ -51,6 +52,14 @@ public final class SumLocalStep<E extends Number, S extends Iterable<E>> extends
throw FastNoSuchElementException.instance();
}
+ private E untilNonNull(final Iterator<E> itty) {
+ E result = null;
+ while (itty.hasNext() && null == result) {
+ result = itty.next();
+ }
+ return result;
+ }
+
@Override
public Set<TraverserRequirement> getRequirements() {
return Collections.singleton(TraverserRequirement.OBJECT);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ReducingBarrierStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ReducingBarrierStep.java
index 62e12f8..b39576f 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ReducingBarrierStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/util/ReducingBarrierStep.java
@@ -24,8 +24,11 @@ import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.step.Barrier;
import org.apache.tinkerpop.gremlin.process.traversal.step.Generating;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.SumGlobalStep;
import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
+import java.io.Serializable;
+import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.function.Supplier;
@@ -34,10 +37,21 @@ import java.util.function.Supplier;
*/
public abstract class ReducingBarrierStep<S, E> extends AbstractStep<S, E> implements Barrier<E>, Generating<E, E> {
- protected Supplier<E> seedSupplier;
+ /**
+ * A seed value not to be emitted from the {@code ReducingBarrierStep} helping with flow control within this step.
+ */
+ public static final Object NON_EMITTING_SEED = NonEmittingSeed.INSTANCE;
+
+ /**
+ * If the {@code seedSupplier} is {@code null} then the default behavior is to generate the seed from the starts.
+ * This supplier must be callable as a constant and not rely on state from the class. Prefer overriding
+ * {@link #generateSeedFromStarts()} otherwise.
+ */
+ protected Supplier<E> seedSupplier = null;
protected BinaryOperator<E> reducingBiOperator;
- private boolean hasProcessedOnce = false;
- private E seed = null;
+ protected boolean hasProcessedOnce = false;
+
+ private E seed = (E) NON_EMITTING_SEED;
public ReducingBarrierStep(final Traversal.Admin traversal) {
super(traversal);
@@ -47,8 +61,18 @@ public abstract class ReducingBarrierStep<S, E> extends AbstractStep<S, E> imple
this.seedSupplier = seedSupplier;
}
+ /**
+ * Gets the provided seed supplier or provides {@link #generateSeedFromStarts()}.
+ */
public Supplier<E> getSeedSupplier() {
- return this.seedSupplier;
+ return Optional.ofNullable(this.seedSupplier).orElse(this::generateSeedFromStarts);
+ }
+
+ /**
+ * If the {@code seedSupplier} is {@code null} then this method is called.
+ */
+ protected E generateSeedFromStarts() {
+ return starts.hasNext() ? projectTraverser(this.starts.next()) : null;
}
public abstract E projectTraverser(final Traverser.Admin<S> traverser);
@@ -64,13 +88,13 @@ public abstract class ReducingBarrierStep<S, E> extends AbstractStep<S, E> imple
public void reset() {
super.reset();
this.hasProcessedOnce = false;
- this.seed = null;
+ this.seed = (E) NON_EMITTING_SEED;
}
@Override
public void done() {
this.hasProcessedOnce = true;
- this.seed = null;
+ this.seed = (E) NON_EMITTING_SEED;
}
@Override
@@ -78,7 +102,11 @@ public abstract class ReducingBarrierStep<S, E> extends AbstractStep<S, E> imple
if (this.hasProcessedOnce && !this.starts.hasNext())
return;
this.hasProcessedOnce = true;
- if (this.seed == null) this.seed = this.seedSupplier.get();
+
+ if (this.seed == NON_EMITTING_SEED) {
+ this.seed = getSeedSupplier().get();
+ }
+
while (this.starts.hasNext())
this.seed = this.reducingBiOperator.apply(this.seed, this.projectTraverser(this.starts.next()));
}
@@ -86,7 +114,7 @@ public abstract class ReducingBarrierStep<S, E> extends AbstractStep<S, E> imple
@Override
public boolean hasNextBarrier() {
this.processAllStarts();
- return null != this.seed;
+ return NON_EMITTING_SEED != this.seed;
}
@Override
@@ -95,14 +123,14 @@ public abstract class ReducingBarrierStep<S, E> extends AbstractStep<S, E> imple
throw FastNoSuchElementException.instance();
else {
final E temp = this.seed;
- this.seed = null;
+ this.seed = (E) NON_EMITTING_SEED;
return temp;
}
}
@Override
public void addBarrier(final E barrier) {
- this.seed = null == this.seed ?
+ this.seed = NON_EMITTING_SEED == this.seed ?
barrier :
this.reducingBiOperator.apply(this.seed, barrier);
}
@@ -110,10 +138,9 @@ public abstract class ReducingBarrierStep<S, E> extends AbstractStep<S, E> imple
@Override
public Traverser.Admin<E> processNextStart() {
this.processAllStarts();
- if (this.seed == null)
- throw FastNoSuchElementException.instance();
+ if (this.seed == NON_EMITTING_SEED) throw FastNoSuchElementException.instance();
final Traverser.Admin<E> traverser = this.getTraversal().getTraverserGenerator().generate(this.generateFinalResult(this.seed), (Step<E, E>) this, 1l);
- this.seed = null;
+ this.seed = (E) NON_EMITTING_SEED;
return traverser;
}
@@ -121,7 +148,7 @@ public abstract class ReducingBarrierStep<S, E> extends AbstractStep<S, E> imple
public ReducingBarrierStep<S, E> clone() {
final ReducingBarrierStep<S, E> clone = (ReducingBarrierStep<S, E>) super.clone();
clone.hasProcessedOnce = false;
- clone.seed = null;
+ clone.seed = (E) NON_EMITTING_SEED;
return clone;
}
@@ -130,4 +157,8 @@ public abstract class ReducingBarrierStep<S, E> extends AbstractStep<S, E> imple
return MemoryComputeKey.of(this.getId(), this.getBiOperator(), false, true);
}
+ public static final class NonEmittingSeed implements Serializable {
+ public static final NonEmittingSeed INSTANCE = new NonEmittingSeed();
+ private NonEmittingSeed() {}
+ }
}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
index eb9566e..f6d06fe 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
@@ -45,6 +45,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.map.OrderGlobalStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.TreeStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.BulkSet;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.ProfileStep;
+import org.apache.tinkerpop.gremlin.process.traversal.step.util.ReducingBarrierStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.Tree;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ConnectiveStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.HaltedTraverserStrategy;
@@ -335,7 +336,7 @@ public enum GryoVersion {
add(GryoTypeReg.of(HaltedTraverserStrategy.class, 139));
add(GryoTypeReg.of(PartitionStrategy.class, 140, new JavaSerializer()));
add(GryoTypeReg.of(SubgraphStrategy.class, 141, new JavaSerializer()));
- add(GryoTypeReg.of(SeedStrategy.class, 192, new JavaSerializer())); // ***LAST ID***
+ add(GryoTypeReg.of(SeedStrategy.class, 192, new JavaSerializer()));
add(GryoTypeReg.of(VertexProgramStrategy.class, 142, new JavaSerializer()));
add(GryoTypeReg.of(MatchAlgorithmStrategy.class, 143));
add(GryoTypeReg.of(MatchStep.GreedyMatchAlgorithm.class, 144));
@@ -401,6 +402,7 @@ public enum GryoVersion {
add(GryoTypeReg.of(OrderGlobalStep.OrderBiOperator.class, 118));
add(GryoTypeReg.of(ProfileStep.ProfileBiOperator.class, 119));
add(GryoTypeReg.of(IndexedTraverserSet.VertexIndexedTraverserSet.class, 173));
+ add(GryoTypeReg.of(ReducingBarrierStep.NonEmittingSeed.class, 194)); // ***LAST ID***
add(GryoTypeReg.of(B_LP_NL_O_P_S_SE_SL_Traverser.class, 174));
add(GryoTypeReg.of(B_NL_O_S_SE_SL_Traverser.class, 175));
@@ -516,7 +518,7 @@ public enum GryoVersion {
add(GryoTypeReg.of(Bytecode.class, 122, new GryoSerializersV1d0.BytecodeSerializer()));
add(GryoTypeReg.of(P.class, 124, new GryoSerializersV1d0.PSerializer()));
- add(GryoTypeReg.of(TextP.class, 186, new GryoSerializersV1d0.TextPSerializer())); // ***LAST ID***
+ add(GryoTypeReg.of(TextP.class, 186, new GryoSerializersV1d0.TextPSerializer()));
add(GryoTypeReg.of(Lambda.class, 125, new GryoSerializersV1d0.LambdaSerializer()));
add(GryoTypeReg.of(Bytecode.Binding.class, 126, new GryoSerializersV1d0.BindingSerializer()));
add(GryoTypeReg.of(Order.class, 127));
@@ -565,6 +567,8 @@ public enum GryoVersion {
add(GryoTypeReg.of(MeanGlobalStep.MeanGlobalBiOperator.class, 110));
add(GryoTypeReg.of(MeanGlobalStep.MeanNumber.class, 111));
add(GryoTypeReg.of(TreeStep.TreeBiOperator.class, 112));
+ add(GryoTypeReg.of(ReducingBarrierStep.NonEmittingSeed.class, 194)); // ***LAST ID***
+
// skip 113
add(GryoTypeReg.of(RangeGlobalStep.RangeBiOperator.class, 114));
add(GryoTypeReg.of(OrderGlobalStep.OrderBiOperator.class, 118, new JavaSerializer()));
@@ -574,7 +578,7 @@ public enum GryoVersion {
add(GryoTypeReg.of(HaltedTraverserStrategy.class, 139));
add(GryoTypeReg.of(PartitionStrategy.class, 140, new JavaSerializer()));
add(GryoTypeReg.of(SubgraphStrategy.class, 141, new JavaSerializer()));
- add(GryoTypeReg.of(SeedStrategy.class, 192, new JavaSerializer())); // ***LAST ID***
+ add(GryoTypeReg.of(SeedStrategy.class, 192, new JavaSerializer()));
add(GryoTypeReg.of(VertexProgramStrategy.class, 142, new JavaSerializer()));
add(GryoTypeReg.of(MatchAlgorithmStrategy.class, 143));
add(GryoTypeReg.of(MatchStep.GreedyMatchAlgorithm.class, 144));
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/NumberHelper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/NumberHelper.java
index c457c58..8293195 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/NumberHelper.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/NumberHelper.java
@@ -316,36 +316,127 @@ public final class NumberHelper {
return determineNumberClass(bits, fp);
}
+ /**
+ * Adds two numbers returning the highest common number class between them.
+ *
+ * <pre>
+ * a = 1, b = 1 -> 2
+ * a = null, b = 1 -> null
+ * a = 1, b = null -> 1
+ * a = null, b = null -> null
+ * </pre>
+ *
+ * @param a should be thought of as the seed to be modified by {@code b}
+ * @param b the modifier to {code a}
+ */
public static Number add(final Number a, final Number b) {
+ if (null == a || null == b) return a;
final Class<? extends Number> clazz = getHighestCommonNumberClass(a, b);
return getHelper(clazz).add.apply(a, b);
}
+
+ /**
+ * Subtracts two numbers returning the highest common number class between them.
+ *
+ * <pre>
+ * a = 1, b = 1 -> 0
+ * a = null, b = 1 -> null
+ * a = 1, b = null -> 1
+ * a = null, b = null -> null
+ * </pre>
+ *
+ * @param a should be thought of as the seed to be modified by {@code b}
+ * @param b the modifier to {code a}
+ */
public static Number sub(final Number a, final Number b) {
+ if (null == a || null == b) return a;
final Class<? extends Number> clazz = getHighestCommonNumberClass(a, b);
return getHelper(clazz).sub.apply(a, b);
}
+ /**
+ * Multiplies two numbers returning the highest common number class between them.
+ *
+ * <pre>
+ * a = 1, b = 2 -> 2
+ * a = null, b = 1 -> null
+ * a = 1, b = null -> 1
+ * a = null, b = null -> null
+ * </pre>
+ *
+ * @param a should be thought of as the seed to be modified by {@code b}
+ * @param b the modifier to {code a}
+ */
public static Number mul(final Number a, final Number b) {
+ if (null == a || null == b) return a;
final Class<? extends Number> clazz = getHighestCommonNumberClass(a, b);
return getHelper(clazz).mul.apply(a, b);
}
+ /**
+ * Divides two numbers returning the highest common number class between them calling
+ * {@link #div(Number, Number, boolean)} with a {@code false}.
+ */
public static Number div(final Number a, final Number b) {
+ if (null == a || null == b) return a;
return div(a, b, false);
}
+ /**
+ * Divides two numbers returning the highest common number class between them.
+ *
+ * <pre>
+ * a = 4, b = 2 -> 2
+ * a = null, b = 1 -> null
+ * a = 1, b = null -> 1
+ * a = null, b = null -> null
+ * </pre>
+ *
+ * @param a should be thought of as the seed to be modified by {@code b}
+ * @param b the modifier to {code a}
+ * @param forceFloatingPoint when set to {@code true} ensures that the return value is the highest common floating number class
+ */
public static Number div(final Number a, final Number b, final boolean forceFloatingPoint) {
+ if (null == a || null == b) return null;
final Class<? extends Number> clazz = getHighestCommonNumberClass(forceFloatingPoint, a, b);
return getHelper(clazz).div.apply(a, b);
}
+ /**
+ * Gets the smaller number of the two provided returning the highest common number class between them.
+ *
+ * <pre>
+ * a = 4, b = 2 -> 2
+ * a = null, b = 1 -> 1
+ * a = 1, b = null -> 1
+ * a = null, b = null -> null
+ * </pre>
+ */
public static Number min(final Number a, final Number b) {
+ if (null == a && null == b) return null;
final Class<? extends Number> clazz = getHighestCommonNumberClass(a, b);
return getHelper(clazz).min.apply(a, b);
}
+ /**
+ * Gets the smaller number of the two provided returning the highest common number class between them.
+ *
+ * <pre>
+ * a = 4, b = 2 -> 2
+ * a = null, b = 1 -> 1
+ * a = 1, b = null -> 1
+ * a = null, b = null -> null
+ * </pre>
+ */
public static Comparable min(final Comparable a, final Comparable b) {
+ if (null == a || null == b) {
+ if (null == a && null == b)
+ return null;
+ else
+ return null == a ? b : a;
+ }
+
if (a instanceof Number && b instanceof Number && !a.equals(Double.NaN) && !b.equals(Double.NaN)) {
final Number an = (Number) a, bn = (Number) b;
final Class<? extends Number> clazz = getHighestCommonNumberClass(an, bn);
@@ -356,12 +447,40 @@ public final class NumberHelper {
a.compareTo(b) < 0 ? a : b;
}
+ /**
+ * Gets the larger number of the two provided returning the highest common number class between them.
+ *
+ * <pre>
+ * a = 4, b = 2 -> 4
+ * a = null, b = 1 -> 1
+ * a = 1, b = null -> 1
+ * a = null, b = null -> null
+ * </pre>
+ */
public static Number max(final Number a, final Number b) {
+ if (null == a && null == b) return null;
final Class<? extends Number> clazz = getHighestCommonNumberClass(a, b);
return getHelper(clazz).max.apply(a, b);
}
+ /**
+ * Gets the larger number of the two provided returning the highest common number class between them.
+ *
+ * <pre>
+ * a = 4, b = 2 -> 4
+ * a = null, b = 1 -> 1
+ * a = 1, b = null -> 1
+ * a = null, b = null -> null
+ * </pre>
+ */
public static Comparable max(final Comparable a, final Comparable b) {
+ if (null == a || null == b) {
+ if (null == a && null == b)
+ return null;
+ else
+ return null == a ? b : a;
+ }
+
if (a instanceof Number && b instanceof Number && !a.equals(Double.NaN) && !b.equals(Double.NaN)) {
final Number an = (Number) a, bn = (Number) b;
final Class<? extends Number> clazz = getHighestCommonNumberClass(an, bn);
@@ -372,7 +491,24 @@ public final class NumberHelper {
a.compareTo(b) > 0 ? a : b;
}
+ /**
+ * Compares two numbers.
+ *
+ * <pre>
+ * a = 4, b = 2 -> 1
+ * a = 2, b = 4 -> -1
+ * a = null, b = 1 -> -1
+ * a = 1, b = null -> 1
+ * a = null, b = null -> 0
+ * </pre>
+ */
public static Integer compare(final Number a, final Number b) {
+ if (null == a || null == b) {
+ if (a == b)
+ return 0;
+ else
+ return null == a ? -1 : 1;
+ }
final Class<? extends Number> clazz = getHighestCommonNumberClass(a, b);
return getHelper(clazz).cmp.apply(a, b);
}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/NumberHelperTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/NumberHelperTest.java
index 8f919d8..01ad1ca 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/NumberHelperTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/NumberHelperTest.java
@@ -36,6 +36,7 @@ import static org.apache.tinkerpop.gremlin.util.NumberHelper.min;
import static org.apache.tinkerpop.gremlin.util.NumberHelper.mul;
import static org.apache.tinkerpop.gremlin.util.NumberHelper.sub;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
@@ -50,49 +51,49 @@ public class NumberHelperTest {
private final static List<Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>> COMMON_NUMBER_CLASSES =
Arrays.asList(
// BYTE
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>((byte) 1, (byte) 1, Byte.class, Float.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>((byte) 1, (short) 1, Short.class, Float.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>((byte) 1, 1, Integer.class, Float.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>((byte) 1, 1L, Long.class, Double.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>((byte) 1, 1F, Float.class, Float.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>((byte) 1, 1D, Double.class, Double.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>((byte) 1, BigInteger.ONE, BigInteger.class, BigDecimal.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>((byte) 1, BigDecimal.ONE, BigDecimal.class, BigDecimal.class),
+ new Quartet<>((byte) 1, (byte) 1, Byte.class, Float.class),
+ new Quartet<>((byte) 1, (short) 1, Short.class, Float.class),
+ new Quartet<>((byte) 1, 1, Integer.class, Float.class),
+ new Quartet<>((byte) 1, 1L, Long.class, Double.class),
+ new Quartet<>((byte) 1, 1F, Float.class, Float.class),
+ new Quartet<>((byte) 1, 1D, Double.class, Double.class),
+ new Quartet<>((byte) 1, BigInteger.ONE, BigInteger.class, BigDecimal.class),
+ new Quartet<>((byte) 1, BigDecimal.ONE, BigDecimal.class, BigDecimal.class),
// SHORT
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>((short) 1, (short) 1, Short.class, Float.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>((short) 1, 1, Integer.class, Float.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>((short) 1, 1L, Long.class, Double.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>((short) 1, 1F, Float.class, Float.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>((short) 1, 1D, Double.class, Double.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>((short) 1, BigInteger.ONE, BigInteger.class, BigDecimal.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>((short) 1, BigDecimal.ONE, BigDecimal.class, BigDecimal.class),
+ new Quartet<>((short) 1, (short) 1, Short.class, Float.class),
+ new Quartet<>((short) 1, 1, Integer.class, Float.class),
+ new Quartet<>((short) 1, 1L, Long.class, Double.class),
+ new Quartet<>((short) 1, 1F, Float.class, Float.class),
+ new Quartet<>((short) 1, 1D, Double.class, Double.class),
+ new Quartet<>((short) 1, BigInteger.ONE, BigInteger.class, BigDecimal.class),
+ new Quartet<>((short) 1, BigDecimal.ONE, BigDecimal.class, BigDecimal.class),
// INTEGER
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1, 1, Integer.class, Float.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1, 1L, Long.class, Double.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1, 1F, Float.class, Float.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1, 1D, Double.class, Double.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1, BigInteger.ONE, BigInteger.class, BigDecimal.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1, BigDecimal.ONE, BigDecimal.class, BigDecimal.class),
+ new Quartet<>(1, 1, Integer.class, Float.class),
+ new Quartet<>(1, 1L, Long.class, Double.class),
+ new Quartet<>(1, 1F, Float.class, Float.class),
+ new Quartet<>(1, 1D, Double.class, Double.class),
+ new Quartet<>(1, BigInteger.ONE, BigInteger.class, BigDecimal.class),
+ new Quartet<>(1, BigDecimal.ONE, BigDecimal.class, BigDecimal.class),
// LONG
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1L, 1L, Long.class, Double.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1L, 1F, Double.class, Double.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1L, 1D, Double.class, Double.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1L, BigInteger.ONE, BigInteger.class, BigDecimal.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1L, BigDecimal.ONE, BigDecimal.class, BigDecimal.class),
+ new Quartet<>(1L, 1L, Long.class, Double.class),
+ new Quartet<>(1L, 1F, Double.class, Double.class),
+ new Quartet<>(1L, 1D, Double.class, Double.class),
+ new Quartet<>(1L, BigInteger.ONE, BigInteger.class, BigDecimal.class),
+ new Quartet<>(1L, BigDecimal.ONE, BigDecimal.class, BigDecimal.class),
// FLOAT
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1F, 1F, Float.class, Float.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1F, 1D, Double.class, Double.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1F, BigInteger.ONE, BigDecimal.class, BigDecimal.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1F, BigDecimal.ONE, BigDecimal.class, BigDecimal.class),
+ new Quartet<>(1F, 1F, Float.class, Float.class),
+ new Quartet<>(1F, 1D, Double.class, Double.class),
+ new Quartet<>(1F, BigInteger.ONE, BigDecimal.class, BigDecimal.class),
+ new Quartet<>(1F, BigDecimal.ONE, BigDecimal.class, BigDecimal.class),
// DOUBLE
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1D, 1D, Double.class, Double.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1D, BigInteger.ONE, BigDecimal.class, BigDecimal.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(1D, BigDecimal.ONE, BigDecimal.class, BigDecimal.class),
+ new Quartet<>(1D, 1D, Double.class, Double.class),
+ new Quartet<>(1D, BigInteger.ONE, BigDecimal.class, BigDecimal.class),
+ new Quartet<>(1D, BigDecimal.ONE, BigDecimal.class, BigDecimal.class),
// BIG INTEGER
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(BigInteger.ONE, BigInteger.ONE, BigInteger.class, BigDecimal.class),
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(BigInteger.ONE, BigDecimal.ONE, BigDecimal.class, BigDecimal.class),
+ new Quartet<>(BigInteger.ONE, BigInteger.ONE, BigInteger.class, BigDecimal.class),
+ new Quartet<>(BigInteger.ONE, BigDecimal.ONE, BigDecimal.class, BigDecimal.class),
// BIG DECIMAL
- new Quartet<Number, Number, Class<? extends Number>, Class<? extends Number>>(BigDecimal.ONE, BigDecimal.ONE, BigDecimal.class, BigDecimal.class)
+ new Quartet<>(BigDecimal.ONE, BigDecimal.ONE, BigDecimal.class, BigDecimal.class)
);
@Test
@@ -113,11 +114,28 @@ public class NumberHelperTest {
}
@Test
+ public void shouldHandleAllNullInput() {
+ assertNull(add(null, null));
+ assertNull(sub(null, null));
+ assertNull(mul(null, null));
+ assertNull(div(null, null));
+ assertNull(max((Number) null, null));
+ assertNull(max((Comparable) null, null));
+ assertNull(min((Number) null, null));
+ assertNull(min((Comparable) null, null));
+ assertEquals(0, compare(null, null).intValue());
+ }
+
+ @Test
public void shouldAddAndReturnCorrectType() {
// NOTE: The smallest possible number type for return values is Integer; this seems to be a JVM thing and has
// nothing to do with the actual NumberHelper implementation
+ // the above NOTE isn't quite true - TINKERPOP-2610
+ assertEquals((byte) 1, add((byte) 1, (Byte) null));
+ assertNull(add((Byte) null, (byte) 1));
+
// BYTE
assertEquals(2, add((byte) 1, (byte) 1));
assertEquals(2, add((byte) 1, (short) 1));
@@ -177,6 +195,10 @@ public class NumberHelperTest {
// NOTE: The smallest possible number type for return values is Integer; this seems to be a JVM thing and has
// nothing to do with the actual NumberHelper implementation
+ // the above NOTE isn't quite true - TINKERPOP-2610
+ assertEquals((byte) 1, sub((byte) 1, (Byte) null));
+ assertNull(sub((Byte) null, (byte) 1));
+
// BYTE
assertEquals(0, sub((byte) 1, (byte) 1));
assertEquals(0, sub((byte) 1, (short) 1));
@@ -236,6 +258,10 @@ public class NumberHelperTest {
// NOTE: The smallest possible number type for return values is Integer; this seems to be a JVM thing and has
// nothing to do with the actual NumberHelper implementation
+ // the above NOTE isn't quite true - TINKERPOP-2610
+ assertEquals((byte) 1, mul((byte) 1, (Byte) null));
+ assertNull(mul((Byte) null, (byte) 1));
+
// BYTE
assertEquals(1, mul((byte) 1, (byte) 1));
assertEquals(1, mul((byte) 1, (short) 1));
@@ -295,6 +321,10 @@ public class NumberHelperTest {
// NOTE: The smallest possible number type for return values is Integer; this seems to be a JVM thing and has
// nothing to do with the actual NumberHelper implementation
+ // the above NOTE isn't quite true - TINKERPOP-2610
+ assertEquals((byte) 1, div((byte) 1, (Byte) null));
+ assertNull(div((Byte) null, (byte) 1));
+
// BYTE
assertEquals(1, div((byte) 1, (byte) 1));
assertEquals(1, div((byte) 1, (short) 1));
@@ -425,5 +455,15 @@ public class NumberHelperTest {
assertTrue(compare(one, one) == 0);
}
}
+
+ for (Number one : ones) {
+ assertEquals(1, min(null, one).intValue());
+ assertEquals(1, min(one, null).intValue());
+ assertEquals(1, max(null, one).intValue());
+ assertEquals(1, max(one, null).intValue());
+
+ assertEquals(-1, compare(null, one).intValue());
+ assertEquals(1, compare(one, null).intValue());
+ }
}
}
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs
index c3a67b0..ce33709 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/GherkinTestRunner.cs
@@ -46,11 +46,16 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
// Add here the name of scenarios to ignore and the reason, e.g.:
{"g_V_group_byXageX", IgnoreReason.NullKeysInMapNotSupported},
- // expect to fix the following before 3.5.0 release. they are not failing as a result of the Gremlin
- // itself - they are failing because of shortcomings in the test suite.
+ // they are not failing as a result of the Gremlin itself - they are failing because of shortcomings in
+ // the test suite.
// https://issues.apache.org/jira/browse/TINKERPOP-2518
{"g_withSackX0X_V_outE_sackXsumX_byXweightX_inV_sack_sum", IgnoreReason.NoReason},
+ {"g_V_aggregateXaX_byXageX_capXaX_unfold_sum", IgnoreReason.NoReason},
{"g_withSackX0X_V_repeatXoutE_sackXsumX_byXweightX_inVX_timesX2X_sack", IgnoreReason.NoReason},
+ {"g_injectXlistXnull_10_20_nullXX_meanXlocalX", IgnoreReason.NoReason},
+ {"g_injectXnull_10_20_nullX_mean", IgnoreReason.NoReason},
+ {"g_injectXnull_10_5_nullX_sum", IgnoreReason.NoReason},
+ {"g_injectXlistXnull_10_5_nullXX_sumXlocalX", IgnoreReason.NoReason},
{
"g_withBulkXfalseX_withSackX1_sumX_VX1X_localXoutEXknowsX_barrierXnormSackX_inVX_inXknowsX_barrier_sack",
IgnoreReason.NoReason
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index a3966da..cc043f2 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -432,6 +432,10 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_V_foo_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Max<object>()}},
{"g_V_name_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Max<object>()}},
{"g_V_age_fold_maxXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Fold().Max<object>(Scope.Local)}},
+ {"g_V_aggregateXaX_byXageX_capXaX_maxXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Max<object>(Scope.Local)}},
+ {"g_V_aggregateXaX_byXageX_capXaX_unfold_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Unfold<object>().Max<object>()}},
+ {"g_V_aggregateXaX_byXfooX_capXaX_maxXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Max<object>(Scope.Local)}},
+ {"g_V_aggregateXaX_byXfooX_capXaX_unfold_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Unfold<object>().Max<object>()}},
{"g_V_foo_fold_maxXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Fold().Max<object>(Scope.Local)}},
{"g_V_name_fold_maxXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Fold().Max<object>(Scope.Local)}},
{"g_V_repeatXbothX_timesX5X_age_max", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Repeat(__.Both()).Times(5).Values<object>("age").Max<object>()}},
@@ -441,10 +445,20 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_V_age_fold_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Fold().Mean<object>(Scope.Local)}},
{"g_V_foo_fold_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Fold().Mean<object>(Scope.Local)}},
{"g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_meanX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("software").Group<object,object>().By("name").By(__.BothE().Values<object>("weight").Mean<object>())}},
+ {"g_V_aggregateXaX_byXageX_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Mean<object>(Scope.Local)}},
+ {"g_V_aggregateXaX_byXageX_capXaX_unfold_mean", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Unfold<object>().Mean<object>()}},
+ {"g_V_aggregateXaX_byXfooX_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Mean<object>(Scope.Local)}},
+ {"g_V_aggregateXaX_byXfooX_capXaX_unfold_mean", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Unfold<object>().Mean<object>()}},
+ {"g_injectXnull_10_20_nullX_mean", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null,p["xx1"],p["xx2"],null).Mean<object>()}},
+ {"g_injectXlistXnull_10_20_nullXX_meanXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Mean<object>(Scope.Local)}},
{"g_V_age_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Min<object>()}},
{"g_V_foo_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Min<object>()}},
{"g_V_name_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Min<object>()}},
{"g_V_age_fold_minXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Fold().Min<object>(Scope.Local)}},
+ {"g_V_aggregateXaX_byXageX_capXaX_minXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Min<object>(Scope.Local)}},
+ {"g_V_aggregateXaX_byXageX_capXaX_unfold_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Unfold<object>().Min<object>()}},
+ {"g_V_aggregateXaX_byXfooX_capXaX_minXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Min<object>(Scope.Local)}},
+ {"g_V_aggregateXaX_byXfooX_capXaX_unfold_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Unfold<object>().Min<object>()}},
{"g_V_foo_fold_minXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Fold().Min<object>(Scope.Local)}},
{"g_V_name_fold_minXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("name").Fold().Min<object>(Scope.Local)}},
{"g_V_repeatXbothX_timesX5X_age_min", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Repeat(__.Both()).Times(5).Values<object>("age").Min<object>()}},
@@ -583,6 +597,12 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_V_age_fold_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("age").Fold().Sum<object>(Scope.Local)}},
{"g_V_foo_fold_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Values<object>("foo").Fold().Sum<object>(Scope.Local)}},
{"g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_sumX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().HasLabel("software").Group<object,object>().By("name").By(__.BothE().Values<object>("weight").Sum<object>())}},
+ {"g_V_aggregateXaX_byXageX_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Sum<object>(Scope.Local)}},
+ {"g_V_aggregateXaX_byXageX_capXaX_unfold_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("age").Cap<object>("a").Unfold<object>().Sum<object>()}},
+ {"g_V_aggregateXaX_byXfooX_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Sum<object>(Scope.Local)}},
+ {"g_V_aggregateXaX_byXfooX_capXaX_unfold_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Aggregate("a").By("foo").Cap<object>("a").Unfold<object>().Sum<object>()}},
+ {"g_injectXnull_10_5_nullX_sum", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject<object>(null,p["xx1"],p["xx2"],null).Sum<object>()}},
+ {"g_injectXlistXnull_10_5_nullXX_sumXlocalX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.Inject(p["xx1"]).Sum<object>(Scope.Local)}},
{"g_V_localXoutE_foldX_unfold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().Local<object>(__.OutE().Fold()).Unfold<object>()}},
{"g_V_valueMap_unfold_mapXkeyX", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V().ValueMap<object,object>().Unfold<object>().Map<object>((IFunction) p["l1"])}},
{"g_VX1X_repeatXboth_simplePathX_untilXhasIdX6XX_path_byXnameX_unfold", new List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>> {(g,p) =>g.V(p["vid1"]).Repeat(__.Both().SimplePath()).Until(__.HasId(p["vid6"])).Path().By("name").Unfold<object>()}},
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
index d0aa270..7548afe 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
@@ -421,6 +421,10 @@ const gremlins = {
g_V_foo_max: [function({g}) { return g.V().values("foo").max() }],
g_V_name_max: [function({g}) { return g.V().values("name").max() }],
g_V_age_fold_maxXlocalX: [function({g}) { return g.V().values("age").fold().max(Scope.local) }],
+ g_V_aggregateXaX_byXageX_capXaX_maxXlocalX: [function({g}) { return g.V().aggregate("a").by("age").cap("a").max(Scope.local) }],
+ g_V_aggregateXaX_byXageX_capXaX_unfold_max: [function({g}) { return g.V().aggregate("a").by("age").cap("a").unfold().max() }],
+ g_V_aggregateXaX_byXfooX_capXaX_maxXlocalX: [function({g}) { return g.V().aggregate("a").by("foo").cap("a").max(Scope.local) }],
+ g_V_aggregateXaX_byXfooX_capXaX_unfold_max: [function({g}) { return g.V().aggregate("a").by("foo").cap("a").unfold().max() }],
g_V_foo_fold_maxXlocalX: [function({g}) { return g.V().values("foo").fold().max(Scope.local) }],
g_V_name_fold_maxXlocalX: [function({g}) { return g.V().values("name").fold().max(Scope.local) }],
g_V_repeatXbothX_timesX5X_age_max: [function({g}) { return g.V().repeat(__.both()).times(5).values("age").max() }],
@@ -430,10 +434,20 @@ const gremlins = {
g_V_age_fold_meanXlocalX: [function({g}) { return g.V().values("age").fold().mean(Scope.local) }],
g_V_foo_fold_meanXlocalX: [function({g}) { return g.V().values("foo").fold().mean(Scope.local) }],
g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_meanX: [function({g}) { return g.V().hasLabel("software").group().by("name").by(__.bothE().values("weight").mean()) }],
+ g_V_aggregateXaX_byXageX_meanXlocalX: [function({g}) { return g.V().aggregate("a").by("age").cap("a").mean(Scope.local) }],
+ g_V_aggregateXaX_byXageX_capXaX_unfold_mean: [function({g}) { return g.V().aggregate("a").by("age").cap("a").unfold().mean() }],
+ g_V_aggregateXaX_byXfooX_meanXlocalX: [function({g}) { return g.V().aggregate("a").by("foo").cap("a").mean(Scope.local) }],
+ g_V_aggregateXaX_byXfooX_capXaX_unfold_mean: [function({g}) { return g.V().aggregate("a").by("foo").cap("a").unfold().mean() }],
+ g_injectXnull_10_20_nullX_mean: [function({g, xx1, xx2}) { return g.inject(null,xx1,xx2,null).mean() }],
+ g_injectXlistXnull_10_20_nullXX_meanXlocalX: [function({g, xx1}) { return g.inject(xx1).mean(Scope.local) }],
g_V_age_min: [function({g}) { return g.V().values("age").min() }],
g_V_foo_min: [function({g}) { return g.V().values("foo").min() }],
g_V_name_min: [function({g}) { return g.V().values("name").min() }],
g_V_age_fold_minXlocalX: [function({g}) { return g.V().values("age").fold().min(Scope.local) }],
+ g_V_aggregateXaX_byXageX_capXaX_minXlocalX: [function({g}) { return g.V().aggregate("a").by("age").cap("a").min(Scope.local) }],
+ g_V_aggregateXaX_byXageX_capXaX_unfold_min: [function({g}) { return g.V().aggregate("a").by("age").cap("a").unfold().min() }],
+ g_V_aggregateXaX_byXfooX_capXaX_minXlocalX: [function({g}) { return g.V().aggregate("a").by("foo").cap("a").min(Scope.local) }],
+ g_V_aggregateXaX_byXfooX_capXaX_unfold_min: [function({g}) { return g.V().aggregate("a").by("foo").cap("a").unfold().min() }],
g_V_foo_fold_minXlocalX: [function({g}) { return g.V().values("foo").fold().min(Scope.local) }],
g_V_name_fold_minXlocalX: [function({g}) { return g.V().values("name").fold().min(Scope.local) }],
g_V_repeatXbothX_timesX5X_age_min: [function({g}) { return g.V().repeat(__.both()).times(5).values("age").min() }],
@@ -572,6 +586,12 @@ const gremlins = {
g_V_age_fold_sumXlocalX: [function({g}) { return g.V().values("age").fold().sum(Scope.local) }],
g_V_foo_fold_sumXlocalX: [function({g}) { return g.V().values("foo").fold().sum(Scope.local) }],
g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_sumX: [function({g}) { return g.V().hasLabel("software").group().by("name").by(__.bothE().values("weight").sum()) }],
+ g_V_aggregateXaX_byXageX_sumXlocalX: [function({g}) { return g.V().aggregate("a").by("age").cap("a").sum(Scope.local) }],
+ g_V_aggregateXaX_byXageX_capXaX_unfold_sum: [function({g}) { return g.V().aggregate("a").by("age").cap("a").unfold().sum() }],
+ g_V_aggregateXaX_byXfooX_sumXlocalX: [function({g}) { return g.V().aggregate("a").by("foo").cap("a").sum(Scope.local) }],
+ g_V_aggregateXaX_byXfooX_capXaX_unfold_sum: [function({g}) { return g.V().aggregate("a").by("foo").cap("a").unfold().sum() }],
+ g_injectXnull_10_5_nullX_sum: [function({g, xx1, xx2}) { return g.inject(null,xx1,xx2,null).sum() }],
+ g_injectXlistXnull_10_5_nullXX_sumXlocalX: [function({g, xx1}) { return g.inject(xx1).sum(Scope.local) }],
g_V_localXoutE_foldX_unfold: [function({g}) { return g.V().local(__.outE().fold()).unfold() }],
g_V_valueMap_unfold_mapXkeyX: [function({g, l1}) { return g.V().valueMap().unfold().map(l1) }],
g_VX1X_repeatXboth_simplePathX_untilXhasIdX6XX_path_byXnameX_unfold: [function({g, vid6, vid1}) { return g.V(vid1).repeat(__.both().simplePath()).until(__.hasId(vid6)).path().by("name").unfold() }],
diff --git a/gremlin-python/src/main/python/radish/gremlin.py b/gremlin-python/src/main/python/radish/gremlin.py
index 317a387..b4e6d81 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -406,6 +406,10 @@ world.gremlins = {
'g_V_foo_max': [(lambda g:g.V().foo.max())],
'g_V_name_max': [(lambda g:g.V().name.max())],
'g_V_age_fold_maxXlocalX': [(lambda g:g.V().age.fold().max(Scope.local))],
+ 'g_V_aggregateXaX_byXageX_capXaX_maxXlocalX': [(lambda g:g.V().aggregate('a').by('age').cap('a').max(Scope.local))],
+ 'g_V_aggregateXaX_byXageX_capXaX_unfold_max': [(lambda g:g.V().aggregate('a').by('age').cap('a').unfold().max())],
+ 'g_V_aggregateXaX_byXfooX_capXaX_maxXlocalX': [(lambda g:g.V().aggregate('a').by('foo').cap('a').max(Scope.local))],
+ 'g_V_aggregateXaX_byXfooX_capXaX_unfold_max': [(lambda g:g.V().aggregate('a').by('foo').cap('a').unfold().max())],
'g_V_foo_fold_maxXlocalX': [(lambda g:g.V().foo.fold().max(Scope.local))],
'g_V_name_fold_maxXlocalX': [(lambda g:g.V().name.fold().max(Scope.local))],
'g_V_repeatXbothX_timesX5X_age_max': [(lambda g:g.V().repeat(__.both()).times(5).age.max())],
@@ -415,10 +419,20 @@ world.gremlins = {
'g_V_age_fold_meanXlocalX': [(lambda g:g.V().age.fold().mean(Scope.local))],
'g_V_foo_fold_meanXlocalX': [(lambda g:g.V().foo.fold().mean(Scope.local))],
'g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_meanX': [(lambda g:g.V().hasLabel('software').group().by('name').by(__.bothE().weight.mean()))],
+ 'g_V_aggregateXaX_byXageX_meanXlocalX': [(lambda g:g.V().aggregate('a').by('age').cap('a').mean(Scope.local))],
+ 'g_V_aggregateXaX_byXageX_capXaX_unfold_mean': [(lambda g:g.V().aggregate('a').by('age').cap('a').unfold().mean())],
+ 'g_V_aggregateXaX_byXfooX_meanXlocalX': [(lambda g:g.V().aggregate('a').by('foo').cap('a').mean(Scope.local))],
+ 'g_V_aggregateXaX_byXfooX_capXaX_unfold_mean': [(lambda g:g.V().aggregate('a').by('foo').cap('a').unfold().mean())],
+ 'g_injectXnull_10_20_nullX_mean': [(lambda g, xx1=None,xx2=None:g.inject(None,xx1,xx2,None).mean())],
+ 'g_injectXlistXnull_10_20_nullXX_meanXlocalX': [(lambda g, xx1=None:g.inject(xx1).mean(Scope.local))],
'g_V_age_min': [(lambda g:g.V().age.min())],
'g_V_foo_min': [(lambda g:g.V().foo.min())],
'g_V_name_min': [(lambda g:g.V().name.min())],
'g_V_age_fold_minXlocalX': [(lambda g:g.V().age.fold().min(Scope.local))],
+ 'g_V_aggregateXaX_byXageX_capXaX_minXlocalX': [(lambda g:g.V().aggregate('a').by('age').cap('a').min(Scope.local))],
+ 'g_V_aggregateXaX_byXageX_capXaX_unfold_min': [(lambda g:g.V().aggregate('a').by('age').cap('a').unfold().min())],
+ 'g_V_aggregateXaX_byXfooX_capXaX_minXlocalX': [(lambda g:g.V().aggregate('a').by('foo').cap('a').min(Scope.local))],
+ 'g_V_aggregateXaX_byXfooX_capXaX_unfold_min': [(lambda g:g.V().aggregate('a').by('foo').cap('a').unfold().min())],
'g_V_foo_fold_minXlocalX': [(lambda g:g.V().foo.fold().min(Scope.local))],
'g_V_name_fold_minXlocalX': [(lambda g:g.V().name.fold().min(Scope.local))],
'g_V_repeatXbothX_timesX5X_age_min': [(lambda g:g.V().repeat(__.both()).times(5).age.min())],
@@ -557,6 +571,12 @@ world.gremlins = {
'g_V_age_fold_sumXlocalX': [(lambda g:g.V().age.fold().sum(Scope.local))],
'g_V_foo_fold_sumXlocalX': [(lambda g:g.V().foo.fold().sum(Scope.local))],
'g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_sumX': [(lambda g:g.V().hasLabel('software').group().by('name').by(__.bothE().weight.sum()))],
+ 'g_V_aggregateXaX_byXageX_sumXlocalX': [(lambda g:g.V().aggregate('a').by('age').cap('a').sum(Scope.local))],
+ 'g_V_aggregateXaX_byXageX_capXaX_unfold_sum': [(lambda g:g.V().aggregate('a').by('age').cap('a').unfold().sum())],
+ 'g_V_aggregateXaX_byXfooX_sumXlocalX': [(lambda g:g.V().aggregate('a').by('foo').cap('a').sum(Scope.local))],
+ 'g_V_aggregateXaX_byXfooX_capXaX_unfold_sum': [(lambda g:g.V().aggregate('a').by('foo').cap('a').unfold().sum())],
+ 'g_injectXnull_10_5_nullX_sum': [(lambda g, xx1=None,xx2=None:g.inject(None,xx1,xx2,None).sum())],
+ 'g_injectXlistXnull_10_5_nullXX_sumXlocalX': [(lambda g, xx1=None:g.inject(xx1).sum(Scope.local))],
'g_V_localXoutE_foldX_unfold': [(lambda g:g.V().local(__.outE().fold()).unfold())],
'g_V_valueMap_unfold_mapXkeyX': [(lambda g, l1=None:g.V().valueMap().unfold().map(l1))],
'g_VX1X_repeatXboth_simplePathX_untilXhasIdX6XX_path_byXnameX_unfold': [(lambda g, vid6=None,vid1=None:g.V(vid1).repeat(__.both().simplePath()).until(__.hasId(vid6)).path().by('name').unfold())],
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphBinaryRemoteGraphComputerProvider.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphBinaryRemoteGraphComputerProvider.java
index e938ce1..54ab3df 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphBinaryRemoteGraphComputerProvider.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphBinaryRemoteGraphComputerProvider.java
@@ -90,7 +90,23 @@ import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComp
@Graph.OptOut(
test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertiesTest",
method = "g_injectXg_VX1X_propertiesXnameX_nextX_value",
- reason = "Needs investigation")
+ reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.SumTest",
+ method = "g_injectXnull_10_5_nullX_sum",
+ reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.SumTest",
+ method = "g_injectXlistXnull_10_5_nullXX_sumXlocalX",
+ reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanTest",
+ method = "g_injectXlistXnull_10_20_nullXX_meanXlocalX",
+ reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanTest",
+ method = "g_injectXnull_10_20_nullX_mean",
+ reason = "The inject() step is not supported by GraphComputer")
@Graph.OptOut(
test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ReadTest",
method = "*",
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphSONRemoteGraphComputerProvider.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphSONRemoteGraphComputerProvider.java
index b9b251d..2678738 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphSONRemoteGraphComputerProvider.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GraphSONRemoteGraphComputerProvider.java
@@ -90,7 +90,23 @@ import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComp
@Graph.OptOut(
test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertiesTest",
method = "g_injectXg_VX1X_propertiesXnameX_nextX_value",
- reason = "Needs investigation")
+ reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.SumTest",
+ method = "g_injectXnull_10_5_nullX_sum",
+ reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.SumTest",
+ method = "g_injectXlistXnull_10_5_nullXX_sumXlocalX",
+ reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanTest",
+ method = "g_injectXlistXnull_10_20_nullXX_meanXlocalX",
+ reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanTest",
+ method = "g_injectXnull_10_20_nullX_mean",
+ reason = "The inject() step is not supported by GraphComputer")
@Graph.OptOut(
test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ReadTest",
method = "*",
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GryoRemoteGraphComputerProvider.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GryoRemoteGraphComputerProvider.java
index 032e845..3d5c510 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GryoRemoteGraphComputerProvider.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/driver/remote/GryoRemoteGraphComputerProvider.java
@@ -91,7 +91,23 @@ import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComp
@Graph.OptOut(
test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertiesTest",
method = "g_injectXg_VX1X_propertiesXnameX_nextX_value",
- reason = "Needs investigation")
+ reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.SumTest",
+ method = "g_injectXnull_10_5_nullX_sum",
+ reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.SumTest",
+ method = "g_injectXlistXnull_10_5_nullXX_sumXlocalX",
+ reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanTest",
+ method = "g_injectXlistXnull_10_20_nullXX_meanXlocalX",
+ reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanTest",
+ method = "g_injectXnull_10_20_nullX_mean",
+ reason = "The inject() step is not supported by GraphComputer")
@Graph.OptOut(
test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ReadTest",
method = "*",
diff --git a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GryoRemoteGraphGroovyTranslatorComputerProvider.java b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GryoRemoteGraphGroovyTranslatorComputerProvider.java
index a049904..01b3a21 100644
--- a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GryoRemoteGraphGroovyTranslatorComputerProvider.java
+++ b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GryoRemoteGraphGroovyTranslatorComputerProvider.java
@@ -93,7 +93,23 @@ import org.apache.tinkerpop.gremlin.tinkergraph.process.computer.TinkerGraphComp
@Graph.OptOut(
test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.PropertiesTest",
method = "g_injectXg_VX1X_propertiesXnameX_nextX_value",
- reason = "Needs investigation")
+ reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.SumTest",
+ method = "g_injectXnull_10_5_nullX_sum",
+ reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.SumTest",
+ method = "g_injectXlistXnull_10_5_nullXX_sumXlocalX",
+ reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanTest",
+ method = "g_injectXlistXnull_10_20_nullXX_meanXlocalX",
+ reason = "The inject() step is not supported by GraphComputer")
+@Graph.OptOut(
+ test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.MeanTest",
+ method = "g_injectXnull_10_20_nullX_mean",
+ reason = "The inject() step is not supported by GraphComputer")
@Graph.OptOut(
test = "org.apache.tinkerpop.gremlin.process.traversal.step.map.ReadTest",
method = "*",
diff --git a/gremlin-test/features/map/Max.feature b/gremlin-test/features/map/Max.feature
index 44d9640..4288563 100644
--- a/gremlin-test/features/map/Max.feature
+++ b/gremlin-test/features/map/Max.feature
@@ -59,6 +59,50 @@ Feature: Step - max()
| result |
| d[35].i |
+ Scenario: g_V_aggregateXaX_byXageX_capXaX_maxXlocalX
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().aggregate("a").by("age").cap("a").max(Scope.local)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[35].i |
+
+ Scenario: g_V_aggregateXaX_byXageX_capXaX_unfold_max
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().aggregate("a").by("age").cap("a").unfold().max()
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[35].i |
+
+ Scenario: g_V_aggregateXaX_byXfooX_capXaX_maxXlocalX
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().aggregate("a").by("foo").cap("a").max(Scope.local)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | null |
+
+ Scenario: g_V_aggregateXaX_byXfooX_capXaX_unfold_max
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().aggregate("a").by("foo").cap("a").unfold().max()
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | null |
+
Scenario: g_V_foo_fold_maxXlocalX
Given the modern graph
And the traversal of
diff --git a/gremlin-test/features/map/Mean.feature b/gremlin-test/features/map/Mean.feature
index dc28ecb..5e01db1 100644
--- a/gremlin-test/features/map/Mean.feature
+++ b/gremlin-test/features/map/Mean.feature
@@ -67,3 +67,76 @@ Feature: Step - mean()
Then the result should be unordered
| result |
| m[{"ripple":"d[1.0].d","lop":"d[0.3333333333333333].d"}] |
+
+ # null values are ignored in mean() which is similar to sum aggregation works in SQL
+ Scenario: g_V_aggregateXaX_byXageX_meanXlocalX
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().aggregate("a").by("age").cap("a").mean(Scope.local)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[30.75].d |
+
+ # null values are ignored in mean() which is similar to sum aggregation works in SQL
+ Scenario: g_V_aggregateXaX_byXageX_capXaX_unfold_mean
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().aggregate("a").by("age").cap("a").unfold().mean()
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[30.75].d |
+
+ # if all values are null then the result is null
+ Scenario: g_V_aggregateXaX_byXfooX_meanXlocalX
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().aggregate("a").by("foo").cap("a").mean(Scope.local)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | null |
+
+ # if all values are null then the result is null
+ Scenario: g_V_aggregateXaX_byXfooX_capXaX_unfold_mean
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().aggregate("a").by("foo").cap("a").unfold().mean()
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | null |
+
+ Scenario: g_injectXnull_10_20_nullX_mean
+ Given the modern graph
+ And using the parameter xx1 defined as "d[10].i"
+ And using the parameter xx2 defined as "d[20].i"
+ And the traversal of
+ """
+ g.inject(null, xx1, xx2, null).mean()
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[15].i |
+
+ Scenario: g_injectXlistXnull_10_20_nullXX_meanXlocalX
+ Given the modern graph
+ And using the parameter xx1 defined as "l[null,d[10].i,d[20].i,null]"
+ And the traversal of
+ """
+ g.inject(xx1).mean(local)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[15].i |
\ No newline at end of file
diff --git a/gremlin-test/features/map/Min.feature b/gremlin-test/features/map/Min.feature
index ad866ec..8d90a64 100644
--- a/gremlin-test/features/map/Min.feature
+++ b/gremlin-test/features/map/Min.feature
@@ -59,6 +59,50 @@ Feature: Step - min()
| result |
| d[27].i |
+ Scenario: g_V_aggregateXaX_byXageX_capXaX_minXlocalX
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().aggregate("a").by("age").cap("a").min(Scope.local)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[27].i |
+
+ Scenario: g_V_aggregateXaX_byXageX_capXaX_unfold_min
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().aggregate("a").by("age").cap("a").unfold().min()
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[27].i |
+
+ Scenario: g_V_aggregateXaX_byXfooX_capXaX_minXlocalX
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().aggregate("a").by("foo").cap("a").min(Scope.local)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | null |
+
+ Scenario: g_V_aggregateXaX_byXfooX_capXaX_unfold_min
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().aggregate("a").by("foo").cap("a").unfold().min()
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | null |
+
Scenario: g_V_foo_fold_minXlocalX
Given the modern graph
And the traversal of
diff --git a/gremlin-test/features/map/Sum.feature b/gremlin-test/features/map/Sum.feature
index 0e20368..50c5c16 100644
--- a/gremlin-test/features/map/Sum.feature
+++ b/gremlin-test/features/map/Sum.feature
@@ -67,3 +67,76 @@ Feature: Step - sum()
Then the result should be unordered
| result |
| m[{"ripple":"d[1.0].d","lop":"d[1.0].d"}] |
+
+ # null values are ignored in sum() which is similar to sum aggregation works in SQL
+ Scenario: g_V_aggregateXaX_byXageX_sumXlocalX
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().aggregate("a").by("age").cap("a").sum(Scope.local)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[123].i |
+
+ # null values are ignored in sum() which is similar to sum aggregation works in SQL
+ Scenario: g_V_aggregateXaX_byXageX_capXaX_unfold_sum
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().aggregate("a").by("age").cap("a").unfold().sum()
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[123].i |
+
+ # if all values are null then the result is null
+ Scenario: g_V_aggregateXaX_byXfooX_sumXlocalX
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().aggregate("a").by("foo").cap("a").sum(Scope.local)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | null |
+
+ # if all values are null then the result is null
+ Scenario: g_V_aggregateXaX_byXfooX_capXaX_unfold_sum
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().aggregate("a").by("foo").cap("a").unfold().sum()
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | null |
+
+ Scenario: g_injectXnull_10_5_nullX_sum
+ Given the modern graph
+ And using the parameter xx1 defined as "d[10].i"
+ And using the parameter xx2 defined as "d[5].i"
+ And the traversal of
+ """
+ g.inject(null, xx1, xx2, null).sum()
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[15].i |
+
+ Scenario: g_injectXlistXnull_10_5_nullXX_sumXlocalX
+ Given the modern graph
+ And using the parameter xx1 defined as "l[null,d[10].i,d[5].i,null]"
+ And the traversal of
+ """
+ g.inject(xx1).sum(local)
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | d[15].i |
\ No newline at end of file
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/GremlinProcessRunner.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/GremlinProcessRunner.java
index b70f303..0cda9fa 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/GremlinProcessRunner.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/GremlinProcessRunner.java
@@ -73,8 +73,8 @@ public class GremlinProcessRunner extends BlockJUnit4ClassRunner {
// for remote tests ex will be a IllegalStateException holding a VerificationException or only have a
// string message to compare on
if (ex instanceof VerificationException || ex.getCause() instanceof VerificationException ||
- ex.getMessage().contains("It is not possible to access more than a path element's id on GraphComputer") ||
- ex.getMessage().contains("Local traversals may not traverse past the local star-graph on GraphComputer"))
+ (ex.getMessage() != null && (ex.getMessage().contains("It is not possible to access more than a path element's id on GraphComputer") ||
+ ex.getMessage().contains("Local traversals may not traverse past the local star-graph on GraphComputer"))))
return true;
else if (ex instanceof NotSerializableException)
return true;
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MaxTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MaxTest.java
index 1ddd859..bb85fa2 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MaxTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MaxTest.java
@@ -35,6 +35,7 @@ import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.both;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.bothE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
@@ -47,6 +48,14 @@ public abstract class MaxTest extends AbstractGremlinProcessTest {
public abstract Traversal<Vertex, Integer> get_g_V_age_fold_maxXlocalX();
+ public abstract Traversal<Vertex, Integer> get_g_V_aggregateXaX_byXageX_capXaX_maxXlocalX();
+
+ public abstract Traversal<Vertex, Integer> get_g_V_aggregateXaX_byXageX_capXaX_unfold_max();
+
+ public abstract Traversal<Vertex, Comparable> get_g_V_aggregateXaX_byXfooX_capXaX_maxXlocalX();
+
+ public abstract Traversal<Vertex, Comparable> get_g_V_aggregateXaX_byXfooX_capXaX_unfold_max();
+
public abstract Traversal<Vertex, Comparable> get_g_V_foo_max();
public abstract Traversal<Vertex, Comparable> get_g_V_foo_fold_maxXlocalX();
@@ -77,6 +86,40 @@ public abstract class MaxTest extends AbstractGremlinProcessTest {
@Test
@LoadGraphWith(MODERN)
+ public void g_V_aggregateXaX_byXageX_capXaX_maxXlocalX() {
+ final Traversal<Vertex, Integer> traversal = get_g_V_aggregateXaX_byXageX_capXaX_maxXlocalX();
+ printTraversalForm(traversal);
+ checkResults(Arrays.asList(35), traversal);
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
+ public void g_V_aggregateXaX_byXageX_capXaX_unfold_max() {
+ final Traversal<Vertex, Integer> traversal = get_g_V_aggregateXaX_byXageX_capXaX_unfold_max();
+ printTraversalForm(traversal);
+ checkResults(Arrays.asList(35), traversal);
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
+ public void g_V_aggregateXaX_byXfooX_capXaX_maxXlocalX() {
+ final Traversal<Vertex, Comparable> traversal = get_g_V_aggregateXaX_byXfooX_capXaX_maxXlocalX();
+ printTraversalForm(traversal);
+ assertNull(traversal.next());
+ assertFalse(traversal.hasNext());
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
+ public void g_V_aggregateXaX_byXfooX_capXaX_unfold_max() {
+ final Traversal<Vertex, Comparable> traversal = get_g_V_aggregateXaX_byXfooX_capXaX_unfold_max();
+ printTraversalForm(traversal);
+ assertNull(traversal.next());
+ assertFalse(traversal.hasNext());
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
public void g_V_foo_max() {
final Traversal<Vertex, Comparable> traversal = get_g_V_foo_max();
printTraversalForm(traversal);
@@ -136,11 +179,31 @@ public abstract class MaxTest extends AbstractGremlinProcessTest {
}
@Override
+ public Traversal<Vertex, Comparable> get_g_V_aggregateXaX_byXfooX_capXaX_maxXlocalX() {
+ return g.V().aggregate("a").by("foo").cap("a").max(Scope.local);
+ }
+
+ @Override
+ public Traversal<Vertex, Comparable> get_g_V_aggregateXaX_byXfooX_capXaX_unfold_max() {
+ return g.V().aggregate("a").by("foo").cap("a").unfold().max();
+ }
+
+ @Override
public Traversal<Vertex, Integer> get_g_V_age_fold_maxXlocalX() {
return g.V().values("age").fold().max(Scope.local);
}
@Override
+ public Traversal<Vertex, Integer> get_g_V_aggregateXaX_byXageX_capXaX_maxXlocalX() {
+ return g.V().aggregate("a").by("age").cap("a").max(Scope.local);
+ }
+
+ @Override
+ public Traversal<Vertex, Integer> get_g_V_aggregateXaX_byXageX_capXaX_unfold_max() {
+ return g.V().aggregate("aaazzz").by("age").cap("aaazzz").unfold().max();
+ }
+
+ @Override
public Traversal<Vertex, Comparable> get_g_V_foo_max() {
return g.V().values("foo").max();
}
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanTest.java
index cb2aac4..0908646 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MeanTest.java
@@ -28,12 +28,14 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
+import java.util.List;
import java.util.Map;
import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.bothE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
@@ -52,6 +54,18 @@ public abstract class MeanTest extends AbstractGremlinProcessTest {
public abstract Traversal<Vertex, Map<String, Number>> get_g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_meanX();
+ public abstract Traversal<Vertex, Double> get_g_V_aggregateXaX_byXageX_meanXlocalX();
+
+ public abstract Traversal<Vertex, Double> get_g_V_aggregateXaX_byXageX_capXaX_unfold_mean();
+
+ public abstract Traversal<Vertex, Double> get_g_V_aggregateXaX_byXfooX_meanXlocalX();
+
+ public abstract Traversal<Vertex, Double> get_g_V_aggregateXaX_byXfooX_capXaX_unfold_mean();
+
+ public abstract Traversal<Integer, Double> get_g_injectXnull_10_20_nullX_mean();
+
+ public abstract Traversal<List<Integer>, Double> get_g_injectXlistXnull_10_20_nullXX_meanXlocalX();
+
@Test
@LoadGraphWith(MODERN)
public void g_V_age_mean() {
@@ -74,6 +88,24 @@ public abstract class MeanTest extends AbstractGremlinProcessTest {
@Test
@LoadGraphWith(MODERN)
+ public void g_V_aggregateXaX_byXfooX_meanXlocalX() {
+ final Traversal<Vertex, Double> traversal = get_g_V_aggregateXaX_byXfooX_meanXlocalX();
+ printTraversalForm(traversal);
+ assertNull(traversal.next());
+ assertFalse(traversal.hasNext());
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
+ public void g_V_aggregateXaX_byXfooX_capXaX_unfold_mean() {
+ final Traversal<Vertex, Double> traversal = get_g_V_aggregateXaX_byXfooX_capXaX_unfold_mean();
+ printTraversalForm(traversal);
+ assertNull(traversal.next());
+ assertFalse(traversal.hasNext());
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
public void g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_meanX() {
final Traversal<Vertex, Map<String, Number>> traversal = get_g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_meanX();
printTraversalForm(traversal);
@@ -85,6 +117,37 @@ public abstract class MeanTest extends AbstractGremlinProcessTest {
assertEquals(1.0 / 3, map.get("lop"));
}
+ @Test
+ @LoadGraphWith(MODERN)
+ public void g_V_aggregateXaX_byXageX_meanXlocalX() {
+ for (final Traversal<Vertex, Double> traversal : Arrays.asList(get_g_V_aggregateXaX_byXageX_capXaX_unfold_mean(), get_g_V_aggregateXaX_byXageX_meanXlocalX())) {
+ printTraversalForm(traversal);
+ final Double mean = traversal.next();
+ assertEquals(30.75, mean, 0.05);
+ assertFalse(traversal.hasNext());
+ }
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
+ public void g_injectXnull_10_20_nullX_mean() {
+ final Traversal<Integer, Double> traversal = get_g_injectXnull_10_20_nullX_mean();
+ printTraversalForm(traversal);
+ final Double mean = traversal.next();
+ assertEquals(15.00, mean, 0.05);
+ assertFalse(traversal.hasNext());
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
+ public void g_injectXlistXnull_10_20_nullXX_meanXlocalX() {
+ final Traversal<List<Integer>, Double> traversal = get_g_injectXlistXnull_10_20_nullXX_meanXlocalX();
+ printTraversalForm(traversal);
+ final Double mean = traversal.next();
+ assertEquals(15.00, mean, 0.05);
+ assertFalse(traversal.hasNext());
+ }
+
public static class Traversals extends MeanTest {
@Override
@@ -93,6 +156,16 @@ public abstract class MeanTest extends AbstractGremlinProcessTest {
}
@Override
+ public Traversal<Vertex, Double> get_g_V_aggregateXaX_byXfooX_meanXlocalX() {
+ return g.V().aggregate("a").by("foo").cap("a").mean(Scope.local);
+ }
+
+ @Override
+ public Traversal<Vertex, Double> get_g_V_aggregateXaX_byXfooX_capXaX_unfold_mean() {
+ return g.V().aggregate("a").by("foo").cap("a").unfold().mean();
+ }
+
+ @Override
public Traversal<Vertex, Double> get_g_V_age_fold_meanXlocalX() {
return g.V().values("age").fold().mean(Scope.local);
}
@@ -111,5 +184,25 @@ public abstract class MeanTest extends AbstractGremlinProcessTest {
public Traversal<Vertex, Map<String, Number>> get_g_V_hasLabelXsoftwareX_group_byXnameX_byXbothE_weight_meanX() {
return g.V().hasLabel("software").<String, Number>group().by("name").by(bothE().values("weight").mean());
}
+
+ @Override
+ public Traversal<Vertex, Double> get_g_V_aggregateXaX_byXageX_meanXlocalX() {
+ return g.V().aggregate("a").by("age").cap("a").mean(Scope.local);
+ }
+
+ @Override
+ public Traversal<Vertex, Double> get_g_V_aggregateXaX_byXageX_capXaX_unfold_mean() {
+ return g.V().aggregate("a").by("age").cap("a").unfold().mean();
+ }
+
+ @Override
+ public Traversal<Integer, Double> get_g_injectXnull_10_20_nullX_mean() {
+ return g.inject(null, 10, 20, null).mean();
+ }
+
+ @Override
+ public Traversal<List<Integer>, Double> get_g_injectXlistXnull_10_20_nullXX_meanXlocalX() {
+ return g.inject(Arrays.asList(null, 10, 20, null)).mean(Scope.local);
+ }
}
}
\ No newline at end of file
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MinTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MinTest.java
index d4a13b8..51c2e2a 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MinTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/MinTest.java
@@ -35,6 +35,7 @@ import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.both;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.bothE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
@@ -47,6 +48,14 @@ public abstract class MinTest extends AbstractGremlinProcessTest {
public abstract Traversal<Vertex, Integer> get_g_V_age_fold_minXlocalX();
+ public abstract Traversal<Vertex, Integer> get_g_V_aggregateXaX_byXageX_capXaX_minXlocalX();
+
+ public abstract Traversal<Vertex, Integer> get_g_V_aggregateXaX_byXageX_capXaX_unfold_min();
+
+ public abstract Traversal<Vertex, Comparable> get_g_V_aggregateXaX_byXfooX_capXaX_minXlocalX();
+
+ public abstract Traversal<Vertex, Comparable> get_g_V_aggregateXaX_byXfooX_capXaX_unfold_min();
+
public abstract Traversal<Vertex, Comparable> get_g_V_foo_min();
public abstract Traversal<Vertex, Comparable> get_g_V_foo_fold_minXlocalX();
@@ -79,6 +88,22 @@ public abstract class MinTest extends AbstractGremlinProcessTest {
@Test
@LoadGraphWith(MODERN)
+ public void g_V_aggregateXaX_byXageX_capXaX_minXlocalX() {
+ final Traversal<Vertex, Integer> traversal = get_g_V_aggregateXaX_byXageX_capXaX_minXlocalX();
+ printTraversalForm(traversal);
+ checkResults(Arrays.asList(27), traversal);
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
+ public void g_V_aggregateXaX_byXageX_capXaX_unfold_min() {
+ final Traversal<Vertex, Integer> traversal = get_g_V_aggregateXaX_byXageX_capXaX_unfold_min();
+ printTraversalForm(traversal);
+ checkResults(Arrays.asList(27), traversal);
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
public void g_V_foo_min() {
final Traversal<Vertex, Comparable> traversal = get_g_V_foo_min();
printTraversalForm(traversal);
@@ -103,6 +128,24 @@ public abstract class MinTest extends AbstractGremlinProcessTest {
@Test
@LoadGraphWith(MODERN)
+ public void g_V_aggregateXaX_byXfooX_capXaX_minXlocalX() {
+ final Traversal<Vertex, Comparable> traversal = get_g_V_aggregateXaX_byXfooX_capXaX_minXlocalX();
+ printTraversalForm(traversal);
+ assertNull(traversal.next());
+ assertFalse(traversal.hasNext());
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
+ public void g_V_aggregateXaX_byXfooX_capXaX_unfold_min() {
+ final Traversal<Vertex, Comparable> traversal = get_g_V_aggregateXaX_byXfooX_capXaX_unfold_min();
+ printTraversalForm(traversal);
+ assertNull(traversal.next());
+ assertFalse(traversal.hasNext());
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
public void g_V_name_fold_minXlocalX() {
final Traversal<Vertex, String> traversal = get_g_V_name_fold_minXlocalX();
printTraversalForm(traversal);
@@ -148,6 +191,16 @@ public abstract class MinTest extends AbstractGremlinProcessTest {
}
@Override
+ public Traversal<Vertex, Comparable> get_g_V_aggregateXaX_byXfooX_capXaX_minXlocalX() {
+ return g.V().aggregate("a").by("foo").cap("a").min(Scope.local);
+ }
+
+ @Override
+ public Traversal<Vertex, Comparable> get_g_V_aggregateXaX_byXfooX_capXaX_unfold_min() {
+ return g.V().aggregate("a").by("foo").cap("a").unfold().min();
+ }
+
+ @Override
public Traversal<Vertex, Integer> get_g_V_age_fold_minXlocalX() {
return g.V().values("age").fold().min(Scope.local);
}
@@ -173,6 +226,16 @@ public abstract class MinTest extends AbstractGremlinProcessTest {
}
@Override
+ public Traversal<Vertex, Integer> get_g_V_aggregateXaX_byXageX_capXaX_minXlocalX() {
+ return g.V().aggregate("a").by("age").cap("a").min(Scope.local);
+ }
+
+ @Override
+ public Traversal<Vertex, Integer> get_g_V_aggregateXaX_byXageX_capXaX_unfold_min() {
+ return g.V().aggregate("a").by("age").cap("a").unfold().min();
+ }
+
+ @Override
public Traversal<Vertex, Integer> get_g_V_repeatXbothX_timesX5X_age_min() {
return g.V().repeat(both()).times(5).values("age").min();
}
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumTest.java
index bd981c1..6d45fed 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/SumTest.java
@@ -27,12 +27,15 @@ import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
+import java.util.List;
import java.util.Map;
import static org.apache.tinkerpop.gremlin.LoadGraphWith.GraphData.MODERN;
import static org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__.bothE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
@@ -43,6 +46,18 @@ public abstract class SumTest extends AbstractGremlinProcessTest {
public abstract Traversal<Vertex, Number> get_g_V_age_sum();
+ public abstract Traversal<Vertex, Number> get_g_V_aggregateXaX_byXageX_sumXlocalX();
+
+ public abstract Traversal<Vertex, Number> get_g_V_aggregateXaX_byXfooX_sumXlocalX();
+
+ public abstract Traversal<Vertex, Number> get_g_V_aggregateXaX_byXageX_capXaX_unfold_sum();
+
+ public abstract Traversal<Integer, Number> get_g_injectXnull_10_5_nullX_sum();
+
+ public abstract Traversal<List<Integer>, Number> get_g_injectXlistXnull_10_5_nullXX_sumXlocalX();
+
+ public abstract Traversal<Vertex, Number> get_g_V_aggregateXaX_byXfooX_capXaX_unfold_sum();
+
public abstract Traversal<Vertex, Number> get_g_V_age_fold_sumXlocalX();
public abstract Traversal<Vertex, Number> get_g_V_foo_sum();
@@ -62,6 +77,62 @@ public abstract class SumTest extends AbstractGremlinProcessTest {
}
@Test
+ public void g_injectXnull_10_5_nullX_sum() {
+ final Traversal<Integer, Number> traversal = get_g_injectXnull_10_5_nullX_sum();
+ printTraversalForm(traversal);
+ final Number sum = traversal.next();
+ assertEquals(15, sum.intValue());
+ assertFalse(traversal.hasNext());
+ }
+
+ @Test
+ public void g_injectXlistXnull_10_5_nullXX_sumXlocalX() {
+ final Traversal<List<Integer>, Number> traversal = get_g_injectXlistXnull_10_5_nullXX_sumXlocalX();
+ printTraversalForm(traversal);
+ final Number sum = traversal.next();
+ assertEquals(15, sum.intValue());
+ assertFalse(traversal.hasNext());
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
+ public void g_V_aggregateXaX_byXageX_sumXlocalX() {
+ final Traversal<Vertex, Number> traversal = get_g_V_aggregateXaX_byXageX_sumXlocalX();
+ printTraversalForm(traversal);
+ final Number sum = traversal.next();
+ assertEquals(123, sum.intValue());
+ assertFalse(traversal.hasNext());
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
+ public void g_V_aggregateXaX_byXageX_capXaX_unfold_sum() {
+ final Traversal<Vertex, Number> traversal = get_g_V_aggregateXaX_byXageX_capXaX_unfold_sum();
+ printTraversalForm(traversal);
+ final Number sum = traversal.next();
+ assertEquals(123, sum.intValue());
+ assertFalse(traversal.hasNext());
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
+ public void g_V_aggregateXaX_byXfooX_sumXlocalX() {
+ final Traversal<Vertex, Number> traversal = get_g_V_aggregateXaX_byXfooX_sumXlocalX();
+ printTraversalForm(traversal);
+ assertNull(traversal.next());
+ assertFalse(traversal.hasNext());
+ }
+
+ @Test
+ @LoadGraphWith(MODERN)
+ public void g_V_aggregateXaX_byXfooX_capXaX_unfold_sum() {
+ final Traversal<Vertex, Number> traversal = get_g_V_aggregateXaX_byXfooX_capXaX_unfold_sum();
+ printTraversalForm(traversal);
+ assertNull(traversal.next());
+ assertFalse(traversal.hasNext());
+ }
+
+ @Test
@LoadGraphWith(MODERN)
public void g_V_age_fold_sumXlocalX() {
final Traversal<Vertex, Number> traversal = get_g_V_age_fold_sumXlocalX();
@@ -108,6 +179,35 @@ public abstract class SumTest extends AbstractGremlinProcessTest {
}
@Override
+ public Traversal<Integer, Number> get_g_injectXnull_10_5_nullX_sum() {
+ return g.inject(null, 10, 5, null).sum();
+ }
+ @Override
+ public Traversal<List<Integer>, Number> get_g_injectXlistXnull_10_5_nullXX_sumXlocalX() {
+ return g.inject(Arrays.asList(null, 10, 5, null)).sum(Scope.local);
+ }
+
+ @Override
+ public Traversal<Vertex, Number> get_g_V_aggregateXaX_byXageX_sumXlocalX() {
+ return g.V().aggregate("a").by("age").cap("a").sum(Scope.local);
+ }
+
+ @Override
+ public Traversal<Vertex, Number> get_g_V_aggregateXaX_byXageX_capXaX_unfold_sum() {
+ return g.V().aggregate("a").by("age").cap("a").unfold().sum();
+ }
+
+ @Override
+ public Traversal<Vertex, Number> get_g_V_aggregateXaX_byXfooX_sumXlocalX() {
+ return g.V().aggregate("a").by("foo").cap("a").sum(Scope.local);
+ }
+
+ @Override
+ public Traversal<Vertex, Number> get_g_V_aggregateXaX_byXfooX_capXaX_unfold_sum() {
+ return g.V().aggregate("a").by("foo").cap("a").unfold().sum();
+ }
+
+ @Override
public Traversal<Vertex, Number> get_g_V_age_fold_sumXlocalX() {
return g.V().values("age").fold().sum(Scope.local);
}
diff --git a/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/process/computer/SparkMemory.java b/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/process/computer/SparkMemory.java
index 5a04162..9f9a4a9 100644
--- a/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/process/computer/SparkMemory.java
+++ b/spark-gremlin/src/main/java/org/apache/tinkerpop/gremlin/spark/process/computer/SparkMemory.java
@@ -167,6 +167,5 @@ public final class SparkMemory implements Memory.Admin, Serializable {
private void checkKeyValue(final String key, final Object value) {
if (!this.memoryComputeKeys.containsKey(key))
throw GraphComputer.Exceptions.providedKeyIsNotAMemoryComputeKey(key);
- MemoryHelper.validateValue(value);
}
}
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerMemory.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerMemory.java
index 1502d84..f577c26 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerMemory.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/computer/TinkerMemory.java
@@ -29,6 +29,7 @@ import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import java.util.HashMap;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@@ -41,13 +42,14 @@ import java.util.stream.Collectors;
public final class TinkerMemory implements Memory.Admin {
public final Map<String, MemoryComputeKey> memoryKeys = new HashMap<>();
- public Map<String, Object> previousMap;
- public Map<String, Object> currentMap;
+ public Map<String, Optional<Object>> previousMap;
+ public Map<String, Optional<Object>> currentMap;
private final AtomicInteger iteration = new AtomicInteger(0);
private final AtomicLong runtime = new AtomicLong(0l);
private boolean inExecute = false;
public TinkerMemory(final VertexProgram<?> vertexProgram, final Set<MapReduce> mapReducers) {
+ // ConcurrentHashMap makes us use Optional since you cant store null in them as values (or keys)
this.currentMap = new ConcurrentHashMap<>();
this.previousMap = new ConcurrentHashMap<>();
if (null != vertexProgram) {
@@ -108,10 +110,11 @@ public final class TinkerMemory implements Memory.Admin {
@Override
public <R> R get(final String key) throws IllegalArgumentException {
- final R r = (R) this.previousMap.get(key);
- if (null == r)
- throw Memory.Exceptions.memoryDoesNotExist(key);
- else if (this.inExecute && !this.memoryKeys.get(key).isBroadcast())
+ if (!this.previousMap.containsKey(key)) throw Memory.Exceptions.memoryDoesNotExist(key);
+
+ final Optional<Object> o = this.previousMap.get(key);
+ final R r = (R) o.orElse(null);
+ if (this.inExecute && !this.memoryKeys.get(key).isBroadcast())
throw Memory.Exceptions.memoryDoesNotExist(key);
else
return r;
@@ -122,7 +125,7 @@ public final class TinkerMemory implements Memory.Admin {
checkKeyValue(key, value);
if (this.inExecute)
throw Memory.Exceptions.memorySetOnlyDuringVertexProgramSetUpAndTerminate(key);
- this.currentMap.put(key, value);
+ this.currentMap.put(key, Optional.ofNullable(value));
}
@Override
@@ -130,7 +133,7 @@ public final class TinkerMemory implements Memory.Admin {
checkKeyValue(key, value);
if (!this.inExecute)
throw Memory.Exceptions.memoryAddOnlyDuringVertexProgramExecute(key);
- this.currentMap.compute(key, (k, v) -> null == v ? value : this.memoryKeys.get(key).getReducer().apply(v, value));
+ this.currentMap.compute(key, (k, v) -> Optional.ofNullable(null == v || !v.isPresent() ? value : this.memoryKeys.get(key).getReducer().apply(v.get(), value)));
}
@Override
@@ -141,6 +144,6 @@ public final class TinkerMemory implements Memory.Admin {
protected void checkKeyValue(final String key, final Object value) {
if (!this.memoryKeys.containsKey(key))
throw GraphComputer.Exceptions.providedKeyIsNotAMemoryComputeKey(key);
- MemoryHelper.validateValue(value);
+ //MemoryHelper.validateValue(value);
}
}
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/TinkerGraphComputerProvider.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/TinkerGraphComputerProvider.java
index 6121ef7..e7a6c16 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/TinkerGraphComputerProvider.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/process/TinkerGraphComputerProvider.java
@@ -42,7 +42,8 @@ public class TinkerGraphComputerProvider extends TinkerGraphProvider {
@Override
public GraphTraversalSource traversal(final Graph graph) {
return graph.traversal().withStrategies(VertexProgramStrategy.create(new MapConfiguration(new HashMap<String, Object>() {{
- put(VertexProgramStrategy.WORKERS, RANDOM.nextInt(Runtime.getRuntime().availableProcessors()) + 1);
+ put(VertexProgramStrategy.WORKERS, 1);
+ //put(VertexProgramStrategy.WORKERS, RANDOM.nextInt(Runtime.getRuntime().availableProcessors()) + 1);
put(VertexProgramStrategy.GRAPH_COMPUTER, RANDOM.nextBoolean() ?
GraphComputer.class.getCanonicalName() :
TinkerGraphComputer.class.getCanonicalName());