- Notifications
You must be signed in to change notification settings - Fork26
An ORM / OGM for the TinkerPop graph stack.
License
Syncleus/Ferma
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
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 lackedfeatures needed by the community, and its performance was cripplingly slow. Today Ferma is a robust framework thattakes on a role similar to an Object-relational Model (ORM) library for traditional databases. Ferma is often referred toas a Object-graph Model (OGM) library, and maps Java objects to elements in a graph such as a Vertex or an Edge. Inshort it allows a schema to be defined using java interfaces and classes which provides a level of abstraction forinteracting with the underlying graph.
Ferma 3.xSupports 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 ByteBuddy, avoiding the need for proxy classes. This in turn significantly improves performance when compared with TinkerPopFrames and other frameworks. Ferma offers many features including several annotation types to reduce the need forboilerplate code as well as handling Java typing transparently. This ensures whatever the type of the object is when youpersist 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 asuper-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 theTinkerPop features are available to the end-user. The TinkerPop stack provides several tools which can be used to workwith 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 bothOLTP andOLAP engines.
- TinkerGraph a Graph Database and the reference implementation for TinkerPop.
- NativeGephi integration for visualizing graphs.
- Interfaces for most major Graph Compute Engines includingHadoop M/R.Spark, andGiraph.
Ferma also supports any of the many databases compatible with TinkerPop including the following.
The official Ferma source repository is hosted onQOTO GitLab with an up-to-date mirror maintained atGithub.
For documentation refer to ourproject page as well as thelatest Javadocs.
For support please useGitteror theofficial Ferma mailing list and Discourse forum.
Please file bugs and feature requests on theQOTO GitLab.
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>
Ferma provides three levels of type resolution: untyped, simple, and annotated. In untyped mode Ferma doesn't handletyping at all, instead the type must be explicitly indicated whenever querying. In simple mode Ferma provides typecontext encoded as graph element properties which ensures the same type comes out that goes in to a graph. In annotatedmode all the features of simple mode are provided as well as enabling the use of annotations on abstract methods toinstruct Ferma to dynamically construct byte-code to implement the abstract methods at start up.
In untyped mode there is no automatic typing. Whatever class is explicitly indicated is the type that will beinstantiated when performing queries. Lets start with a simple example domain.
publicclassPersonextendsAbstractVertexFrame {publicStringgetName() {returngetProperty("name"); }publicvoidsetName(Stringname) {setProperty("name",name); }publicList<?extendsKnows>getKnowsList() {returntraverse((v) ->v.outE("knows")).toList(Knows.class); }publicKnowsaddKnows(Personfriend) {returnaddFramedEdge("knows",friend,Knows.class); }}publicclassKnowsextendsAbstractEdgeFrame {publicvoidsetYears(intyears) {setProperty("years",years); }publicintgetYears() {returngetProperty("years"); }}
And here is how you interact with the framed elements:
publicvoidtestUntyped() {Graphgraph =TinkerGraph.open();// implies untyped modeFramedGraphfg =newDelegatingFramedGraph(graph);Personp1 =fg.addFramedVertex(Person.class);p1.setName("Jeff");Personp2 =fg.addFramedVertex(Person.class);p2.setName("Julia");Knowsknows =p1.addKnows(p2);knows.setYears(15);Personjeff =fg.traverse((g) ->g.V().has("name","Jeff")).next(Person.class);Assert.assertEquals("Jeff",jeff.getName());}
In simple mode you must provide concrete classes, no abstract or interfaces allowed, and the class should always extendfrom a FramedVertex or FramedEdge. Simple mode doesn't provide any annotations either. The purpose of simple mode is toprovide type resolution. Basically the type of object you use when adding to the graph is the same type you get out whenreading from the graph.
Say we extend the Person class with the Programmer class.
publicclassProgrammerextendsPerson {}
Using simple mode will save the type of Java class the element was created with for use later:
publicvoidtestSimpleTyping() {Graphgraph =TinkerGraph.open();// implies simple modeFramedGraphfg =newDelegatingFramedGraph(graph,true,false);Personp1 =fg.addFramedVertex(Programmer.class);p1.setName("Jeff");Personp2 =fg.addFramedVertex(Person.class);p2.setName("Julia");Personjeff =fg.traverse((g) ->g.V().has("name","Jeff")).next(Person.class);Personjulia =fg.traverse((g) ->g.V().has("name","Julia")).next(Person.class);Assert.assertEquals(Programmer.class,jeff.getClass());Assert.assertEquals(Person.class,julia.getClass());}
In annotated mode you can either provide concrete classes, abstract classes, or even interfaces. Abstract classes andconcrete 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 beimplemented by the engine. Annotated mode provides the same type resolution as provided by simple mode with a bit morepower to determine parent-child relationships at runtime.
The same example as above done with annotations would look something like this.
publicabstractclassPersonextendsAbstractVertexFrame {@Property("name")publicabstractStringgetName();@Property("name")publicabstractvoidsetName(Stringname);@Adjacency(label ="knows")publicabstractList<Person>getKnowsPeople();@Incidence(label ="knows")publicabstractList<Knows>getKnows();@Incidence(label ="knows")publicabstractKnowsaddKnows(Personfriend);publicList<?extendsPerson>getFriendsNamedBill() {returnthis.traverse(input ->input.out("knows").has("name","bill")).toList(Person.class); }}publicabstractclassKnowsextendsAbstractEdgeFrame {@Property("years")publicabstractvoidsetYears(intyears);@Property("years")publicabstractintgetYears();@InVertexpublicabstractPersongetIn();@OutVertexpublicabstractPersongetOut();}publicabstractclassProgrammerextendsPerson {}
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 andconstruct the byte-code for any abstract annotated methods.
publicvoidtestAnnotatedTyping() {Set<Class<?>>types =newHashSet<Class<?>>(Arrays.asList(newClass<?>[]{Person.class,Programmer.class,Knows.class}));Graphgraph =TinkerGraph.open();//implies annotated modeFramedGraphfg =newDelegatingFramedGraph(graph,true,types);Personjeff =fg.addFramedVertex(Programmer.class);jeff.setName("Jeff");Personjulia =fg.addFramedVertex(Person.class);julia.setName("Julia");julia.addKnows(jeff);PersonjuliaAgain =fg.traverse((g) ->g.V().has("name","Julia")).next(Person.class);PersonjeffAgain =juliaAgain.getKnowsPeople().get(0);Assert.assertTrue(Programmer.class.isAssignableFrom(jeffAgain.getClass()));Assert.assertTrue(Person.class.isAssignableFrom(juliaAgain.getClass()));}
The official source repository for Ferma is located in the Syncleus Github repository and can be cloned using thefollowing command.
git clone https://git.qoto.org/Ferma/Ferma.git
Install dependencies
pip install mkdocs mkdocs-material pymdown-extensions
Build documentation
mkdocs build
Deploy to Github
mkdocs gh-deploy
About
An ORM / OGM for the TinkerPop graph stack.