Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

An Object Graph Mapping Library For Gremlin

License

NotificationsYou must be signed in to change notification settings

karthicks/gremlin-ogm

Repository files navigation

Karthick Sankaracharyhttp://github.com/karthicks

Thegremlin-objectsmodule defines a library that puts an object-oriented spin on the gremlin property graph.It aims to make it much easier to specify businessdomain specific languages around Gremlin, without any loss of expressive power.While it targets the Gremlin-Java variant, the concept itself is language-independent.

Introduction

Every element in the property graph, whether it be a vertex (property) or an edge, is made up of properties.Each such property is aString key and an arbitraryJava value.It only seems fitting then to try and represent that property as a strongly-typedJava field.The specific class in which that field is defined then becomes the vertex (property) or edge, which the property describes.Agremlin object model such as this would need abstractions to query and update the graph in terms of those objects.To get the library that facilitates all of this, add this dependency to yourpom.xml:

<dependency>  <groupId>com.github.karthicks</groupId>  <artifactId>gremlin-objects</artifactId>  <version>3.3.1-RC1</version></dependency>

A reference use case of this library is available in the followingtinkergraph-test module:

<dependency>  <groupId>com.github.karthicks</groupId>  <artifactId>tinkergraph-test</artifactId>  <version>3.3.1-RC1</version></dependency>

The Object Graph

In this section, we go over how gremlin elements may be modeled, and how those models may be queried and stored.

Object Model

Let’s consider the example of theperson vertex, taken from the "modern" and "the crew" graphs defined in theTinkerFactory.In our object world, it would be defined as aPerson class that extendsVertex.By default, the vertex’s label matches its simple class name, hence we have to un-capitalize it using the@Alias annotation.

The person’sname andage properties become primitive fields in the class.The@PrimaryKey and@OrderingKey annotations on them not only indicate that they are mandatory,but also allow theperson to be found easily through theHasKeys.of(person)SubTraversal.Think of theSubTraversal as a reusable function that takes aGraphTraversal, performs a few steps on it, and returns it back (to allow for chaining).TheKnowsPeople field in this class is an example of an in-lineSubTraversal, albeit a stronger-typed version of it calledToVertex, to indicate that it ends up selecting vertices.Note that these traversal functions are not stored in the graph.

@Data@Alias(label ="person")publicclassPersonextendsVertex {publicstaticToVertexKnowsPeople =traversal ->traversal      .out(Label.of(Knows.class))      .hasLabel(Label.of(Person.class));@PrimaryKeyprivateStringname;@OrderingKeyprivateintage;privateSet<String>titles;privateList<Location>locations;}

Next, we look at itstitles field, which is defined to be aSet.As you might expect, the cardinality of the underlying property becomesset.Similarly, thelocations field takes on thelist cardinality.Further, each element in thelocations list has it’s own meta-properties, and ergo deserves aLocation class of it’s own.

@Data@Alias(label ='location')publicclassLocationextendsElement {@OrderingKey@PropertyValueprivateStringname;@OrderingKeyprivateInstantstartTime;privateInstantendTime;}
Note
The value of thelocation is stored inname, due to the placement of the@PropertyValue annotation.Every other field in theLocation class becomes the `location’s meta-property.

An edge is defined much like the vertex, except it extends theEdge class.By default, an edge’s label is it’s un-capitalized simple class name, and hence no@Alias is needed:

@DatapublicclassKnowsextendsEdge {privateDoubleweight;privateInstantsince;}

You can find more examples of gremlin object verticeshere and edgeshere.

Updating Objects

TheGraph interface lets you update the graph usingVertex orEdge objects.You can get it via dependency injection, assuming you’ve anObject provider forGraphTraversalSource:

@Inject@ObjectprivateGraphgraph;

Or, the good old fashioned way, using theGraphFactory:

privateGraphFactorygraphFactory =GraphFactory.of(TinkerGraph.open().traversal());// This gets you the factory for TinkerGraph.privateGraph =graphFactory.graph();

Now that we know how to obtain aGraph instance, let’s see how to change it usingJava objects.Here, we createsoftware vertices fortinkergraph andgremlin, and add atraverses edge fromgremlin totinkergraph.

graph    .addVertex(Software.of("tinkergraph")).as("tinkergraph")    .addVertex(Software.of("gremlin")).as("gremlin")    .addEdge(Traverses.of(),"tinkergraph");

Below, aperson vertex containing a list oflocations is added, along with three outgoing edges.

graph    .addVertex(Person.of("marko",Location.of("san diego",1997,2001),Location.of("santa cruz",2001,2004),Location.of("brussels",2004,2005),Location.of("santa fe",2005))).as("marko")    .addEdge(Develops.of(2010),"tinkergraph")    .addEdge(Uses.of(Proficient),"gremlin")    .addEdge(Uses.of(Expert),"tinkergraph")

To see how themodern and thecrew reference graphs may be created using the objectGraph interface, gohere.

Tip
Since the object being added may already exist in the graph, we providevarious options to resolve "merge conflicts", such asMERGE,REPLACE,CREATE,IGNORE ANDINSERT.

Querying Objects

There are two ways to get a handle to theQuery interface.You can inject it like so:

@Inject@ObjectprivateQueryquery;

Otherwise, you can create it using theGraphFactory like so:

privateGraphFactorygraphFactory =GraphFactory.of(TinkerGraph.open().traversal());privateQuery =graphFactory.query();

Next, let’s see how to use theQuery interface.The following snippet queries the graph by chaining twoSubTraversals (a function denoting a partial traversal), and parses the result into a list ofPerson vertices.

List<Person>friends =query    .by(HasKeys.of(modern.marko),Person.KnowsPeople)    .list(Person.class);

Below, we query by anAnyTraversal (a function on theGraphTraversalSource), and get a singlePerson back.

Personmarko =Person.of("marko");Personactual =query    .by(g ->g.V().hasLabel(marko.label()).has("name",marko.name()))    .one(Person.class);

The type of the result may be primitives too, and that is handled as shown below.

longcount =query    .by(HasKeys.of(crew.marko),Count.of())    .one(Long.class);

Last, we show a traversal involving select steps, which requires special handling as it may return a map.

Selectionsselections =query    .by(g ->g.V().as("a").properties("locations").as("b").hasNot("endTime").as("c").order().by("startTime").select("a","b","c").by("name").by(T.value).by("startTime").dedup())    .as("a",String.class)    .as("b",String.class)    .as("c",Instant.class)    .select();

To see more examples showcasing how the objectQuery interface may be used, gohere.

Providers

In this section, we talk about how thegremlin-objects library can be customized for agraph system provider.

Service Provider Interface

A provider that wishes to plug intogremlin-objects through dependency injection, will need to provide aGraphTraversalSource of it’s choice, through theObject qualifier.For users that don’t use dependency injection, they may manually pass theGraphTraversalSource to theGraphFactory.

Registering Native Types

Typically, gremlin property values are Java primitives.Sometimes, a provider treats a custom type as a primitive.For instance,DataStax lets you define property keys of the primitive geometric typePoint.Such types can be registered using thePrimitives#registerPrimitiveClass methods.

Registering Custom Parsers

When aGraphTraversal is completed, it usually returns (a list of) gremlinElement(s).However, when some providers execute a traversal, the result comprises custom element types.For instance, whenDataStax executes a graph query, it returns a result set made up ofGraphNode(s), a proprietary element type.We give such providers a way to tell us how to parse such custom elements using theParsers#registerElementParser method.

Analysis

While there exist similar OGM libraries, this one has some key differentiating factors. Now, let’s consider the alternatives:

GremlinDsl Traversals

Thegremlin-core module defines aGremlinDsl annotation that lets you define custom traversals by extending theGraphTraversal andGraphTraversalSource.However, it requires some familiarity ofgremlin-core internals.

Peopod for Tinkerpop 3

Peopod represents elements as annotated interfaces or abstract classes.While it generates boilerplate for traversals to adjacent vertices, it doesn’t let you co-locate arbitrary traversals.This library is less intrusive and more flexible.

User Defined Steps

An older version of TinkerPop allowed you to define custom steps usingClosures, not unlike theAnyTraversal andSubTraversal functions.However, they aren’t as developer friendly as the functional interfaces provided here.Moreover, it doesn’t allow for co-locating the traversal logic along with the element model, as we do here.

Future Work

So far, we have thegremlin-objects library, and atinkergraph-test reference use case for it.Here, we list a few directions in which we see the library evolving:

Language Variants

The concept of lifting the property graph into objects is language-independent.To quote the TinkerPop docs, "with JSR-223, any language compiler written for the JVM can directly access the JVM and any of its libraries", and that would includegremlin-objects.For GLVs not written for the JVM, it can be ported over as long as it supports basic reflection.Case in point, the Gremlin-Python variant could achieve the object mapping through thedir,getattr andsetattr built-in functions.

Provider Support

In reality, it is fairly easy for a provider to plug-intogremlin-objects simply by supplying aGraphTraversalSource of their choosing.The ability to register custom primitive types and traversal result parsers allows for further customization.Sinceneo4j already has it’s ownNeo4jGraph, it’s a good candidate to become the next test case.

DataFrame Support

Some providers useGraphFrames to execute bulk operations and graph algorithms on top of Tinkerpop.Assuming they can work withDataFrames, one could build aGraphTraversalSource,which translates the objectGraph andQuery operations intoDataFrame tables, and adapt’s it to the provider’sGraphFrame.

Traversal Storage

TheAnyTraversal andSubTraversalinterfaces extendFormattable so that the steps defined in it’s body can be revealed.Let’s say that we stored the bytecode of these types of functional fields as a hidden property in the element.That could potentially allow us to executeuser defined traversals using a, say,traversal.call('function-name') step.


[8]ページ先頭

©2009-2025 Movatter.jp