!https://github.com/tinkerpop/gremlin/raw/master/doc/images/gremlin-kilt.png!

Many graph applications have to do with identifying patterns in the graph. That is, identifying sub-graphs that match a particular topology and/or feature set. In single-relational graphs, usually a topology is only considered. For graphs that are labeled and have key/value pairs on their elements (i.e. property graphs), then features on that topology must be considered as well to be generally useful.

h2. Table and Supporting Pipes

The @table@ step is useful for cataloging parts of the graph that match a particular pattern. The best way to explain its use is with due respect to the graph pattern match language "SPARQL":http://www.w3.org/TR/rdf-sparql-query/. An example SPARQL query is provided below. The query will return those vertices that Marko knows along with their respective creations

```text
SELECT ?x ?y WHERE {
  marko knows ?x
  ?x created ?y
}
```

SPARQL is an excellent, intuitive language for defining patterns in an "RDF":http://www.w3.org/RDF/ graph (i.e. multi-relational graph). Unfortunately, RDF (and thus, SPARQL) does not support key/value pairs on the elements of the graph--it does not support property graphs (save through complex and verbose reification constructs). As such, SPARQL does not easily map over to the property graph domain.

In Gremlin, the above query is accomplished using the @table@ step and respective @TablePipe@ and @Table@ object. First lets load our toy graph diagrammed in [[Defining a Property Graph]].

```text
gremlin> g = TinkerGraphFactory.createTinkerGraph()
==>tinkergraph[vertices:6 edges:6]
```

In the example below, a traversal is executed starting from marko (vertex 1). The @table@ step will select the 1st and 2nd parts of the paths that reach it an insert them into the @Table@ @t@ as rows.

```text
gremlin> t = new Table()    
gremlin> g.v(1).out('knows').as('x').out('created').as('y').table(t)
==>v[5]
==>v[3]
gremlin> t
==>[x:v[4], y:v[5]]
==>[x:v[4], y:v[3]]
```

As such, components of the traversals history (path) are saved into the table and serve as the desired return bindings. Given the SPARQL query from previous, the columns of @t@ are equivalent to @?x@ and @?y@.

To provide the developer flexibility, its possible to evaluate closures over column elements prior to inserting them into the table.

```
gremlin> t = new Table()                                                       
gremlin> g.v(1).out('knows').as('x').out('created').as('y').table(t){it.name}{it.name}
==>v[5]
==>v[3]
gremlin> t
==>[x:josh, y:ripple]
==>[x:josh, y:lop]
```

In the above Gremlin snippets, we found all the people that Marko knows who have created a product. Specifically, Marko knows Josh and Josh has created both "Ripple":http://ripple.fortytwo.net/ and "LoP":http://xmpp.org/extensions/inbox/lop.html.

For those familiar with "Pipes":http://pipes.tinkerpop.com, @TablePipe@ is a @SideEffectPipe@ and as such, can be "capped." 

```text  
gremlin> g.v(1).out('knows').as('x').out('created').as('y').table(new Table()).cap.next()
==>[x:v[4], y:v[5]]
==>[x:v[4], y:v[3]]
```

h2. Recursive Pattern Matching

Its possible to use @table@ match any pattern within a pipeline expression. Thus, its possible to use @table@ and @loop@ within the same expression and get meaningful results.

```text
gremlin> t = new Table()
gremlin> g.v(1).as('x').out.as('y').table(t).loop('x'){it.loops < 3}
==>v[5]
==>v[3]
gremlin> t
==>[x:v[1], y:v[2]]
==>[x:v[1], y:v[3]]
==>[x:v[1], y:v[4]]
==>[x:v[1], y:v[5]]
==>[x:v[1], y:v[3]]
gremlin> t.apply{it.age}{it.name}
==>[29, vadas]
==>[29, lop]
==>[29, josh]
==>[29, ripple]
==>[29, lop]
```

h2. Table Methods

There are numerous methods that are offered by @Table@. Here are some examples of its use:

```
gremlin> t = new Table()   
gremlin> g.v(1).out('knows').as('x').out('created').as('y').table(t){it.name}{it.name}
==>v[5]
==>v[3]
gremlin> t
==>[x:josh, y:ripple]
==>[x:josh, y:lop]
gremlin> t.get(0,0)
==>josh
gremlin> t.get(0,'x')
==>josh
gremlin> t.getRow(0)
==>josh
==>ripple
gremlin> t.getColumn(1)
==>ripple
==>lop
gremlin> t.getRow(1).getColumn('y')      
==>lop
gremlin> t.getRow(1).getColumn(1)  
==>lop
gremlin> t.sort()
==>[x:josh, y:lop]
==>[x:josh, y:ripple]
gremlin> t.sort{a,b -> b[1].length() <=> a[1].length()}
==>[x:josh, y:ripple]
==>[x:josh, y:lop]
```