Features
The following gives a quick breakdown of some of the features of the Ferma framework along with examples.
- JPA-like Annotations
- Type information encoded into graph
- Framing of elements instantiated according to type hierarchy
- Element queried by type hierarchy
- Turning off type resolution on a per call basis
- Changing the encoded graph type already stored in the database
- Customizing the way type information is stored in the graph
- Tinkerpop 2 support
- Tinkerpop 3 support
JPA-like Annotations¶
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(); } public abstract class Programmer extends Person { } 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 jeffAgain instanceof Programmer; assert jeffAgain.getName().equals("Jeff"); }
Type information encoded into graph¶
Set<Class<?>> types = new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{Person.class})); Graph g = new TinkerGraph(); FramedGraph fg = new DelegatingFramedGraph(g, types); fg.addFramedVertex(Person.class); Person person = fg.traverse(g -> g.V()).next(Program.class); String personClassName = Person.class.getName(); String encodedClassName = person.getProperty(PolymorphicTypeResolver.TYPE_RESOLUTION_KEY) assert personClassName.equals(encodedClassName);
Framing instantiated by type hierarchy¶
Set<Class<?>> types = new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{Person.class, Programmer.class})); TinkerGraph g = new TinkerGraph(); FramedGraph fg = new DelegatingFramedGraph(g, types); fg.addFramedVertex(Programmer.class); //make sure the newly added node is actually a programmer Person programmer = fg.traverse(g -> g.V()).next(Person.class); assert programmer instanceof Programmer;
Element queried by type hierarchy¶
Set<Class<?>> types = new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{Person.class, Programmer.class})); TinkerGraph g = new TinkerGraph(); FramedGraph fg = new DelegatingFramedGraph(g, types); fg.addFramedVertex(Programmer.class); fg.addFramedVertex(Person.class); //counts how many people (or subclasses thereof) in the graph. assert fg.traverse(g -> g.getTypeResolver().hasType(g.V(), Person.class)).toList(Person.class).size() == 2; //counts how many programmers are in the graph assert fg.traverse(g -> g.getTypeResolver().hasType(g.V(), Programmer.class)).toList(Person.class).size() == 1;
Turning off type resolution per call¶
Set<Class<?>> types = new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{Person.class, Programmer.class})); TinkerGraph g = new TinkerGraph(); FramedGraph fg = new DelegatingFramedGraph(g, types); fg.addFramedVertex(Programmer.class); //With type resolution is active it should be a programmer assert fg.traverse(g -> g.V()).next(Person.class) instanceof Programmer; //With type resolution bypassed it is no longer a programmer assert !(fg.traverse(g -> g.V()).nextExplicit(Person.class) instanceof Programmer);
Changing type encoded in the graph¶
Set<Class<?>> types = new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{Person.class, Programmer.class})); TinkerGraph g = new TinkerGraph(); FramedGraph fg = new DelegatingFramedGraph(g, types); fg.addFramedVertex(Programmer.class); //make sure the newly added node is actually a programmer Person programmer = fg.traverse(g -> g.V()).next(Person.class); assert programmer instanceof Programmer; //change the type resolution to person programmer.setTypeResolution(Person.class); //make sure the newly added node is actually a programmer Person person = fg.traverse(g -> g.V()).next(Person.class); assert !(person instanceof Programmer);
Customizing how types are encoded¶
Set<Class<?>> types = new HashSet<Class<?>>(Arrays.asList(new Class<?>[]{Person.class})); final ReflectionCache cache = new ReflectionCache(types); FrameFactory factory = new AnnotationFrameFactory(cache); TypeResolver resolver = new PolymorphicTypeResolver(cache, "customTypeKey"); Graph g = new TinkerGraph(); FramedGraph fg = new DelegatingFramedGraph(g, factory, resolver); fg.addFramedVertex(Person.class); Person person = fg.traverse(g -> g.V()).next(Programmer.class); String personClassName = Person.class.getName(); String encodedClassName = person.getProperty("customTypeKey") assert personClassName.equals(encodedClassName);