You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by ok...@apache.org on 2017/01/18 16:38:25 UTC
tinkerpop git commit: We now have ProjectedTraveser which is a
Traverser with List
Repository: tinkerpop
Updated Branches:
refs/heads/TINKERPOP-1248 501e97a1e -> 5045f67f4
We now have ProjectedTraveser which is a Traverser with List<Object> of projections. OrderGlobalStep uses this and thus, can order for everything within the local star graph. Added MultiComparator which is like ChainedComparator but doesn't contain traversal projections -- just comparators.
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/5045f67f
Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/5045f67f
Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/5045f67f
Branch: refs/heads/TINKERPOP-1248
Commit: 5045f67f469e163d1363f953672a3f38b4ff2a3f
Parents: 501e97a
Author: Marko A. Rodriguez <ok...@gmail.com>
Authored: Wed Jan 18 09:38:21 2017 -0700
Committer: Marko A. Rodriguez <ok...@gmail.com>
Committed: Wed Jan 18 09:38:21 2017 -0700
----------------------------------------------------------------------
.../traversal/step/map/OrderGlobalStep.java | 39 ++-
.../traversal/traverser/OrderedTraverser.java | 235 -------------------
.../traversal/traverser/ProjectedTraverser.java | 199 ++++++++++++++++
.../gremlin/structure/io/gryo/GryoVersion.java | 8 +-
.../gremlin/util/function/MultiComparator.java | 60 +++++
5 files changed, 295 insertions(+), 246 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/5045f67f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStep.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStep.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStep.java
index ac5df90..9c071f1 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStep.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/step/map/OrderGlobalStep.java
@@ -27,10 +27,12 @@ import org.apache.tinkerpop.gremlin.process.traversal.step.ByModulating;
import org.apache.tinkerpop.gremlin.process.traversal.step.ComparatorHolder;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalParent;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.CollectingBarrierStep;
-import org.apache.tinkerpop.gremlin.process.traversal.traverser.OrderedTraverser;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.ProjectedTraverser;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.TraverserRequirement;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
+import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
+import org.apache.tinkerpop.gremlin.util.function.MultiComparator;
import org.javatuples.Pair;
import java.io.Serializable;
@@ -49,6 +51,7 @@ import java.util.stream.Collectors;
public final class OrderGlobalStep<S, C extends Comparable> extends CollectingBarrierStep<S> implements ComparatorHolder<S, C>, TraversalParent, ByModulating {
private List<Pair<Traversal.Admin<S, C>, Comparator<C>>> comparators = new ArrayList<>();
+ private MultiComparator<C> multiComparator = null;
private long limit = Long.MAX_VALUE;
private boolean isShuffle = false;
@@ -61,14 +64,16 @@ public final class OrderGlobalStep<S, C extends Comparable> extends CollectingBa
if (this.isShuffle)
traverserSet.shuffle();
else
- traverserSet.sort(Comparator.naturalOrder());
+ traverserSet.sort((Comparator) this.multiComparator);
}
@Override
public void processAllStarts() {
+ if (null == this.multiComparator)
+ this.multiComparator = this.createMultiComparator();
if (this.starts.hasNext()) {
while (this.starts.hasNext()) {
- this.traverserSet.add(new OrderedTraverser<S>(this.starts.next(), (List) this.comparators));
+ this.traverserSet.add(this.createOrderedTraverser(this.starts.next()));
}
this.barrierConsumer(this.traverserSet);
}
@@ -81,7 +86,7 @@ public final class OrderGlobalStep<S, C extends Comparable> extends CollectingBa
} else if (this.starts.hasNext()) {
this.processAllStarts();
}
- return ((OrderedTraverser) this.traverserSet.remove()).getInternal();
+ return ((ProjectedTraverser) this.traverserSet.remove()).getInternal();
}
public void setLimit(final long limit) {
@@ -155,7 +160,25 @@ public final class OrderGlobalStep<S, C extends Comparable> extends CollectingBa
@Override
public MemoryComputeKey<TraverserSet<S>> getMemoryComputeKey() {
- return MemoryComputeKey.of(this.getId(), new OrderBiOperator<>(this.limit, this.isShuffle), false, true);
+ if (null == this.multiComparator)
+ this.multiComparator = this.createMultiComparator();
+ return MemoryComputeKey.of(this.getId(), new OrderBiOperator<>(this.limit, this.isShuffle, this.multiComparator), false, true);
+ }
+
+ private ProjectedTraverser<S> createOrderedTraverser(final Traverser.Admin<S> traverser) {
+ final List<Object> projections = new ArrayList<>(this.comparators.size());
+ for (final Pair<Traversal.Admin<S, C>, Comparator<C>> pair : this.comparators) {
+ projections.add(TraversalUtil.apply(traverser, pair.getValue0()));
+ }
+ return new ProjectedTraverser<S>(traverser, projections);
+ }
+
+ private MultiComparator<C> createMultiComparator() {
+ final List<Comparator<C>> list = new ArrayList<>(this.comparators.size());
+ for (final Pair<Traversal.Admin<S, C>, Comparator<C>> pair : this.comparators) {
+ list.add(pair.getValue1());
+ }
+ return new MultiComparator<>(list);
}
////////////////
@@ -164,14 +187,16 @@ public final class OrderGlobalStep<S, C extends Comparable> extends CollectingBa
private long limit;
private boolean isShuffle;
+ private MultiComparator comparator;
private OrderBiOperator() {
// for serializers that need a no-arg constructor
}
- public OrderBiOperator(final long limit, final boolean isShuffle) {
+ public OrderBiOperator(final long limit, final boolean isShuffle, final MultiComparator multiComparator) {
this.limit = limit;
this.isShuffle = isShuffle;
+ this.comparator = multiComparator;
}
@Override
@@ -181,7 +206,7 @@ public final class OrderGlobalStep<S, C extends Comparable> extends CollectingBa
if (this.isShuffle)
setA.shuffle();
else
- setA.sort(Comparator.naturalOrder());
+ setA.sort(this.comparator);
long counter = 0L;
final Iterator<Traverser.Admin<S>> traversers = setA.iterator();
while (traversers.hasNext()) {
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/5045f67f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/OrderedTraverser.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/OrderedTraverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/OrderedTraverser.java
deleted file mode 100644
index 4dddaa3..0000000
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/OrderedTraverser.java
+++ /dev/null
@@ -1,235 +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.traverser;
-
-import org.apache.tinkerpop.gremlin.process.traversal.Order;
-import org.apache.tinkerpop.gremlin.process.traversal.Path;
-import org.apache.tinkerpop.gremlin.process.traversal.Step;
-import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
-import org.apache.tinkerpop.gremlin.process.traversal.TraversalSideEffects;
-import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
-import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalUtil;
-import org.apache.tinkerpop.gremlin.structure.util.Attachable;
-import org.javatuples.Pair;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Set;
-import java.util.function.Function;
-
-/**
- * @author Marko A. Rodriguez (http://markorodriguez.com)
- */
-public final class OrderedTraverser<T> implements Traverser.Admin<T> {
-
- private Traverser.Admin<T> internal;
- private int order;
- private final List<Pair<Object, Comparator<?>>> orderChecks = new ArrayList<>();
-
- private OrderedTraverser() {
- // for serialization
- }
-
- public OrderedTraverser(final Traverser.Admin<T> internal, final List<Pair<Traversal.Admin<T, ?>, Comparator>> checks) {
- this(internal, 0);
- for (final Pair<Traversal.Admin<T, ?>, Comparator> pairs : checks) {
- this.orderChecks.add(Pair.with(TraversalUtil.apply(this.internal, pairs.getValue0()), pairs.getValue1()));
- }
- }
-
- public OrderedTraverser(final Traverser.Admin<T> internal, final int order) {
- this.internal = internal instanceof OrderedTraverser ? ((OrderedTraverser) internal).internal : internal;
- this.order = order;
- }
-
- public Traverser.Admin<T> getInternal() {
- return this.internal;
- }
-
- public int order() {
- return this.order;
- }
-
- @Override
- public void merge(final Admin<?> other) {
- this.internal.merge(other);
- }
-
- @Override
- public <R> Admin<R> split(R r, Step<T, R> step) {
- return new OrderedTraverser<>(this.internal.split(r, step), this.order);
- }
-
- @Override
- public Admin<T> split() {
- return new OrderedTraverser<>(this.internal.split(), this.order);
- }
-
- @Override
- public void addLabels(final Set<String> labels) {
- this.internal.addLabels(labels);
- }
-
- @Override
- public void keepLabels(final Set<String> labels) {
- this.internal.keepLabels(labels);
- }
-
- @Override
- public void dropLabels(final Set<String> labels) {
- this.internal.dropLabels(labels);
- }
-
- @Override
- public void dropPath() {
- this.internal.dropPath();
- }
-
- @Override
- public void set(final T t) {
- this.internal.set(t);
- }
-
- @Override
- public void incrLoops(final String stepLabel) {
- this.internal.incrLoops(stepLabel);
- }
-
- @Override
- public void resetLoops() {
- this.internal.resetLoops();
- }
-
- @Override
- public String getStepId() {
- return this.internal.getStepId();
- }
-
- @Override
- public void setStepId(final String stepId) {
- this.internal.setStepId(stepId);
- }
-
- @Override
- public void setBulk(final long count) {
- this.internal.setBulk(count);
- }
-
- @Override
- public Admin<T> detach() {
- this.internal = this.internal.detach();
- return this;
- }
-
- @Override
- public T attach(final Function<Attachable<T>, T> method) {
- return this.internal.attach(method);
- }
-
- @Override
- public void setSideEffects(final TraversalSideEffects sideEffects) {
- this.internal.setSideEffects(sideEffects);
- }
-
- @Override
- public TraversalSideEffects getSideEffects() {
- return this.internal.getSideEffects();
- }
-
- @Override
- public Set<String> getTags() {
- return this.internal.getTags();
- }
-
- @Override
- public T get() {
- return this.internal.get();
- }
-
- @Override
- public <S> S sack() {
- return this.internal.sack();
- }
-
- @Override
- public <S> void sack(final S object) {
- this.internal.sack(object);
- }
-
- @Override
- public Path path() {
- return this.internal.path();
- }
-
- @Override
- public int loops() {
- return this.internal.loops();
- }
-
- @Override
- public long bulk() {
- return this.internal.bulk();
- }
-
- @Override
- public int hashCode() {
- return this.internal.hashCode();
- }
-
- @Override
- public boolean equals(final Object object) {
- return object instanceof OrderedTraverser && ((OrderedTraverser) object).internal.equals(this.internal);
- }
-
- @Override
- public OrderedTraverser<T> clone() {
- try {
- final OrderedTraverser<T> clone = (OrderedTraverser<T>) super.clone();
- clone.internal = (Traverser.Admin<T>) this.internal.clone();
- return clone;
- } catch (final CloneNotSupportedException e) {
- throw new IllegalStateException(e.getMessage(), e);
- }
- }
-
- @Override
- public int compareTo(final Traverser<T> o) {
- if (!(o instanceof OrderedTraverser))
- return 0;
- else {
- if (this.orderChecks.isEmpty()) {
- return Order.incr.compare(this.get(), o.get());
- } else {
- final OrderedTraverser<T> other = (OrderedTraverser<T>) o;
- for (int i = 0; i < this.orderChecks.size(); i++) {
- final Comparator comparator = this.orderChecks.get(i).getValue1();
- final Object thisObject = this.orderChecks.get(i).getValue0();
- final Object otherObject = other.orderChecks.get(i).getValue0();
- final int comparison = comparator.compare(thisObject, otherObject);
- if (comparison != 0)
- return comparison;
-
- }
- return 0;
- }
- }
- }
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/5045f67f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/ProjectedTraverser.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/ProjectedTraverser.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/ProjectedTraverser.java
new file mode 100644
index 0000000..67e723a
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/traverser/ProjectedTraverser.java
@@ -0,0 +1,199 @@
+/*
+ * 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.traverser;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Path;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalSideEffects;
+import org.apache.tinkerpop.gremlin.process.traversal.Traverser;
+import org.apache.tinkerpop.gremlin.structure.util.Attachable;
+
+import java.util.List;
+import java.util.Set;
+import java.util.function.Function;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public final class ProjectedTraverser<T> implements Traverser.Admin<T> {
+
+ private Traverser.Admin<T> internal;
+ private List<Object> projections;
+
+ private ProjectedTraverser() {
+ // for serialization
+ }
+
+ public ProjectedTraverser(final Traverser.Admin<T> internal, final List<Object> projections) {
+ this.internal = internal;
+ this.projections = projections;
+ }
+
+
+ public Traverser.Admin<T> getInternal() {
+ return this.internal;
+ }
+
+ public List<Object> getProjections() {
+ return this.projections;
+ }
+
+ @Override
+ public void merge(final Admin<?> other) {
+ this.internal.merge(other);
+ }
+
+ @Override
+ public <R> Admin<R> split(R r, Step<T, R> step) {
+ return new ProjectedTraverser<>(this.internal.split(r, step), this.projections);
+ }
+
+ @Override
+ public Admin<T> split() {
+ return new ProjectedTraverser<>(this.internal.split(), this.projections);
+ }
+
+ @Override
+ public void addLabels(final Set<String> labels) {
+ this.internal.addLabels(labels);
+ }
+
+ @Override
+ public void keepLabels(final Set<String> labels) {
+ this.internal.keepLabels(labels);
+ }
+
+ @Override
+ public void dropLabels(final Set<String> labels) {
+ this.internal.dropLabels(labels);
+ }
+
+ @Override
+ public void dropPath() {
+ this.internal.dropPath();
+ }
+
+ @Override
+ public void set(final T t) {
+ this.internal.set(t);
+ }
+
+ @Override
+ public void incrLoops(final String stepLabel) {
+ this.internal.incrLoops(stepLabel);
+ }
+
+ @Override
+ public void resetLoops() {
+ this.internal.resetLoops();
+ }
+
+ @Override
+ public String getStepId() {
+ return this.internal.getStepId();
+ }
+
+ @Override
+ public void setStepId(final String stepId) {
+ this.internal.setStepId(stepId);
+ }
+
+ @Override
+ public void setBulk(final long count) {
+ this.internal.setBulk(count);
+ }
+
+ @Override
+ public Admin<T> detach() {
+ this.internal = this.internal.detach();
+ return this;
+ }
+
+ @Override
+ public T attach(final Function<Attachable<T>, T> method) {
+ return this.internal.attach(method);
+ }
+
+ @Override
+ public void setSideEffects(final TraversalSideEffects sideEffects) {
+ this.internal.setSideEffects(sideEffects);
+ }
+
+ @Override
+ public TraversalSideEffects getSideEffects() {
+ return this.internal.getSideEffects();
+ }
+
+ @Override
+ public Set<String> getTags() {
+ return this.internal.getTags();
+ }
+
+ @Override
+ public T get() {
+ return this.internal.get();
+ }
+
+ @Override
+ public <S> S sack() {
+ return this.internal.sack();
+ }
+
+ @Override
+ public <S> void sack(final S object) {
+ this.internal.sack(object);
+ }
+
+ @Override
+ public Path path() {
+ return this.internal.path();
+ }
+
+ @Override
+ public int loops() {
+ return this.internal.loops();
+ }
+
+ @Override
+ public long bulk() {
+ return this.internal.bulk();
+ }
+
+ @Override
+ public int hashCode() {
+ return this.internal.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object object) {
+ return object instanceof ProjectedTraverser && ((ProjectedTraverser) object).internal.equals(this.internal);
+ }
+
+ @Override
+ public ProjectedTraverser<T> clone() {
+ try {
+ final ProjectedTraverser<T> clone = (ProjectedTraverser<T>) super.clone();
+ clone.internal = (Traverser.Admin<T>) this.internal.clone();
+ return clone;
+ } catch (final CloneNotSupportedException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/5045f67f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
index a04f2d9..f4e31fd 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
@@ -51,13 +51,11 @@ import org.apache.tinkerpop.gremlin.process.traversal.traverser.LP_O_OB_P_S_SE_S
import org.apache.tinkerpop.gremlin.process.traversal.traverser.LP_O_OB_S_SE_SL_Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.O_OB_S_SE_SL_Traverser;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.O_Traverser;
-import org.apache.tinkerpop.gremlin.process.traversal.traverser.OrderedTraverser;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.ProjectedTraverser;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.util.TraverserSet;
-import org.apache.tinkerpop.gremlin.process.traversal.util.AndP;
import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalMetrics;
import org.apache.tinkerpop.gremlin.process.traversal.util.ImmutableMetrics;
import org.apache.tinkerpop.gremlin.process.traversal.util.MutableMetrics;
-import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalExplanation;
import org.apache.tinkerpop.gremlin.structure.Column;
import org.apache.tinkerpop.gremlin.structure.Direction;
@@ -81,6 +79,7 @@ import org.apache.tinkerpop.gremlin.structure.util.star.StarGraph;
import org.apache.tinkerpop.gremlin.structure.util.star.StarGraphSerializer;
import org.apache.tinkerpop.gremlin.util.function.HashSetSupplier;
import org.apache.tinkerpop.gremlin.util.function.Lambda;
+import org.apache.tinkerpop.gremlin.util.function.MultiComparator;
import org.apache.tinkerpop.shaded.kryo.KryoSerializable;
import org.apache.tinkerpop.shaded.kryo.serializers.JavaSerializer;
import org.javatuples.Pair;
@@ -246,7 +245,7 @@ public enum GryoVersion {
add(GryoTypeReg.of(O_OB_S_SE_SL_Traverser.class, 89));
add(GryoTypeReg.of(LP_O_OB_S_SE_SL_Traverser.class, 90));
add(GryoTypeReg.of(LP_O_OB_P_S_SE_SL_Traverser.class, 91));
- add(GryoTypeReg.of(OrderedTraverser.class, 164)); // ***LAST ID***
+ add(GryoTypeReg.of(ProjectedTraverser.class, 164));
add(GryoTypeReg.of(DefaultRemoteTraverser.class, 123, new GryoSerializers.DefaultRemoteTraverserSerializer()));
add(GryoTypeReg.of(Bytecode.class, 122, new GryoSerializers.BytecodeSerializer()));
@@ -261,6 +260,7 @@ public enum GryoVersion {
add(GryoTypeReg.of(SackFunctions.Barrier.class, 135));
add(GryoTypeReg.of(TraversalOptionParent.Pick.class, 137));
add(GryoTypeReg.of(HashSetSupplier.class, 136, new UtilSerializers.HashSetSupplierSerializer()));
+ add(GryoTypeReg.of(MultiComparator.class, 165)); // ***LAST ID***
add(GryoTypeReg.of(TraverserSet.class, 58));
add(GryoTypeReg.of(Tree.class, 61));
http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/5045f67f/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/function/MultiComparator.java
----------------------------------------------------------------------
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/function/MultiComparator.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/function/MultiComparator.java
new file mode 100644
index 0000000..427aa3d
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/function/MultiComparator.java
@@ -0,0 +1,60 @@
+/*
+ * 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.function;
+
+import org.apache.tinkerpop.gremlin.process.traversal.Order;
+import org.apache.tinkerpop.gremlin.process.traversal.traverser.ProjectedTraverser;
+
+import java.io.Serializable;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * @author Marko A. Rodriguez (http://markorodriguez.com)
+ */
+public final class MultiComparator<C> implements Comparator<C>, Serializable {
+
+ private final List<Comparator> comparators;
+
+ public MultiComparator(final List<Comparator<C>> comparators) {
+ this.comparators = (List) comparators;
+ }
+
+ @Override
+ public int compare(final C objectA, final C objectB) {
+ if (this.comparators.isEmpty()) {
+ return Order.incr.compare(objectA, objectB);
+ } else {
+ for (int i = 0; i < this.comparators.size(); i++) {
+ final int comparison = this.comparators.get(i).compare(this.getObject(objectA, i), this.getObject(objectB, i));
+ if (comparison != 0)
+ return comparison;
+ }
+ return 0;
+ }
+ }
+
+ private final Object getObject(final C object, final int index) {
+ if (object instanceof ProjectedTraverser)
+ return ((ProjectedTraverser) object).getProjections().get(index);
+ else
+ return object;
+ }
+}