This is an automated email from the ASF dual-hosted git repository.
colegreer pushed a commit to branch 3.8-dev
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/3.8-dev by this push:
new a543fceefe [TINKERPOP-3173] Simplify comparability semantics (#3195)
a543fceefe is described below
commit a543fceefe7da8379c6280f2e431d18fdc8fec78
Author: Cole Greer <[email protected]>
AuthorDate: Thu Sep 11 16:12:59 2025 -0700
[TINKERPOP-3173] Simplify comparability semantics (#3195)
Replaces the old system of ternary boolean semantics with simple binary
semantics. The triggers for "ERROR" states from illegal comparisons are
unchanged (typically comparisons with NaN or between incomparable types
such as String and int). The difference now is that instead of the ERROR
being propagated according to ternary logic semantics until a reduction
point is reached, the error now immediately returns a value of FALSE.
This will be most visible in expressions which include negations. Prior
to this change, `g.inject(NaN).not(is(1))` would produce no results as
!(NaN == 1) -> !(ERROR) -> ERROR -> traverser is filtered out. After
this change, the same traversal will return NaN as the same expression
now evaluates as !(NaN == 1) -> !(FALSE) -> TRUE -> traverser is not
filtered.
This commit also introduces NotP to model a negated P, as the old
method of getting the complement of the PBiPredicate may handle edge
cases incorrectly:
!(1 < NaN) != (1 >= NaN) -> !(FALSE) != (FALSE)
---
CHANGELOG.asciidoc | 2 +
docs/src/dev/provider/gremlin-semantics.asciidoc | 87 +++------------
docs/src/upgrade/release-3.8.x.asciidoc | 25 +++++
.../gremlin/process/traversal/Compare.java | 65 +++--------
.../gremlin/process/traversal/Contains.java | 20 +---
.../traversal/GremlinTypeErrorException.java | 40 -------
.../tinkerpop/gremlin/process/traversal/NotP.java | 120 +++++++++++++++++++++
.../tinkerpop/gremlin/process/traversal/P.java | 2 +-
.../gremlin/process/traversal/PBiPredicate.java | 7 --
.../tinkerpop/gremlin/process/traversal/Text.java | 11 +-
.../tinkerpop/gremlin/process/traversal/TextP.java | 4 +-
.../process/traversal/step/filter/AllStep.java | 12 +--
.../process/traversal/step/filter/AndStep.java | 13 +--
.../process/traversal/step/filter/AnyStep.java | 12 +--
.../traversal/step/filter/BinaryReductionStep.java | 29 -----
.../process/traversal/step/filter/FilterStep.java | 22 +---
.../process/traversal/step/filter/NoneStep.java | 12 +--
.../process/traversal/step/filter/OrStep.java | 13 +--
.../traversal/step/filter/TraversalFilterStep.java | 2 +-
.../traversal/step/filter/WhereTraversalStep.java | 2 +-
.../strategy/optimization/CountStrategy.java | 7 +-
.../traversal/translator/GroovyTranslator.java | 4 +
.../gremlin/process/traversal/util/AndP.java | 13 +--
.../gremlin/process/traversal/util/OrP.java | 14 +--
.../io/binary/TypeSerializerRegistry.java | 2 +
.../structure/io/binary/types/PSerializer.java | 11 +-
.../structure/io/graphson/GraphSONModule.java | 3 +
.../io/graphson/TraversalSerializersV2.java | 3 +
.../io/graphson/TraversalSerializersV3.java | 3 +
.../structure/io/gryo/GryoClassResolverV1.java | 3 +
.../structure/io/gryo/GryoClassResolverV3.java | 3 +
.../structure/io/gryo/GryoSerializersV1.java | 27 +++--
.../structure/io/gryo/GryoSerializersV3.java | 27 +++--
.../gremlin/util/GremlinValueComparator.java | 106 +++++++++++-------
.../gremlin/process/traversal/CompareTest.java | 114 ++++++++++----------
.../gremlin/process/traversal/ConnectiveTest.java | 16 ---
.../tinkerpop/gremlin/process/traversal/PTest.java | 49 ++++++---
.../traversal/step/filter/NoneStepTest.java | 7 +-
.../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 5 +-
gremlin-go/driver/cucumber/gremlin.go | 5 +-
.../gremlin-javascript/test/cucumber/gremlin.js | 5 +-
gremlin-python/src/main/python/radish/gremlin.py | 5 +-
.../process/ProcessEmbeddedStandardSuite.java | 4 +-
.../gremlin/process/ProcessStandardSuite.java | 4 +-
...csTest.java => ComparabilitySemanticsTest.java} | 42 +++-----
.../gremlin/test/features/filter/Not.feature | 61 +++++++++++
.../test/features/semantics/Comparability.feature | 6 +-
.../traversal/step/sideEffect/TinkerGraphStep.java | 19 +---
48 files changed, 537 insertions(+), 531 deletions(-)
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 355dda9c2a..26bf36c219 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -84,6 +84,8 @@ This release also includes changes from <<release-3-7-XXX,
3.7.XXX>>.
* Deprecated `ProcessStandardSuite` and the `ProcessComputerSuite` in favor of
Gherkin testing and the `ProcessEmbeddedStandardSuite` and
`ProcessEmbeddedComputerSuite` for testing JVM-specific Gremlin behaviors.
* Removed lambda oriented Gremlin testing from Gherkin test suite.
* Removed `P.getOriginalValue()` in favor of `P.getValue()`
+* Simplified comparability semantics from ternary boolean logic to binary
logic.
+* Introduced `NotP` class for negation of `P`
* Increase minimum Java version from 1.8 to 11 for building and running.
* Moved all lambda oriented Gremlin tests to `LambdaStepTest` in the Java test
suite.
* Removed the `@RemoteOnly` testing tag in Gherkin as lambda tests have all
been moved to the Java test suite.
diff --git a/docs/src/dev/provider/gremlin-semantics.asciidoc
b/docs/src/dev/provider/gremlin-semantics.asciidoc
index 749188a595..49aa52b343 100644
--- a/docs/src/dev/provider/gremlin-semantics.asciidoc
+++ b/docs/src/dev/provider/gremlin-semantics.asciidoc
@@ -144,67 +144,10 @@ Key differences include handling of numeric types and NaN.
Both Equality and Equivalence can be understood as complete, i.e. the result
of equality and equivalence checks is
always either `TRUE` or `FALSE` (in particular, it never returns `nulltype` or
throws an exception). Similarly,
Orderability can be also understood as complete - any two values can be
compared without error for ordering purposes.
-Comparability semantics are not complete with respect to binary boolean
semantics, and as such, Gremlin introduces a
-ternary boolean semantics for Comparability that includes a third boolean
state - `ERROR`, with its own well-defined
-semantics.
-
-=== Ternary Boolean Logics
-
-When evaluating boolean value expressions, we sometimes encounter situations
that cannot be proved as either `TRUE` or
-`FALSE`. Common `ERROR` cases are Comparability against `NaN`, cross-type
Comparability (e.g. `String` vs `Numeric`), or
-other invalid arguments to other boolean value expressions.
-
-Rather than throwing an exception and halting the traversal, we extend normal
binary boolean logics and introduce a
-third boolean option - `ERROR`. How this `ERROR` result is handled is Graph
provider dependent. For the reference
-implementation, `ERROR` is an internal representation only and will not be
propagated back to the client as an
-exception - it will eventually hit a binary reduction operation and be reduced
to `FALSE` (thus quietly filters the
-solution that produced the `ERROR`). Before that happens though, it will be
treated as its own boolean value with its
-own semantics that can be used in other boolean value expressions, such as
Connective predicates (`P.and/or`) and
-negation (`P.not`).
-
-==== Ternary Boolean Semantics for `AND`
-
-|===
-|A|B|AND|Intuition
-
-| `TRUE` | `TRUE` | `TRUE` |
-| `TRUE` | `FALSE` | `FALSE` |
-| `TRUE` | `ERROR` | `ERROR` | `TRUE && X == X`
-| `FALSE` | `TRUE` | `FALSE` |
-| `FALSE` | `FALSE` | `FALSE` |
-| `FALSE` | `ERROR` | `FALSE` | `FALSE && X == FALSE`
-| `ERROR` | `TRUE` | `ERROR` | `X && TRUE == X`
-| `ERROR` | `FALSE` | `FALSE` | `X && FALSE == FALSE`
-| `ERROR` | `ERROR` | `ERROR` | `X && X == X`
-|===
-
-==== Ternary Boolean Semantics for `OR`
-
-|===
-|A|B|OR|Intuition
-
-| `TRUE` | `TRUE` | `TRUE` |
-| `TRUE` | `FALSE` | `TRUE` |
-| `TRUE` | `ERROR` | `TRUE` | `TRUE \|\| X == TRUE`
-| `FALSE` | `TRUE` | `TRUE` |
-| `FALSE` | `FALSE` | `FALSE` |
-| `FALSE` | `ERROR` | `ERROR` | `FALSE \|\| X == X`
-| `ERROR` | `TRUE` | `TRUE` | `X \|\| TRUE == TRUE`
-| `ERROR` | `FALSE` | `ERROR` | `X \|\| FALSE == X`
-| `ERROR` | `ERROR` | `ERROR` | `X \|\| X == X`
-|===
-
-==== Ternary Boolean Semantics for `NOT`
-
-The `NOT` predicate inverts `TRUE` and `FALSE`, respectively, but maintains
`ERROR` values. The key idea is that, for an
-`ERROR` value, we can neither prove nor disprove the expression, and hence
stick with `ERROR`.
-|===
-|Argument | Result
-
-|`TRUE` | `FALSE`
-|`FALSE` | `TRUE`
-|`ERROR` | `ERROR`
-|===
+Comparability semantics are not complete with respect to traditional binary
boolean semantics, as certain comparisons
+cannot be proved as either `TRUE` or `FALSE`. Common examples of such cases
are Comparability against `NaN`, cross-type
+Comparability (e.g. `String` vs `Numeric`). For the purposes of Comparability
within Gremlin, any such incomputable
+comparison is defined to be `FALSE`, and traditional binary boolean semantics
apply thereafter.
[[gremlin-semantics-equality-comparability]]
=== Equality and Comparability
@@ -243,12 +186,12 @@ classes of comparison:
|Comparisons Involving `NaN`|`(NaN,X)` +
-where `X` = any value, including `NaN`|`ERROR` +
+where `X` = any value, including `NaN`|`FALSE` +
Comparing `NaN` to anything (including itself) cannot be evaluated.|`FALSE`
|Comparisons Involving `null`|`(null,null)`|`compare() == 0`|`TRUE`
-||`(null, X)`|`ERROR` +
+||`(null, X)`|`FALSE` +
Since `nulltype` is its own type, this falls under the umbrella of cross-type
comparisons. |`FALSE`
|Comparisons within the same type family (i.e. String vs. String, Number vs.
Number, etc.)|`(X, Y)` +
@@ -256,7 +199,7 @@ Since `nulltype` is its own type, this falls under the
umbrella of cross-type co
where `X` and `Y` of same type|Result of `compare()` depends on type
semantics, defined below.|`TRUE` iff `compare() == 0`
|Comparisons across types (i.e. String vs. Number)|`(X, Y)` +
-where `X` and `Y` of different type|`ERROR`|`FALSE`
+where `X` and `Y` of different type|`FALSE`|`FALSE`
|===
@@ -316,8 +259,8 @@ not <<Equality,Equal>> and not <<Comparability,Comparable>>.
===== List
Lists are compared pairwise, element-by-element, in their natural list order.
For each element, if the pairs are
-<<Equality and Comparability,Equal>>, we simply move on to the next element
pair until we encounter a pair whose
-`Comparability.compare()` value is non-zero (`-1`, `1`, or `ERROR`), and we
return that value. Lists can be evaluated
+<<Equality and Comparability,Equal>>, we simply move on to the next element
pair until we encounter a pair which is not
+comparable, or whose `Comparability.compare()` value is non-zero, and we
return that value. Lists can be evaluated
for <<Equality and Comparability,Equality and Comparability>> even if they
contain multiple types of elements, so long
as their elements are pairwise comparable per <<Equality and
Comparability,Equality/Comparability>> semantics. During
this element by element comparison, if iteration `A` exhausts its elements
before iteration `B` then `A < B`, and
@@ -400,7 +343,7 @@ treat pairwise entries with semantically equivalent keys as
the same.
<<Equality and Comparability,Equality and Comparability>> were described in
depth in the sections above, and their
semantics map to the `P` predicates. <<Comparability,Comparability>> in
particular is limited to
comparison of values within the same type family. Comparability is complete
within a given type (except for `NaN`,
-which results in `ERROR` for any comparison), but returns `ERROR` for
comparisons across types (e.g., an integer cannot
+which results in `FALSE` for any comparison), but returns `FALSE` for
comparisons across types (e.g., an integer cannot
be compared to a string).
Orderability semantics are very similar to Comparability for the most part,
except that Orderability will never result
@@ -409,8 +352,8 @@ we will still be able to determine their respective order.
This allows for a tot
the reference implementation, any step using `Order.asc` or `Order.desc` (e.g.
OrderGlobalStep, OrderLocalStep) will
follow these semantics.
-To achieve this globally complete order, we need to address any cases in
Comparability that produce a comparison
-`ERROR`, we must define a global order across type families, and we must
provide semantics for ordering "unknown"
+To achieve this globally complete order, we need to address any cases in
Comparability which are incomputable, we must
+define a global order across type families, and we must provide semantics for
ordering "unknown"
values (for cases of in-process JVM implementations, like the TinkerGraph).
We define the type space, and the global order across the type space as
follows:
@@ -439,11 +382,11 @@ Within a given type space, Orderability determines if two
values are ordered at
positioned before or after the another. When the position is identical, which
value comes first (in other words,
whether it should perform stable sort) depends on graph providers'
implementation.
-To allow for this total ordering, we must also address the cases in <<Equality
and Comparability,Comparability>> that
-produce an comparison `ERROR`:
+To allow for this total ordering, we must also address the cases in <<Equality
and Comparability,Comparability>> where
+the comparison is incomputable:
|===
-|`ERROR` Scenario|Comparability|Orderability
+|Incomputable Scenario|Comparability|Orderability
|Comparison against `NaN`|`NaN` not comparable to anything, including
itself.|`NaN` appears after `+Infinity` in the numeric type space.
|Comparison across types|Cannot compare values of different types. This
includes the `nulltype`.|Subject to a total
diff --git a/docs/src/upgrade/release-3.8.x.asciidoc
b/docs/src/upgrade/release-3.8.x.asciidoc
index 3adaf7b310..fb92e63e56 100644
--- a/docs/src/upgrade/release-3.8.x.asciidoc
+++ b/docs/src/upgrade/release-3.8.x.asciidoc
@@ -128,6 +128,22 @@ version, the `none()` step was used to "throw away" all
traversers that passed i
renamed to `discard()`. The `discard()` step with its verb tone arguably makes
for a better name for that feature, but
it also helped make room for `none()` to be repurposed as `none(P)` which is a
complement to `any(P)` and `all(P) steps.
+==== Simplified Comparability Semantics
+
+The previous system of ternary boolean semantics has been replaced with
simplified binary semantics. The triggers for
+"ERROR" states from illegal comparisons are unchanged (typically comparisons
with NaN or between incomparable types
+such as String and int). The difference now is that instead of the ERROR being
propagated according to ternary logic
+semantics until a reduction point is reached, the error now immediately
returns a value of FALSE.
+
+This will be most visible in expressions which include negations. Prior to
this change, `g.inject(NaN).not(is(1))` would
+produce no results as `!(NaN == 1)` -> `!(ERROR)` -> `ERROR` -> traverser is
filtered out. After this change, the same
+traversal will return NaN as the same expression now evaluates as `!(NaN ==
1)` -> `!(FALSE)` -> `TRUE` -> traverser is
+not filtered.
+
+See:
link:https://tinkerpop.apache.org/docs/3.8.0/dev/provider/#gremlin-semantics-equality-comparability[Comparability
semantics docs]
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-3173[TINKERPOP-3173]
+
==== Set minimum Java version to 11
TinkerPop 3.8 requires a minimum of Java 11 for building and running. Support
for Java 1.8 has been dropped.
@@ -691,6 +707,15 @@ The `DiscardStep` is now renamed to `DiscardStep`.
Providers who developed strat
`DiscardStep` should switch to `DiscardStep`. Note that `DiscardStep` has been
repurposed as `none(P)` for filtering
collections as a complement to `any(P)` and `all(P)`.
+===== Added `NotP`
+
+Added a new subclass of `P` to model negated predicates. This has been
introduced as the previous system of taking the
+complementary PBiPredicate during negation does not account for edge cases
involving illegal comparisons:
+
+`!(1 < NaN) != (1 >= NaN)` -> `!(FALSE) != (FALSE)`
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-3173[TINKERPOP-3173]
+
===== Set minimum Java version to 11
TinkerPop 3.8 requires a minimum of Java 11 for building and running. Support
for Java 1.8 has been dropped.
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Compare.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Compare.java
index 09387614f8..a2f94d22f3 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Compare.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Compare.java
@@ -41,14 +41,6 @@ public enum Compare implements PBiPredicate<Object, Object> {
public boolean test(final Object first, final Object second) {
return GremlinValueComparator.COMPARABILITY.equals(first, second);
}
-
- /**
- * The negative of {@code eq} is {@link #neq}.
- */
- @Override
- public Compare negate() {
- return neq;
- }
},
/**
@@ -61,14 +53,6 @@ public enum Compare implements PBiPredicate<Object, Object> {
public boolean test(final Object first, final Object second) {
return !eq.test(first, second);
}
-
- /**
- * The negative of {@code neq} is {@link #eq}
- */
- @Override
- public Compare negate() {
- return eq;
- }
},
/**
@@ -79,16 +63,11 @@ public enum Compare implements PBiPredicate<Object, Object>
{
gt {
@Override
public boolean test(final Object first, final Object second) {
+ if (!GremlinValueComparator.COMPARABILITY.comparable(first,
second)) {
+ return false;
+ }
return GremlinValueComparator.COMPARABILITY.compare(first, second)
> 0;
}
-
- /**
- * The negative of {@code gt} is {@link #lte}.
- */
- @Override
- public Compare negate() {
- return lte;
- }
},
/**
@@ -99,16 +78,11 @@ public enum Compare implements PBiPredicate<Object, Object>
{
gte {
@Override
public boolean test(final Object first, final Object second) {
+ if (!GremlinValueComparator.COMPARABILITY.comparable(first,
second)) {
+ return false;
+ }
return GremlinValueComparator.COMPARABILITY.compare(first, second)
>= 0;
}
-
- /**
- * The negative of {@code gte} is {@link #lt}.
- */
- @Override
- public Compare negate() {
- return lt;
- }
},
/**
@@ -119,16 +93,11 @@ public enum Compare implements PBiPredicate<Object,
Object> {
lt {
@Override
public boolean test(final Object first, final Object second) {
+ if (!GremlinValueComparator.COMPARABILITY.comparable(first,
second)) {
+ return false;
+ }
return GremlinValueComparator.COMPARABILITY.compare(first, second)
< 0;
}
-
- /**
- * The negative of {@code lt} is {@link #gte}.
- */
- @Override
- public Compare negate() {
- return gte;
- }
},
/**
@@ -139,21 +108,11 @@ public enum Compare implements PBiPredicate<Object,
Object> {
lte {
@Override
public boolean test(final Object first, final Object second) {
+ if (!GremlinValueComparator.COMPARABILITY.comparable(first,
second)) {
+ return false;
+ }
return GremlinValueComparator.COMPARABILITY.compare(first, second)
<= 0;
}
-
- /**
- * The negative of {@code lte} is {@link #gt}.
- */
- @Override
- public Compare negate() {
- return gt;
- }
};
- /**
- * Produce the opposite representation of the current {@code Compare} enum.
- */
- @Override
- public abstract Compare negate();
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Contains.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Contains.java
index 3231f60dbd..bfdc9f9c10 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Contains.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Contains.java
@@ -64,18 +64,10 @@ public enum Contains implements PBiPredicate<Object,
Collection> {
*/
return second.contains(first);
}
- GremlinTypeErrorException typeError = null;
for (final Object o : second) {
- try {
- if (Compare.eq.test(first, o))
- return true;
- } catch (GremlinTypeErrorException ex) {
- // hold onto it until the end in case any other arguments
evaluate to TRUE
- typeError = ex;
- }
+ if (Compare.eq.test(first, o))
+ return true;
}
- if (typeError != null)
- throw typeError;
return false;
}
},
@@ -98,12 +90,4 @@ public enum Contains implements PBiPredicate<Object,
Collection> {
*/
@Override
public abstract boolean test(final Object first, final Collection second);
-
- /**
- * Produce the opposite representation of the current {@code Contains}
enum.
- */
- @Override
- public Contains negate() {
- return this.equals(within) ? without : within;
- }
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinTypeErrorException.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinTypeErrorException.java
deleted file mode 100644
index 063a759ef3..0000000000
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/GremlinTypeErrorException.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tinkerpop.gremlin.process.traversal;
-
-/**
- * We use this exception type to signify ERROR in Ternary Boolean Logics. This
exception will never propagate to the
- * user for boolean value expressions, it will always be ultimately reduced to
FALSE.
- */
-public class GremlinTypeErrorException extends RuntimeException {
- public GremlinTypeErrorException() {
- }
-
- public GremlinTypeErrorException(final String message) {
- super(message);
- }
-
- public GremlinTypeErrorException(final String message, final Throwable
cause) {
- super(message, cause);
- }
-
- public GremlinTypeErrorException(final Throwable cause) {
- super(cause);
- }
-}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/NotP.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/NotP.java
new file mode 100644
index 0000000000..93f5e30ce5
--- /dev/null
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/NotP.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tinkerpop.gremlin.process.traversal;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * A NotP wraps a P and represents its negation. This class provides the
logical NOT operation
+ * for predicates, inverting the result of the wrapped predicate's test method.
+ */
+public class NotP<V> extends P<V> {
+ private P<V> originalP;
+ public NotP(final P<V> p) {
+ super(null, (V) null);
+
+ if (null == p) {
+ throw new IllegalArgumentException("Cannot negate a null P");
+ }
+ this.originalP = p;
+ this.biPredicate = new NotPBiPredicate<>(p.getBiPredicate());
+ }
+
+ /**
+ * Gets the current value to be passed to the predicate for testing.
+ */
+ @Override
+ public V getValue() {
+ return originalP.getValue();
+ }
+
+ @Override
+ public void setValue(final V value) {
+ super.setValue(value);
+ if (originalP != null) {
+ originalP.setValue(value);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return null == this.getValue() ? this.biPredicate.toString() :
String.format("not(%s(%s))", this.originalP.biPredicate.toString(),
this.getValue());
+ }
+
+ /**
+ * Returns the original unwrapped P contained within this NotP, as double
negation cancels out.
+ */
+ @Override
+ public P<V> negate() {
+ return originalP;
+ }
+
+ public P<V> clone() {
+ return new NotP<>(this.originalP.clone());
+ }
+
+ /**
+ * A NotPBiPredicate wraps a PBiPredicate and represents its negation.
+ */
+ public final static class NotPBiPredicate<T, U> implements PBiPredicate<T,
U>, Serializable {
+ PBiPredicate<T, U> original;
+
+ public NotPBiPredicate(final PBiPredicate<T, U> predicate) {
+ this.original = predicate;
+ }
+
+ @Override
+ public boolean test(final T t, final U u) {
+ return !original.test(t, u);
+ }
+
+ @Override
+ public PBiPredicate<T, U> negate() {
+ return original;
+ }
+
+ @Override
+ public String getPredicateName() {
+ return "not";
+ }
+
+ public PBiPredicate<T, U> getOriginal() {
+ return original;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ NotPBiPredicate<?, ?> that = (NotPBiPredicate<?, ?>) o;
+ return Objects.equals(original, that.original);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(original);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("not(%s)", original.toString());
+ }
+ }
+}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
index 4d152db63d..4fba110cae 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java
@@ -167,7 +167,7 @@ public class P<V> implements Predicate<V>, Serializable,
Cloneable {
@Override
public P<V> negate() {
- return new P<>(this.biPredicate.negate(), this.literals,
this.variables, this.isCollection);
+ return new NotP<>(this);
}
@Override
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/PBiPredicate.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/PBiPredicate.java
index aadca69c65..0d907843b5 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/PBiPredicate.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/PBiPredicate.java
@@ -36,11 +36,4 @@ public interface PBiPredicate<T, U> extends BiPredicate<T,
U> {
return toString();
}
- /**
- * Returns a predicate that represents the logical negation of this
predicate.
- **/
- default PBiPredicate<T, U> negate() {
- return (T t, U u) -> !test(t, u);
- }
-
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Text.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Text.java
index 15c717704c..bc283c5e01 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Text.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Text.java
@@ -40,7 +40,7 @@ public enum Text implements PBiPredicate<String, String> {
startingWith {
@Override
public boolean test(final String value, final String prefix) {
- checkNull(value, prefix);
+ if (isNull(value, prefix)) return false;
return value.startsWith(prefix);
}
@@ -81,7 +81,7 @@ public enum Text implements PBiPredicate<String, String> {
endingWith {
@Override
public boolean test(final String value, final String suffix) {
- checkNull(value, suffix);
+ if (isNull(value, suffix)) return false;
return value.endsWith(suffix);
}
@@ -122,7 +122,7 @@ public enum Text implements PBiPredicate<String, String> {
containing {
@Override
public boolean test(final String value, final String search) {
- checkNull(value, search);
+ if (isNull(value, search)) return false;
return value.contains(search);
}
@@ -155,10 +155,11 @@ public enum Text implements PBiPredicate<String, String> {
}
};
- private static void checkNull(final String... args) {
+ private static boolean isNull(final String... args) {
for (String arg : args)
if (arg == null)
- throw new GremlinTypeErrorException();
+ return true;
+ return false;
}
/**
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TextP.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TextP.java
index f7060d82b6..22a69c9c15 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TextP.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TextP.java
@@ -48,8 +48,8 @@ public class TextP extends P<String> {
}
@Override
- public TextP negate() {
- return new TextP(this.biPredicate.negate(), this.literals,
this.variables);
+ public P<String> negate() {
+ return new NotP<>(this);
}
public TextP clone() {
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/AllStep.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/AllStep.java
index 6f002bbea6..2e7c33b13e 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/AllStep.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/AllStep.java
@@ -18,7 +18,6 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
-import
org.apache.tinkerpop.gremlin.process.traversal.GremlinTypeErrorException;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
@@ -49,19 +48,12 @@ public final class AllStep<S, S2> extends FilterStep<S> {
final S item = traverser.get();
if (item instanceof Iterable || item instanceof Iterator || ((item !=
null) && item.getClass().isArray())) {
- GremlinTypeErrorException typeError = null;
final Iterator<S2> iterator = IteratorUtils.asIterator(item);
while (iterator.hasNext()) {
- try {
- if (!this.predicate.test(iterator.next())) {
- return false;
- }
- } catch (GremlinTypeErrorException gtee) {
- // hold onto it until the end in case any other element
evaluates to FALSE
- typeError = gtee;
+ if (!this.predicate.test(iterator.next())) {
+ return false;
}
}
- if (typeError != null) throw typeError;
return true;
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/AndStep.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/AndStep.java
index b8c7475bf5..5c20cd842f 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/AndStep.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/AndStep.java
@@ -18,7 +18,6 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
-import
org.apache.tinkerpop.gremlin.process.traversal.GremlinTypeErrorException;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
@@ -34,18 +33,10 @@ public final class AndStep<S> extends ConnectiveStep<S> {
@Override
protected boolean filter(final Traverser.Admin<S> traverser) {
- GremlinTypeErrorException typeError = null;
for (final Traversal.Admin<S, ?> traversal : this.traversals) {
- try {
- if (!TraversalUtil.test(traverser, traversal))
- return false;
- } catch (GremlinTypeErrorException ex) {
- // hold onto it until the end in case any other arguments
evaluate to FALSE
- typeError = ex;
- }
+ if (!TraversalUtil.test(traverser, traversal))
+ return false;
}
- if (typeError != null)
- throw typeError;
return true;
}
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/AnyStep.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/AnyStep.java
index 4144dd5d66..760e29af3d 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/AnyStep.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/AnyStep.java
@@ -18,7 +18,6 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
-import
org.apache.tinkerpop.gremlin.process.traversal.GremlinTypeErrorException;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
@@ -49,20 +48,13 @@ public final class AnyStep<S, S2> extends FilterStep<S> {
final S item = traverser.get();
if (item instanceof Iterable || item instanceof Iterator || ((item !=
null) && item.getClass().isArray())) {
- GremlinTypeErrorException typeError = null;
final Iterator<S2> iterator = IteratorUtils.asIterator(item);
while (iterator.hasNext()) {
- try {
- if (this.predicate.test(iterator.next())) {
- return true;
- }
- } catch (GremlinTypeErrorException gtee) {
- // hold onto it until the end in case any other element
evaluates to TRUE
- typeError = gtee;
+ if (this.predicate.test(iterator.next())) {
+ return true;
}
}
- if (typeError != null) throw typeError;
}
return false;
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/BinaryReductionStep.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/BinaryReductionStep.java
deleted file mode 100644
index c2373fe9e8..0000000000
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/BinaryReductionStep.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
-
-import
org.apache.tinkerpop.gremlin.process.traversal.GremlinTypeErrorException;
-
-/**
- * Any {@link FilterStep} marked as a BinaryReductionStep will catch {@link
GremlinTypeErrorException}s produced by
- * boolean value expressions (predicates) contained within the filter and
treat them as FALSE, thus filtering out
- * the solution. This is a reduction from Ternary Boolean logics (TRUE, FALSE,
ERROR) to ordinary boolean logics.
- */
-public interface BinaryReductionStep {
-}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/FilterStep.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/FilterStep.java
index 02a8515a7e..e07d951b5a 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/FilterStep.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/FilterStep.java
@@ -18,11 +18,9 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
-import
org.apache.tinkerpop.gremlin.process.traversal.GremlinTypeErrorException;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.AbstractStep;
-import org.apache.tinkerpop.gremlin.process.traversal.step.util.EmptyStep;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
@@ -36,23 +34,9 @@ public abstract class FilterStep<S> extends AbstractStep<S,
S> {
@Override
protected Traverser.Admin<S> processNextStart() {
while (true) {
- try {
- final Traverser.Admin<S> traverser = this.starts.next();
- if (this.filter(traverser))
- return traverser;
- } catch (GremlinTypeErrorException ex) {
- if (this instanceof BinaryReductionStep ||
getTraversal().isRoot() || !(getTraversal().getParent() instanceof FilterStep))
{
- /*
- * Either we are at a known reduction point
(TraversalFilterStep, WhereTraversalStep), we
- * are at the top level of the query, or our parent query
is not a FilterStep and thus cannot handle
- * a GremlinTypeErrorException. In any of these cases we
do a binary reduction from
- * ERROR -> FALSE and filter the solution quietly.
- */
- } else {
- // not a ternary -> binary reducer, pass the ERROR on
- throw ex;
- }
- }
+ final Traverser.Admin<S> traverser = this.starts.next();
+ if (this.filter(traverser))
+ return traverser;
}
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/NoneStep.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/NoneStep.java
index 78f4f755e8..f2090d6218 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/NoneStep.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/NoneStep.java
@@ -18,7 +18,6 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
-import
org.apache.tinkerpop.gremlin.process.traversal.GremlinTypeErrorException;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
@@ -49,19 +48,12 @@ public final class NoneStep<S, S2> extends FilterStep<S> {
final S item = traverser.get();
if (item instanceof Iterable || item instanceof Iterator || ((item !=
null) && item.getClass().isArray())) {
- GremlinTypeErrorException typeError = null;
final Iterator<S2> iterator = IteratorUtils.asIterator(item);
while (iterator.hasNext()) {
- try {
- if (this.predicate.test(iterator.next())) {
- return false;
- }
- } catch (GremlinTypeErrorException gtee) {
- // hold onto it until the end in case any other element
evaluates to TRUE
- typeError = gtee;
+ if (this.predicate.test(iterator.next())) {
+ return false;
}
}
- if (typeError != null) throw typeError;
return true;
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/OrStep.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/OrStep.java
index 2e4f0a905b..72866a17b8 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/OrStep.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/OrStep.java
@@ -18,7 +18,6 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.step.filter;
-import
org.apache.tinkerpop.gremlin.process.traversal.GremlinTypeErrorException;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
@@ -34,18 +33,10 @@ public final class OrStep<S> extends ConnectiveStep<S> {
@Override
protected boolean filter(final Traverser.Admin<S> traverser) {
- GremlinTypeErrorException typeError = null;
for (final Traversal.Admin<S, ?> traversal : this.traversals) {
- try {
- if (TraversalUtil.test(traverser, traversal))
- return true;
- } catch (GremlinTypeErrorException ex) {
- // hold onto it until the end in case any other arguments
evaluate to TRUE
- typeError = ex;
- }
+ if (TraversalUtil.test(traverser, traversal))
+ return true;
}
- if (typeError != null)
- throw typeError;
return false;
}
}
\ No newline at end of file
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TraversalFilterStep.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TraversalFilterStep.java
index 63aa62d3e2..773e389360 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TraversalFilterStep.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/TraversalFilterStep.java
@@ -34,7 +34,7 @@ import java.util.Set;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-public final class TraversalFilterStep<S> extends FilterStep<S> implements
TraversalParent, Configuring, BinaryReductionStep {
+public final class TraversalFilterStep<S> extends FilterStep<S> implements
TraversalParent, Configuring {
private final Parameters parameters = new Parameters();
private Traversal.Admin<S, ?> filterTraversal;
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java
index 1fa30b98b9..2d3bee3665 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/WhereTraversalStep.java
@@ -42,7 +42,7 @@ import java.util.Set;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
-public final class WhereTraversalStep<S> extends FilterStep<S> implements
TraversalParent, Scoping, PathProcessor, BinaryReductionStep {
+public final class WhereTraversalStep<S> extends FilterStep<S> implements
TraversalParent, Scoping, PathProcessor {
protected Traversal.Admin<?, ?> whereTraversal;
protected final Set<String> scopeKeys = new HashSet<>();
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/CountStrategy.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/CountStrategy.java
index 3b0cdc526e..49b4f2785c 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/CountStrategy.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/optimization/CountStrategy.java
@@ -20,7 +20,9 @@ package
org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization;
import org.apache.tinkerpop.gremlin.process.traversal.Compare;
import org.apache.tinkerpop.gremlin.process.traversal.Contains;
+import org.apache.tinkerpop.gremlin.process.traversal.NotP;
import org.apache.tinkerpop.gremlin.process.traversal.P;
+import org.apache.tinkerpop.gremlin.process.traversal.PBiPredicate;
import org.apache.tinkerpop.gremlin.process.traversal.Step;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
@@ -46,7 +48,6 @@ import
org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
import java.util.Collection;
import java.util.Collections;
-import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@@ -72,8 +73,8 @@ public final class CountStrategy extends
AbstractTraversalStrategy<TraversalStra
put(Contains.within, 1L);
put(Contains.without, 0L);
}};
- private static final Set<Compare> INCREASED_OFFSET_SCALAR_PREDICATES =
- EnumSet.of(Compare.eq, Compare.neq, Compare.lte, Compare.gt);
+ private static final Set<PBiPredicate> INCREASED_OFFSET_SCALAR_PREDICATES =
+ Set.of(Compare.eq, Compare.neq, Compare.lte, Compare.gt, new
NotP.NotPBiPredicate(Compare.eq), new NotP.NotPBiPredicate(Compare.neq), new
NotP.NotPBiPredicate(Compare.lt), new NotP.NotPBiPredicate(Compare.gt));
private static final CountStrategy INSTANCE = new CountStrategy();
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslator.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslator.java
index 91cc7fc2de..647073b2eb 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslator.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/translator/GroovyTranslator.java
@@ -23,6 +23,7 @@ import
org.apache.commons.configuration2.ConfigurationConverter;
import org.apache.commons.text.StringEscapeUtils;
import org.apache.tinkerpop.gremlin.jsr223.CoreImports;
import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
+import org.apache.tinkerpop.gremlin.process.traversal.NotP;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Pick;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
@@ -394,6 +395,9 @@ public final class GroovyTranslator implements
Translator.ScriptTranslator {
script.append(".").append(connector).append("(");
}
}
+ } else if (p instanceof NotP) {
+ script.append("P.not(");
+ produceScript(p.negate());
} else {
script.append("P.").append(p.getPredicateName()).append("(");
convertToScript(p.getValue());
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/AndP.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/AndP.java
index 6f0a93b863..22c146736b 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/AndP.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/AndP.java
@@ -18,7 +18,6 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.util;
-import
org.apache.tinkerpop.gremlin.process.traversal.GremlinTypeErrorException;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.PBiPredicate;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
@@ -79,18 +78,10 @@ public final class AndP<V> extends ConnectiveP<V> {
@Override
public boolean test(final V valueA, final V valueB) {
- GremlinTypeErrorException typeError = null;
for (final P<V> predicate : this.andP.predicates) {
- try {
- if (!predicate.test(valueA))
- return false;
- } catch (GremlinTypeErrorException ex) {
- // hold onto it until the end in case any other arguments
evaluate to FALSE
- typeError = ex;
- }
+ if (!predicate.test(valueA))
+ return false;
}
- if (typeError != null)
- throw typeError;
return true;
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/OrP.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/OrP.java
index 4e270d8b7f..cc6770a5b9 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/OrP.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/util/OrP.java
@@ -18,7 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.process.traversal.util;
-import
org.apache.tinkerpop.gremlin.process.traversal.GremlinTypeErrorException;
+import org.apache.tinkerpop.gremlin.process.traversal.NotP;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.PBiPredicate;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
@@ -79,18 +79,10 @@ public final class OrP<V> extends ConnectiveP<V> {
@Override
public boolean test(final V valueA, final V valueB) {
- GremlinTypeErrorException typeError = null;
for (final P<V> predicate : this.orP.predicates) {
- try {
- if (predicate.test(valueA))
- return true;
- } catch (GremlinTypeErrorException ex) {
- // hold onto it until the end in case any other arguments
evaluate to TRUE
- typeError = ex;
- }
+ if (predicate.test(valueA))
+ return true;
}
- if (typeError != null)
- throw typeError;
return false;
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java
index 56a85b590b..435494fd1f 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/TypeSerializerRegistry.java
@@ -22,6 +22,7 @@ import
org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.process.traversal.DT;
import org.apache.tinkerpop.gremlin.process.traversal.Merge;
import org.apache.tinkerpop.gremlin.process.traversal.N;
+import org.apache.tinkerpop.gremlin.process.traversal.NotP;
import org.apache.tinkerpop.gremlin.process.traversal.Operator;
import org.apache.tinkerpop.gremlin.process.traversal.Order;
import org.apache.tinkerpop.gremlin.process.traversal.P;
@@ -177,6 +178,7 @@ public class TypeSerializerRegistry {
new RegistryEntry<>(P.class, new PSerializer<>(DataType.P,
P.class)),
new RegistryEntry<>(AndP.class, new PSerializer<>(DataType.P,
AndP.class)),
new RegistryEntry<>(OrP.class, new PSerializer<>(DataType.P,
OrP.class)),
+ new RegistryEntry<>(NotP.class, new PSerializer<>(DataType.P,
NotP.class)),
new RegistryEntry<>(TextP.class, new PSerializer<>(DataType.TEXTP,
TextP.class)),
new RegistryEntry<>(Scope.class, EnumSerializer.ScopeSerializer),
new RegistryEntry<>(T.class, EnumSerializer.TSerializer),
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/PSerializer.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/PSerializer.java
index 5190457a3f..035bd801c2 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/PSerializer.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/binary/types/PSerializer.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.structure.io.binary.types;
+import org.apache.tinkerpop.gremlin.process.traversal.NotP;
import org.apache.tinkerpop.gremlin.structure.io.binary.DataType;
import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader;
import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter;
@@ -151,9 +152,15 @@ public class PSerializer<T extends P> extends
SimpleTypeSerializer<T> {
@Override
protected void writeValue(final T value, final Buffer buffer, final
GraphBinaryWriter context) throws IOException {
// the predicate name is either a static method of P or an instance
method when a type ConnectiveP
- final boolean isConnectedP = value instanceof ConnectiveP;
final String predicateName = value.getPredicateName();
- final Object args = isConnectedP ? ((ConnectiveP<?>)
value).getPredicates() : value.getValue();
+ final Object args;
+ if (value instanceof ConnectiveP) {
+ args = ((ConnectiveP<?>) value).getPredicates();
+ } else if (value instanceof NotP) {
+ args = value.negate();
+ } else {
+ args = value.getValue();
+ }
final List<Object> argsAsList = args instanceof Collection ? new
ArrayList<>((Collection) args) : Collections.singletonList(args);
final int length = argsAsList.size();
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
index b40d029a98..de1b871509 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
@@ -27,6 +27,7 @@ import
org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.process.traversal.DT;
import org.apache.tinkerpop.gremlin.process.traversal.Merge;
import org.apache.tinkerpop.gremlin.process.traversal.N;
+import org.apache.tinkerpop.gremlin.process.traversal.NotP;
import org.apache.tinkerpop.gremlin.process.traversal.Operator;
import org.apache.tinkerpop.gremlin.process.traversal.Order;
import org.apache.tinkerpop.gremlin.process.traversal.P;
@@ -177,6 +178,7 @@ abstract class GraphSONModule extends
TinkerPopJacksonModule {
put(Bytecode.Binding.class, "Binding");
put(AndP.class, "P");
put(OrP.class, "P");
+ put(NotP.class, "P");
put(P.class, "P");
put(TextP.class, "TextP");
put(TraversalStrategyProxy.class, "TraversalStrategy");
@@ -440,6 +442,7 @@ abstract class GraphSONModule extends
TinkerPopJacksonModule {
put(Bytecode.Binding.class, "Binding");
put(AndP.class, "P");
put(OrP.class, "P");
+ put(NotP.class, "P");
put(P.class, "P");
put(TextP.class, "TextP");
put(TraversalStrategyProxy.class, "TraversalStrategy");
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV2.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV2.java
index 8141bbeaba..ce57622c4e 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV2.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV2.java
@@ -24,6 +24,7 @@ import
org.apache.commons.configuration2.ConfigurationConverter;
import
org.apache.tinkerpop.gremlin.process.remote.traversal.DefaultRemoteTraverser;
import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.process.traversal.N;
+import org.apache.tinkerpop.gremlin.process.traversal.NotP;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
@@ -169,6 +170,8 @@ final class TraversalSerializersV2 {
jsonGenerator.writeObject(predicate);
}
jsonGenerator.writeEndArray();
+ } else if (p instanceof NotP) {
+ jsonGenerator.writeObjectField(GraphSONTokens.VALUE,
p.negate());
} else {
if (p.getValue() instanceof Collection) {
jsonGenerator.writeArrayFieldStart(GraphSONTokens.VALUE);
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3.java
index daf3b14d7c..3fa7fab076 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/TraversalSerializersV3.java
@@ -24,6 +24,7 @@ import
org.apache.commons.configuration2.ConfigurationConverter;
import
org.apache.tinkerpop.gremlin.process.remote.traversal.DefaultRemoteTraverser;
import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.process.traversal.N;
+import org.apache.tinkerpop.gremlin.process.traversal.NotP;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
@@ -176,6 +177,8 @@ final class TraversalSerializersV3 {
jsonGenerator.writeObject(predicate);
}
jsonGenerator.writeEndArray();
+ } else if (p instanceof NotP) {
+ jsonGenerator.writeObjectField(GraphSONTokens.VALUE,
p.negate());
} else
jsonGenerator.writeObjectField(GraphSONTokens.VALUE,
p.getValue());
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoClassResolverV1.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoClassResolverV1.java
index 7acd276a28..16fd4abaea 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoClassResolverV1.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoClassResolverV1.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.structure.io.gryo;
+import org.apache.tinkerpop.gremlin.process.traversal.NotP;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Path;
import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
@@ -71,6 +72,8 @@ public class GryoClassResolverV1 extends
AbstractGryoClassResolver {
type = InetAddress.class;
else if (ConnectiveP.class.isAssignableFrom(clazz))
type = P.class;
+ else if (NotP.class.isAssignableFrom(clazz))
+ type = P.class;
else
type = clazz;
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoClassResolverV3.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoClassResolverV3.java
index 2066f8db09..488d18f4c8 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoClassResolverV3.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoClassResolverV3.java
@@ -18,6 +18,7 @@
*/
package org.apache.tinkerpop.gremlin.structure.io.gryo;
+import org.apache.tinkerpop.gremlin.process.traversal.NotP;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.Path;
import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
@@ -73,6 +74,8 @@ public class GryoClassResolverV3 extends
AbstractGryoClassResolver {
type = InetAddress.class;
else if (ConnectiveP.class.isAssignableFrom(clazz))
type = P.class;
+ else if (NotP.class.isAssignableFrom(clazz))
+ type = P.class;
else if (Metrics.class.isAssignableFrom(clazz))
type = Metrics.class;
else if (TraversalMetrics.class.isAssignableFrom(clazz))
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1.java
index 3e7a4c0b91..4200825315 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV1.java
@@ -194,7 +194,7 @@ public final class GryoSerializersV1 {
@Override
public <I extends InputShim> P read(final KryoShim<I, ?> kryo, final I
input, final Class<P> clazz) {
- final String predicate = input.readString();
+ String predicate = input.readString();
final boolean isCollection = input.readByte() == (byte) 0;
final Object value;
if (isCollection) {
@@ -208,23 +208,32 @@ public final class GryoSerializersV1 {
}
try {
+ boolean negated = false;
+ P result = null;
+ if (predicate.startsWith("not(")) {
+ predicate = predicate.substring(4, predicate.length()-1);
+ negated = true;
+ }
+
if (predicate.equals("and") || predicate.equals("or"))
- return predicate.equals("and") ? new AndP((List<P>) value)
: new OrP((List<P>) value);
+ result = predicate.equals("and") ? new AndP((List<P>)
value) : new OrP((List<P>) value);
else if (value instanceof Collection) {
if (predicate.equals("between"))
- return P.between(((List) value).get(0), ((List)
value).get(1));
+ result = P.between(((List) value).get(0), ((List)
value).get(1));
else if (predicate.equals("inside"))
- return P.inside(((List) value).get(0), ((List)
value).get(1));
+ result = P.inside(((List) value).get(0), ((List)
value).get(1));
else if (predicate.equals("outside"))
- return P.outside(((List) value).get(0), ((List)
value).get(1));
+ result = P.outside(((List) value).get(0), ((List)
value).get(1));
else if (predicate.equals("within"))
- return P.within((Collection) value);
+ result = P.within((Collection) value);
else if (predicate.equals("without"))
- return P.without((Collection) value);
+ result = P.without((Collection) value);
else
- return (P) P.class.getMethod(predicate,
Collection.class).invoke(null, (Collection) value);
+ result = (P) P.class.getMethod(predicate,
Collection.class).invoke(null, (Collection) value);
} else
- return (P) P.class.getMethod(predicate,
Object.class).invoke(null, value);
+ result = (P) P.class.getMethod(predicate,
Object.class).invoke(null, value);
+
+ return negated ? result.negate() : result;
} catch (final Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3.java
index 9a846a3a2f..f22ec60883 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoSerializersV3.java
@@ -309,7 +309,7 @@ public final class GryoSerializersV3 {
@Override
public <I extends InputShim> P read(final KryoShim<I, ?> kryo, final I
input, final Class<P> clazz) {
- final String predicate = input.readString();
+ String predicate = input.readString();
final boolean isCollection = input.readByte() == (byte) 0;
final Object value;
if (isCollection) {
@@ -323,23 +323,32 @@ public final class GryoSerializersV3 {
}
try {
+ boolean negated = false;
+ P result = null;
+ if (predicate.startsWith("not(")) {
+ predicate = predicate.substring(4, predicate.length()-1);
+ negated = true;
+ }
+
if (predicate.equals("and") || predicate.equals("or"))
- return predicate.equals("and") ? new AndP((List<P>) value)
: new OrP((List<P>) value);
+ result = predicate.equals("and") ? new AndP((List<P>)
value) : new OrP((List<P>) value);
else if (value instanceof Collection) {
if (predicate.equals("between"))
- return P.between(((List) value).get(0), ((List)
value).get(1));
+ result = P.between(((List) value).get(0), ((List)
value).get(1));
else if (predicate.equals("inside"))
- return P.inside(((List) value).get(0), ((List)
value).get(1));
+ result = P.inside(((List) value).get(0), ((List)
value).get(1));
else if (predicate.equals("outside"))
- return P.outside(((List) value).get(0), ((List)
value).get(1));
+ result = P.outside(((List) value).get(0), ((List)
value).get(1));
else if (predicate.equals("within"))
- return P.within((Collection) value);
+ result = P.within((Collection) value);
else if (predicate.equals("without"))
- return P.without((Collection) value);
+ result = P.without((Collection) value);
else
- return (P) P.class.getMethod(predicate,
Collection.class).invoke(null, (Collection) value);
+ result = (P) P.class.getMethod(predicate,
Collection.class).invoke(null, (Collection) value);
} else
- return (P) P.class.getMethod(predicate,
Object.class).invoke(null, value);
+ result = (P) P.class.getMethod(predicate,
Object.class).invoke(null, value);
+
+ return negated ? result.negate() : result;
} catch (final Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/GremlinValueComparator.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/GremlinValueComparator.java
index f99c191b30..2f6e374d24 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/GremlinValueComparator.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/GremlinValueComparator.java
@@ -18,15 +18,16 @@
*/
package org.apache.tinkerpop.gremlin.util;
-import
org.apache.tinkerpop.gremlin.process.traversal.GremlinTypeErrorException;
import org.apache.tinkerpop.gremlin.process.traversal.Path;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.Property;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -104,13 +105,9 @@ public abstract class GremlinValueComparator implements
Comparator<Object> {
*/
@Override
public int compare(final Object f, final Object s) {
- // For Compare, NaN always produces ERROR
- if (eitherAreNaN(f, s))
- throwTypeError();
-
// For Compare we do not cross type boundaries, including null
if (!comparable(f, s))
- throwTypeError();
+ throw new IllegalStateException("Objects are not comparable");
// comparable(f, s) assures that type(f) == type(s)
final Type type = Type.type(f);
@@ -134,30 +131,13 @@ public abstract class GremlinValueComparator implements
Comparator<Object> {
if (containersOfDifferentSize(f, s))
return false;
- // For Compare, NaN always produces ERROR
- if (eitherAreNaN(f, s))
- return false;
-
// For Compare we do not cross type boundaries, including null
if (!comparable(f, s))
return false;
- try {
- // comparable(f, s) assures that type(f) == type(s)
- final Type type = Type.type(f);
- return comparator(type).compare(f, s) == 0;
- } catch (GremlinTypeErrorException ex) {
- /**
- * By routing through the compare(f, s) path we expose
ourselves to type errors, which should be
- * reduced to false for equality:
- *
- * compare(NaN, anything) -> ERROR -> FALSE for equality
- * compare(Type1, Type2) -> ERROR -> FALSE for equality
- *
- * Can also happen for elements nested inside of collections.
- */
- return false;
- }
+ // comparable(f, s) assures that type(f) == type(s)
+ final Type type = Type.type(f);
+ return comparator(type).compare(f, s) == 0;
}
private boolean containersOfDifferentSize(final Object f, final Object
s) {
@@ -173,10 +153,6 @@ public abstract class GremlinValueComparator implements
Comparator<Object> {
}
};
- private static <T> T throwTypeError() {
- throw new GremlinTypeErrorException();
- }
-
/**
* Boolean, Date, String, UUID.
*/
@@ -200,11 +176,11 @@ public abstract class GremlinValueComparator implements
Comparator<Object> {
Comparator.<Property,Object>comparing(Property::key,
this).thenComparing(Property::value, this);
/**
- * Sort List, Set, Path, and Map element-by-element in the order presented
by their natural iterator.
+ * Sort List, Set, Path, and Array element-by-element in the order
presented by their natural iterator.
*/
- private final Comparator<Iterable> iterableComparator = (f, s) -> {
- final Iterator fi = f.iterator();
- final Iterator si = s.iterator();
+ private final Comparator<Object> iterableComparator = (f, s) -> {
+ final Iterator fi = IteratorUtils.asIterator(f); // Use IteratorUtils
as input may be Iterable or Array
+ final Iterator si = IteratorUtils.asIterator(s);
while (fi.hasNext() && si.hasNext()) {
final int i = this.compare(fi.next(), si.next());
@@ -286,6 +262,9 @@ public abstract class GremlinValueComparator implements
Comparator<Object> {
public static Type type(final Object o) {
if (o == null)
return Nulltype;
+ if (o.getClass().isArray())
+ return List;
+
final Type[] types = Type.values();
for (int i = 1; i < types.length; i++) {
@@ -312,11 +291,13 @@ public abstract class GremlinValueComparator implements
Comparator<Object> {
/**
* Compare the two objects using their natural comparator. Also handles
the special case: f.equals(s) -> 0 even
* for objects without a natural comparator.
+ * @throws IllegalStateException if objects are not naturally comparable
*/
private static int naturallyCompare(final Object f, final Object s) {
if (f instanceof Comparable && s instanceof Comparable)
return ((Comparable) f).compareTo(s);
- return f.equals(s) ? 0 : throwTypeError();
+ if (f.equals(s)) return 0;
+ throw new IllegalStateException("Objects are not naturally
comparable");
}
/**
@@ -331,17 +312,70 @@ public abstract class GremlinValueComparator implements
Comparator<Object> {
/**
* Return true if the two objects are of the same comparison type
(although they may not be the exact same Class)
*/
- private static boolean comparable(final Object f, final Object s) {
+ public static boolean comparable(final Object f, final Object s) {
if (f == null || s == null)
return f == s; // true iff both in the null space
+ if (eitherAreNaN(f, s))
+ return false;
+
final Type ft = Type.type(f);
final Type st = Type.type(s);
+ // if objects are collections or composites, their contents must be
mutually comparable
+ if (ft == Type.List && st == Type.List) {
+ return contentsComparable(IteratorUtils.asIterator(f),
IteratorUtils.asIterator(s));
+ }
+ else if (ft == Type.Path && st == Type.Path) {
+ return contentsComparable(((Path) f).iterator(), ((Path)
s).iterator());
+ }
+ else if (ft == Type.Set && st == Type.Set) {
+ final List l1 = new ArrayList((Set) f);
+ final List l2 = new ArrayList((Set) s);
+ Collections.sort(l1, ORDERABILITY);
+ Collections.sort(l2, ORDERABILITY);
+
+ return contentsComparable(l1.iterator(), l2.iterator());
+ }
+ else if (ft == Type.Map && st == Type.Map) {
+ final List l1 = new ArrayList(((Map) f).entrySet());
+ final List l2 = new ArrayList(((Map) s).entrySet());
+ Collections.sort(l1, ORDERABILITY);
+ Collections.sort(l2, ORDERABILITY);
+
+ return contentsComparable(l1.iterator(), l2.iterator());
+ }
+ else if (ft == Type.MapEntry && st == Type.MapEntry) {
+ return comparable(((Map.Entry) f).getKey(), ((Map.Entry)
s).getKey()) &&
+ comparable(((Map.Entry) f).getValue(), ((Map.Entry)
s).getValue());
+ }
+ else if (ft == Type.Vertex && st == Type.Vertex ||
+ ft == Type.Edge && st == Type.Edge ||
+ ft == Type.VertexProperty && st == Type.VertexProperty) {
+ return comparable(((Element) f).id(), ((Element) s).id());
+ }
+ else if (ft == Type.Property && st == Type.Property) {
+ return comparable(((Property) f).key(), ((Property) s).key()) &&
+ comparable(((Property) f).value(), ((Property) s).value());
+ }
+
// Check for same type. If they're both the unknown type then return
true iff they are naturally Comparable
return ft == Type.Unknown && st == Type.Unknown ?
naturallyComparable(f, s) : ft == st;
}
+ private static boolean contentsComparable(Iterator fi, Iterator si) {
+ while (fi.hasNext() && si.hasNext()) {
+ final boolean b = comparable(fi.next(), si.next());
+ if (!b) {
+ return false;
+ }
+ }
+ if (fi.hasNext() || si.hasNext()) {
+ return false;
+ }
+ return true;
+ }
+
private final Map<Type, Comparator> comparators = new EnumMap<Type,
Comparator>(Type.class) {{
put(Type.Nulltype, nulltypeComparator);
put(Type.Boolean, naturalOrderComparator);
diff --git
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/CompareTest.java
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/CompareTest.java
index a98d6a9f23..38633df8cd 100644
---
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/CompareTest.java
+++
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/CompareTest.java
@@ -100,44 +100,44 @@ public class CompareTest {
{Compare.gt, 1, 1.0d, false},
{Compare.gte, 1, 1.0d, true},
- // Incomparable types produce ERROR
- {Compare.gt, 23, "23", GremlinTypeErrorException.class},
- {Compare.gte, 23, "23", GremlinTypeErrorException.class},
- {Compare.lt, 23, "23", GremlinTypeErrorException.class},
- {Compare.lte, 23, "23", GremlinTypeErrorException.class},
- {Compare.lte, new CompareTest.A(), new CompareTest.B(),
GremlinTypeErrorException.class},
- {Compare.lte, new CompareTest.B(), new CompareTest.A(),
GremlinTypeErrorException.class},
- {Compare.lte, new CompareTest.C(), new CompareTest.D(),
GremlinTypeErrorException.class},
- {Compare.gte, new Object(), new Object(),
GremlinTypeErrorException.class},
- {Compare.gte, new Object(), new Object(),
GremlinTypeErrorException.class},
- {Compare.gte, new Object(), new Object(),
GremlinTypeErrorException.class},
+ // Incomparable types produce FALSE
+ {Compare.gt, 23, "23", false},
+ {Compare.gte, 23, "23", false},
+ {Compare.lt, 23, "23", false},
+ {Compare.lte, 23, "23", false},
+ {Compare.lte, new CompareTest.A(), new CompareTest.B(), false},
+ {Compare.lte, new CompareTest.B(), new CompareTest.A(), false},
+ {Compare.lte, new CompareTest.C(), new CompareTest.D(), false},
+ {Compare.gte, new Object(), new Object(), false},
+ {Compare.gte, new Object(), new Object(), false},
+ {Compare.gte, new Object(), new Object(), false},
/*
* NaN has pretty much the same comparability behavior against
any argument (including itself):
* P.eq(NaN, any) = FALSE
* P.neq(NaN, any) = TRUE
- * P.lt/lte/gt/gte(NaN, any) = ERROR -> FALSE
+ * P.lt/lte/gt/gte(NaN, any) = FALSE
*/
{Compare.eq, NaN, NaN, false},
{Compare.neq, NaN, NaN, true},
- {Compare.gt, NaN, NaN, GremlinTypeErrorException.class},
- {Compare.gte, NaN, NaN, GremlinTypeErrorException.class},
- {Compare.lt, NaN, NaN, GremlinTypeErrorException.class},
- {Compare.lte, NaN, NaN, GremlinTypeErrorException.class},
+ {Compare.gt, NaN, NaN, false},
+ {Compare.gte, NaN, NaN, false},
+ {Compare.lt, NaN, NaN, false},
+ {Compare.lte, NaN, NaN, false},
{Compare.eq, NaN, 0, false},
{Compare.neq, NaN, 0, true},
- {Compare.gt, NaN, 0, GremlinTypeErrorException.class},
- {Compare.gte, NaN, 0, GremlinTypeErrorException.class},
- {Compare.lt, NaN, 0, GremlinTypeErrorException.class},
- {Compare.lte, NaN, 0, GremlinTypeErrorException.class},
+ {Compare.gt, NaN, 0, false},
+ {Compare.gte, NaN, 0, false},
+ {Compare.lt, NaN, 0, false},
+ {Compare.lte, NaN, 0, false},
{Compare.eq, NaN, "foo", false},
{Compare.neq, NaN, "foo", true},
- {Compare.gt, NaN, "foo", GremlinTypeErrorException.class},
- {Compare.gte, NaN, "foo", GremlinTypeErrorException.class},
- {Compare.lt, NaN, "foo", GremlinTypeErrorException.class},
- {Compare.lte, NaN, "foo", GremlinTypeErrorException.class},
+ {Compare.gt, NaN, "foo", false},
+ {Compare.gte, NaN, "foo", false},
+ {Compare.lt, NaN, "foo", false},
+ {Compare.lte, NaN, "foo", false},
/*
* We consider null to be in its own type space, and thus not
comparable (lt/lte/gt/gte) with
@@ -145,7 +145,7 @@ public class CompareTest {
*
* P.eq(null, any non-null) = FALSE
* P.neq(null, any non-null) = TRUE
- * P.lt/lte/gt/gte(null, any non-null) = ERROR -> FALSE
+ * P.lt/lte/gt/gte(null, any non-null) = FALSE
*/
{Compare.eq, null, null, true},
{Compare.neq, null, null, false},
@@ -156,30 +156,30 @@ public class CompareTest {
{Compare.eq, "foo", null, false},
{Compare.neq, "foo", null, true},
- {Compare.gt, "foo", null, GremlinTypeErrorException.class},
- {Compare.gte, "foo", null, GremlinTypeErrorException.class},
- {Compare.lt, "foo", null, GremlinTypeErrorException.class},
- {Compare.lte, "foo", null, GremlinTypeErrorException.class},
+ {Compare.gt, "foo", null, false},
+ {Compare.gte, "foo", null, false},
+ {Compare.lt, "foo", null, false},
+ {Compare.lte, "foo", null, false},
{Compare.eq, null, 1, false},
{Compare.eq, 1, null, false},
{Compare.neq, null, 1, true},
{Compare.neq, 1, null, true},
- {Compare.gt, null, 1, GremlinTypeErrorException.class},
- {Compare.gt, 1, null, GremlinTypeErrorException.class},
- {Compare.gte, null, 1, GremlinTypeErrorException.class},
- {Compare.gte, 1, null, GremlinTypeErrorException.class},
- {Compare.lt, null, 1, GremlinTypeErrorException.class},
- {Compare.lt, 1, null, GremlinTypeErrorException.class},
- {Compare.lte, null, 1, GremlinTypeErrorException.class},
- {Compare.lte, 1, null, GremlinTypeErrorException.class},
+ {Compare.gt, null, 1, false},
+ {Compare.gt, 1, null, false},
+ {Compare.gte, null, 1, false},
+ {Compare.gte, 1, null, false},
+ {Compare.lt, null, 1, false},
+ {Compare.lt, 1, null, false},
+ {Compare.lte, null, 1, false},
+ {Compare.lte, 1, null, false},
{Compare.eq, NaN, null, false},
{Compare.neq, NaN, null, true},
- {Compare.gt, NaN, null, GremlinTypeErrorException.class},
- {Compare.gte, NaN, null, GremlinTypeErrorException.class},
- {Compare.lt, NaN, null, GremlinTypeErrorException.class},
- {Compare.lte, NaN, null, GremlinTypeErrorException.class},
+ {Compare.gt, NaN, null, false},
+ {Compare.gte, NaN, null, false},
+ {Compare.lt, NaN, null, false},
+ {Compare.lte, NaN, null, false},
/*
* Collections
@@ -207,31 +207,31 @@ public class CompareTest {
{Compare.eq, asList(Double.NaN), asList(Double.NaN), false},
{Compare.neq, asList(Double.NaN), asList(Double.NaN), true},
- {Compare.lt, asList(Double.NaN), asList(Double.NaN),
GremlinTypeErrorException.class},
- {Compare.lte, asList(Double.NaN), asList(Double.NaN),
GremlinTypeErrorException.class},
- {Compare.gt, asList(Double.NaN), asList(Double.NaN),
GremlinTypeErrorException.class},
- {Compare.gte, asList(Double.NaN), asList(Double.NaN),
GremlinTypeErrorException.class},
+ {Compare.lt, asList(Double.NaN), asList(Double.NaN), false},
+ {Compare.lte, asList(Double.NaN), asList(Double.NaN), false},
+ {Compare.gt, asList(Double.NaN), asList(Double.NaN), false},
+ {Compare.gte, asList(Double.NaN), asList(Double.NaN), false},
{Compare.eq, asList(Double.NaN), asList(0), false},
{Compare.neq, asList(Double.NaN), asList(0), true},
- {Compare.lt, asList(Double.NaN), asList(0),
GremlinTypeErrorException.class},
- {Compare.lte, asList(Double.NaN), asList(0),
GremlinTypeErrorException.class},
- {Compare.gt, asList(Double.NaN), asList(0),
GremlinTypeErrorException.class},
- {Compare.gte, asList(Double.NaN), asList(0),
GremlinTypeErrorException.class},
+ {Compare.lt, asList(Double.NaN), asList(0), false},
+ {Compare.lte, asList(Double.NaN), asList(0), false},
+ {Compare.gt, asList(Double.NaN), asList(0), false},
+ {Compare.gte, asList(Double.NaN), asList(0), false},
{Compare.eq, asMap(1, 1), asMap(1, null), false},
{Compare.neq, asMap(1, 1), asMap(1, null), true},
- {Compare.lt, asMap(1, 1), asMap(1, null),
GremlinTypeErrorException.class},
- {Compare.lte, asMap(1, 1), asMap(1, null),
GremlinTypeErrorException.class},
- {Compare.gt, asMap(1, 1), asMap(1, null),
GremlinTypeErrorException.class},
- {Compare.gte, asMap(1, 1), asMap(1, null),
GremlinTypeErrorException.class},
+ {Compare.lt, asMap(1, 1), asMap(1, null), false},
+ {Compare.lte, asMap(1, 1), asMap(1, null), false},
+ {Compare.gt, asMap(1, 1), asMap(1, null), false},
+ {Compare.gte, asMap(1, 1), asMap(1, null), false},
{Compare.eq, asList(0), asList("foo"), false},
{Compare.neq, asList(0), asList("foo"), true},
- {Compare.lt, asList(0), asList("foo"),
GremlinTypeErrorException.class},
- {Compare.lte, asList(0), asList("foo"),
GremlinTypeErrorException.class},
- {Compare.gt, asList(0), asList("foo"),
GremlinTypeErrorException.class},
- {Compare.gte, asList(0), asList("foo"),
GremlinTypeErrorException.class},
+ {Compare.lt, asList(0), asList("foo"), false},
+ {Compare.lte, asList(0), asList("foo"), false},
+ {Compare.gt, asList(0), asList("foo"), false},
+ {Compare.gte, asList(0), asList("foo"), false},
// sets are sorted
{Compare.eq, asSet(1.0, 2.0), asSet(2, 1), true},
diff --git
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/ConnectiveTest.java
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/ConnectiveTest.java
index 6e77f8d088..7e1c4557f6 100644
---
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/ConnectiveTest.java
+++
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/ConnectiveTest.java
@@ -40,7 +40,6 @@ public class ConnectiveTest {
private static final Object VAL = 1;
private static final P TRUE = P.eq(1);
private static final P FALSE = P.gt(1);
- private static final P ERROR = P.lt(Double.NaN);
@RunWith(Parameterized.class)
public static class OrTest {
@@ -52,13 +51,8 @@ public class ConnectiveTest {
return Arrays.asList(new Object[][]{
{TRUE, TRUE, true},
{TRUE, FALSE, true},
- {TRUE, ERROR, true},
{FALSE, TRUE, true},
{FALSE, FALSE, false},
- {FALSE, ERROR, GremlinTypeErrorException.class},
- {ERROR, TRUE, true},
- {ERROR, FALSE, GremlinTypeErrorException.class},
- {ERROR, ERROR, GremlinTypeErrorException.class},
});
}
@@ -90,13 +84,8 @@ public class ConnectiveTest {
return Arrays.asList(new Object[][]{
{TRUE, TRUE, true},
{TRUE, FALSE, false},
- {TRUE, ERROR, GremlinTypeErrorException.class},
{FALSE, TRUE, false},
{FALSE, FALSE, false},
- {FALSE, ERROR, false},
- {ERROR, TRUE, GremlinTypeErrorException.class},
- {ERROR, FALSE, false},
- {ERROR, ERROR, GremlinTypeErrorException.class},
});
}
@@ -128,13 +117,8 @@ public class ConnectiveTest {
return Arrays.asList(new Object[][]{
{TRUE, TRUE, false},
{TRUE, FALSE, true},
- {TRUE, ERROR, GremlinTypeErrorException.class},
{FALSE, TRUE, true},
{FALSE, FALSE, false},
- {FALSE, ERROR, GremlinTypeErrorException.class},
- {ERROR, TRUE, GremlinTypeErrorException.class},
- {ERROR, FALSE, GremlinTypeErrorException.class},
- {ERROR, ERROR, GremlinTypeErrorException.class},
});
}
diff --git
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java
index b5ae11d9d6..162eb113b9 100644
---
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java
+++
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/PTest.java
@@ -103,18 +103,32 @@ public class PTest {
{P.lt(GValue.of("x",0)), -1, true},
{P.lt(GValue.of("x",0)), 0, false},
{P.lt(GValue.of("x",0)), 1, false},
+ {P.lt(new int[]{1,2,3}), new int[]{0,1,2}, true},
+ {P.lt(new int[]{1,2,3}), new int[]{5,6,7}, false},
+ {P.lt(GValue.of("x", new int[]{1,2,3})), new int[]{0,1,2},
true},
+ {P.lt(GValue.of("x", new int[]{1,2,3})), new int[]{5,6,7},
false},
{P.gte(0), -1, false},
{P.gte(0), 0, true},
{P.gte(0), 1, true},
{P.gte(GValue.of("x",0)), -1, false},
{P.gte(GValue.of("x",0)), 0, true},
{P.gte(GValue.of("x",0)), 1, true},
+ {P.gte(new String[]{"a","b","c"}), new
String[]{"x","y","z"}, true},
+ {P.gte(new String[]{"a","b","c"}), new
String[]{"a","b","c"}, true},
+ {P.gte(new String[]{"a","b","c"}), new
String[]{"a","B","z"}, false},
+ {P.gte(GValue.of("x",new String[]{"a","b","c"})), new
String[]{"x","y","z"}, true},
+ {P.gte(GValue.of("x",new String[]{"a","b","c"})), new
String[]{"a","b","c"}, true},
+ {P.gte(GValue.of("x",new String[]{"a","b","c"})), new
String[]{"a","B","z"}, false},
{P.lte(0), -1, true},
{P.lte(0), 0, true},
{P.lte(0), 1, false},
{P.lte(GValue.of("x",0)), -1, true},
{P.lte(GValue.of("x",0)), 0, true},
{P.lte(GValue.of("x",0)), 1, false},
+ {P.lte(new double[]{0.0, 1.1, 2.2}), Arrays.asList(0.0,
1.1, 2.1), true},
+ {P.lte(new double[]{0.0, 1.1, 2.2}), Arrays.asList(0.0,
1.1, 2.3), false},
+ {P.lte(GValue.of("x",new double[]{0.0, 1.1, 2.2})),
Arrays.asList(0.0, 1.1, 2.1), true},
+ {P.lte(GValue.of("x",new double[]{0.0, 1.1, 2.2})),
Arrays.asList(0.0, 1.1, 2.3), false},
{P.between(1, 10), 0, false},
{P.between(1, 10), 1, true},
{P.between(1, 10), 9, true},
@@ -249,19 +263,20 @@ public class PTest {
{TextP.containing(GValue.ofString("x",
"o")).and(P.gte(GValue.ofString("y", "j"))).and(TextP.endingWith("ko")),
"marko", true},
{TextP.containing(GValue.ofString("x",
"o")).and(P.gte(GValue.ofString("y", "j"))).and(TextP.endingWith("ko")),
"josh", false},
- // type errors
- {P.outside(Double.NaN, Double.NaN), 0,
GremlinTypeErrorException.class},
- {P.inside(-1, Double.NaN), 0,
GremlinTypeErrorException.class},
- {P.inside(Double.NaN, 1), 0,
GremlinTypeErrorException.class},
- {TextP.containing((String) null), "abc",
GremlinTypeErrorException.class},
- {TextP.containing("abc"), null,
GremlinTypeErrorException.class},
- {TextP.containing((String) null), null,
GremlinTypeErrorException.class},
- {TextP.startingWith((String) null), "abc",
GremlinTypeErrorException.class},
- {TextP.startingWith("abc"), null,
GremlinTypeErrorException.class},
- {TextP.startingWith((String) null), null,
GremlinTypeErrorException.class},
- {TextP.endingWith((String) null), "abc",
GremlinTypeErrorException.class},
- {TextP.endingWith("abc"), null,
GremlinTypeErrorException.class},
- {TextP.endingWith((String) null), null,
GremlinTypeErrorException.class},
+ // non-comparable cases
+ {P.outside(Double.NaN, Double.NaN), 0, false},
+ {P.inside(-1, Double.NaN), 0, false},
+ {P.inside(Double.NaN, 1), 0, false},
+ {P.lt(Double.NaN), 0, false},
+ {TextP.containing((String) null), "abc", false},
+ {TextP.containing("abc"), null, false},
+ {TextP.containing((String) null), null, false},
+ {TextP.startingWith((String) null), "abc", false},
+ {TextP.startingWith("abc"), null, false},
+ {TextP.startingWith((String) null), null, false},
+ {TextP.endingWith((String) null), "abc", false},
+ {TextP.endingWith("abc"), null, false},
+ {TextP.endingWith((String) null), null, false},
// regex
{TextP.regex("D"), "Dallas Fort Worth", true},
@@ -392,9 +407,9 @@ public class PTest {
private static final int INITIAL_VALUE = 5;
private static final int UPDATED_VALUE = 10;
private static final String EQ_FORMAT = "eq(%d)";
- private static final String NEQ_FORMAT = "neq(%d)";
public static final String GT_FORMAT = "gt(%d)";
public static final String LT_FORMAT = "lt(%d)";
+ public static final String NOT_FORMAT = "not(%s)";
@Test
public void shouldUseUpdatedValueAfterSetValue() {
@@ -418,13 +433,13 @@ public class PTest {
P<Integer> predicate = P.eq(INITIAL_VALUE);
P<Integer> negated = predicate.negate();
assertFalse(negated.test(INITIAL_VALUE));
- assertEquals(String.format(NEQ_FORMAT, INITIAL_VALUE),
negated.toString());
-
+ assertEquals(String.format(NOT_FORMAT, String.format(EQ_FORMAT,
INITIAL_VALUE)), negated.toString());
+
predicate.setValue(UPDATED_VALUE);
P<Integer> updatedNegated = predicate.negate();
assertTrue(updatedNegated.test(INITIAL_VALUE));
assertFalse(updatedNegated.test(UPDATED_VALUE));
- assertEquals(String.format(NEQ_FORMAT, UPDATED_VALUE),
updatedNegated.toString());
+ assertEquals(String.format(NOT_FORMAT, String.format(EQ_FORMAT,
UPDATED_VALUE)), updatedNegated.toString());
}
@Test
diff --git
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/NoneStepTest.java
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/NoneStepTest.java
index f53ec9f28a..f7c1f37fb1 100644
---
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/NoneStepTest.java
+++
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/step/filter/NoneStepTest.java
@@ -78,12 +78,13 @@ public class NoneStepTest extends StepTest {
final List validOne = new ArrayList() {{ add(20); }};
final List validTwo = new ArrayList() {{ add(21); add(25);}};
final List validThree = new ArrayList() {{ add(51); add(57); add(71);
}};
+ final List validIncorrectType = new ArrayList() {{ add(100);
add("25"); }};
+ final List validEmpty = new ArrayList();
final List containsNull = new ArrayList() {{ add(50); add(null);
add(60); }};
- final List empty = new ArrayList();
- final List incorrectType = new ArrayList() {{ add(100); add("25"); }};
+ final List invalidIncorrectType = new ArrayList() {{ add(2);
add("25"); }};
final List valueTooSmall = new ArrayList() {{ add(101); add(1);
add(10);}};
- assertEquals(4L, __.__(validOne, null, containsNull, empty,
incorrectType, valueTooSmall, validTwo, validThree)
+ assertEquals(6L, __.__(validOne, null, containsNull, validEmpty,
invalidIncorrectType, validIncorrectType, valueTooSmall, validTwo, validThree)
.none(P.lte(3)).count().next().longValue());
}
}
\ No newline at end of file
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index 822d4c871b..f9787aec36 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -384,6 +384,9 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_injectXnull_1_emptyX_noneXeqXnullXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(new List<object> { null, 1 }, new List<object> {
}).None(P.Eq(null))}},
{"g_injectXnull_nullX_noneXnotXnullXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(new List<object> { null, null }).None(P.Neq(null))}},
{"g_injectX3_threeX_noneXeqX3XX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(new List<object> { 3, "three" }).None(P.Eq(3))}},
+ {"g_V_notXhasXage_gt_27XX_name", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Not(__.Has("age", P.Gt(27))).Values<object>("name")}},
+ {"g_V_notXnotXhasXage_gt_27XXX_name", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Not(__.Not(__.Has("age", P.Gt(27)))).Values<object>("name")}},
+ {"g_V_notXhasXname_gt_27XX_name", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Not(__.Has("name", P.Gt(27))).Values<object>("name")}},
{"g_V_orXhasXage_gt_27X__outE_count_gte_2X_name", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Or(__.Has("age", P.Gt(27)),
__.OutE().Count().Is(P.Gte(2))).Values<object>("name")}},
{"g_V_orXoutEXknowsX__hasXlabel_softwareX_or_hasXage_gte_35XX_name", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().Or(__.OutE("knows"), __.Has(T.Label, "software").Or().Has("age",
P.Gte(35))).Values<object>("name")}},
{"g_V_asXaX_orXselectXaX_selectXaXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V().As("a").Or(__.Select<object>("a"), __.Select<object>("a"))}},
@@ -1622,7 +1625,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"InjectX1dX_isXerror_or_errorX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(1d).Is(P.Lt(Double.NaN).Or(P.Gt(Double.NaN)))}},
{"InjectX1dX_notXtrueX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.Inject<object>(1d).Not(__.Is(P.Gt(0)))}},
{"InjectX1dX_notXfalseX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.Inject<object>(1d).Not(__.Is(P.Lt(0)))}},
- {"InjectX1dX_notXerrorX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.Inject<object>(1d).Not(__.Is(P.Gt(Double.NaN)))}},
+ {"InjectX1dX_notXNaNX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.Inject<object>(1d).Not(__.Is(P.Gt(Double.NaN)))}},
{"InjectX1dX_notXisXeqXNaNXXX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.Inject<object>(1d).Not(__.Is(P.Eq(Double.NaN)))}},
{"InjectXInfX_eqXInfX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.Inject<object>(Double.PositiveInfinity).Is(P.Eq(Double.PositiveInfinity))}},
{"InjectXInfX_neqXInfX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.Inject<object>(Double.PositiveInfinity).Is(P.Neq(Double.PositiveInfinity))}},
diff --git a/gremlin-go/driver/cucumber/gremlin.go
b/gremlin-go/driver/cucumber/gremlin.go
index 63357550d1..73c0e36d91 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -354,6 +354,9 @@ var translationMap = map[string][]func(g
*gremlingo.GraphTraversalSource, p map[
"g_injectXnull_1_emptyX_noneXeqXnullXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.Inject([]interface{}{nil, 1},
[]interface{}{}).None(gremlingo.P.Eq(nil))}},
"g_injectXnull_nullX_noneXnotXnullXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.Inject([]interface{}{nil,
nil}).None(gremlingo.P.Neq(nil))}},
"g_injectX3_threeX_noneXeqX3XX": {func(g *gremlingo.GraphTraversalSource,
p map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject([]interface{}{int32(3), "three"}).None(gremlingo.P.Eq(3))}},
+ "g_V_notXhasXage_gt_27XX_name": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.V().Not(gremlingo.T__.Has("age", gremlingo.P.Gt(27))).Values("name")}},
+ "g_V_notXnotXhasXage_gt_27XXX_name": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V().Not(gremlingo.T__.Not(gremlingo.T__.Has("age",
gremlingo.P.Gt(27)))).Values("name")}},
+ "g_V_notXhasXname_gt_27XX_name": {func(g *gremlingo.GraphTraversalSource,
p map[string]interface{}) *gremlingo.GraphTraversal {return
g.V().Not(gremlingo.T__.Has("name", gremlingo.P.Gt(27))).Values("name")}},
"g_V_orXhasXage_gt_27X__outE_count_gte_2X_name": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.V().Or(gremlingo.T__.Has("age",
gremlingo.P.Gt(27)),
gremlingo.T__.OutE().Count().Is(gremlingo.P.Gte(2))).Values("name")}},
"g_V_orXoutEXknowsX__hasXlabel_softwareX_or_hasXage_gte_35XX_name":
{func(g *gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.V().Or(gremlingo.T__.OutE("knows"),
gremlingo.T__.Has(gremlingo.T.Label, "software").Or().Has("age",
gremlingo.P.Gte(35))).Values("name")}},
"g_V_asXaX_orXselectXaX_selectXaXX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return g.V().As("a").Or(gremlingo.T__.Select("a"),
gremlingo.T__.Select("a"))}},
@@ -1592,7 +1595,7 @@ var translationMap = map[string][]func(g
*gremlingo.GraphTraversalSource, p map[
"InjectX1dX_isXerror_or_errorX": {func(g *gremlingo.GraphTraversalSource,
p map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(1).Is(gremlingo.P.Lt(math.NaN()).Or(gremlingo.P.Gt(math.NaN())))}},
"InjectX1dX_notXtrueX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(1).Not(gremlingo.T__.Is(gremlingo.P.Gt(0)))}},
"InjectX1dX_notXfalseX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(1).Not(gremlingo.T__.Is(gremlingo.P.Lt(0)))}},
- "InjectX1dX_notXerrorX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(1).Not(gremlingo.T__.Is(gremlingo.P.Gt(math.NaN())))}},
+ "InjectX1dX_notXNaNX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(1).Not(gremlingo.T__.Is(gremlingo.P.Gt(math.NaN())))}},
"InjectX1dX_notXisXeqXNaNXXX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(1).Not(gremlingo.T__.Is(gremlingo.P.Eq(math.NaN())))}},
"InjectXInfX_eqXInfX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(math.Inf(1)).Is(gremlingo.P.Eq(math.Inf(1)))}},
"InjectXInfX_neqXInfX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(math.Inf(1)).Is(gremlingo.P.Neq(math.Inf(1)))}},
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 9e499f4603..954b7092e0 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
@@ -385,6 +385,9 @@ const gremlins = {
g_injectXnull_1_emptyX_noneXeqXnullXX: [function({g}) { return
g.inject([null, 1], []).none(P.eq(null)) }],
g_injectXnull_nullX_noneXnotXnullXX: [function({g}) { return
g.inject([null, null]).none(P.neq(null)) }],
g_injectX3_threeX_noneXeqX3XX: [function({g}) { return g.inject([3,
"three"]).none(P.eq(3)) }],
+ g_V_notXhasXage_gt_27XX_name: [function({g}) { return
g.V().not(__.has("age", P.gt(27))).values("name") }],
+ g_V_notXnotXhasXage_gt_27XXX_name: [function({g}) { return
g.V().not(__.not(__.has("age", P.gt(27)))).values("name") }],
+ g_V_notXhasXname_gt_27XX_name: [function({g}) { return
g.V().not(__.has("name", P.gt(27))).values("name") }],
g_V_orXhasXage_gt_27X__outE_count_gte_2X_name: [function({g}) { return
g.V().or(__.has("age", P.gt(27)),
__.outE().count().is(P.gte(2))).values("name") }],
g_V_orXoutEXknowsX__hasXlabel_softwareX_or_hasXage_gte_35XX_name:
[function({g}) { return g.V().or(__.outE("knows"), __.has(T.label,
"software").or().has("age", P.gte(35))).values("name") }],
g_V_asXaX_orXselectXaX_selectXaXX: [function({g}) { return
g.V().as("a").or(__.select("a"), __.select("a")) }],
@@ -1623,7 +1626,7 @@ const gremlins = {
InjectX1dX_isXerror_or_errorX: [function({g}) { return
g.inject(1).is(P.lt(Number.NaN).or(P.gt(Number.NaN))) }],
InjectX1dX_notXtrueX: [function({g}) { return
g.inject(1).not(__.is(P.gt(0))) }],
InjectX1dX_notXfalseX: [function({g}) { return
g.inject(1).not(__.is(P.lt(0))) }],
- InjectX1dX_notXerrorX: [function({g}) { return
g.inject(1).not(__.is(P.gt(Number.NaN))) }],
+ InjectX1dX_notXNaNX: [function({g}) { return
g.inject(1).not(__.is(P.gt(Number.NaN))) }],
InjectX1dX_notXisXeqXNaNXXX: [function({g}) { return
g.inject(1).not(__.is(P.eq(Number.NaN))) }],
InjectXInfX_eqXInfX: [function({g}) { return
g.inject(Number.POSITIVE_INFINITY).is(P.eq(Number.POSITIVE_INFINITY)) }],
InjectXInfX_neqXInfX: [function({g}) { return
g.inject(Number.POSITIVE_INFINITY).is(P.neq(Number.POSITIVE_INFINITY)) }],
diff --git a/gremlin-python/src/main/python/radish/gremlin.py
b/gremlin-python/src/main/python/radish/gremlin.py
index 038a1af2fa..4ce9a1ac0f 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -357,6 +357,9 @@ world.gremlins = {
'g_injectXnull_1_emptyX_noneXeqXnullXX': [(lambda g:g.inject([None, 1],
[]).none(P.eq(None)))],
'g_injectXnull_nullX_noneXnotXnullXX': [(lambda g:g.inject([None,
None]).none(P.neq(None)))],
'g_injectX3_threeX_noneXeqX3XX': [(lambda g:g.inject([3,
'three']).none(P.eq(3)))],
+ 'g_V_notXhasXage_gt_27XX_name': [(lambda g:g.V().not_(__.has('age',
P.gt(27))).values('name'))],
+ 'g_V_notXnotXhasXage_gt_27XXX_name': [(lambda
g:g.V().not_(__.not_(__.has('age', P.gt(27)))).values('name'))],
+ 'g_V_notXhasXname_gt_27XX_name': [(lambda g:g.V().not_(__.has('name',
P.gt(27))).values('name'))],
'g_V_orXhasXage_gt_27X__outE_count_gte_2X_name': [(lambda
g:g.V().or_(__.has('age', P.gt(27)),
__.out_e().count().is_(P.gte(2))).values('name'))],
'g_V_orXoutEXknowsX__hasXlabel_softwareX_or_hasXage_gte_35XX_name':
[(lambda g:g.V().or_(__.out_e('knows'), __.has(T.label,
'software').or_().has('age', P.gte(35))).values('name'))],
'g_V_asXaX_orXselectXaX_selectXaXX': [(lambda
g:g.V().as_('a').or_(__.select('a'), __.select('a')))],
@@ -1595,7 +1598,7 @@ world.gremlins = {
'InjectX1dX_isXerror_or_errorX': [(lambda
g:g.inject(1).is_(P.lt(float('nan')).or_(P.gt(float('nan')))))],
'InjectX1dX_notXtrueX': [(lambda g:g.inject(1).not_(__.is_(P.gt(0))))],
'InjectX1dX_notXfalseX': [(lambda g:g.inject(1).not_(__.is_(P.lt(0))))],
- 'InjectX1dX_notXerrorX': [(lambda
g:g.inject(1).not_(__.is_(P.gt(float('nan')))))],
+ 'InjectX1dX_notXNaNX': [(lambda
g:g.inject(1).not_(__.is_(P.gt(float('nan')))))],
'InjectX1dX_notXisXeqXNaNXXX': [(lambda
g:g.inject(1).not_(__.is_(P.eq(float('nan')))))],
'InjectXInfX_eqXInfX': [(lambda
g:g.inject(float('inf')).is_(P.eq(float('inf'))))],
'InjectXInfX_neqXInfX': [(lambda
g:g.inject(float('inf')).is_(P.neq(float('inf'))))],
diff --git
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessEmbeddedStandardSuite.java
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessEmbeddedStandardSuite.java
index 48df852032..e207ea52b9 100644
---
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessEmbeddedStandardSuite.java
+++
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessEmbeddedStandardSuite.java
@@ -23,7 +23,7 @@ import
org.apache.tinkerpop.gremlin.process.traversal.CoreTraversalTest;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalEngine;
import
org.apache.tinkerpop.gremlin.process.traversal.TraversalInterruptionTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.LambdaStepTest;
-import
org.apache.tinkerpop.gremlin.process.traversal.step.TernaryBooleanLogicsTest;
+import
org.apache.tinkerpop.gremlin.process.traversal.step.ComparabilitySemanticsTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.MatchTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.ProfileTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.WriteTest;
@@ -76,7 +76,7 @@ public class ProcessEmbeddedStandardSuite extends
AbstractGremlinSuite {
EarlyLimitStrategyProcessTest.class,
// semantics
- TernaryBooleanLogicsTest.class,
+ ComparabilitySemanticsTest.class,
};
/**
diff --git
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
index 2e1a72f6ca..6207aa8f07 100644
---
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
+++
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/ProcessStandardSuite.java
@@ -25,7 +25,7 @@ import
org.apache.tinkerpop.gremlin.process.traversal.TraversalInterruptionTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.ComplexTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.LambdaStepTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.OrderabilityTest;
-import
org.apache.tinkerpop.gremlin.process.traversal.step.TernaryBooleanLogicsTest;
+import
org.apache.tinkerpop.gremlin.process.traversal.step.ComparabilitySemanticsTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.branch.BranchTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.branch.ChooseTest;
import org.apache.tinkerpop.gremlin.process.traversal.step.branch.LocalTest;
@@ -216,7 +216,7 @@ public class ProcessStandardSuite extends
AbstractGremlinSuite {
// semantics
OrderabilityTest.Traversals.class,
- TernaryBooleanLogicsTest.class,
+ ComparabilitySemanticsTest.class,
};
/**
diff --git
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TernaryBooleanLogicsTest.java
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/ComparabilitySemanticsTest.java
similarity index 90%
rename from
gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TernaryBooleanLogicsTest.java
rename to
gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/ComparabilitySemanticsTest.java
index 729b12cc59..d4fbbd2fa0 100644
---
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/TernaryBooleanLogicsTest.java
+++
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/ComparabilitySemanticsTest.java
@@ -41,10 +41,10 @@ import static
org.apache.tinkerpop.gremlin.structure.Graph.Features.GraphFeature
* @author Mike Personick (http://github.com/mikepersonick)
*/
@RunWith(GremlinProcessRunner.class)
-public class TernaryBooleanLogicsTest extends AbstractGremlinProcessTest {
+public class ComparabilitySemanticsTest extends AbstractGremlinProcessTest {
/**
- * NaN comparisons always produce UNDEF comparison, reducing to FALSE in
ternary->binary reduction.
+ * NaN comparisons always produce UNDEF comparison, which by definition
results in FALSE.
*/
@Test
@FeatureRequirement(featureClass = GraphFeatures.class, feature =
GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS)
@@ -274,15 +274,15 @@ public class TernaryBooleanLogicsTest extends
AbstractGremlinProcessTest {
checkHasNext(false, g.inject(1).not(is(TRUE)));
// FALSE -> TRUE
checkHasNext(true, g.inject(1).not(is(FALSE)));
- // ERROR -> ERROR -> FALSE
- checkHasNext(false, g.inject(1).not(is(ERROR)));
+ // ERROR -> FALSE -> TRUE
+ checkHasNext(true, g.inject(1).not(is(ERROR)));
// Binary reduction with NaN
checkHasNext(false, g.inject(1).is(P.eq(Double.NaN)));
checkHasNext(true, g.inject(1).is(P.neq(Double.NaN)));
checkHasNext(true, g.inject(1).not(is(P.eq(Double.NaN))));
checkHasNext(false, g.inject(1).not(not(is(P.eq(Double.NaN)))));
- checkHasNext(false, g.inject(1).where(__.inject(1).not(is(ERROR))));
+ checkHasNext(true, g.inject(1).where(__.inject(1).not(is(ERROR))));
}
/**
@@ -302,39 +302,21 @@ public class TernaryBooleanLogicsTest extends
AbstractGremlinProcessTest {
checkHasNext(false, g.inject(1).filter(xor.apply(TRUE, TRUE)));
// TRUE, FALSE -> TRUE
checkHasNext(true, g.inject(1).filter(xor.apply(TRUE, FALSE)));
- // TRUE, ERROR -> ERROR -> FALSE
- checkHasNext(false, g.inject(1).filter(xor.apply(TRUE, ERROR)));
+ // TRUE, ERROR -> TRUE, FALSE -> TRUE
+ checkHasNext(true, g.inject(1).filter(xor.apply(TRUE, ERROR)));
// FALSE, TRUE -> TRUE
checkHasNext(true, g.inject(1).filter(xor.apply(FALSE, TRUE)));
// FALSE, FALSE -> FALSE
checkHasNext(false, g.inject(1).filter(xor.apply(FALSE, FALSE)));
- // FALSE, ERROR -> ERROR -> FALSE
+ // FALSE, ERROR -> FALSE, FALSE -> FALSE
checkHasNext(false, g.inject(1).filter(xor.apply(FALSE, ERROR)));
- // ERROR, TRUE -> ERROR -> FALSE
- checkHasNext(false, g.inject(1).filter(xor.apply(ERROR, TRUE)));
- // ERROR, FALSE -> ERROR -> FALSE
+ // ERROR, TRUE -> FALSE, TRUE -> TRUE
+ checkHasNext(true, g.inject(1).filter(xor.apply(ERROR, TRUE)));
+ // ERROR, FALSE -> FALSE, FALSE -> FALSE
checkHasNext(false, g.inject(1).filter(xor.apply(ERROR, FALSE)));
- // ERROR, ERROR -> ERROR -> FALSE
+ // ERROR, ERROR -> FALSE, FALSE -> FALSE
checkHasNext(false, g.inject(1).filter(xor.apply(ERROR, ERROR)));
}
-
- /**
- * Child traversals should propagate error states to their parent
traversal if and only if the parent step is a
- * filter step.
- */
- @Test
- @FeatureRequirement(featureClass = GraphFeatures.class, feature =
GraphFeatures.FEATURE_ORDERABILITY_SEMANTICS)
- public void testErrorPropagation() {
- final P ERROR = P.lt(Double.NaN);
-
- // Propagates Error to parent not()
- checkHasNext(false, g.inject(1).not(not(is(ERROR))));
- checkHasNext(false, g.inject(1).not(is(ERROR)));
-
- // Does not propagate Error through non-filter parent
- checkHasNext(true, g.inject(1).not(union((is(ERROR)))));
- checkHasNext(false, g.inject(1).union((is(ERROR))));
- }
}
diff --git
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Not.feature
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Not.feature
new file mode 100644
index 0000000000..af323a3ca8
--- /dev/null
+++
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/filter/Not.feature
@@ -0,0 +1,61 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+@StepClassFilter @StepNot
+Feature: Step - not()
+
+ Scenario: g_V_notXhasXage_gt_27XX_name
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().not(__.has("age", P.gt(27))).values("name")
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | vadas |
+ | lop |
+ | ripple |
+
+ Scenario: g_V_notXnotXhasXage_gt_27XXX_name
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().not(__.not(__.has("age", P.gt(27)))).values("name")
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | marko |
+ | josh |
+ | peter |
+
+ Scenario: g_V_notXhasXname_gt_27XX_name
+ Given the modern graph
+ And the traversal of
+ """
+ g.V().not(__.has("name", P.gt(27))).values("name")
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | marko |
+ | vadas |
+ | lop |
+ | josh |
+ | ripple |
+ | peter |
diff --git
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/semantics/Comparability.feature
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/semantics/Comparability.feature
index 3059393412..8963199cad 100644
---
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/semantics/Comparability.feature
+++
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/semantics/Comparability.feature
@@ -834,14 +834,16 @@ Feature: Comparability
| d[1].d |
@GraphComputerVerificationInjectionNotSupported
- Scenario: InjectX1dX_notXerrorX
+ Scenario: InjectX1dX_notXNaNX
Given the empty graph
And the traversal of
"""
g.inject(1d).not(is(P.gt(NaN)))
"""
When iterated to list
- Then the result should be empty
+ Then the result should be unordered
+ | result |
+ | d[1].d |
@GraphComputerVerificationInjectionNotSupported
Scenario: InjectX1dX_notXisXeqXNaNXXX
diff --git
a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/TinkerGraphStep.java
b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/TinkerGraphStep.java
index f56903d166..a9615f6c07 100644
---
a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/TinkerGraphStep.java
+++
b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/process/traversal/step/sideEffect/TinkerGraphStep.java
@@ -19,10 +19,8 @@
package
org.apache.tinkerpop.gremlin.tinkergraph.process.traversal.step.sideEffect;
import org.apache.tinkerpop.gremlin.process.traversal.Compare;
-import
org.apache.tinkerpop.gremlin.process.traversal.GremlinTypeErrorException;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.step.HasContainerHolder;
-import org.apache.tinkerpop.gremlin.process.traversal.step.filter.FilterStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
import org.apache.tinkerpop.gremlin.process.traversal.util.AndP;
@@ -134,21 +132,8 @@ public final class TinkerGraphStep<S, E extends Element>
extends GraphStep<S, E>
try {
while (iterator.hasNext()) {
final E e = iterator.next();
- try {
- if (HasContainer.testAll(e, this.hasContainers))
- list.add(e);
- } catch (GremlinTypeErrorException ex) {
- if (getTraversal().isRoot() ||
!(getTraversal().getParent() instanceof FilterStep)) {
- /*
- * Either we are at the top level of the query, or our
parent query is not a FilterStep and thus
- * cannot handle a GremlinTypeErrorException. In any
of these cases we do a binary reduction
- * from ERROR -> FALSE and filter the solution quietly.
- */
- } else {
- // not a ternary -> binary reducer, pass the ERROR on
- throw ex;
- }
- }
+ if (HasContainer.testAll(e, this.hasContainers))
+ list.add(e);
}
} finally {
// close the old iterator to release resources since we are
returning a new iterator (over list)