Skip to content

Commit b1e7276

Browse files
committed
Pipes 2.5.0 release.
1 parent ea9a72b commit b1e7276

13 files changed

+807
-5
lines changed

CHANGELOG.textile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ h2. Pipes 2.y.z
55

66
!https://github.com/tinkerpop/pipes/raw/master/doc/images/pipes-2.png!
77

8-
h3. Version 2.5.0 (NOT OFFICIALLY RELEASED YET)
8+
h3. Version 2.5.0 (April 14, 2014)
99

1010
```xml
1111
<dependency>
1212
<groupId>com.tinkerpop</groupId>
1313
<artifactId>pipes</artifactId>
14-
<version>2.5.0-SNAPSHOT</version>
14+
<version>2.5.0</version>
1515
</dependency>
1616
```
1717

doc/Acknowledgments.textile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<img width="100" src="https://github.com/tinkerpop/pipes/raw/master/doc/images/pipes-character-2.png"/>
2+
3+
This section provides a list of the people that have contributed in some way to the creation of Pipes.
4+
5+
# "Marko A. Rodriguez":http://markorodriguez.com -- designed, developed, tested, and documented Pipes.
6+
# "Darrick Weibe":http://github.com/pangloss -- implemented Pipe paths.
7+
# "Zach Cox":http://theza.ch - helped develop the fluent model.
8+
# "Ketrina Yim":http://www.ketrinayim.com/ -- designed the Pipes logo.
9+
10+
Please review Pipes' "pom.xml":http://github.com/tinkerpop/pipes/blob/master/pom.xml. Pipes would not be possible without the work done by others to create these useful packages.

doc/Basic-Pipes.textile

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
!https://github.com/tinkerpop/pipes/raw/master/doc/images/pipes-plumber.png!
2+
3+
A @Pipe<S,E>@ is a Java interface that extends both the @Iterable<E>@ and @Iterator<E>@ interface. A @Pipe<S,E>@ takes, as input, an iterator or iterable yielding objects of type @S@ and produces/emits objects of type @E@. The character "S" stands for "starts" and the character "E" stands for "ends".
4+
5+
Here is a simple example demonstrating a single pipe that capitalizes the characters of the strings that come into it.
6+
7+
```java
8+
Pipe<String,String> capsPipe = new CapitalizePipe();
9+
capsPipe.setStarts(Arrays.asList("this", "is", "the", "end."));
10+
while(capsPipe.hasNext()) {
11+
System.out.print(capPipe.next() + " ");
12+
}
13+
```
14+
15+
This pipe will produce the following output.
16+
17+
bc. THIS IS THE END.
18+
19+
Given that @Pipe<S,E>@ extends @Iterator<E>@ and @Iterable<E>@, its possible to string together pipes to create a processing line.
20+
21+
```java
22+
Pipe<String,String> capsPipe = new CapitalizePipe();
23+
Pipe<String,Integer> countPipe = new CountPipe();
24+
capsPipe.setStarts(Arrays.asList("this", "is", "the", "end."));
25+
countPipe.setStarts(capsPipe);
26+
while(countPipe.hasNext()) {
27+
System.out.print(countPipe.next() + " ");
28+
}
29+
```
30+
31+
If @CountPipe@ takes a @String@ and emits the number of characters in that @String@, then the previous code will yield the following output.
32+
33+
bc. 4 2 3 4
34+
35+
Realize that the output of one pipe must be of the same type as the input of the next pipe. Given that @Pipe<S,E>@ extends @Iterator<E>@, The @E@ of the first pipe becomes the @S@ of the second pipe. In order to make it easier to create chains of pipes, there is a handy @Pipeline<S,E>@ class. This class implements @Pipe<S,E>@ and thus, @Pipeline<S,E>@ objects can be combined like any other pipe (i.e. you can create pipelines of pipelines). Here is an example using a @Pipeline<S,E>@ object.
36+
37+
```java
38+
Pipe<String,String> capsPipe = new CapitalizePipe();
39+
Pipe<String,Integer> countPipe = new CountPipe();
40+
Pipe<Integer,String> wordPipe = new WordPipe();
41+
Pipeline<String,String> pipeline = new Pipeline<String,String>(capsPipe, countPipe, wordPipe);
42+
pipeline.setStarts(Arrays.asList("this", "is", "the", "end."));
43+
while(pipeline.hasNext()) {
44+
System.out.print(pipeline.next() + " ");
45+
}
46+
```
47+
48+
Assuming that @WordPipe@ emits the word version of an incoming integer, the pipeline will produce the following output.
49+
50+
bc. four two three four
51+
52+
That's Pipes in a nutshell.

doc/Creating-a-Pipe.textile

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
[[https://github.com/tinkerpop/pipes/raw/master/doc/images/plumber-3.png]]
2+
3+
The pipes that come prepackaged with Pipes are not sufficient for every use case. In many situations, it will be necessary to create a pipe that will solve a particular mapping from an input to an output. An easy way to accomplish this is via the respective @XXXFunctionPipe@ and @PipeFunction@ implementation. However, it is possible to write a specific pipe without using @XXXFunctionPipe@-based pipes.
4+
5+
h2. Transform-Based Pipe with AbstractPipe
6+
7+
Pipes make it easy to create new pipes with the @AbstractPipe@ class. In @AbstractPipe@, there is a single method that needs to be implemented: @AbstractPipe.processNextStart()@. The example below demonstrates the internals of a simple pipe that maps a string to the number of characters contained in that string.
8+
9+
```java
10+
public class WordLengthPipe extends AbstractPipe<String, Integer> implements TransformPipe<String,Integer> {
11+
public Integer processNextStart() {
12+
String start = this.starts.next();
13+
return start.length();
14+
}
15+
}
16+
```
17+
18+
h2. Filter-Based Pipe with AbstractPipe
19+
20+
The general pattern for filter-based pipes is explained with an example.
21+
22+
```java
23+
public class WordFilterPipe extends AbstractPipe<String,String> implements FilterPipe<String> {
24+
public String processNextStarts() {
25+
while(true) {
26+
String start = this.starts.next();
27+
if(start.length() > 4)
28+
return start;
29+
}
30+
}
31+
}
32+
```
33+
34+
A @FilterPipe@ will usually make use of a @while(true)@ loop that will continually pull in new objects. If the current object meets some criteria, it will be emitted, else, the @while(true)@ will loop again and pull in a new object. There is no need to worry about handling @NoSuchElementExceptions@. If @this.starts.next()@ throws a @NoSuchElementException@ then this will be handled appropriately by @AbstractPipe@.
35+
36+
h2. SideEffect-Based Pipe with AbstractPipe
37+
38+
Below is an example of a side-effect based pipe using @AbstractPipe@.
39+
40+
```java
41+
public class WordPrintPipe extends AbstractPipe<String,String> implements SideEffectPipe<String,Object> {
42+
public String processNextStarts() {
43+
String start = this.starts.next();
44+
System.out.println(start);
45+
return start;
46+
}
47+
48+
public Object getSideEffect() {
49+
return null;
50+
}
51+
}
52+
```
53+
54+
Given that the side-effect is simply a @println@ to the @System.out@, there is no @Object@ side-effect that can be later retrieved. As such, the @SideEffectPipe.getSideEffect()@ method simply returns @null@.

doc/Filter-Pipes.textile

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
!https://github.com/tinkerpop/pipes/raw/master/doc/images/duck-plumber.jpg!
2+
3+
A common Pipes pattern is the filtering of objects. A filter-based pipe will consume objects and either emit them or not. If they are not emitted, then they are considered filtered by the pipe. A useful interface to implement that describes this behavior is @FilterPipe<S> extends Pipe<S,S>@.
4+
5+
h2. Generic Filter Pipe
6+
7+
The generic filter pipe is @FilterFunctionPipe@. A @FilterFunctionPipe@ takes a @PipeFunction@ (see [[Pipe Types]]) that computes on @S@ and either emits it or not. An example @PipeFunction@ is provided below:
8+
9+
```java
10+
public class CharCountPipeFunction implements PipeFunction<String,Boolean> {
11+
12+
private final int number;
13+
14+
public CharCountPipeFunction(int number) {
15+
this.number = number;
16+
}
17+
18+
public Boolean compute(String argument) {
19+
return argument.length() == this.number;
20+
}
21+
}
22+
```
23+
24+
When put in the context of a @FilterFunctionPipe@, the code looks as follows:
25+
26+
```java
27+
Pipe<String, String> pipe = new FilterFunctionPipe<String>(new CharCountPipeFunction(4));
28+
pipe.setStarts(Arrays.asList("tell", "me", "your", "name"));
29+
// the results of the iteration are: "tell", "your", "name"
30+
```
31+
32+
h2. Basic Filtering
33+
34+
The @RandomFilterPipe@ comes with the Pipes distribution. @RandomFilterPipe@ will only allow a consumed object to be emitted if a biased coin toss lands on "heads." At the extremes, if @bias@ is 0.0 then no incoming objects are emitted and if @bias@ is 1.0, then every incoming object is emitted.
35+
36+
```java
37+
public class RandomFilterPipe<S> extends AbstractPipe<S, S> implements FilterPipe<S> {
38+
private static final Random RANDOM = new Random();
39+
private final double bias;
40+
public SampleFilterPipe(final double bias) {
41+
this.bias = bias;
42+
}
43+
protected S processNextStart() {
44+
while (this.starts.hasNext()) {
45+
S s = this.starts.next();
46+
if (bias >= RANDOM.nextDouble()) {
47+
return s;
48+
}
49+
}
50+
throw new NoSuchElementException();
51+
}
52+
}
53+
```
54+
55+
The @processNextStart()@ method structure above is a common pattern in filter-based pipes. In short, a @while(this.starts.hasNext())@ is evaluated until an incoming start object meets the criteria and is returned (i.e. emitted). If there are no more starts and the criteria is not met, then a @NoSuchElementException@ is thrown. Beware of using recursion to accomplish a similar behavior. As the amount of data grows that is streaming through, recursion-based methods can easily yield a @StackOverflowError@.
56+
57+
h2. Comparison-Based Filtering
58+
59+
More complicated filters can be created than what was presented above. These filters usually check objects to determine whether to emit them or not. There is an interface called @ComparisonFilterPipe<S,T>@. Implementations of this interface consume objects of type @S@. If a check of that object passes (i.e. return @true@), then the @S@ object is emitted, else it is filtered. @ComparisonFilterPipe@ has the following signature:
60+
61+
```java
62+
public interface ComparisonFilterPipe<S, T> extends FilterPipe<S> {
63+
public enum Filter {
64+
EQUAL, NOT_EQUAL, GREATER_THAN, LESS_THAN, GREATER_THAN_EQUAL, LESS_THAN_EQUAL
65+
}
66+
public boolean compareObjects(T leftObject, T rightObject);
67+
}
68+
```
69+
70+
The important method is @compareObjects()@. This method returns @true@ if the left hand object is @EQUAL@, @NOT_EQUAL@, etc. to the right hand object.
71+
72+
Next, there is an abstract class called @AbstractComparisonFilterPipe<S,T>@ that implements @ComparisonFilterPipe@. More specifically, it provides a standard implementation of @ComparisonFilterPipe.compareObjects()@. For most situations, this method is sufficient. However, if not, simply override the method with an implementation that meets the requirements of the designed pipe.
73+
74+
```java
75+
public boolean compareObjects(final T leftObject, final T rightObject) {
76+
switch (this.filter) {
77+
case EQUAL:
78+
if (null == leftObject)
79+
return rightObject == null;
80+
return leftObject.equals(rightObject);
81+
case NOT_EQUAL:
82+
if (null == leftObject)
83+
return rightObject != null;
84+
return !leftObject.equals(rightObject);
85+
case GREATER_THAN:
86+
if (null == leftObject || rightObject == null)
87+
return false;
88+
return ((Comparable) leftObject).compareTo(rightObject) == 1;
89+
case LESS_THAN:
90+
if (null == leftObject || rightObject == null)
91+
return false;
92+
return ((Comparable) leftObject).compareTo(rightObject) == -1;
93+
case GREATER_THAN_EQUAL:
94+
if (null == leftObject || rightObject == null)
95+
return false;
96+
return ((Comparable) leftObject).compareTo(rightObject) >= 0;
97+
case LESS_THAN_EQUAL:
98+
if (null == leftObject || rightObject == null)
99+
return false;
100+
return ((Comparable) leftObject).compareTo(rightObject) <= 0;
101+
default:
102+
throw new RuntimeException("Invalid state as no valid filter was provided");
103+
}
104+
}
105+
```
106+
107+
Note: it many situations, such as @ObjectFilterPipe@, the right hand object to allow or disallow is stored in the pipe and compared with each object passed through it.
108+
109+
!http://github.com/tinkerpop/pipes/raw/master/doc/images/filter-example.png!

doc/Home.textile

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
[[https://github.com/tinkerpop/pipes/raw/master/doc/images/pipes-logo.png|height=149px]]
2+
3+
Pipes is a "dataflow":http://en.wikipedia.org/wiki/Dataflow_programming framework using "process graphs":http://en.wikipedia.org/wiki/Kahn_process_networks. A process graph is composed of @Pipe@ vertices connected by communication edges. A @Pipe@ implements a simple computational step that can be composed with other @Pipe@ objects to create a larger computation. Such data flow graphs allow for the splitting, merging, looping, and in general, the transformation of data from input to output.
4+
5+
There are numerous Pipe classes that come with the main Pipes distribution. Once a good understanding of each Pipe accomplished, then using the framework is straightforward. In general, the best way to learn about all the Pipes provided is through the project JavaDoc.
6+
7+
Please join the Gremlin users group at "http://groups.google.com/group/gremlin-users":http://groups.google.com/group/gremlin-users for all "TinkerPop":http://tinkerpop.com related discussions.
8+
9+
Pipes JavaDoc: "2.5.0":http://tinkerpop.com/docs/javadocs/pipes/2.5.0/ - "2.4.0":http://tinkerpop.com/docs/javadocs/pipes/2.4.0/ - "2.3.0":http://tinkerpop.com/docs/javadocs/pipes/2.3.0/ - "2.2.0":http://tinkerpop.com/docs/javadocs/pipes/2.2.0/ - "2.1.0":http://tinkerpop.com/docs/javadocs/pipes/2.1.0/ - "2.0.0":http://tinkerpop.com/docs/javadocs/pipes/2.0.0/ - "1.0":http://tinkerpop.com/maven2/com/tinkerpop/pipes/1.0/api/ - "0.9":http://tinkerpop.com/maven2/com/tinkerpop/pipes/0.9/api/ - "0.8":http://tinkerpop.com/maven2/com/tinkerpop/pipes/0.8/api/ - "0.7":http://tinkerpop.com/maven2/com/tinkerpop/pipes/0.7/api/ - "0.6":http://tinkerpop.com/maven2/com/tinkerpop/pipes/0.6/api/ - "0.5":http://tinkerpop.com/maven2/com/tinkerpop/pipes/0.5/api/ - "0.4":http://tinkerpop.com/maven2/com/tinkerpop/pipes/0.4/api/ - "0.3":http://tinkerpop.com/maven2/com/tinkerpop/pipes/0.3/api/ - "0.2":http://tinkerpop.com/maven2/com/tinkerpop/pipes/0.2/api/ - "0.1":http://tinkerpop.com/maven2/com/tinkerpop/pipes/0.1/api/
10+
Pipes WikiDocs: "2.5.0":http://tinkerpop.com/docs/wikidocs/pipes/2.5.0/Home.html - "2.4.0":http://tinkerpop.com/docs/wikidocs/pipes/2.4.0/Home.html - "2.3.0":http://tinkerpop.com/docs/wikidocs/pipes/2.3.0/Home.html - "2.2.0":http://tinkerpop.com/docs/wikidocs/pipes/2.2.0/Home.html - "2.1.0":http://tinkerpop.com/docs/wikidocs/pipes/2.1.0/Home.html - "2.0.0":http://tinkerpop.com/docs/wikidocs/pipes/2.0.0/Home.html
11+
12+
```xml
13+
<dependency>
14+
<groupId>com.tinkerpop</groupId>
15+
<artifactId>pipes</artifactId>
16+
<version>2.5.0</version>
17+
</dependency>
18+
```
19+
20+
Non-Maven users can get the raw release jars from Apache's "Central Repository":http://maven.org. Snapshots can be obtained from "Sonatype":https://oss.sonatype.org/content/repositories/snapshots/ (see "Maven Repositories":https://github.com/tinkerpop/homepage/wiki/Maven-Repositories for more information).
21+
22+
```java
23+
Pipe<String,Integer> pipeline = new Pipeline<String,Integer>(
24+
new RemoveCharacterPipe("o"),
25+
new CountCharactersPipe());
26+
pipeline.setStarts(Arrays.asList("marko","josh","peter"));
27+
for(Integer number : pipeline) {
28+
System.out.println(number);
29+
}
30+
31+
4
32+
3
33+
5
34+
```
35+
36+
"On the Nature of Pipes":http://markorodriguez.com/2011/08/03/on-the-nature-of-pipes/ (*Graphical Presentation of the Pipe Mechanics*)
37+
38+
==<hr/>==
39+
40+
* Basic Concepts
41+
** [[Basic Pipes]]
42+
** [[Pipe Types]]
43+
*** [[Transform Pipes]]
44+
*** [[Filter Pipes]]
45+
*** [[SideEffect Pipes]]
46+
** Pipe SubTypes
47+
*** [[MetaPipes]]
48+
*** Branch Pipes
49+
* Advanced Concepts
50+
** [[Creating a Pipe]]
51+
** [[Transformation Paths]]
52+
* Conclusion
53+
** [[Acknowledgments]]
54+
** [[Release Notes]]
55+
56+
==<hr/>==
57+
58+
fn1. Pipes documentation is up to date with the current Pipes "codebase":http://github.com/tinkerpop/pipes/tree/master, not with the latest Pipes release.

doc/MetaPipes.textile

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
!https://github.com/tinkerpop/pipes/raw/master/doc/images/plumber-3.png!
2+
3+
A metapipe is a pipe that "wraps" another pipe. MetaPipes are useful in that they can make decisions based upon the behavior of the pipe(s) they wrap. There are numerous metapipes and this section will discuss a few of the more interesting cases.
4+
5+
h2. Basic Pattern
6+
7+
In many situations, the basic pattern used when creating a metapipe is to have the constructor of the metapipe take @n@ number of pipes. Then for each incoming @S@ object to the metapipe, it pushes that @S@ object to its @n@ pipes and makes a emit decision based upon how the pipes behave. In many cases, you can think of a metapipe as an "overseer" of its internal pipes. This idea is presented in the typical code pattern found in metapipes.
8+
9+
```java
10+
public E processNextStart() {
11+
S s = this.starts.next();
12+
for (Pipe<S, E> pipe : this.pipes) {
13+
pipe.setStarts(new SingleIterator<S>(s));
14+
E e = pipe.next();
15+
// decide about what to do given the output of the internal pipe
16+
}
17+
// if all pipes behave as desired, perhaps do:
18+
return s.getE();
19+
}
20+
```
21+
22+
@SingleIterator<S>@ is a handy class that wraps an object for a single legal @next()@. This class is much more efficient than doing @Arrays.asList(s).iterator()@.
23+
24+
h2. Pipelines
25+
26+
!https://github.com/tinkerpop/pipes/raw/master/doc/images/pipeline-example.png!
27+
28+
A @Pipeline@ is a commonly used pipe. A @Pipeline<S,E>@ implements @Pipe<S,E>@ and as such, a @Pipeline@ is simply a @Pipe@. A @Pipeline@ takes an ordered list of pipes in its constructor. It connects these pipes, whereby the input of pipe @n@ is connected to the output of pipe @n-1@. Note that the output type of pipe @n-1@ must be the same type as the input to pipe @n@. This idea is elucidated in the diagram above.
29+
30+
The benefit of using a @Pipeline@ is that is greatly reduces the complexity of a process and allows for easy reuse. It is analogous, in many ways, to creating a function to wrap a body of code/instructions. As such, the function serves as a blackbox with input and output.
31+
32+
h2. And/Or Pipes
33+
34+
!https://github.com/tinkerpop/pipes/raw/master/doc/images/andfilterpipe-example.png!
35+
36+
There are two logical @FilterPipe@ pipes called @AndFilterPipe<S>@ and @OrFilterPipe<S>@. These pipes take an object of type @S@ and emit an object of type @S@. However, they only emit the @S@ object if the collection of @Pipe<S,Boolean>@ pipes that they wrap return @true@. For @AndFilterPipe@ to emit its incoming @S@ object, all of its wrapped pipes must return @true@ that @S@ object. For the @OrFilterPipe@, only one of its wrapped pipes must return @true@. If you want to see if a number is greater than or equal to 1 *and* less than 10, then use the following @AndFilterPipe@.
37+
38+
```java
39+
Pipe<Integer,Integer> pipeA = new ObjectFilterPipe<Integer>(1, ComparisonFilterPipe.GREATER_THAN_EQUAL);
40+
Pipe<Integer,Integer> pipeB = new ObjectFilterPipe<Integer>(10, ComparisonFilterPipe.LESS_THAN);
41+
Pipe<Integer,Integer> pipe1 = new AndFilterPipe<Integer>(new HasNextPipe<Integer>(pipeA), new HasNextPipe<Integer>(pipeB));
42+
pipe1.setStarts(Arrays.asList(1,22,10,136,7,2,67));
43+
while(pipe1.hasNext()) {
44+
System.out.print(pipe1.next() + "...");
45+
}
46+
```
47+
48+
The @System.out@ of the previous code is as follows.
49+
50+
bc. 1...7...2...
51+
52+
Note the use of @HasNextPipe<S>@ which implements @Pipe<S,Boolean>@. If the @ObjectFilterPipe@ pipes that compose the @AndFilterPipe@ filter their incoming @S@ object, then they will return false on a call to @hasNext()@. Thus, @HasNextPipe@ is useful for determining if a filter pipe has filtered an object.

0 commit comments

Comments
 (0)