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+ }
0 commit comments