Package com.tinkerpop.gremlin.java

Source Code of com.tinkerpop.gremlin.java.GremlinPipeline

package com.tinkerpop.gremlin.java;

import com.tinkerpop.blueprints.Direction;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Element;
import com.tinkerpop.blueprints.Graph;
import com.tinkerpop.blueprints.Predicate;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.gremlin.Tokens;
import com.tinkerpop.pipes.FunctionPipe;
import com.tinkerpop.pipes.IdentityPipe;
import com.tinkerpop.pipes.Pipe;
import com.tinkerpop.pipes.PipeFunction;
import com.tinkerpop.pipes.branch.CopySplitPipe;
import com.tinkerpop.pipes.branch.ExhaustMergePipe;
import com.tinkerpop.pipes.branch.FairMergePipe;
import com.tinkerpop.pipes.branch.IfThenElsePipe;
import com.tinkerpop.pipes.branch.LoopPipe;
import com.tinkerpop.pipes.filter.AndFilterPipe;
import com.tinkerpop.pipes.filter.BackFilterPipe;
import com.tinkerpop.pipes.filter.CyclicPathFilterPipe;
import com.tinkerpop.pipes.filter.DuplicateFilterPipe;
import com.tinkerpop.pipes.filter.ExceptFilterPipe;
import com.tinkerpop.pipes.filter.FilterFunctionPipe;
import com.tinkerpop.pipes.filter.IdFilterPipe;
import com.tinkerpop.pipes.filter.IntervalFilterPipe;
import com.tinkerpop.pipes.filter.LabelFilterPipe;
import com.tinkerpop.pipes.filter.OrFilterPipe;
import com.tinkerpop.pipes.filter.PropertyFilterPipe;
import com.tinkerpop.pipes.filter.RandomFilterPipe;
import com.tinkerpop.pipes.filter.RangeFilterPipe;
import com.tinkerpop.pipes.filter.RetainFilterPipe;
import com.tinkerpop.pipes.sideeffect.AggregatePipe;
import com.tinkerpop.pipes.sideeffect.GroupByPipe;
import com.tinkerpop.pipes.sideeffect.GroupByReducePipe;
import com.tinkerpop.pipes.sideeffect.GroupCountFunctionPipe;
import com.tinkerpop.pipes.sideeffect.GroupCountPipe;
import com.tinkerpop.pipes.sideeffect.LinkPipe;
import com.tinkerpop.pipes.sideeffect.OptionalPipe;
import com.tinkerpop.pipes.sideeffect.SideEffectFunctionPipe;
import com.tinkerpop.pipes.sideeffect.SideEffectPipe;
import com.tinkerpop.pipes.sideeffect.StorePipe;
import com.tinkerpop.pipes.sideeffect.TablePipe;
import com.tinkerpop.pipes.sideeffect.TreePipe;
import com.tinkerpop.pipes.transform.BothEdgesPipe;
import com.tinkerpop.pipes.transform.BothPipe;
import com.tinkerpop.pipes.transform.BothVerticesPipe;
import com.tinkerpop.pipes.transform.GatherFunctionPipe;
import com.tinkerpop.pipes.transform.GatherPipe;
import com.tinkerpop.pipes.transform.GraphQueryPipe;
import com.tinkerpop.pipes.transform.IdEdgePipe;
import com.tinkerpop.pipes.transform.IdPipe;
import com.tinkerpop.pipes.transform.IdVertexPipe;
import com.tinkerpop.pipes.transform.InEdgesPipe;
import com.tinkerpop.pipes.transform.InPipe;
import com.tinkerpop.pipes.transform.InVertexPipe;
import com.tinkerpop.pipes.transform.LabelPipe;
import com.tinkerpop.pipes.transform.MemoizePipe;
import com.tinkerpop.pipes.transform.OrderMapPipe;
import com.tinkerpop.pipes.transform.OrderPipe;
import com.tinkerpop.pipes.transform.OutEdgesPipe;
import com.tinkerpop.pipes.transform.OutPipe;
import com.tinkerpop.pipes.transform.OutVertexPipe;
import com.tinkerpop.pipes.transform.PathPipe;
import com.tinkerpop.pipes.transform.PropertyMapPipe;
import com.tinkerpop.pipes.transform.PropertyPipe;
import com.tinkerpop.pipes.transform.ScatterPipe;
import com.tinkerpop.pipes.transform.SelectPipe;
import com.tinkerpop.pipes.transform.ShufflePipe;
import com.tinkerpop.pipes.transform.SideEffectCapPipe;
import com.tinkerpop.pipes.transform.TransformFunctionPipe;
import com.tinkerpop.pipes.transform.TransformPipe;
import com.tinkerpop.pipes.transform.VertexQueryPipe;
import com.tinkerpop.pipes.util.AsPipe;
import com.tinkerpop.pipes.util.FluentUtility;
import com.tinkerpop.pipes.util.MetaPipe;
import com.tinkerpop.pipes.util.PipeHelper;
import com.tinkerpop.pipes.util.Pipeline;
import com.tinkerpop.pipes.util.StartPipe;
import com.tinkerpop.pipes.util.structures.AsMap;
import com.tinkerpop.pipes.util.structures.Pair;
import com.tinkerpop.pipes.util.structures.Row;
import com.tinkerpop.pipes.util.structures.Table;
import com.tinkerpop.pipes.util.structures.Tree;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
public class GremlinPipeline<S, E> extends Pipeline<S, E> implements GremlinFluentPipeline<S, E> {

    private boolean doQueryOptimization = true;
    protected final AsMap asMap = new AsMap(this);

    public GremlinPipeline() {
        super();
    }

    public GremlinPipeline(final Object starts, final boolean doQueryOptimization) {
        super(new GremlinStartPipe(starts));
        this.doQueryOptimization = doQueryOptimization;
        FluentUtility.setStarts(this, starts);
    }

    public GremlinPipeline(final Object starts) {
        this(starts, true);
    }

    /**
     * Add an arbitrary pipe to the GremlinPipeline
     *
     * @param pipe the pipe to add to the pipeline
     * @param <T>  the type of the end of the pipe
     * @return the extended Pipeline
     */
    public <T> GremlinPipeline<S, T> add(final Pipe<?, T> pipe) {
        this.addPipe(pipe);
        return (GremlinPipeline<S, T>) this;
    }

    /**
     * Add a GraphQueryPipe to the end of the Pipeline.
     * If optimizations are enabled, then the the next steps can fold into a GraphQueryPipe compilation.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> V() {
        return this.add(new GraphQueryPipe<Vertex>(Vertex.class));
    }

    /**
     * Add a GraphQueryPipe to the end of the Pipeline.
     * If optimizations are enabled, then the the next steps can fold into a GraphQueryPipe compilation.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Edge> E() {
        return this.add(new GraphQueryPipe<Edge>(Edge.class));
    }

    /**
     * Add a GraphQueryPipe to the end of the Pipeline.
     * If optimizations are enabled, then the the next steps can fold into a GraphQueryPipe compilation.
     *
     * @param key   they key that all the emitted vertices should be checked on
     * @param value the value that all the emitted vertices should have for the key
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> V(final String key, final Object value) {
        return this.add(new GraphQueryPipe(Vertex.class)).has(key, value);
    }

    /**
     * Add a GraphQueryPipe to the end of the Pipeline.
     * If optimizations are enabled, then the the next steps can fold into a GraphQueryPipe compilation.
     *
     * @param key   they key that all the emitted edges should be checked on
     * @param value the value that all the emitted edges should have for the key
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Edge> E(final String key, final Object value) {
        return this.add(new GraphQueryPipe(Edge.class)).has(key, value);
    }

    /**
     * Check if the element has a property with provided key.
     *
     * @param key the property key to check
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ? extends Element> has(final String key) {
        return this.has(key, Tokens.T.neq, null);
    }

    /**
     * Add an IdFilterPipe, LabelFilterPipe, or PropertyFilterPipe to the end of the Pipeline.
     * If the incoming element has the provided key/value as check with .equals(), then let the element pass.
     * If the key is id or label, then use respect id or label filtering.
     *
     * @param key   the property key to check
     * @param value the object to filter on (in an OR manner)
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ? extends Element> has(final String key, final Object value) {
        return this.has(key, Tokens.T.eq, value);
    }

    /**
     * Add an IdFilterPipe, LabelFilterPipe, or PropertyFilterPipe to the end of the Pipeline.
     * If the incoming element has the provided key/value as check with .equals(), then let the element pass.
     * If the key is id or label, then use respect id or label filtering.
     *
     * @param key          the property key to check
     * @param compareToken the comparison to use
     * @param value        the object to filter on
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ? extends Element> has(final String key, final Tokens.T compareToken, final Object value) {
        return this.has(key, Tokens.mapPredicate(compareToken), value);
    }

    /**
     * Add an IdFilterPipe, LabelFilterPipe, or PropertyFilterPipe to the end of the Pipeline.
     * If the incoming element has the provided key/value as check with .equals(), then let the element pass.
     * If the key is id or label, then use respect id or label filtering.
     *
     * @param key       the property key to check
     * @param predicate the comparison to use
     * @param value     the object to filter on
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ? extends Element> has(final String key, final Predicate predicate, final Object value) {
        if (key.equals(Tokens.ID)) {
            return this.add(new IdFilterPipe(predicate, value));
        } else if (key.equals(Tokens.LABEL)) {
            return this.add(new LabelFilterPipe(predicate, value));
        } else {
            final Pipe pipe = new PropertyFilterPipe(key, predicate, value);
            return this.doQueryOptimization ? GremlinFluentUtility.optimizePipelineForQuery(this, pipe) : this.add(pipe);
        }
    }

    /**
     * Check if the element does not have a property with provided key.
     *
     * @param key the property key to check
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ? extends Element> hasNot(final String key) {
        return this.has(key, Tokens.T.eq, null);
    }

    /**
     * Add an IdFilterPipe, LabelFilterPipe, or PropertyFilterPipe to the end of the Pipeline.
     * If the incoming element has the provided key/value as check with .equals(), then filter the element.
     * If the key is id or label, then use respect id or label filtering.
     *
     * @param key   the property key to check
     * @param value the objects to filter on (in an OR manner)
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ? extends Element> hasNot(final String key, final Object value) {
        return this.has(key, Tokens.T.neq, value);
    }

    /**
     * Add an IntervalFilterPipe to the end of the Pipeline.
     * If the incoming element has a value that is within the interval value range specified, then the element is allows to pass.
     * If hte incoming element's value for the key is null, the element is filtered.
     *
     * @param key        the property key to check
     * @param startValue the start of the interval (inclusive)
     * @param endValue   the end of the interval (exclusive)
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ? extends Element> interval(final String key, final Comparable startValue, final Comparable endValue) {
        final Pipe pipe = new IntervalFilterPipe<Element>(key, startValue, endValue);
        return this.doQueryOptimization ? GremlinFluentUtility.optimizePipelineForQuery(this, pipe) : this.add(pipe);
    }

    /**
     * Add a BothEdgesPipe to the end of the Pipeline.
     * Emit both incoming and outgoing edges for the incoming vertex.
     *
     * @param labels the edge labels to traverse
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Edge> bothE(final String... labels) {
        return this.bothE(Integer.MAX_VALUE, labels);
    }

    /**
     * Add a BothEdgesPipe to the end of the Pipeline.
     * Emit both incoming and outgoing edges for the incoming vertex.
     *
     * @param branchFactor the number of max incident edges for each incoming vertex
     * @param labels       the edge labels to traverse
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Edge> bothE(final int branchFactor, final String... labels) {
        return this.doQueryOptimization ?
                this.add(new VertexQueryPipe(Edge.class, Direction.BOTH, null, null, branchFactor, 0, Integer.MAX_VALUE, labels)) :
                this.add(new BothEdgesPipe(branchFactor, labels));
    }


    /**
     * Add a BothPipe to the end of the Pipeline.
     * Emit both the incoming and outgoing adjacent vertices for the incoming vertex.
     *
     * @param labels the edge labels to traverse
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> both(final String... labels) {
        return this.both(Integer.MAX_VALUE, labels);
    }

    /**
     * Add a BothPipe to the end of the Pipeline.
     * Emit both the incoming and outgoing adjacent vertices for the incoming vertex.
     *
     * @param branchFactor the number of max adjacent vertices for each incoming vertex
     * @param labels       the edge labels to traverse
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> both(final int branchFactor, final String... labels) {
        return this.doQueryOptimization ?
                this.add(new VertexQueryPipe(Vertex.class, Direction.BOTH, null, null, branchFactor, 0, Integer.MAX_VALUE, labels)) :
                this.add(new BothPipe(branchFactor, labels));
    }

    /**
     * Add a BothVerticesPipe to the end of the Pipeline.
     * Emit both the tail and head vertices of the incoming edge.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> bothV() {
        return this.add(new BothVerticesPipe());
    }

    /**
     * Add an IdEdgePipe to the end of the Pipeline.
     * Emit the edges of the graph whose ids are those of the incoming id objects.
     *
     * @param graph the graph of the pipe
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Edge> idEdge(final Graph graph) {
        return this.add(new IdEdgePipe(graph));
    }

    /**
     * Add an IdPipe to the end of the Pipeline.
     * Emit the id of the incoming element.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Object> id() {
        return this.add(new IdPipe());
    }

    /**
     * Add an IdVertexPipe to the end of the Pipeline.
     * Emit the vertices of the graph whose ids are those of the incoming id objects.
     *
     * @param graph the graph of the pipe
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> idVertex(final Graph graph) {
        return this.add(new IdVertexPipe(graph));
    }

    /**
     * Add an InEdgesPipe to the end of the Pipeline.
     * Emit the incoming edges for the incoming vertex.
     *
     * @param labels the edge labels to traverse
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Edge> inE(final String... labels) {
        return this.inE(Integer.MAX_VALUE, labels);
    }

    /**
     * Add an InEdgesPipe to the end of the Pipeline.
     * Emit the incoming edges for the incoming vertex.
     *
     * @param branchFactor the number of max incident edges for each incoming vertex
     * @param labels       the edge labels to traverse
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Edge> inE(final int branchFactor, final String... labels) {
        return this.doQueryOptimization ?
                this.add(new VertexQueryPipe(Edge.class, Direction.IN, null, null, branchFactor, 0, Integer.MAX_VALUE, labels)) :
                this.add(new InEdgesPipe(branchFactor, labels));
    }

    /**
     * Add a InPipe to the end of the Pipeline.
     * Emit the adjacent incoming vertices for the incoming vertex.
     *
     * @param labels the edge labels to traverse
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> in(final String... labels) {
        return this.in(Integer.MAX_VALUE, labels);
    }

    /**
     * Add a InPipe to the end of the Pipeline.
     * Emit the adjacent incoming vertices for the incoming vertex.
     *
     * @param branchFactor the number of max adjacent vertices for each incoming vertex
     * @param labels       the edge labels to traverse
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> in(final int branchFactor, final String... labels) {
        return this.doQueryOptimization ?
                this.add(new VertexQueryPipe(Vertex.class, Direction.IN, null, null, branchFactor, 0, Integer.MAX_VALUE, labels)) :
                this.add(new InPipe(branchFactor, labels));
    }

    /**
     * Add an InVertexPipe to the end of the Pipeline.
     * Emit the head vertex of the incoming edge.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> inV() {
        return this.add(new InVertexPipe());
    }

    /**
     * Add an LabelPipe to the end of the Pipeline.
     * Emit the label of the incoming edge.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, String> label() {
        return this.add(new LabelPipe());
    }

    /**
     * Add an OutEdgesPipe to the end of the Pipeline.
     * Emit the outgoing edges for the incoming vertex.
     *
     * @param labels the edge labels to traverse
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Edge> outE(final String... labels) {
        return this.outE(Integer.MAX_VALUE, labels);
    }

    /**
     * Add an OutEdgesPipe to the end of the Pipeline.
     * Emit the outgoing edges for the incoming vertex.
     *
     * @param branchFactor the number of max incident edges for each incoming vertex
     * @param labels       the edge labels to traverse
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Edge> outE(final int branchFactor, final String... labels) {
        return this.doQueryOptimization ?
                this.add(new VertexQueryPipe(Edge.class, Direction.OUT, null, null, branchFactor, 0, Integer.MAX_VALUE, labels)) :
                this.add(new OutEdgesPipe(branchFactor, labels));
    }

    /**
     * Add an OutPipe to the end of the Pipeline.
     * Emit the adjacent outgoing vertices of the incoming vertex.
     *
     * @param labels the edge labels to traverse
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> out(final String... labels) {
        return this.out(Integer.MAX_VALUE, labels);
    }

    /**
     * Add an OutPipe to the end of the Pipeline.
     * Emit the adjacent outgoing vertices of the incoming vertex.
     *
     * @param branchFactor the number of max adjacent vertices for each incoming vertex
     * @param labels       the edge labels to traverse
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> out(final int branchFactor, final String... labels) {
        return this.doQueryOptimization ?
                this.add(new VertexQueryPipe(Vertex.class, Direction.OUT, null, null, branchFactor, 0, Integer.MAX_VALUE, labels)) :
                this.add(new OutPipe(branchFactor, labels));
    }

    /**
     * Add an OutVertexPipe to the end of the Pipeline.
     * Emit the tail vertex of the incoming edge.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> outV() {
        return this.add(new OutVertexPipe());
    }

    /**
     * Add a PropertyMapPipe to the end of the Pipeline.
     * Emit the properties of the incoming element as a java.util.Map.
     *
     * @param keys the keys to get from the element (if none provided, all keys retrieved)
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Map<String, Object>> map(final String... keys) {
        return this.add(new PropertyMapPipe(keys));
    }

    /**
     * Add a PropertyPipe to the end of the Pipeline.
     * Emit the respective property of the incoming element.
     *
     * @param key the property key
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Object> property(final String key) {
        return this.add(new PropertyPipe(key));
    }

    /**
     * Add a FunctionPipe to the end of the pipeline.
     * The provide provided PipeFunction emits whatever is defined by the function.
     * This serves as an arbitrary step computation.
     *
     * @param function the function of the FunctionPipe
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ?> step(final PipeFunction function) {
        return this.add(new FunctionPipe(FluentUtility.prepareFunction(this.asMap, function)));
    }

    /**
     * Add an arbitrary Pipe to the end of the pipeline.
     *
     * @param pipe The provided pipe.
     * @param <T>  the object type emitted by the provided pipe.
     * @return the extended Pipeline
     */
    public <T> GremlinPipeline<S, T> step(final Pipe<E, T> pipe) {
        return this.add(pipe);
    }

    ////////////////////
    /// BRANCH PIPES ///
    ////////////////////

    /**
     * Add a CopySplitPipe to the end of the pipeline.
     * The incoming objects are copied to the provided pipes.
     * This "split-pipe" is used in conjunction with some type of "merge-pipe."
     *
     * @param pipes the internal pipes of the CopySplitPipe
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ?> copySplit(final Pipe<E, ?>... pipes) {
        return this.add(new CopySplitPipe(pipes));
    }

    /**
     * Add an ExhaustMergePipe to the end of the pipeline.
     * The one-step previous MetaPipe in the pipeline's pipes are used as the internal pipes.
     * The pipes' emitted objects are merged where the first pipe's objects are exhausted, then the second, etc.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ?> exhaustMerge() {
        return this.add(new ExhaustMergePipe(((MetaPipe) FluentUtility.getPreviousPipe(this)).getPipes()));
    }

    /**
     * Add a FairMergePipe to the end of the Pipeline.
     * The one-step previous MetaPipe in the pipeline's pipes are used as the internal pipes.
     * The pipes' emitted objects are merged in a round robin fashion.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ?> fairMerge() {
        return this.add(new FairMergePipe(((MetaPipe) FluentUtility.getPreviousPipe(this)).getPipes()));
    }

    /**
     * Add an IfThenElsePipe to the end of the Pipeline.
     * If the ifFunction is true, then the results of the thenFunction are emitted.
     * If the ifFunction is false, then the results of the elseFunction are emitted.
     *
     * @param ifFunction   the function denoting the "if" part of the pipe
     * @param thenFunction the function denoting the "then" part of the pipe
     * @param elseFunction the function denoting the "else" part of the pipe
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ?> ifThenElse(final PipeFunction<E, Boolean> ifFunction, final PipeFunction<E, ?> thenFunction, final PipeFunction<E, ?> elseFunction) {
        return this.add(new IfThenElsePipe(FluentUtility.prepareFunction(this.asMap, ifFunction), FluentUtility.prepareFunction(this.asMap, thenFunction), FluentUtility.prepareFunction(this.asMap, elseFunction)));
    }

    /**
     * Add a LoopPipe to the end of the Pipeline.
     * Looping is useful for repeating a section of a pipeline.
     * The provided whileFunction determines when to drop out of the loop.
     * The whileFunction is provided a LoopBundle object which contains the object in loop along with other useful metadata.
     *
     * @param numberedStep  the number of steps to loop back to
     * @param whileFunction whether or not to continue looping on the current object
     * @return the extended Pipeline
     */
    @Deprecated
    public GremlinPipeline<S, E> loop(final int numberedStep, final PipeFunction<LoopPipe.LoopBundle<E>, Boolean> whileFunction) {
        return this.add(new LoopPipe(new Pipeline(FluentUtility.removePreviousPipes(this, numberedStep)), FluentUtility.prepareFunction(this.asMap, whileFunction)));
    }

    /**
     * Add a LoopPipe to the end of the Pipeline.
     * Looping is useful for repeating a section of a pipeline.
     * The provided whileFunction determines when to drop out of the loop.
     * The whileFunction is provided a LoopBundle object which contains the object in loop along with other useful metadata.
     *
     * @param namedStep     the name of the step to loop back to
     * @param whileFunction whether or not to continue looping on the current object
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> loop(final String namedStep, final PipeFunction<LoopPipe.LoopBundle<E>, Boolean> whileFunction) {
        return this.add(new LoopPipe(new Pipeline(FluentUtility.removePreviousPipes(this, namedStep)), FluentUtility.prepareFunction(this.asMap, whileFunction)));
    }

    /**
     * Add a LoopPipe to the end of the Pipeline.
     * Looping is useful for repeating a section of a pipeline.
     * The provided whileFunction determines when to drop out of the loop.
     * The provided emitFunction can be used to emit objects that are still going through a loop.
     * The whileFunction and emitFunctions are provided a LoopBundle object which contains the object in loop along with other useful metadata.
     *
     * @param numberedStep  the number of steps to loop back to
     * @param whileFunction whether or not to continue looping on the current object
     * @param emitFunction  whether or not to emit the current object (irrespective of looping)
     * @return the extended Pipeline
     */
    @Deprecated
    public GremlinPipeline<S, E> loop(final int numberedStep, final PipeFunction<LoopPipe.LoopBundle<E>, Boolean> whileFunction, final PipeFunction<LoopPipe.LoopBundle<E>, Boolean> emitFunction) {
        return this.add(new LoopPipe(new Pipeline(FluentUtility.removePreviousPipes(this, numberedStep)), FluentUtility.prepareFunction(this.asMap, whileFunction), FluentUtility.prepareFunction(this.asMap, emitFunction)));
    }

    /**
     * Add a LoopPipe to the end of the Pipeline.
     * Looping is useful for repeating a section of a pipeline.
     * The provided whileFunction determines when to drop out of the loop.
     * The provided emitFunction can be used to emit objects that are still going through a loop.
     * The whileFunction and emitFunctions are provided a LoopBundle object which contains the object in loop along with other useful metadata.
     *
     * @param namedStep     the number of steps to loop back to
     * @param whileFunction whether or not to continue looping on the current object
     * @param emitFunction  whether or not to emit the current object (irrespective of looping)
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> loop(final String namedStep, final PipeFunction<LoopPipe.LoopBundle<E>, Boolean> whileFunction, final PipeFunction<LoopPipe.LoopBundle<E>, Boolean> emitFunction) {
        return this.add(new LoopPipe(new Pipeline(FluentUtility.removePreviousPipes(this, namedStep)), FluentUtility.prepareFunction(this.asMap, whileFunction), FluentUtility.prepareFunction(this.asMap, emitFunction)));
    }

    ////////////////////
    /// FILTER PIPES ///
    ////////////////////

    /**
     * Add an AndFilterPipe to the end the Pipeline.
     * If the internal pipes all yield objects, then the object is not filtered.
     * The provided pipes are provided the object as their starts.
     *
     * @param pipes the internal pipes of the AndFilterPipe
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> and(final Pipe<E, ?>... pipes) {
        return this.add(new AndFilterPipe<E>(pipes));
    }

    /**
     * Add a BackFilterPipe to the end of the Pipeline.
     * The object that was seen numberedSteps ago is emitted.
     *
     * @param numberedStep the number of steps previous to back up to
     * @return the extended Pipeline
     */
    @Deprecated
    public GremlinPipeline<S, ?> back(final int numberedStep) {
        return this.add(new BackFilterPipe(new Pipeline(FluentUtility.removePreviousPipes(this, numberedStep))));
    }

    /**
     * Add a BackFilterPipe to the end of the Pipeline.
     * The object that was seen namedSteps ago is emitted.
     *
     * @param namedStep the name of the step previous to back up to
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ?> back(final String namedStep) {
        return this.add(new BackFilterPipe(new Pipeline(FluentUtility.removePreviousPipes(this, namedStep))));
    }

    /**
     * Add a DuplicateFilterPipe to the end of the Pipeline.
     * Will only emit the object if it has not been seen before.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> dedup() {
        return this.add(new DuplicateFilterPipe<E>());
    }

    /**
     * Add a DuplicateFilterPipe to the end of the Pipeline.
     * Will only emit the object if the object generated by its function hasn't been seen before.
     *
     * @param dedupFunction a function to call on the object to yield the object to dedup on
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> dedup(final PipeFunction<E, ?> dedupFunction) {
        return this.add(new DuplicateFilterPipe<E>(FluentUtility.prepareFunction(this.asMap, dedupFunction)));
    }

    /**
     * Add an ExceptFilterPipe to the end of the Pipeline.
     * Will only emit the object if it is not in the provided collection.
     *
     * @param collection the collection except from the stream
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> except(final Collection<E> collection) {
        return this.add(new ExceptFilterPipe<E>(collection));
    }

    /**
     * Add an ExceptFilterPipe to the end of the Pipeline.
     * Will only emit the object if it is not equal to any of the objects contained at the named steps.
     *
     * @param namedSteps the named steps in the pipeline
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> except(final String... namedSteps) {
        return this.add(new ExceptFilterPipe<E>(this.asMap, namedSteps));
    }

    /**
     * Add an FilterFunctionPipe to the end of the Pipeline.
     * The serves are an arbitrary filter where the filter criteria is provided by the filterFunction.
     *
     * @param filterFunction the filter function of the pipe
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> filter(final PipeFunction<E, Boolean> filterFunction) {
        return this.add(new FilterFunctionPipe<E>(FluentUtility.prepareFunction(this.asMap, filterFunction)));
    }

    /**
     * Add an OrFilterPipe to the end the Pipeline.
     * Will only emit the object if one or more of the provides pipes yields an object.
     * The provided pipes are provided the object as their starts.
     *
     * @param pipes the internal pipes of the OrFilterPipe
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> or(final Pipe<E, ?>... pipes) {
        return this.add(new OrFilterPipe<E>(pipes));
    }

    /**
     * Add a RandomFilterPipe to the end of the Pipeline.
     * A biased coin toss determines if the object is emitted or not.
     *
     * @param bias the bias of the random coin
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> random(final Double bias) {
        return this.add(new RandomFilterPipe<E>(bias));
    }

    /**
     * Add a RageFilterPipe to the end of the Pipeline.
     * Analogous to a high/low index lookup.
     *
     * @param low  the low end of the range
     * @param high the high end of the range
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> range(final int low, final int high) {
        final Pipe pipe = new RangeFilterPipe<E>(low, high);
        if (!this.doQueryOptimization)
            return this.add(pipe);
        else {
            return GremlinFluentUtility.optimizePipelineForQuery(this, pipe);
        }
    }

    /**
     * Add a RetainFilterPipe to the end of the Pipeline.
     * Will emit the object only if it is in the provided collection.
     *
     * @param collection the collection to retain
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> retain(final Collection<E> collection) {
        return this.add(new RetainFilterPipe<E>(collection));
    }

    /**
     * Add a RetainFilterPipe to the end of the Pipeline.
     * Will only emit the object if it is equal to any of the objects contained at the named steps.
     *
     * @param namedSteps the named steps in the pipeline
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> retain(final String... namedSteps) {
        return this.add(new RetainFilterPipe<E>(this.asMap, namedSteps));
    }

    /**
     * Add a CyclicPathFilterPipe to the end of the Pipeline.
     * If the object's path is repeating (looping), then the object is filtered.
     * Thus, what is emitted are those objects whose history is composed of unique objects.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> simplePath() {
        return this.add(new CyclicPathFilterPipe<E>());
    }

    /////////////////////////
    /// SIDE-EFFECT PIPES ///
    /////////////////////////

    /**
     * Add an AggregatePipe to the end of the Pipeline.
     * The objects prior to aggregate are greedily collected into an ArrayList.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> aggregate() {
        return this.aggregate(new ArrayList<E>());
    }

    /**
     * Add an AggregatePipe to the end of the Pipeline.
     * The objects prior to aggregate are greedily collected into the provided collection.
     *
     * @param aggregate the collection to aggregate results into
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> aggregate(final Collection<E> aggregate) {
        return this.add(new AggregatePipe<E>(aggregate));
    }

    /**
     * Add an AggregatePipe to the end of the Pipeline.
     * The results of the function evaluated on the objects prior to the aggregate are greedily collected into the provided collection.
     *
     * @param aggregate         the collection to aggregate results into
     * @param aggregateFunction the function to run over each object prior to insertion into the aggregate
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> aggregate(final Collection aggregate, final PipeFunction<E, ?> aggregateFunction) {
        return this.add(new AggregatePipe<E>(aggregate, FluentUtility.prepareFunction(this.asMap, aggregateFunction)));
    }

    /**
     * Add an AggregatePipe to the end of the Pipeline.
     * The results of the function evaluated on the objects prior to the aggregate are greedily collected into an ArrayList.
     *
     * @param aggregateFunction the function to run over each object prior to insertion into the aggregate
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> aggregate(final PipeFunction<E, ?> aggregateFunction) {
        return this.aggregate(new ArrayList(), FluentUtility.prepareFunction(this.asMap, aggregateFunction));
    }

    /**
     * Add an OptionalPipe to the end of the Pipeline.
     * The section of pipeline back to the numbered step is evaluated.
     *
     * @param numberedStep the number of steps previous to optional back to
     * @return the extended Pipeline
     */
    @Deprecated
    public GremlinPipeline<S, ?> optional(final int numberedStep) {
        return this.add(new OptionalPipe(new Pipeline(FluentUtility.removePreviousPipes(this, numberedStep))));
    }

    /**
     * Add an OptionalPipe to the end of the Pipeline.
     * The section of pipeline back to the partition step is evaluated.
     *
     * @param namedStep the name of the step previous to optional back to
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ?> optional(final String namedStep) {
        return this.add(new OptionalPipe(new Pipeline(FluentUtility.removePreviousPipes(this, namedStep))));
    }

    /**
     * Add a GroupByPipe to the end of the Pipeline.
     * Group the objects inputted objects according to a key generated from the object and a value generated from the object.
     * The grouping map has values that are Lists.
     *
     * @param map           the map to store the grouping in
     * @param keyFunction   the function that generates the key from the object
     * @param valueFunction the function that generates the value from the function
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> groupBy(final Map<?, List<?>> map, final PipeFunction keyFunction, final PipeFunction valueFunction) {
        return this.add(new GroupByPipe(map, FluentUtility.prepareFunction(this.asMap, keyFunction), FluentUtility.prepareFunction(this.asMap, valueFunction)));
    }

    /**
     * Add a GroupByPipe to the end of the Pipeline.
     * Group the objects inputted objects according to a key generated from the object and a value generated from the object.
     * The grouping map has values that are Lists.
     *
     * @param keyFunction   the function that generates the key from the object
     * @param valueFunction the function that generates the value from the function
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> groupBy(final PipeFunction keyFunction, final PipeFunction valueFunction) {
        return this.add(new GroupByPipe(FluentUtility.prepareFunction(this.asMap, keyFunction), FluentUtility.prepareFunction(this.asMap, valueFunction)));
    }

    /**
     * Add a GroupByReducePipe to the end of the Pipeline.
     * Group the objects inputted objects according to a key generated from the object and a value generated from the object.
     * The grouping map has values that are Lists.
     * When the pipe has no more incoming objects, apply the reduce function to the keyed Lists.
     * The sideEffect is only fully available when the pipe is empty.
     *
     * @param reduceMap      a map to perform the reduce operation on (good for having a later reference)
     * @param keyFunction    the function that generates the key from the object
     * @param valueFunction  the function that generates the value from the function
     * @param reduceFunction the function that reduces the value lists
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> groupBy(final Map reduceMap, final PipeFunction keyFunction, final PipeFunction valueFunction, final PipeFunction reduceFunction) {
        return this.add(new GroupByReducePipe(reduceMap, FluentUtility.prepareFunction(this.asMap, keyFunction), FluentUtility.prepareFunction(this.asMap, valueFunction), FluentUtility.prepareFunction(this.asMap, reduceFunction)));
    }

    /**
     * Add a GroupByReducePipe to the end of the Pipeline.
     * Group the objects inputted objects according to a key generated from the object and a value generated from the object.
     * The grouping map has values that are Lists.
     * When the pipe has no more incoming objects, apply the reduce function to the keyed Lists.
     * The sideEffect is only fully available when the pipe is empty.
     *
     * @param keyFunction    the function that generates the key from the object
     * @param valueFunction  the function that generates the value from the function
     * @param reduceFunction the function that reduces the value lists
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> groupBy(final PipeFunction keyFunction, final PipeFunction valueFunction, final PipeFunction reduceFunction) {
        return this.add(new GroupByReducePipe(FluentUtility.prepareFunction(this.asMap, keyFunction), FluentUtility.prepareFunction(this.asMap, valueFunction), FluentUtility.prepareFunction(this.asMap, reduceFunction)));
    }

    /**
     * Add a GroupCountPipe or GroupCountFunctionPipe to the end of the Pipeline.
     * A map is maintained of counts.
     * The map keys are determined by the function on the incoming object.
     * The map values are determined by the function on the incoming object (getA()) and the previous value (getB()).
     *
     * @param map           a provided count map
     * @param keyFunction   the key function to determine map key
     * @param valueFunction the value function to determine map value
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> groupCount(final Map<?, Number> map, final PipeFunction keyFunction, final PipeFunction<Pair<?, Number>, Number> valueFunction) {
        return this.add(new GroupCountFunctionPipe(map, FluentUtility.prepareFunction(this.asMap, keyFunction), FluentUtility.prepareFunction(this.asMap, valueFunction)));
    }

    /**
     * Add a GroupCountPipe or GroupCountFunctionPipe to the end of the Pipeline.
     * map is maintained of counts.
     * The map keys are determined by the function on the incoming object.
     * The map values are determined by the function on the incoming object (getA()) and the previous value (getB()).
     *
     * @param keyFunction   the key function to determine map key
     * @param valueFunction the value function to determine map value
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> groupCount(final PipeFunction keyFunction, final PipeFunction<Pair<?, Number>, Number> valueFunction) {
        return this.add(new GroupCountFunctionPipe(FluentUtility.prepareFunction(this.asMap, keyFunction), FluentUtility.prepareFunction(this.asMap, valueFunction)));
    }

    /**
     * Add a GroupCountPipe or GroupCountFunctionPipe to the end of the Pipeline.
     * A map is maintained of counts.
     * The map keys are determined by the function on the incoming object.
     * The map values are 1 plus the previous value for that key.
     *
     * @param map         a provided count map
     * @param keyFunction the key function to determine map key
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> groupCount(final Map<?, Number> map, final PipeFunction keyFunction) {
        return this.add(new GroupCountFunctionPipe(map, FluentUtility.prepareFunction(this.asMap, keyFunction), null));
    }

    /**
     * Add a GroupCountPipe or GroupCountFunctionPipe to the end of the Pipeline.
     * A map is maintained of counts.
     * The map keys are determined by the function on the incoming object.
     * The map values are 1 plus the previous value for that key.
     *
     * @param keyFunction the key function to determine map key
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> groupCount(final PipeFunction keyFunction) {
        return this.add(new GroupCountFunctionPipe(FluentUtility.prepareFunction(this.asMap, keyFunction), null));
    }

    /**
     * Add a GroupCountPipe to the end of the Pipeline.
     * A map is maintained of counts.
     * The map keys are the incoming objects.
     * The map values are 1 plus the previous value for that key.
     *
     * @param map a provided count map
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> groupCount(final Map<?, Number> map) {
        return this.add(new GroupCountPipe(map));
    }

    /**
     * Add a GroupCountPipe to the end of the Pipeline.
     * A map is maintained of counts.
     * The map keys are the incoming objects.
     * The map values are 1 plus the previous value for that key.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> groupCount() {
        return this.add(new GroupCountPipe());
    }

    /**
     * Add a LinkPipe to the end of the Pipeline.
     * Emit the incoming vertex, but have other vertex provide an outgoing edge to incoming vertex.
     *
     * @param label     the edge label
     * @param namedStep the step name that has the other vertex to link to
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> linkOut(final String label, final String namedStep) {
        return this.add(new LinkPipe(Direction.OUT, label, FluentUtility.getAsPipe(this, namedStep)));
    }

    /**
     * Add a LinkPipe to the end of the Pipeline.
     * Emit the incoming vertex, but have other vertex provide an incoming edge to incoming vertex.
     *
     * @param label     the edge label
     * @param namedStep the step name that has the other vertex to link to
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> linkIn(final String label, final String namedStep) {
        return this.add(new LinkPipe(Direction.IN, label, FluentUtility.getAsPipe(this, namedStep)));
    }

    /**
     * Add a LinkPipe to the end of the Pipeline.
     * Emit the incoming vertex, but have other vertex provide an incoming and outgoing edge to incoming vertex.
     *
     * @param label     the edge label
     * @param namedStep the step name that has the other vertex to link to
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> linkBoth(final String label, final String namedStep) {
        return this.add(new LinkPipe(Direction.BOTH, label, FluentUtility.getAsPipe(this, namedStep)));
    }

    /**
     * Add a LinkPipe to the end of the Pipeline.
     * Emit the incoming vertex, but have other vertex provide an outgoing edge to incoming vertex.
     *
     * @param label the edge label
     * @param other the other vertex
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> linkOut(final String label, final Vertex other) {
        return this.add(new LinkPipe(Direction.OUT, label, other));
    }

    /**
     * Add a LinkPipe to the end of the Pipeline.
     * Emit the incoming vertex, but have other vertex provide an incoming edge to incoming vertex.
     *
     * @param label the edge label
     * @param other the other vertex
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> linkIn(final String label, final Vertex other) {
        return this.add(new LinkPipe(Direction.IN, label, other));
    }

    /**
     * Add a LinkPipe to the end of the Pipeline.
     * Emit the incoming vertex, but have other vertex provide an incoming and outgoing edge to incoming vertex.
     *
     * @param label the edge label
     * @param other the other vertex
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Vertex> linkBoth(final String label, final Vertex other) {
        return this.add(new LinkPipe(Direction.BOTH, label, other));
    }

    /**
     * Add a SideEffectFunctionPipe to the end of the Pipeline.
     * The provided function is evaluated and the incoming object is the outgoing object.
     *
     * @param sideEffectFunction the function of the pipe
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> sideEffect(final PipeFunction<E, ?> sideEffectFunction) {
        return this.add(new SideEffectFunctionPipe(FluentUtility.prepareFunction(this.asMap, sideEffectFunction)));
    }

    /**
     * Add a StorePipe to the end of the Pipeline.
     * Lazily store the incoming objects into the provided collection.
     *
     * @param storage the collection to store results into
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> store(final Collection<E> storage) {
        return this.add(new StorePipe<E>(storage));
    }

    /**
     * Add a StorePipe to the end of the Pipeline.
     * Lazily store the object returned by the function over the incoming object into the provided collection.
     *
     * @param storage         the collection to store results into
     * @param storageFunction the function to run over each object prior to insertion into the storage collection
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> store(final Collection storage, final PipeFunction<E, ?> storageFunction) {
        return this.add(new StorePipe<E>(storage, FluentUtility.prepareFunction(this.asMap, storageFunction)));
    }

    /**
     * Add an StorePipe to the end of the Pipeline.
     * An ArrayList storage collection is provided and filled lazily with incoming objects.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> store() {
        return this.store(new ArrayList<E>());
    }

    /**
     * Add a StorePipe to the end of the Pipeline.
     * An ArrayList storage collection is provided and filled lazily with the return of the function evaluated over the incoming objects.
     *
     * @param storageFunction the function to run over each object prior to insertion into the storage collection
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> store(final PipeFunction<E, ?> storageFunction) {
        return this.store(new ArrayList(), FluentUtility.prepareFunction(this.asMap, storageFunction));
    }

    /**
     * Add a TablePipe to the end of the Pipeline.
     * This step is used for grabbing previously seen objects the pipeline as identified by as-steps.
     *
     * @param table           the table to fill
     * @param stepNames       the partition steps to include in the filling
     * @param columnFunctions the post-processing function for each column
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> table(final Table table, final Collection<String> stepNames, final PipeFunction... columnFunctions) {
        return this.add(new TablePipe(table, stepNames, FluentUtility.getAsPipes(this), FluentUtility.prepareFunctions(this.asMap, columnFunctions)));
    }

    /**
     * Add a TablePipe to the end of the Pipeline.
     * This step is used for grabbing previously seen objects the pipeline as identified by as-steps.
     *
     * @param table           the table to fill
     * @param columnFunctions the post-processing function for each column
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> table(final Table table, final PipeFunction... columnFunctions) {
        return this.add(new TablePipe(table, null, FluentUtility.getAsPipes(this), FluentUtility.prepareFunctions(this.asMap, columnFunctions)));
    }

    /**
     * Add a TablePipe to the end of the Pipeline.
     * This step is used for grabbing previously seen objects the pipeline as identified by as-steps.
     *
     * @param columnFunctions the post-processing function for each column
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> table(final PipeFunction... columnFunctions) {
        return this.add(new TablePipe(new Table(), null, FluentUtility.getAsPipes(this), FluentUtility.prepareFunctions(this.asMap, columnFunctions)));
    }

    /**
     * Add a TablePipe to the end of the Pipeline.
     * This step is used for grabbing previously seen objects the pipeline as identified by as-steps.
     *
     * @param table the table to fill
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> table(final Table table) {
        return this.add(new TablePipe(table, null, FluentUtility.getAsPipes(this)));
    }

    /**
     * Add a TablePipe to the end of the Pipeline.
     * This step is used for grabbing previously seen objects the pipeline as identified by as-steps.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> table() {
        return this.add(new TablePipe(new Table(), null, FluentUtility.getAsPipes(this)));
    }

    /**
     * Add a TreePipe to the end of the Pipeline
     * This step maintains an internal tree representation of the paths that have flowed through the step.
     *
     * @param tree            an embedded Map data structure to store the tree representation in
     * @param branchFunctions functions to apply to each path object in a round robin fashion
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> tree(final Tree tree, final PipeFunction... branchFunctions) {
        return this.add(new TreePipe<E>(tree, FluentUtility.prepareFunctions(this.asMap, branchFunctions)));
    }

    /**
     * Add a TreePipe to the end of the Pipeline
     * This step maintains an internal tree representation of the paths that have flowed through the step.
     *
     * @param branchFunctions functions to apply to each path object in a round robin fashion
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> tree(final PipeFunction... branchFunctions) {
        return this.add(new TreePipe<E>(FluentUtility.prepareFunctions(this.asMap, branchFunctions)));
    }

    ///////////////////////
    /// TRANSFORM PIPES ///
    ///////////////////////

    /**
     * Add a GatherPipe to the end of the Pipeline.
     * All the objects previous to this step are aggregated in a greedy fashion and emitted as a List.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, List> gather() {
        return this.add(new GatherPipe());
    }

    /**
     * Add a GatherPipe to the end of the Pipeline.
     * All the objects previous to this step are aggregated in a greedy fashion into a List.
     * The provided function is applied to the aggregate and the results of the function are emitted.
     * Typically, the output of the function is a pruned List.
     * This is good for selective breadth-first searching.
     *
     * @param function a transformation to apply to the gathered list
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ?> gather(PipeFunction<List, ?> function) {
        return this.add(new GatherFunctionPipe(FluentUtility.prepareFunction(this.asMap, function)));
    }

    /**
     * Add an IdentityPipe to the end of the Pipeline.
     * Useful in various situations where a step is needed without processing.
     * For example, useful when two as-steps are needed in a row.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> _() {
        return this.add(new IdentityPipe());
    }

    /**
     * Add a MemoizePipe to the end of the Pipeline.
     * This step will hold a Map of the objects that have entered into its pipeline section.
     * If an input is seen twice, then the map stored output is emitted instead of recomputing the pipeline section.
     *
     * @param namedStep the name of the step previous to memoize to
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> memoize(final String namedStep) {
        return this.add(new MemoizePipe(new Pipeline(FluentUtility.removePreviousPipes(this, namedStep))));
    }

    /**
     * Add a MemoizePipe to the end of the Pipeline.
     * This step will hold a Map of the objects that have entered into its pipeline section.
     * If an input is seen twice, then the map stored output is emitted instead of recomputing the pipeline section.
     *
     * @param numberedStep the number of the step previous to memoize to
     * @return the extended Pipeline
     */
    @Deprecated
    public GremlinPipeline<S, E> memoize(final int numberedStep) {
        return this.add(new MemoizePipe(new Pipeline(FluentUtility.removePreviousPipes(this, numberedStep))));
    }

    /**
     * Add a MemoizePipe to the end of the Pipeline.
     * This step will hold a Map of the objects that have entered into its pipeline section.
     * If an input is seen twice, then the map stored output is emitted instead of recomputing the pipeline section.
     *
     * @param namedStep the name of the step previous to memoize to
     * @param map       the memoization map
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> memoize(final String namedStep, final Map map) {
        return this.add(new MemoizePipe(new Pipeline(FluentUtility.removePreviousPipes(this, namedStep)), map));
    }

    /**
     * Add a MemoizePipe to the end of the Pipeline.
     * This step will hold a Map of the objects that have entered into its pipeline section.
     * If an input is seen twice, then the map stored output is emitted instead of recomputing the pipeline section.
     *
     * @param numberedStep the number of the step previous to memoize to
     * @param map          the memoization map
     * @return the extended Pipeline
     */
    @Deprecated
    public GremlinPipeline<S, E> memoize(final int numberedStep, final Map map) {
        return this.add(new MemoizePipe(new Pipeline(FluentUtility.removePreviousPipes(this, numberedStep)), map));
    }

    /**
     * Add an OrderPipe to the end of the Pipeline.
     * This step will sort the objects in the stream in a default Comparable order.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> order() {
        return this.add(new OrderPipe());
    }

    /**
     * Add an OrderPipe to the end of the Pipeline.
     * This step will sort the objects in the stream in a default Comparable order.
     *
     * @param order if the stream is composed of comparable objects, then increment or decrement can be specified
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> order(TransformPipe.Order order) {
        return this.add(new OrderPipe(order));
    }

    /**
     * Add an OrderPipe to the end of the Pipeline.
     * This step will sort the objects in the stream in a default Comparable order.
     *
     * @param order if the stream is composed of comparable objects, then increment or decrement can be specified
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> order(Tokens.T order) {
        return this.add(new OrderPipe(Tokens.mapOrder(order)));
    }

    /**
     * Add an OrderPipe to the end of the Pipeline.
     * This step will sort the objects in the stream according to a comparator defined in the provided function.
     *
     * @param compareFunction a comparator function of two objects of type E
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> order(final PipeFunction<Pair<E, E>, Integer> compareFunction) {
        return this.add(new OrderPipe(FluentUtility.prepareFunction(this.asMap, compareFunction)));
    }

    /**
     * Add a PathPipe to the end of the Pipeline.
     * This will emit the path that has been seen thus far.
     * If path functions are provided, then they are evaluated in a round robin fashion on the objects of the path.
     *
     * @param pathFunctions the path function of the PathPipe
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, List> path(final PipeFunction... pathFunctions) {
        return this.add(new PathPipe<Object>(FluentUtility.prepareFunctions(this.asMap, pathFunctions)));
    }

    /**
     * Add a ScatterPipe to the end of the Pipeline.
     * Any inputted iterator or iterable is unrolled and the iterator/iterable's objects are emitted one at a time.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ?> scatter() {
        return this.add(new ScatterPipe());
    }

    /**
     * Add a SelectPipe to the end of the Pipeline.
     * The objects of the named steps (via as) previous in the pipeline are emitted as a Row object.
     * A Row object extends ArrayList and simply provides named columns and some helper methods.
     * If column functions are provided, then they are evaluated in a round robin fashion on the objects of the Row.
     *
     * @param stepNames       the name of the steps in the expression to retrieve the objects from
     * @param columnFunctions the functions to apply to the column objects prior to filling the Row
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Row> select(final Collection<String> stepNames, final PipeFunction... columnFunctions) {
        return this.add(new SelectPipe(stepNames, FluentUtility.getAsPipes(this), FluentUtility.prepareFunctions(this.asMap, columnFunctions)));
    }

    /**
     * Add a SelectPipe to the end of the Pipeline.
     * The objects of the named steps (via as) previous in the pipeline are emitted as a Row object.
     * A Row object extends ArrayList and simply provides named columns and some helper methods.
     * If column functions are provided, then they are evaluated in a round robin fashion on the objects of the Row.
     *
     * @param columnFunctions the functions to apply to the column objects prior to filling the Row
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Row> select(final PipeFunction... columnFunctions) {
        return this.add(new SelectPipe(null, FluentUtility.getAsPipes(this), FluentUtility.prepareFunctions(this.asMap, columnFunctions)));
    }

    /**
     * Add a SelectPipe to the end of the Pipeline.
     * The objects of the named steps (via as) previous in the pipeline are emitted as a Row object.
     * A Row object extends ArrayList and simply provides named columns and some helper methods.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, Row> select() {
        return this.add(new SelectPipe(null, FluentUtility.getAsPipes(this)));
    }

    /**
     * Add a ShufflePipe to the end of the Pipeline.
     * All the objects previous to this step are aggregated in a greedy fashion, their order randomized and emitted
     * as a List.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, List> shuffle() {
        return this.add(new ShufflePipe());
    }

    /**
     * Add a SideEffectCapPipe to the end of the Pipeline.
     * When the previous step in the pipeline is implements SideEffectPipe, then it has a method called getSideEffect().
     * The cap step will greedily iterate the pipeline and then, when its empty, emit the side effect of the previous pipe.
     *
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ?> cap() {
        return this.add(new SideEffectCapPipe((SideEffectPipe) FluentUtility.removePreviousPipes(this, 1).get(0)));

    }

    /**
     * Add a OrderMapPipe to the end of the Pipeline
     * Given a Map as an input, the map is first ordered and then the keys are emitted in the order.
     *
     * @param order if the values implement Comparable, then a increment or decrement sort is usable
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ?> orderMap(final TransformPipe.Order order) {
        return this.add(new OrderMapPipe<Object>(order));
    }

    /**
     * Add a OrderMapPipe to the end of the Pipeline
     * Given a Map as an input, the map is first ordered and then the keys are emitted in the order.
     *
     * @param order if the values implement Comparable, then a increment or decrement sort is usable
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ?> orderMap(final Tokens.T order) {
        return this.orderMap(Tokens.mapOrder(order));
    }

    /**
     * Add a OrderMapPipe to the end of the Pipeline
     * Given a Map as an input, the map is first ordered and then the keys are emitted in the order.
     *
     * @param compareFunction a function to compare to map entries
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, ?> orderMap(final PipeFunction<Pair<Map.Entry, Map.Entry>, Integer> compareFunction) {
        return this.add(new OrderMapPipe(FluentUtility.prepareFunction(this.asMap, compareFunction)));
    }

    /**
     * Add a TransformFunctionPipe to the end of the Pipeline.
     * Given an input, the provided function is computed on the input and the output of that function is emitted.
     *
     * @param function the transformation function of the pipe
     * @return the extended Pipeline
     */
    public <T> GremlinPipeline<S, T> transform(final PipeFunction<E, T> function) {
        return this.add(new TransformFunctionPipe(FluentUtility.prepareFunction(this.asMap, function)));
    }

    //////////////////////
    /// UTILITY PIPES ///
    //////////////////////

    /**
     * Wrap the previous step in an AsPipe.
     * Useful for naming steps and is used in conjunction with various other steps including: loop, select, back, table, etc.
     *
     * @param name the name of the AsPipe
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, E> as(final String name) {
        final GremlinPipeline<S, E> pipeline = this.add(new AsPipe(name, FluentUtility.removePreviousPipes(this, 1).get(0)));
        this.asMap.refresh();
        return pipeline;
    }

    /**
     * Add a StartPipe to the end of the pipeline.
     * Though, in practice, a StartPipe is usually the beginning.
     * Moreover, the constructor of the Pipeline will internally use StartPipe.
     *
     * @param object the object that serves as the start of the pipeline (iterator/iterable are unfolded)
     * @return the extended Pipeline
     */
    public GremlinPipeline<S, S> start(final S object) {
        this.add(new StartPipe<S>(object));
        FluentUtility.setStarts(this, object);
        return (GremlinPipeline<S, S>) this;
    }

    ///////////////////////
    /// UTILITY METHODS ///
    ///////////////////////

    /**
     * Return the number of objects iterated through the pipeline.
     *
     * @return the number of objects iterated
     */
    public long count() {
        return PipeHelper.counter(this);
    }

    /**
     * Completely drain the pipeline of its objects.
     * Useful when a sideEffect of the pipeline is desired.
     */
    public void iterate() {
        PipeHelper.iterate(this);
    }

    /**
     * Return the next X objects in the pipeline as a list.
     *
     * @param number the number of objects to return
     * @return a list of X objects (if X objects occur)
     */
    public List<E> next(final int number) {
        final List<E> list = new ArrayList<E>(number);
        PipeHelper.fillCollection(this, list, number);
        return list;
    }

    /**
     * Return a list of all the objects in the pipeline.
     *
     * @return a list of all the objects
     */
    public List<E> toList() {
        final List<E> list = new ArrayList<E>();
        PipeHelper.fillCollection(this, list);
        return list;
    }

    /**
     * Fill the provided collection with the objects in the pipeline.
     *
     * @param collection the collection to fill
     * @return the collection filled
     */
    public Collection<E> fill(final Collection<E> collection) {
        PipeHelper.fillCollection(this, collection);
        return collection;
    }

    /**
     * Enable path calculations within the Pipeline.
     * This is typically done automatically and in rare occasions needs to be called.
     *
     * @return the Pipeline with path calculations enabled
     */
    public GremlinPipeline<S, E> enablePath() {
        this.enablePath(true);
        return this;
    }

    /**
     * When possible, Gremlin takes advantage of certain sequences of pipes in order to make a more concise, and generally more efficient expression.
     * This method will turn on and off query optimization from this stage in the pipeline on.
     *
     * @param optimize whether to optimize the pipeline from here on or not
     * @return The GremlinPipeline with the optimization turned off
     */
    public GremlinPipeline<S, E> optimize(final boolean optimize) {
        this.doQueryOptimization = optimize;
        return this;
    }

    /**
     * Remove every element at the end of this Pipeline.
     */
    @Override
    public void remove() {
        for (final Object object : this) {
            ((Element) object).remove();
        }
    }

    /**
     * Returns the current pipeline with a new end type.
     * Useful if the end type of the pipeline cannot be implicitly derived.
     *
     * @return returns the current pipeline with the new end type.
     */
    @Override
    public <E> GremlinPipeline<S, E> cast(Class<E> end) {
        return (GremlinPipeline<S, E>) this;
    }
}
TOP

Related Classes of com.tinkerpop.gremlin.java.GremlinPipeline

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.