An object-graph-wrapper for TinkerPop 3

Introduction

Peapod maps your TinkerPop 3 graph onto a rich Java domain model. Peapod is developed as an alternative to Frames in TinkerPop 2.

Disclaimer: version latest is still an experimental release using Apache Tinkerpop 3.1.1-incubating.

You start by specifying your graph structure through abstract classes, enriched with Peapod annotations. Peapod will then generate at compile-time all the necessary implementation classes, attempting to reduce boilerplate code for basic graph access and traversals.

Peapod favors convention over configuration and therefore is simpler to use than TinkerPop 2 Frames

Example:

@Vertex
public abstract class Person { // vertex example with abstract class
  public abstract String getName();
  public abstract List<Created> getCreated();
}

@Edge
public abstract class Created {
      public abstract Float getWeight();
  @In public abstract Software getSoftware();
}

@Vertex
public interface Software { // vertex example with interface
    public abstract String getName();
}

Starting from a FramedGraph instance, you can query and manipulate the TinkerPop 3 graph. All reads and writes are immediately propagated to the wrapped TinkerPop 3 elements.

Graph g = TinkerGraph.open();

FramedGraph graph = new FramedGraph(g,  Package.getPackage("com.company.application.model"));

Person person = graph.addVertex(1, Person.class);
person.setName("alice");

List<Person> result = graph.V(Person.class).has("name", "alice").toList();
assertEquals(1, result.size());

Peapod is designed to have minimal overhead because code generation happens at compile time using Java annotation processors and reflection is limited to the absolute minimum only at startup time. The generated source code is transparent can be easily debugged.

Traversals in Peapod latest are still basic. The future goal is to have more advanced traversals using the DSL framework in TinkerPop 3. Because the graph structure is defined by the framing classes, Peapod could generate these complex traversal classes easily.

Framing

Define your domain model using annotated anonymous classes. Peapod will generate TinkerPop 3-compliant source code to traverse and manipulate your graph.

Vertices

Create an abstract class or interface and annotate with @peapod.annotations.Vertex.

@Vertex
public abstract class Person {}
@Vertex
public interface Person {}

The class simple name will act as vertex label. You can alter this by setting the annotation value.

@Vertex("individual")
public abstract class Person {}

Next, define a list of vertex properties using abstract JavaBean setter and getter methods.

@Vertex
public abstract class Person {
  public abstract String getName();
  public abstract void setName(String name);
}

The JavaBean property name will be used as vertex property name. The property name can be altered by adding the @peapod.annotations.Property annotation to any of the JavaBean methods.

@Vertex
public abstract class Person {
  @Property("name")
  public abstract String getLastName();
  public abstract void setLastName(String lastName);
}

For multi-properties you can also use adder and remover methods. Peapod recognizes singular and pluralized property names as belonging to the same property group.

@Vertex
public abstract class Person {
  public abstract List<String> getNames();
  public abstract void addName(String name);
  public abstract void removeName(String name);
}

Edges

In a similar way, you can frame TinkerPop 3 edges using abstract classes or interfaces. The class simple name in lowercase will act as the edge label. Add any edge properties and specify at most the two adjacent vertices by using the @In and @Out annotations.

@Edge
public abstract class Created {
  public abstract Float getWeight();

  @Out 
  public abstract Person getPerson();
  
  @In  
  public abstract Software getSoftware();
}

Next, you can integrate the framed edge class in the corresponding vertex classes. The direction is by default outgoing.

@Vertex
public abstract class Person {
  public abstract List<Created> getCreated();
  public abstract Created addCreated(Software software);
  public abstract Created removeCreated(Software software);
}

If you don't need to explicitely frame the edge, you can also create direct relationships between vertices and annotate the method with @Edge. By default the JavaBean property name acts as edge label, but this can be overridden by setting the annotation value

@Vertex
public abstract class Person {
  @Edge("created")
  public abstract List<Software> getSoftware();
  public abstract void addSoftware(Software software);
  public abstract void removeSoftware(Software software);
}

Attention: at this moment, only getter methods can change the default direction (outgoing) by adding @In or @Both annotations. These annotations are ignored on setter, adder and remover methods.

@Vertex
public abstract class Person {
  @In
  @Edge("knows")
  public abstract List<Person> getKnownBy();
}

Meta-properties

Peapod supports meta-properties which is a new feature of TinkerPop 3. You simply define a framing class for the vertex property and add properties which will be mapped onto meta-properties.

You must obligatory implement the FramedVertexProperty interface in order to specify the vertex property type. FramedVertexProperty defines a T getValue() and void setValue(T) method to alter the property value.

@VertexProperty
public abstract class Name implements FramedVertexProperty<String> {
    public abstract String getAcl();
    public abstract void setAcl(String acl);
}

Once the vertex property class is defined you can add it to a @Vertex class.

@Vertex
public abstract class Person {
  public abstract List<Name> getNames();
  public abstract Name getName(String name);
  public abstract Name addName(String name);
  public abstract void removeName(String name);
}

Post construct

Peaopod supports the @javax.annotation.PostConstruct annotation to initialize vertices when added to the framed graph.

@Vertex
public abstract class Person {
    public abstract Date getCreationTms();
    public abstract void setCreationTms(Date date);

    @PostConstruct
    protected void init() {
        setCreationTms(new Date());
    }
}

Person person = graph.addVertex(Person.class);
assertTrue(person.getCreationTms() != null);

Framed graph

The framed graph wraps a TinkerPop 3 Graph and delegates all queries and graph manipulations to the underlying graph. FramedGraph must be initialized with a base package. Peapod will recursively scan the provided package and search for framing classes annotated with @Vertex, @VertexProperty or @Edge.

Graph g = TinkerGraph.open();

FramedGraph graph = new FramedGraph(g,  Package.getPackage("com.company.application.model"));

You create new vertices by specifying the framing class and an optional identifier if the TinkerPop 3 graph database supports it.

Person person = graph.addVertex(1, Person.class);
person.setName("alice");

You can retrieve a graph vertex by id. Peapod will infer the framing class from the vertex label.

Person person = graph.v(1);
assertEquals("alice", person.getName());

When the vertices in the graph don't define a label, you can still provide a framing class yourself.

Person person = graph.v(1, Person.class);
assertEquals("alice", person.getName());

You can query and navigate the graph starting from the framed graph and a framing class.

List<Person> result = graph.V(Person.class).has("name", "alice").toList();
assertEquals(1, result.size());

Traversals

Traversals in Peapod latest are still very basic. The future goal is to generate more advanced traversal classes using the DSL framework in TinkerPop 3. Because the graph structure is defined by the framing classes, Peapod would be able to define these traversal classes easily and save you from writing a lot of unnecessary boilerplate code.

Vertices can optionally implement the FramedVertex interface which defines a serie of traversal methods.

@Vertex
public abstract class Person FramedVertex<Person> {
  @Edge("knows")
  public abstract List<Person> getKnowsPerson();
  
  public List<Person> getKnowsPersonsOlderThan(int age) {
    return out("knows", Person.class).has("age", Compare.gt, age).toList();
  }
}

assertThat(marko.getKnowsPersonsOlderThan(30), contains(josh));

Download

Latest JAR

The source code to Peapod, its samples, and this website is available on GitHub.

Maven

<dependency>
  <groupId>org.bayofmany.peapod</groupId>
  <artifactId>peapod</artifactId>
  <version>latest</version>
</dependency>

Peapod requires at minimum Java 8.

License

Copyright 2015 Bay of Many.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Acknowledgments

Thanks to all the exceptional TinkerPop 3 talents for creating this amazing Open Source Graph Computing Framework.

Thanks to GitHub for providing the GIT repository.

Thanks to Cloudbees for providing the Jenkins build infrastructure.

Thanks to Sonatype for hosting Peapod in the central maven repository.

Thanks to EJ Technologies for providing a JProfiler license, the Award-Winning All-in-One Java Profiler.