You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2021/06/03 19:01:10 UTC

[tinkerpop] branch TINKERPOP-2507 created (now e288984)

This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a change to branch TINKERPOP-2507
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git.


      at e288984  TINKERPOP-2507 Allowed graph filters with mixed id types

This branch includes the following new commits:

     new e288984  TINKERPOP-2507 Allowed graph filters with mixed id types

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[tinkerpop] 01/01: TINKERPOP-2507 Allowed graph filters with mixed id types

Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch TINKERPOP-2507
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit e288984a38935a92283e73d38d647156c3845624
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Thu Jun 3 10:46:38 2021 -0400

    TINKERPOP-2507 Allowed graph filters with mixed id types
    
    Upgrade docs in this commit explain this change fairly well. There doesn't seem to be a need to be so prescriptive about the types that can be filtered on and it should be up to each graph to decide how they handle such input. Removed the general exception, tests and updated the behavior of graphs to support the change.
---
 CHANGELOG.asciidoc                                 |   2 +
 docs/src/upgrade/release-3.6.x.asciidoc            |  43 ++++++++
 .../apache/tinkerpop/gremlin/structure/Graph.java  |   4 -
 .../gremlin/structure/util/ElementHelper.java      |  22 +---
 .../util/iterator/AbortiveMultiIterator.java       | 120 +++++++++++++++++++++
 ...torTest.java => AbortiveMultiIteratorTest.java} |  83 +++++++++++---
 .../gremlin/util/iterator/MultiIteratorTest.java   |  11 +-
 .../process/traversal/CoreTraversalTest.java       |   9 +-
 .../tinkerpop/gremlin/structure/GraphTest.java     |  68 ------------
 .../gremlin/hadoop/structure/HadoopGraph.java      |  62 ++++++-----
 .../gremlin/neo4j/structure/Neo4jGraph.java        |   2 -
 .../gremlin/tinkergraph/structure/TinkerGraph.java |  31 ++----
 .../structure/TinkerGraphIdManagerTest.java        |   6 +-
 .../tinkergraph/structure/TinkerGraphTest.java     |  19 ++++
 14 files changed, 305 insertions(+), 177 deletions(-)

diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 48438e9..2015e76 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -24,6 +24,7 @@ limitations under the License.
 === TinkerPop 3.6.0 (Release Date: NOT OFFICIALLY RELEASED YET)
 
 * `TraversalOpProcessor` no longer accepts a `String` representation of `Bytecode` for the "gremlin" argument which was left to support older versions of the drivers.
+* Removed requirement that "ids" used to filter vertices and edges need to be all of a single type.
 
 == TinkerPop 3.5.0 (The Sleeping Gremlin: No. 18 Entr'acte Symphonique)
 
@@ -364,6 +365,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
 [[release-3-4-12]]
 === TinkerPop 3.4.12 (Release Date: NOT OFFICIALLY RELEASED YET)
 
+* Changed TinkerGraph to allow identifiers to be heterogeneous when filtering.
 * Fixed bug in the `vertexLabelKey` validation for `GraphMLWriter` which was inadvertently validating the `edgeLabelKey`.
 * Changed `IndexStep` to make it easier for providers to determine the type of indexer being used.
 
diff --git a/docs/src/upgrade/release-3.6.x.asciidoc b/docs/src/upgrade/release-3.6.x.asciidoc
new file mode 100644
index 0000000..712c4f4
--- /dev/null
+++ b/docs/src/upgrade/release-3.6.x.asciidoc
@@ -0,0 +1,43 @@
+////
+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.
+////
+
+= TinkerPop 3.6.0
+
+*NOT NAMED YET*
+
+== TinkerPop 3.6.0
+
+*Release Date: NOT OFFICIALLY RELEASED YET*
+
+Please see the link:https://github.com/apache/tinkerpop/blob/3.6.0/CHANGELOG.asciidoc#release-3-6-0[changelog] for a complete list of all the modifications that are part of this release.
+
+=== Upgrading for Users
+
+
+=== Upgrading for Providers
+
+==== Graph System Providers
+
+===== Filters with Mixed Id Types
+
+The requirement that "ids" passed to `Graph.vertices` and `Graph.edges` all be of a single type has been removed. This
+requirement was a bit to prescriptive when there really wasn't a need to enforce such a validation. It even conflicted
+with TinkerGraph operations where mixed `T.id` types is a feature. Graph providers may continue to support this
+requirement if they wish, but it is no longer enforced by TinkerPop and the `Graph.idArgsMustBeEitherIdOrElement` has
+been removed so providers will need to construct their own exception.
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2507[TINKERPOP-2507]
\ No newline at end of file
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Graph.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Graph.java
index da1c998..a8131c1 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Graph.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/Graph.java
@@ -1228,10 +1228,6 @@ public interface Graph extends AutoCloseable, Host {
             return new IllegalArgumentException(String.format("Edge with id already exists: %s", id));
         }
 
-        public static IllegalArgumentException idArgsMustBeEitherIdOrElement() {
-            return new IllegalArgumentException("id arguments must be either ids or Elements");
-        }
-
         public static IllegalArgumentException argumentCanNotBeNull(final String argument) {
             return new IllegalArgumentException(String.format("The provided argument can not be null: %s", argument));
         }
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/ElementHelper.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/ElementHelper.java
index 6ba5b29..9f852ef 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/ElementHelper.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/util/ElementHelper.java
@@ -67,23 +67,6 @@ public final class ElementHelper {
     }
 
     /**
-     * Determine whether an array of ids are either all elements or ids of elements. This is typically used as a pre-condition check.
-     *
-     * @param clazz the class of the element for which the ids will bind
-     * @param ids   the ids that must be either elements or id objects, else
-     * {@link org.apache.tinkerpop.gremlin.structure.Graph.Exceptions#idArgsMustBeEitherIdOrElement()} is thrown.
-     */
-    public static void validateMixedElementIds(final Class<? extends Element> clazz, final Object... ids) throws IllegalArgumentException {
-        if (ids.length > 1) {
-            final boolean element = clazz.isAssignableFrom(ids[0].getClass());
-            for (int i = 1; i < ids.length; i++) {
-                if (clazz.isAssignableFrom(ids[i].getClass()) != element)
-                    throw Graph.Exceptions.idArgsMustBeEitherIdOrElement();
-            }
-        }
-    }
-
-    /**
      * Determines whether the property key/value for the specified thing can be legally set. This is typically used as
      * a pre-condition check prior to setting a property.
      *
@@ -557,8 +540,9 @@ public final class ElementHelper {
 
         // it is OK to evaluate equality of ids via toString() now given that the toString() the test suite
         // enforces the value of id.()toString() to be a first class representation of the identifier
-        if (1 == providedIds.length) return id.toString().equals(providedIds[0].toString());
-        else {
+        if (1 == providedIds.length) {
+            return id.toString().equals(providedIds[0].toString());
+        } else {
             for (final Object temp : providedIds) {
                 if (temp.toString().equals(id.toString()))
                     return true;
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/iterator/AbortiveMultiIterator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/iterator/AbortiveMultiIterator.java
new file mode 100644
index 0000000..2235822
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/iterator/AbortiveMultiIterator.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.util.iterator;
+
+import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
+import org.apache.tinkerpop.gremlin.structure.util.CloseableIterator;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Predicate;
+
+/**
+ * An {@code Iterator} that checks a {@code Predicate} prior to processing the specified child {@code Iterator}
+ * instances.
+ */
+public final class AbortiveMultiIterator<T> implements Iterator<T>, Serializable, AutoCloseable {
+
+    private final List<Iterator<T>> iterators = new ArrayList<>();
+    private final List<Predicate<Long>> checks = new ArrayList<>();
+    private long counter = 0;
+    private int current = 0;
+
+    /**
+     * Adds an {@code Iterator} that will always be iterated.
+     */
+    public void addIterator(final Iterator<T> iterator) {
+        addIterator(iterator, c -> true);
+    }
+
+    /**
+     * Adds an {@code Iterator} that will iterate only if the {@code check} passes.
+     *
+     * @param check when returning {@code true} processing of the associated {@code Iterator} will proceed
+     */
+    public void addIterator(final Iterator<T> iterator, final Predicate<Long> check) {
+        this.iterators.add(iterator);
+        this.checks.add(check);
+    }
+
+    @Override
+    public boolean hasNext() {
+        if (this.current >= this.iterators.size())
+            return false;
+
+        Iterator<T> currentIterator = this.iterators.get(this.current);
+
+        while (true) {
+            if (currentIterator.hasNext()) {
+                return true;
+            } else {
+                this.current++;
+                if (this.current >= iterators.size() || !checks.get(current).test(counter))
+                    break;
+                currentIterator = iterators.get(this.current);
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void remove() {
+        this.iterators.get(this.current).remove();
+    }
+
+    @Override
+    public T next() {
+        if (this.iterators.isEmpty()) throw FastNoSuchElementException.instance();
+
+        Iterator<T> currentIterator = iterators.get(this.current);
+
+        while (true) {
+            if (currentIterator.hasNext()) {
+                this.counter++;
+                return currentIterator.next();
+            } else {
+                this.current++;
+                if (this.current >= iterators.size() || !checks.get(current).test(counter))
+                    break;
+                currentIterator = iterators.get(current);
+            }
+        }
+        throw FastNoSuchElementException.instance();
+    }
+
+    public void clear() {
+        this.iterators.clear();
+        this.checks.clear();
+        this.counter = 0;
+        this.current = 0;
+    }
+
+    /**
+     * Close the underlying iterators if auto-closeable. Note that when Exception is thrown from any iterator
+     * in the for loop on closing, remaining iterators possibly left unclosed.
+     */
+    @Override
+    public void close() {
+        for (Iterator<T> iterator : this.iterators) {
+            CloseableIterator.closeIterator(iterator);
+        }
+    }
+}
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/MultiIteratorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/AbortiveMultiIteratorTest.java
similarity index 61%
copy from gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/MultiIteratorTest.java
copy to gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/AbortiveMultiIteratorTest.java
index 4b97299..36637da 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/MultiIteratorTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/AbortiveMultiIteratorTest.java
@@ -20,6 +20,7 @@ package org.apache.tinkerpop.gremlin.util.iterator;
 
 import org.apache.tinkerpop.gremlin.process.traversal.util.FastNoSuchElementException;
 import org.junit.Test;
+
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
@@ -27,52 +28,51 @@ import java.util.List;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 /**
  * @author Stephen Mallette (http://stephen.genoprime.com)
  */
-public class MultiIteratorTest {
+public class AbortiveMultiIteratorTest {
     @Test
     public void shouldNotHaveNextIfNoIteratorsAreAdded() {
-        final Iterator<String> itty = new MultiIterator<>();
-        assertFalse(itty.hasNext());
+        final Iterator<String> itty = new AbortiveMultiIterator<>();
+        assertThat(itty.hasNext(), is(false));
     }
 
     @Test(expected = FastNoSuchElementException.class)
     public void shouldThrowFastNoSuchElementExceptionIfNoIteratorsAreAdded() {
-        final Iterator<String> itty = new MultiIterator<>();
+        final Iterator<String> itty = new AbortiveMultiIterator<>();
         itty.next();
     }
 
     @Test
     public void shouldNotHaveNextIfEmptyIteratorIsAdded() {
-        final MultiIterator<String> itty = new MultiIterator<>();
+        final AbortiveMultiIterator<String> itty = new AbortiveMultiIterator<>();
         itty.addIterator(EmptyIterator.instance());
-        assertFalse(itty.hasNext());
+        assertThat(itty.hasNext(), is(false));
     }
 
     @Test(expected = FastNoSuchElementException.class)
     public void shouldThrowFastNoSuchElementExceptionIfEmptyIteratorIsAdded() {
-        final MultiIterator<String> itty = new MultiIterator<>();
+        final AbortiveMultiIterator<String> itty = new AbortiveMultiIterator<>();
         itty.addIterator(EmptyIterator.instance());
         itty.next();
     }
 
     @Test
     public void shouldNotHaveNextIfEmptyIteratorsAreAdded() {
-        final MultiIterator<String> itty = new MultiIterator<>();
+        final AbortiveMultiIterator<String> itty = new AbortiveMultiIterator<>();
         itty.addIterator(EmptyIterator.instance());
         itty.addIterator(EmptyIterator.instance());
         itty.addIterator(EmptyIterator.instance());
         itty.addIterator(EmptyIterator.instance());
-        assertFalse(itty.hasNext());
+        assertThat(itty.hasNext(), is(false));
     }
 
     @Test(expected = FastNoSuchElementException.class)
     public void shouldThrowFastNoSuchElementExceptionIfEmptyIteratorsAreAdded() {
-        final MultiIterator<String> itty = new MultiIterator<>();
+        final AbortiveMultiIterator<String> itty = new AbortiveMultiIterator<>();
         itty.addIterator(EmptyIterator.instance());
         itty.addIterator(EmptyIterator.instance());
         itty.addIterator(EmptyIterator.instance());
@@ -87,7 +87,7 @@ public class MultiIteratorTest {
         list.add("test2");
         list.add("test3");
 
-        final MultiIterator<String> itty = new MultiIterator<>();
+        final AbortiveMultiIterator<String> itty = new AbortiveMultiIterator<>();
         itty.addIterator(EmptyIterator.instance());
         itty.addIterator(list.iterator());
 
@@ -95,7 +95,58 @@ public class MultiIteratorTest {
         assertEquals("test1", itty.next());
         assertEquals("test2", itty.next());
         assertEquals("test3", itty.next());
-        assertFalse(itty.hasNext());
+        assertThat(itty.hasNext(), is(false));
+    }
+
+    @Test
+    public void shouldAbortIteration() {
+        final List<String> list1 = new ArrayList<>();
+        list1.add("test1");
+        list1.add("test2");
+        list1.add("test3");
+
+        final List<String> list2 = new ArrayList<>();
+        list2.add("test4");
+        list2.add("test5");
+        list2.add("test6");
+
+        final AbortiveMultiIterator<String> itty = new AbortiveMultiIterator<>();
+        itty.addIterator(EmptyIterator.instance());
+        itty.addIterator(list1.iterator());
+        itty.addIterator(list2.iterator(), c -> c != 3);
+
+        assertThat(itty.hasNext(), is(true));
+        assertEquals("test1", itty.next());
+        assertEquals("test2", itty.next());
+        assertEquals("test3", itty.next());
+        assertThat(itty.hasNext(), is(false));
+    }
+
+    @Test
+    public void shouldAllowFullIteration() {
+        final List<String> list1 = new ArrayList<>();
+        list1.add("test1");
+        list1.add("test2");
+        list1.add("test3");
+
+        final List<String> list2 = new ArrayList<>();
+        list2.add("test4");
+        list2.add("test5");
+        list2.add("test6");
+
+        final AbortiveMultiIterator<String> itty = new AbortiveMultiIterator<>();
+        itty.addIterator(EmptyIterator.instance());
+        itty.addIterator(list1.iterator());
+        itty.addIterator(list2.iterator(), c -> c == 3);
+
+        assertThat(itty.hasNext(), is(true));
+        assertEquals("test1", itty.next());
+        assertEquals("test2", itty.next());
+        assertEquals("test3", itty.next());
+        assertEquals("test4", itty.next());
+        assertEquals("test5", itty.next());
+        assertEquals("test6", itty.next());
+        assertThat(itty.hasNext(), is(false));
     }
 
     @Test
@@ -105,18 +156,18 @@ public class MultiIteratorTest {
         list.add("test2");
         list.add("test3");
 
-        final MultiIterator<String> itty = new MultiIterator<>();
+        final AbortiveMultiIterator<String> itty = new AbortiveMultiIterator<>();
         itty.addIterator(list.iterator());
 
         itty.clear();
 
-        assertFalse(itty.hasNext());
+        assertThat(itty.hasNext(), is(false));
     }
 
     @Test
     public void shouldCloseIterators() {
 
-        final MultiIterator<String> itty = new MultiIterator<>();
+        final AbortiveMultiIterator<String> itty = new AbortiveMultiIterator<>();
         final DummyAutoCloseableIterator<String> inner1 = new DummyAutoCloseableIterator<>();
         final DummyAutoCloseableIterator<String> inner2 = new DummyAutoCloseableIterator<>();
         itty.addIterator(inner1);
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/MultiIteratorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/MultiIteratorTest.java
index 4b97299..ab3ba01 100644
--- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/MultiIteratorTest.java
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/util/iterator/MultiIteratorTest.java
@@ -27,7 +27,6 @@ import java.util.List;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 /**
@@ -37,7 +36,7 @@ public class MultiIteratorTest {
     @Test
     public void shouldNotHaveNextIfNoIteratorsAreAdded() {
         final Iterator<String> itty = new MultiIterator<>();
-        assertFalse(itty.hasNext());
+        assertThat(itty.hasNext(), is(false));
     }
 
     @Test(expected = FastNoSuchElementException.class)
@@ -50,7 +49,7 @@ public class MultiIteratorTest {
     public void shouldNotHaveNextIfEmptyIteratorIsAdded() {
         final MultiIterator<String> itty = new MultiIterator<>();
         itty.addIterator(EmptyIterator.instance());
-        assertFalse(itty.hasNext());
+        assertThat(itty.hasNext(), is(false));
     }
 
     @Test(expected = FastNoSuchElementException.class)
@@ -67,7 +66,7 @@ public class MultiIteratorTest {
         itty.addIterator(EmptyIterator.instance());
         itty.addIterator(EmptyIterator.instance());
         itty.addIterator(EmptyIterator.instance());
-        assertFalse(itty.hasNext());
+        assertThat(itty.hasNext(), is(false));
     }
 
     @Test(expected = FastNoSuchElementException.class)
@@ -95,7 +94,7 @@ public class MultiIteratorTest {
         assertEquals("test1", itty.next());
         assertEquals("test2", itty.next());
         assertEquals("test3", itty.next());
-        assertFalse(itty.hasNext());
+        assertThat(itty.hasNext(), is(false));
     }
 
     @Test
@@ -110,7 +109,7 @@ public class MultiIteratorTest {
 
         itty.clear();
 
-        assertFalse(itty.hasNext());
+        assertThat(itty.hasNext(), is(false));
     }
 
     @Test
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/CoreTraversalTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/CoreTraversalTest.java
index 7fbc8b5..356677c 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/CoreTraversalTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/CoreTraversalTest.java
@@ -169,13 +169,10 @@ public class CoreTraversalTest extends AbstractGremlinProcessTest {
 
     @Test
     @LoadGraphWith(MODERN)
-    public void shouldThrowExceptionWhenIdsMixed() {
+    public void shouldAllowIdsOfMixedTypes() {
         final List<Vertex> vertices = g.V().toList();
-        try {
-            g.V(vertices.get(0), vertices.get(1).id()).toList();
-        } catch (Exception ex) {
-            validateException(Graph.Exceptions.idArgsMustBeEitherIdOrElement(), ex);
-        }
+        assertEquals(2, g.V(vertices.get(0), vertices.get(1).id()).count().next().intValue());
+        assertEquals(2, g.V(vertices.get(0).id(), vertices.get(1)).count().next().intValue());
     }
 
     @Test
diff --git a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/GraphTest.java b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/GraphTest.java
index 95d5adc..6d2cb35 100644
--- a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/GraphTest.java
+++ b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/structure/GraphTest.java
@@ -701,38 +701,6 @@ public class GraphTest extends AbstractGremlinTest {
 
     @Test
     @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES)
-    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ANY_IDS)
-    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_STRING_IDS)
-    public void shouldNotMixTypesForGettingSpecificVerticesWithVertexFirst() {
-        final Vertex v1 = graph.addVertex();
-        try {
-            graph.vertices(v1, graphProvider.convertId("1", Vertex.class));
-            fail("Should have thrown an exception because id arguments were mixed.");
-        } catch (Exception ex) {
-            final Exception expected = Graph.Exceptions.idArgsMustBeEitherIdOrElement();
-            assertEquals(expected.getClass(), ex.getClass());
-            assertEquals(expected.getMessage(), ex.getMessage());
-        }
-    }
-
-    @Test
-    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES)
-    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ANY_IDS)
-    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_STRING_IDS)
-    public void shouldNotMixTypesForGettingSpecificVerticesWithStringFirst() {
-        final Vertex v1 = graph.addVertex();
-        try {
-            graph.vertices(graphProvider.convertId("1", Vertex.class), v1);
-            fail("Should have thrown an exception because id arguments were mixed.");
-        } catch (Exception ex) {
-            final Exception expected = Graph.Exceptions.idArgsMustBeEitherIdOrElement();
-            assertEquals(expected.getClass(), ex.getClass());
-            assertEquals(expected.getMessage(), ex.getMessage());
-        }
-    }
-
-    @Test
-    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES)
     @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_STRING_IDS)
     public void shouldIterateVerticesWithStringIdSupportUsingVertex() {
         // if the graph supports id assigned, it should allow it.  if the graph does not, it will generate one
@@ -1645,42 +1613,6 @@ public class GraphTest extends AbstractGremlinTest {
     @Test
     @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES)
     @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES)
-    @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ANY_IDS)
-    @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_STRING_IDS)
-    public void shouldNotMixTypesForGettingSpecificEdgesWithEdgeFirst() {
-        final Vertex v = graph.addVertex();
-        final Edge e1 = v.addEdge("self", v);
-        try {
-            graph.edges(e1, graphProvider.convertId("1", Edge.class));
-            fail("Should have thrown an exception because id arguments were mixed.");
-        } catch (Exception ex) {
-            final Exception expected = Graph.Exceptions.idArgsMustBeEitherIdOrElement();
-            assertEquals(expected.getClass(), ex.getClass());
-            assertEquals(expected.getMessage(), ex.getMessage());
-        }
-    }
-
-    @Test
-    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES)
-    @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES)
-    @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ANY_IDS)
-    @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_STRING_IDS)
-    public void shouldNotMixTypesForGettingSpecificEdgesWithStringFirst() {
-        final Vertex v = graph.addVertex();
-        final Edge e1 = v.addEdge("self", v);
-        try {
-            graph.edges(graphProvider.convertId("1", Edge.class), e1);
-            fail("Should have thrown an exception because id arguments were mixed.");
-        } catch (Exception ex) {
-            final Exception expected = Graph.Exceptions.idArgsMustBeEitherIdOrElement();
-            assertEquals(expected.getClass(), ex.getClass());
-            assertEquals(expected.getMessage(), ex.getMessage());
-        }
-    }
-
-    @Test
-    @FeatureRequirement(featureClass = Graph.Features.VertexFeatures.class, feature = Graph.Features.VertexFeatures.FEATURE_ADD_VERTICES)
-    @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_ADD_EDGES)
     @FeatureRequirement(featureClass = Graph.Features.EdgeFeatures.class, feature = Graph.Features.EdgeFeatures.FEATURE_STRING_IDS)
     public void shouldIterateEdgesWithStringIdSupportUsingEdge() {
         // if the graph supports id assigned, it should allow it.  if the graph does not, it will generate one
diff --git a/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/structure/HadoopGraph.java b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/structure/HadoopGraph.java
index f4722ea..5a2578b 100644
--- a/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/structure/HadoopGraph.java
+++ b/hadoop-gremlin/src/main/java/org/apache/tinkerpop/gremlin/hadoop/structure/HadoopGraph.java
@@ -35,14 +35,20 @@ import org.apache.tinkerpop.gremlin.structure.Transaction;
 import org.apache.tinkerpop.gremlin.structure.Vertex;
 import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
 import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.iterator.AbortiveMultiIterator;
 import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Optional;
+import java.util.Spliterator;
+import java.util.Spliterators;
 import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 
 /**
  * @author Marko A. Rodriguez (http://markorodriguez.com)
@@ -257,22 +263,18 @@ public final class HadoopGraph implements Graph {
             if (0 == vertexIds.length) {
                 return new HadoopVertexIterator(this);
             } else {
-                // base the conversion function on the first item in the id list as the expectation is that these
-                // id values will be a uniform list
-                if (vertexIds[0] instanceof Vertex) {
-                    // based on the first item assume all vertices in the argument list
-                    if (!Stream.of(vertexIds).allMatch(id -> id instanceof Vertex))
-                        throw Graph.Exceptions.idArgsMustBeEitherIdOrElement();
-
-                    // no need to get the vertices again, so just flip it back - some implementation may want to treat this
-                    // as a refresh operation. that's not necessary for hadoopgraph.
-                    return Stream.of(vertexIds).map(id -> (Vertex) id).iterator();
-                } else {
-                    final Class<?> firstClass = vertexIds[0].getClass();
-                    if (!Stream.of(vertexIds).map(Object::getClass).allMatch(firstClass::equals))
-                        throw Graph.Exceptions.idArgsMustBeEitherIdOrElement();     // todo: change exception to be ids of the same type
-                    return IteratorUtils.filter(new HadoopVertexIterator(this), vertex -> ElementHelper.idExists(vertex.id(), vertexIds));
-                }
+                final Stream<Vertex> idsThatWereVertices = Stream.of(vertexIds).
+                        filter(id -> id instanceof Vertex).map(id -> (Vertex) id);
+                final Stream<Vertex> verticesFromHadoopGraph = StreamSupport.stream(Spliterators.spliteratorUnknownSize(
+                        IteratorUtils.filter(new HadoopVertexIterator(this),
+                                vertex -> ElementHelper.idExists(vertex.id(), vertexIds)), Spliterator.ORDERED), false);
+
+                // if the vertexIds are all Vertex objects then abort the iteration of the full HadoopGraph as there
+                // is no need to refresh the data in this use case as other graphs might
+                final AbortiveMultiIterator<Vertex> iterator = new AbortiveMultiIterator<>();
+                iterator.addIterator(idsThatWereVertices.iterator());
+                iterator.addIterator(verticesFromHadoopGraph.iterator(), c -> c != vertexIds.length);
+                return iterator;
             }
         } catch (final IOException e) {
             throw new IllegalStateException(e.getMessage(), e);
@@ -285,22 +287,18 @@ public final class HadoopGraph implements Graph {
             if (0 == edgeIds.length) {
                 return new HadoopEdgeIterator(this);
             } else {
-                // base the conversion function on the first item in the id list as the expectation is that these
-                // id values will be a uniform list
-                if (edgeIds[0] instanceof Edge) {
-                    // based on the first item assume all Edges in the argument list
-                    if (!Stream.of(edgeIds).allMatch(id -> id instanceof Edge))
-                        throw Graph.Exceptions.idArgsMustBeEitherIdOrElement();
-
-                    // no need to get the vertices again, so just flip it back - some implementation may want to treat this
-                    // as a refresh operation. that's not necessary for hadoopgraph.
-                    return Stream.of(edgeIds).map(id -> (Edge) id).iterator();
-                } else {
-                    final Class<?> firstClass = edgeIds[0].getClass();
-                    if (!Stream.of(edgeIds).map(Object::getClass).allMatch(firstClass::equals))
-                        throw Graph.Exceptions.idArgsMustBeEitherIdOrElement();     // todo: change exception to be ids of the same type
-                    return IteratorUtils.filter(new HadoopEdgeIterator(this), vertex -> ElementHelper.idExists(vertex.id(), edgeIds));
-                }
+                final Stream<Edge> idsThatWereEdges = Stream.of(edgeIds).
+                        filter(id -> id instanceof Edge).map(id -> (Edge) id);
+                final Stream<Edge> edgesFromHadoopGraph = StreamSupport.stream(Spliterators.spliteratorUnknownSize(
+                        IteratorUtils.filter(new HadoopEdgeIterator(this),
+                                edge -> ElementHelper.idExists(edge.id(), edgeIds)), Spliterator.ORDERED), false);
+
+                // if the edgeIds are all Edge objects then abort the iteration of the full HadoopGraph as there
+                // is no need to refresh the data in this use case as other graphs might
+                final AbortiveMultiIterator<Edge> iterator = new AbortiveMultiIterator<>();
+                iterator.addIterator(idsThatWereEdges.iterator());
+                iterator.addIterator(edgesFromHadoopGraph.iterator(), c -> c != edgeIds.length);
+                return iterator;
             }
         } catch (final IOException e) {
             throw new IllegalStateException(e.getMessage(), e);
diff --git a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraph.java b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraph.java
index 55be5f6..d62a81e 100644
--- a/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraph.java
+++ b/neo4j-gremlin/src/main/java/org/apache/tinkerpop/gremlin/neo4j/structure/Neo4jGraph.java
@@ -143,7 +143,6 @@ public final class Neo4jGraph implements Graph, WrappedGraph<Neo4jGraphAPI> {
             return IteratorUtils.stream(this.getBaseGraph().allNodes())
                     .map(node -> (Vertex) new Neo4jVertex(node, this)).iterator();
         } else {
-            ElementHelper.validateMixedElementIds(Vertex.class, vertexIds);
             return Stream.of(vertexIds)
                     .map(id -> {
                         if (id instanceof Number)
@@ -174,7 +173,6 @@ public final class Neo4jGraph implements Graph, WrappedGraph<Neo4jGraphAPI> {
             return IteratorUtils.stream(this.getBaseGraph().allRelationships())
                     .map(relationship -> (Edge) new Neo4jEdge(relationship, this)).iterator();
         } else {
-            ElementHelper.validateMixedElementIds(Edge.class, edgeIds);
             return Stream.of(edgeIds)
                     .map(id -> {
                         if (id instanceof Number)
diff --git a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
index 6f4f821..2795ae0 100644
--- a/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
+++ b/tinkergraph-gremlin/src/main/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraph.java
@@ -305,18 +305,18 @@ public final class TinkerGraph implements Graph {
                                                                   final Object... ids) {
         final Iterator<T> iterator;
         if (0 == ids.length) {
-            iterator = new TinkerGraphIterator<T>(elements.values().iterator());
+            iterator = new TinkerGraphIterator<>(elements.values().iterator());
         } else {
             final List<Object> idList = Arrays.asList(ids);
-            validateHomogenousIds(idList);
 
-            // if the type is of Element - have to look each up because it might be an Attachable instance or
-            // other implementation. the assumption is that id conversion is not required for detached
-            // stuff - doesn't seem likely someone would detach a Titan vertex then try to expect that
-            // vertex to be findable in OrientDB
-            return clazz.isAssignableFrom(ids[0].getClass()) ?
-                    new TinkerGraphIterator<T>(IteratorUtils.filter(IteratorUtils.map(idList, id -> elements.get(clazz.cast(id).id())).iterator(), Objects::nonNull))
-                    : new TinkerGraphIterator<T>(IteratorUtils.filter(IteratorUtils.map(idList, id -> elements.get(idManager.convert(id))).iterator(), Objects::nonNull));
+            // TinkerGraph can take a Vertex/Edge or any object as an "id". If it is an Element then we just cast
+            // to that type and pop off the identifier. there is no need to pass that through the IdManager since
+            // the assumption is that if it's already an Element, its identifier must be valid to the Graph and to
+            // its associated IdManager. All other objects are passed to the IdManager for conversion.
+            return new TinkerGraphIterator<>(IteratorUtils.filter(IteratorUtils.map(idList, id -> {
+                final Object iid = clazz.isAssignableFrom(id.getClass()) ? clazz.cast(id).id() : idManager.convert(id);
+                return elements.get(idManager.convert(iid));
+            }).iterator(), Objects::nonNull));
         }
         return TinkerHelper.inComputerMode(this) ?
                 (Iterator<T>) (clazz.equals(Vertex.class) ?
@@ -336,19 +336,6 @@ public final class TinkerGraph implements Graph {
         return features;
     }
 
-    private void validateHomogenousIds(final List<Object> ids) {
-        final Iterator<Object> iterator = ids.iterator();
-        Object id = iterator.next();
-        if (id == null)
-            throw Graph.Exceptions.idArgsMustBeEitherIdOrElement();
-        final Class firstClass = id.getClass();
-        while (iterator.hasNext()) {
-            id = iterator.next();
-            if (id == null || !id.getClass().equals(firstClass))
-                throw Graph.Exceptions.idArgsMustBeEitherIdOrElement();
-        }
-    }
-
     public class TinkerGraphFeatures implements Features {
 
         private final TinkerGraphGraphFeatures graphFeatures = new TinkerGraphGraphFeatures();
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphIdManagerTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphIdManagerTest.java
index e589c5c..8dd2e12 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphIdManagerTest.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphIdManagerTest.java
@@ -54,7 +54,8 @@ public class TinkerGraphIdManagerTest {
                     {"coerceInt", 100, 200, 300},
                     {"coerceDouble", 100d, 200d, 300d},
                     {"coerceFloat", 100f, 200f, 300f},
-                    {"coerceString", "100", "200", "300"}});
+                    {"coerceString", "100", "200", "300"},
+                    {"coerceMixed", 100d, 200f, "300"}});
         }
 
         @Parameterized.Parameter(value = 0)
@@ -120,7 +121,8 @@ public class TinkerGraphIdManagerTest {
         public static Iterable<Object[]> data() {
             return Arrays.asList(new Object[][]{
                     {"coerceUuid", vertexId, edgeId, vertexPropertyId},
-                    {"coerceString", vertexId.toString(), edgeId.toString(), vertexPropertyId.toString()}});
+                    {"coerceString", vertexId.toString(), edgeId.toString(), vertexPropertyId.toString()},
+                    {"coerceMixed", vertexId, edgeId, vertexPropertyId.toString()}});
         }
 
         @Parameterized.Parameter(value = 0)
diff --git a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphTest.java b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphTest.java
index b78dfc1..7ecc134 100644
--- a/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphTest.java
+++ b/tinkergraph-gremlin/src/test/java/org/apache/tinkerpop/gremlin/tinkergraph/structure/TinkerGraphTest.java
@@ -76,6 +76,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Random;
 import java.util.Set;
+import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
@@ -879,6 +880,24 @@ public class TinkerGraphTest {
         assertEquals(3, traversal().withEmbedded(graph).V().hasLabel("person").count().next().intValue());
     }
 
+    @Test
+    public void shouldAllowHeterogeneousIdsWithAnyManager() {
+        final Configuration anyManagerConfig = new BaseConfiguration();
+        anyManagerConfig.addProperty(TinkerGraph.GREMLIN_TINKERGRAPH_EDGE_ID_MANAGER, TinkerGraph.DefaultIdManager.ANY.name());
+        anyManagerConfig.addProperty(TinkerGraph.GREMLIN_TINKERGRAPH_VERTEX_ID_MANAGER, TinkerGraph.DefaultIdManager.ANY.name());
+        anyManagerConfig.addProperty(TinkerGraph.GREMLIN_TINKERGRAPH_VERTEX_PROPERTY_ID_MANAGER, TinkerGraph.DefaultIdManager.ANY.name());
+        final Graph graph = TinkerGraph.open(anyManagerConfig);
+        final GraphTraversalSource g = traversal().withEmbedded(graph);
+
+        final UUID uuid = UUID.fromString("0E939658-ADD2-4598-A722-2FC178E9B741");
+        g.addV("person").property(T.id, 100).
+                addV("person").property(T.id, "1000").
+                addV("person").property(T.id, "1001").
+                addV("person").property(T.id, uuid).iterate();
+
+        assertEquals(3, g.V(100, "1000", uuid).count().next().intValue());
+    }
+
     /**
      * Coerces a {@code Color} to a {@link TinkerGraph} during serialization.  Demonstrates how custom serializers
      * can be developed that can coerce one value to another during serialization.