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;
}
}