Skip to content

Commit 91b37b7

Browse files
committed
Support of JanusGraph
1 parent 9712385 commit 91b37b7

File tree

8 files changed

+715
-0
lines changed

8 files changed

+715
-0
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@
220220
<module>transponder-orientdb</module>
221221
<module>transponder-arcadedb</module>
222222
<module>transponder-neo4j</module>
223+
<module>transponder-janusgraph</module>
223224
<module>transponder-mongodb</module>
224225
</modules>
225226
<profiles>

transponder-janusgraph/pom.xml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
<parent>
5+
<artifactId>transponder-parent</artifactId>
6+
<groupId>org.orienteer.transponder</groupId>
7+
<version>1.1-SNAPSHOT</version>
8+
</parent>
9+
10+
<artifactId>transponder-janusgraph</artifactId>
11+
12+
<name>transponder-janusgraph</name>
13+
14+
<dependencies>
15+
<dependency>
16+
<groupId>org.orienteer.transponder</groupId>
17+
<artifactId>transponder-core</artifactId>
18+
<version>${project.version}</version>
19+
</dependency>
20+
<dependency>
21+
<groupId>org.janusgraph</groupId>
22+
<artifactId>janusgraph-core</artifactId>
23+
<version>1.1.0</version>
24+
</dependency>
25+
<dependency>
26+
<groupId>org.janusgraph</groupId>
27+
<artifactId>janusgraph-inmemory</artifactId>
28+
<version>1.1.0</version>
29+
<scope>test</scope>
30+
</dependency>
31+
<dependency>
32+
<groupId>org.apache.tinkerpop</groupId>
33+
<artifactId>gremlin-core</artifactId>
34+
<version>3.7.3</version>
35+
</dependency>
36+
<dependency>
37+
<groupId>org.orienteer.transponder</groupId>
38+
<artifactId>transponder-core</artifactId>
39+
<version>${project.version}</version>
40+
<type>test-jar</type>
41+
<scope>test</scope>
42+
</dependency>
43+
</dependencies>
44+
<build>
45+
<plugins>
46+
<plugin>
47+
<groupId>org.apache.maven.plugins</groupId>
48+
<artifactId>maven-compiler-plugin</artifactId>
49+
<configuration>
50+
<release>11</release>
51+
<encoding>UTF-8</encoding>
52+
<showWarnings>true</showWarnings>
53+
<showDeprecation>true</showDeprecation>
54+
<compilerArgs>
55+
<arg>-parameters</arg>
56+
</compilerArgs>
57+
</configuration>
58+
</plugin>
59+
<plugin>
60+
<groupId>org.apache.maven.plugins</groupId>
61+
<artifactId>maven-surefire-plugin</artifactId>
62+
<configuration>
63+
<argLine>--add-opens java.base/java.nio=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED</argLine>
64+
</configuration>
65+
</plugin>
66+
</plugins>
67+
</build>
68+
</project>
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
package org.orienteer.transponder.janusgraph;
2+
3+
import java.lang.reflect.AnnotatedElement;
4+
import java.lang.reflect.Type;
5+
import java.util.Collections;
6+
import java.util.List;
7+
import java.util.Map;
8+
import java.util.stream.Collectors;
9+
10+
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
11+
import org.apache.tinkerpop.gremlin.structure.Vertex;
12+
import org.apache.tinkerpop.gremlin.structure.Element;
13+
import org.apache.tinkerpop.gremlin.structure.T;
14+
import org.janusgraph.core.JanusGraph;
15+
import org.janusgraph.core.VertexLabel;
16+
import org.janusgraph.core.schema.JanusGraphManagement;
17+
import org.orienteer.transponder.IDriver;
18+
19+
import com.google.common.collect.BiMap;
20+
import com.google.common.collect.HashBiMap;
21+
22+
/**
23+
* Transponder {@link IDriver} for JanusGraph
24+
*/
25+
public class JanusGraphDriver implements IDriver {
26+
27+
public static final String DIALECT_JANUSGRAPH = "janusgraph";
28+
29+
public static final String TYPE_CUSTOM_TRANSPONDER_WRAPPER = "transponder.wrapper";
30+
31+
private static final BiMap<String, Class<?>> TYPE_TO_MAIN_CLASS = HashBiMap.create();
32+
33+
private final JanusGraph graph;
34+
private final GraphTraversalSource g;
35+
36+
/**
37+
* Creates {@link IDriver} which associated with provided JanusGraph instance
38+
* @param graph JanusGraph instance to associate driver with
39+
*/
40+
public JanusGraphDriver(JanusGraph graph) {
41+
this.graph = graph;
42+
this.g = graph.traversal();
43+
}
44+
45+
@Override
46+
public void createType(String typeName, boolean isAbstract, Class<?> mainWrapperClass, String... superTypes) {
47+
JanusGraphManagement mgmt = graph.openManagement();
48+
try {
49+
VertexLabel label = mgmt.getVertexLabel(typeName);
50+
if (label == null) {
51+
if (isAbstract) {
52+
// JanusGraph doesn't have explicit abstract concept, but we can track it
53+
label = mgmt.makeVertexLabel(typeName).setStatic().make();
54+
} else {
55+
label = mgmt.makeVertexLabel(typeName).make();
56+
}
57+
}
58+
mgmt.commit();
59+
} catch (Exception e) {
60+
mgmt.rollback();
61+
throw new RuntimeException("Failed to create type: " + typeName, e);
62+
}
63+
64+
TYPE_TO_MAIN_CLASS.put(typeName, mainWrapperClass);
65+
}
66+
67+
@Override
68+
public void createProperty(String typeName, String propertyName, Type propertyType, String referencedType,
69+
int order, AnnotatedElement annotations) {
70+
JanusGraphManagement mgmt = graph.openManagement();
71+
try {
72+
if (mgmt.getPropertyKey(propertyName) == null) {
73+
Class<?> masterClass = org.orienteer.transponder.CommonUtils.typeToMasterClass(propertyType);
74+
Class<?> dataType = getJanusGraphDataType(masterClass);
75+
76+
if (dataType != null) {
77+
mgmt.makePropertyKey(propertyName).dataType(dataType).make();
78+
}
79+
}
80+
mgmt.commit();
81+
} catch (Exception e) {
82+
mgmt.rollback();
83+
throw new RuntimeException("Failed to create property: " + propertyName, e);
84+
}
85+
}
86+
87+
@Override
88+
public void setupRelationship(String type1Name, String property1Name, String type2Name, String property2Name) {
89+
JanusGraphManagement mgmt = graph.openManagement();
90+
try {
91+
if (mgmt.getEdgeLabel(property1Name) == null) {
92+
mgmt.makeEdgeLabel(property1Name).make();
93+
}
94+
mgmt.commit();
95+
} catch (Exception e) {
96+
mgmt.rollback();
97+
throw new RuntimeException("Failed to setup relationship: " + property1Name, e);
98+
}
99+
}
100+
101+
@Override
102+
public void createIndex(String typeName, String indexName, String indexType, AnnotatedElement annotations,
103+
String... properties) {
104+
JanusGraphManagement mgmt = graph.openManagement();
105+
try {
106+
// Create composite index for the properties
107+
if (mgmt.getGraphIndex(indexName) == null && properties.length > 0) {
108+
mgmt.buildIndex(indexName, Vertex.class)
109+
.addKey(mgmt.getPropertyKey(properties[0]))
110+
.buildCompositeIndex();
111+
}
112+
mgmt.commit();
113+
} catch (Exception e) {
114+
mgmt.rollback();
115+
// Index creation might fail if already exists, which is okay
116+
}
117+
}
118+
119+
@Override
120+
public Object getPropertyValue(Object wrapper, String property, Type type) {
121+
return ((VertexWrapper)wrapper).get(property, type);
122+
}
123+
124+
@Override
125+
public void setPropertyValue(Object wrapper, String property, Object value, Type type) {
126+
((VertexWrapper)wrapper).set(property, value, type);
127+
}
128+
129+
@Override
130+
public <T> T newEntityInstance(Class<T> proxyClass, String type) {
131+
try {
132+
Vertex vertex = graph.addVertex(org.apache.tinkerpop.gremlin.structure.T.label, type);
133+
return proxyClass.getConstructor(Vertex.class).newInstance(vertex);
134+
} catch (Exception e) {
135+
throw new IllegalArgumentException("Can't create new entityInstance for class " + proxyClass + " with VertexLabel " + type, e);
136+
}
137+
}
138+
139+
@Override
140+
public void saveEntityInstance(Object wrapper) {
141+
// JanusGraph commits changes automatically within transactions
142+
// We could add explicit transaction handling here if needed
143+
try {
144+
graph.tx().commit();
145+
} catch (Exception e) {
146+
graph.tx().rollback();
147+
throw new RuntimeException("Failed to save entity", e);
148+
}
149+
}
150+
151+
@Override
152+
public <T> T wrapEntityInstance(Class<T> proxyClass, Object seed) {
153+
try {
154+
return proxyClass.getConstructor(Vertex.class).newInstance((Vertex)seed);
155+
} catch (Exception e) {
156+
throw new IllegalArgumentException("Can't wrap seed by class " + proxyClass + ". Seed: " + seed, e);
157+
}
158+
}
159+
160+
@Override
161+
public Class<?> getDefaultEntityBaseClass() {
162+
return VertexWrapper.class;
163+
}
164+
165+
@Override
166+
public Class<?> getEntityMainClass(Object seed) {
167+
if (seed instanceof Vertex) {
168+
String label = ((Vertex)seed).label();
169+
return TYPE_TO_MAIN_CLASS.get(label);
170+
}
171+
return null;
172+
}
173+
174+
@Override
175+
public boolean isSeedClass(Class<?> seedClass) {
176+
return Element.class.isAssignableFrom(seedClass);
177+
}
178+
179+
@Override
180+
public Object toSeed(Object wrapped) {
181+
return ((VertexWrapper)wrapped).getVertex();
182+
}
183+
184+
@Override
185+
public List<Object> query(String language, String query, Map<String, Object> params, Type type) {
186+
try {
187+
// For now, assume Gremlin queries
188+
return g.V().has("name", params.get("name"))
189+
.toList()
190+
.stream()
191+
.map(v -> (Object)v)
192+
.collect(Collectors.toList());
193+
} catch (Exception e) {
194+
throw new RuntimeException("Query execution failed: " + query, e);
195+
}
196+
}
197+
198+
@Override
199+
public String getDialect() {
200+
return DIALECT_JANUSGRAPH;
201+
}
202+
203+
@Override
204+
public void replaceSeed(Object wrapper, Object newSeed) {
205+
((VertexWrapper)wrapper).setVertex((Vertex)newSeed);
206+
}
207+
208+
/**
209+
* @return associated JanusGraph instance
210+
*/
211+
public JanusGraph getGraph() {
212+
return graph;
213+
}
214+
215+
/**
216+
* @return GraphTraversalSource for this graph
217+
*/
218+
public GraphTraversalSource traversal() {
219+
return g;
220+
}
221+
222+
/**
223+
* Map Java class to JanusGraph data type
224+
* @param javaClass Java class to map
225+
* @return JanusGraph compatible data type class
226+
*/
227+
private Class<?> getJanusGraphDataType(Class<?> javaClass) {
228+
if (String.class.equals(javaClass)) return String.class;
229+
if (Integer.class.equals(javaClass) || int.class.equals(javaClass)) return Integer.class;
230+
if (Long.class.equals(javaClass) || long.class.equals(javaClass)) return Long.class;
231+
if (Double.class.equals(javaClass) || double.class.equals(javaClass)) return Double.class;
232+
if (Float.class.equals(javaClass) || float.class.equals(javaClass)) return Float.class;
233+
if (Boolean.class.equals(javaClass) || boolean.class.equals(javaClass)) return Boolean.class;
234+
return null;
235+
}
236+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package org.orienteer.transponder.janusgraph;
2+
3+
import java.util.Arrays;
4+
import java.util.HashSet;
5+
import java.util.List;
6+
import java.util.Set;
7+
8+
import org.apache.tinkerpop.gremlin.structure.Element;
9+
import org.apache.tinkerpop.gremlin.structure.Vertex;
10+
import org.apache.tinkerpop.gremlin.structure.Edge;
11+
import org.orienteer.transponder.Transponder;
12+
13+
/**
14+
* Utility class for JanusGraph specific operations
15+
*/
16+
public class JanusGraphUtils {
17+
18+
private static final Set<Class<?>> SUPPORTED_PROPERTY_CLASSES = new HashSet<>(Arrays.asList(
19+
String.class, Integer.class, int.class, Long.class, long.class,
20+
Double.class, double.class, Float.class, float.class,
21+
Boolean.class, boolean.class, Character.class, char.class,
22+
Byte.class, byte.class, Short.class, short.class
23+
));
24+
25+
/**
26+
* Check if provided class is supported as property by JanusGraph
27+
* @param clazz class to check
28+
* @return true if class can be stored as JanusGraph property
29+
*/
30+
public static boolean isSupportedPropertyClass(Class<?> clazz) {
31+
return SUPPORTED_PROPERTY_CLASSES.contains(clazz);
32+
}
33+
34+
/**
35+
* Get expected TinkerPop Element class for given wrapper class
36+
* @param wrapperClass class to analyze
37+
* @return Vertex.class, Edge.class or null if not a graph element wrapper
38+
*/
39+
public static Class<? extends Element> getExpectedElementClass(Class<?> wrapperClass) {
40+
if(Transponder.ITransponderHolder.class.isAssignableFrom(wrapperClass)) {
41+
// This is a wrapper - determine if it wraps Vertex or Edge
42+
// For now, assume all wrappers are Vertices unless specified otherwise
43+
return Vertex.class;
44+
}
45+
46+
if(Vertex.class.isAssignableFrom(wrapperClass)) return Vertex.class;
47+
if(Edge.class.isAssignableFrom(wrapperClass)) return Edge.class;
48+
49+
return null;
50+
}
51+
52+
/**
53+
* Convert vertex to its label (type)
54+
* @param vertex vertex to get label from
55+
* @return vertex label or null
56+
*/
57+
public static String vertexToType(Vertex vertex) {
58+
if(vertex != null) {
59+
return vertex.label();
60+
}
61+
return null;
62+
}
63+
64+
/**
65+
* Convert element to list of types/labels
66+
* @param element element to get labels from
67+
* @return list of labels
68+
*/
69+
public static List<String> elementToType(Element element) {
70+
if(element != null) {
71+
return Arrays.asList(element.label());
72+
}
73+
return Arrays.asList();
74+
}
75+
}

0 commit comments

Comments
 (0)