Skip to content
An ORM / OGM for the TinkerPop graph stack.
Java JavaScript
Branch: master
Clone or download

Latest commit

freemo Merge branch 'master' into 'master'
Fix typos in documentation

See merge request Ferma/Ferma!4
Latest commit 9a45e2d Apr 3, 2020

Files

Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
docs docs: remove repeated word Mar 31, 2020
src Upgrade: Remove direct and indirect Guava dependency, update reflecti… Mar 7, 2020
.cz-config.js Added code of conduct and contributing markdown files. Configured the… Jun 17, 2017
.cz.json Added code of conduct and contributing markdown files. Configured the… Jun 17, 2017
.editorconfig Added an .editorconfig file, should work with many IDE including inte… Jun 7, 2015
.gitattributes Added a default .gitattributes file that enforces line endings. Jun 16, 2015
.gitignore Added che directory to .gitignore. Sep 1, 2018
.gitlab-ci.yml Removed site compilation in ci. Nov 8, 2016
.travis.yml ci(travis): generating site in travis for codacy report. Sep 29, 2017
CHANGELOG.md Release: Bumping snapshot version post-release. Mar 15, 2020
CODE_OF_CONDUCT.md Added code of conduct and contributing markdown files. Configured the… Jun 17, 2017
CONTRIBUTING.md docs: Updated checklist in contributing.md. Oct 1, 2017
CONTRIBUTORS.md Upgrade: TinkerPop 3.4.6 Mar 6, 2020
LICENSE Added full Apache License text into the license file. Sep 22, 2016
NOTICE chore: updated license headers. Sep 30, 2017
README.md Docs: Updated travis badge in readme to Gitlab CI. Mar 15, 2020
mkdocs.yml Build: Fixed mkdocs build script. Jan 20, 2020
pom.xml Release: Bumping snapshot version post-release. Mar 15, 2020

README.md

Build status Coverage Dependencies Codacy Javadocs Maven Central Gitter

An ORM / OGM for the Apache TinkerPop™ graph stack.

Licensed under the Apache Software License v2

The Ferma project was originally created as an alternative to the TinkerPop2 Frames project. Which at the time lacked features needed by the community, and its performance was cripplingly slow. Today Ferma is a robust framework that takes on a role similar to an Object-relational Model (ORM) library for traditional databases. Ferma is often referred to as a Object-graph Model (OGM) library, and maps Java objects to elements in a graph such as a Vertex or an Edge. In short it allows a schema to be defined using java interfaces and classes which provides a level of abstraction for interacting with the underlying graph.

Ferma 3.x Supports TinkerPop3. For TinkerPop2 support use Ferma version 2.x.

Annotated classes in Ferma have their abstract methods implemented using code generation during start-up with Byte Buddy, avoiding the need for proxy classes. This in turn significantly improves performance when compared with TinkerPop Frames and other frameworks. Ferma offers many features including several annotation types to reduce the need for boilerplate code as well as handling Java typing transparently. This ensures whatever the type of the object is when you persist it to the graph the same Java type will be used when instantiating a class off of the graph.

Ferma is designed to easily replace TinkerPop Frames in existing code, as such, the annotations provided by Ferma are a super-set of those originally provided by TinkerPop Frames.

Ferma is built directly on top of TinkerPop and allows access to all of the internals. This ensures all the TinkerPop features are available to the end-user. The TinkerPop stack provides several tools which can be used to work with the Ferma engine.

  • Gremlin, a database agnostic query language for Graph Databases.
  • Gremlin Server, a server that provides an interface for executing Gremlin on remote machines.
  • a data-flow framework for splitting, merging, filtering, and transforming of data
  • Graph Computer, a framework for running algorithms against a Graph Database.
  • Support for both OLTP and OLAP engines.
  • TinkerGraph a Graph Database and the reference implementation for TinkerPop.
  • Native Gephi integration for visualizing graphs.
  • Interfaces for most major Graph Compute Engines including Hadoop M/R. Spark, and Giraph.

Ferma also supports any of the many databases compatible with TinkerPop including the following.

The official Ferma source repository is hosted on QOTO GitLab with an up-to-date mirror maintained at Github.

For documentation refer to our project page as well as the latest Javadocs.

For support please use Gitter or the official Ferma mailing list and Discourse forum.

Please file bugs and feature requests on the QOTO GitLab.

Dependency

To include Ferma in your project of choice include the following Maven dependency into your build.

<dependency>
    <groupId>com.syncleus.ferma</groupId>
    <artifactId>ferma</artifactId>
    <version>3.3.2</version>
</dependency>

Getting Started

Ferma provides three levels of type resolution: untyped, simple, and annotated. In untyped mode Ferma doesn't handle typing at all, instead the type must be explicitly indicated whenever querying. In simple mode Ferma provides type context encoded as graph element properties which ensures the same type comes out that goes in to a graph. In annotated mode all the features of simple mode are provided as well as enabling the use of annotations on abstract methods to instruct Ferma to dynamically construct byte-code to implement the abstract methods at start up.

Untyped Mode Example

In untyped mode there is no automatic typing. Whatever class is explicitly indicated is the type that will be instantiated when performing queries. Lets start with a simple example domain.

public class Person extends AbstractVertexFrame {
  public String getName() {
    return getProperty("name");
  }

  public void setName(String name) {
    setProperty("name", name);
  }

  public List<? extends Knows> getKnowsList() {
    return traverse((v) -> v.outE("knows")).toList(Knows.class);
  }

  public Knows addKnows(Person friend) {
    return addFramedEdge("knows", friend, Knows.class);
  }
}

public class Knows extends AbstractEdgeFrame {
  public void setYears(int years) {
    setProperty("years", years);
  }

  public int getYears() {
    return getProperty("years");
  }
}

And here is how you interact with the framed elements:

public void testUntyped() {
  Graph graph = TinkerGraph.open();

  // implies untyped mode
  FramedGraph fg = new DelegatingFramedGraph(graph);

  Person p1 = fg.addFramedVertex(Person.class);
  p1.setName("Jeff");

  Person p2 = fg.addFramedVertex(Person.class);
  p2.setName("Julia");
  Knows knows = p1.addKnows(p2);
  knows.setYears(15);

  Person jeff = fg.traverse((g) -> g.V().has("name", "Jeff")).next(Person.class);


  Assert.assertEquals("Jeff", jeff.getName());
}

Simple Mode Example

In simple mode you must provide concrete classes, no abstract or interfaces allowed, and the class should always extend from a FramedVertex or FramedEdge. Simple mode doesn't provide any annotations either. The purpose of simple mode is to provide type resolution. Basically the type of object you use when adding to the graph is the same type you get out when reading from the graph.

Say we extend the Person class with the Programmer class.

public class Programmer extends Person {
}

Using simple mode will save the type of Java class the element was created with for use later:

public void testSimpleTyping() {
  Graph graph = TinkerGraph.open();

  // implies simple mode
  FramedGraph fg = new DelegatingFramedGraph(graph, true, false);

  Person p1 = fg.addFramedVertex(Programmer.class);
  p1.setName("Jeff");

  Person p2 = fg.addFramedVertex(Person.class);
  p2.setName("Julia");

  Person jeff = fg.traverse((g) -> g.V().has("name", "Jeff")).next(Person.class);
  Person julia = fg.traverse((g) -> g.V().has("name", "Julia")).next(Person.class);

  Assert.assertEquals(Programmer.class, jeff.getClass());
  Assert.assertEquals(Person.class, julia.getClass());
}

Annotated Mode Example

In annotated mode you can either provide concrete classes, abstract classes, or even interfaces. Abstract classes and concrete classes must extend from FramedVertex or FramedEdge, however, interfaces do not have this restriction. Annotated mode also provides a set of annotations which must be used to define any abstract methods that are to be implemented by the engine. Annotated mode provides the same type resolution as provided by simple mode with a bit more power to determine parent-child relationships at runtime.

The same example as above done with annotations would look something like this.

public abstract class Person extends AbstractVertexFrame {
  @Property("name")
  public abstract String getName();

  @Property("name")
  public abstract void setName(String name);

  @Adjacency(label = "knows")
  public abstract List<Person> getKnowsPeople();

  @Incidence(label = "knows")
  public abstract List<Knows> getKnows();

  @Incidence(label = "knows")
  public abstract Knows addKnows(Person friend);

  public List<? extends Person> getFriendsNamedBill() {
      return this.traverse(input -> input.out("knows").has("name", "bill")).toList(Person.class);
  }
}

public abstract class Knows extends AbstractEdgeFrame {
  @Property("years")
  public abstract void setYears(int years);

  @Property("years")
  public abstract int getYears();

  @InVertex
  public abstract Person getIn();

  @OutVertex
  public abstract Person getOut();
}

public abstract class Programmer extends Person {
}

If we pass a collection of Class objects to the FramedGraph constructor then the annotated type resolver will be used. In this mode you want to tell the engine what classes you will be using so it can handle type resolution properly and construct the byte-code for any abstract annotated methods.

public void testAnnotatedTyping() {
  Set<Class<?>> types = new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{
                Person.class,
                Programmer.class,
                Knows.class}));
  Graph graph = TinkerGraph.open();

  //implies annotated mode
  FramedGraph fg = new DelegatingFramedGraph(graph, true, types);

  Person jeff = fg.addFramedVertex(Programmer.class);
  jeff.setName("Jeff");

  Person julia = fg.addFramedVertex(Person.class);
  julia.setName("Julia");
  julia.addKnows(jeff);

  Person juliaAgain = fg.traverse((g) -> g.V().has("name", "Julia")).next(Person.class);
  Person jeffAgain = juliaAgain.getKnowsPeople().get(0);

  Assert.assertTrue(Programmer.class.isAssignableFrom(jeffAgain.getClass()));
  Assert.assertTrue(Person.class.isAssignableFrom(juliaAgain.getClass()));
}

Obtaining the Source

The official source repository for Ferma is located in the Syncleus Github repository and can be cloned using the following command.

git clone https://git.qoto.org/Ferma/Ferma.git

Building

Documentation

Install dependencies

pip install mkdocs mkdocs-material pymdown-extensions

Build documentation

mkdocs build

Deploy to Github

mkdocs gh-deploy
You can’t perform that action at this time.