diff --git a/.gitignore b/.gitignore index a1c2a238a965f004ff76978ac1086aa6fe95caea..080cd0dfa5f4986733ab99f93b832f5da5904c23 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,16 @@ -# Compiled class file -*.class - -# Log file +*.iml +.idea/ +.project +.settings/ +.classpath +.DS_Store +.class +target/ +lib/ +metastore_db *.log - -# BlueJ files -*.ctxt - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.nar -*.ear +.evosuite +git.properties +*.versionsBackup *.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* +dependency_tree.txt diff --git a/graph-common/pom.xml b/graph-common/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..89e025b3feaeacdf0c3ac7b771e7ae26b256ef3a --- /dev/null +++ b/graph-common/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + + org.studiox + graph + 1.0.5-SNAPSHOT + + org.studiox.graph + graph-common + 1.0.5-SNAPSHOT + jar + + + junit + junit + test + + + commons-configuration + commons-configuration + + + diff --git a/graph-common/src/main/java/org/studiox/graph/common/GraphConfig.java b/graph-common/src/main/java/org/studiox/graph/common/GraphConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..b97e9b7e507821fdd7c794e7d1bc484cf9a162fe --- /dev/null +++ b/graph-common/src/main/java/org/studiox/graph/common/GraphConfig.java @@ -0,0 +1,66 @@ +package org.studiox.graph.common; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.commons.lang.StringUtils; +import org.studiox.graph.common.exception.GraphException; + +import java.net.URL; + +import static org.studiox.graph.common.GraphConstant.ConfigConstant.CONFIG_FILE_NAME; +import static org.studiox.graph.common.GraphConstant.JanusGraphConfigConstant.CONFIG_PREFIX; +import static org.studiox.graph.common.exception.GraphExceptionCode.*; + +public class GraphConfig { + + private static volatile Configuration config = null; + + public static void forceReload() { + if (config != null) { + synchronized (GraphConfig.class) { + if (config != null) { + config = null; + } + } + } + } + + private static Configuration getConfig(String fileName) throws GraphException { + if (StringUtils.isBlank(fileName)) { + return null; + } + URL url = GraphConfig.class.getClassLoader().getResource(fileName); + if (null == url) { + url = GraphConfig.class.getClassLoader().getResource("/" + fileName); + } + if (null == url) { + throw new GraphException("Config fileName='" + fileName + "'", CONFIG_FILE_NOT_EXIST); + } + try { + return new PropertiesConfiguration(url); + } catch (ConfigurationException e) { + throw new GraphException(e.getMessage(), e, CONFIG_FORMAT_NOT_CORRECT); + } + } + + private static Configuration buildDefaultGraphConfig() throws GraphException { + config = getConfig(CONFIG_FILE_NAME); + try { + return config.subset(CONFIG_PREFIX); + } catch (Throwable t) { + throw new GraphException("Config prefix='" + CONFIG_PREFIX + "'", CONFIG_PREFIX_NOT_EXIST); + } + } + + public static Configuration getGraphConfig() throws GraphException { + if (config == null) { + synchronized (GraphConfig.class) { + if (config == null) { + config = buildDefaultGraphConfig(); + } + } + } + return config; + } +} diff --git a/graph-common/src/main/java/org/studiox/graph/common/GraphConstant.java b/graph-common/src/main/java/org/studiox/graph/common/GraphConstant.java new file mode 100644 index 0000000000000000000000000000000000000000..5b5bba0a54f9155dd535dde658d4d2c002f7b177 --- /dev/null +++ b/graph-common/src/main/java/org/studiox/graph/common/GraphConstant.java @@ -0,0 +1,80 @@ +package org.studiox.graph.common; + +public class GraphConstant { + + public static class ConfigConstant { + + public static final String CONFIG_FILE_NAME = "graph.properties"; + } + + public static class Separator { + + public static final String DOT = "."; + + public static final String PATH = "/"; + + public static final String COMMA = ","; + } + + public static class JanusGraphConfigConstant { + + public static final String CONFIG_PREFIX = "graph"; + + public static final String GREMLIN_GRAPH = "gremlin.graph"; + + public static final String JANUSGRAPH_FACTORY = "org.janusgraph.core.JanusGraphFactory"; + + public static final String STORAGE_BACKEND = "storage.backend"; + + public static final String BACKEND_MEMORY = "inmemory"; + + public static final String BACKEND_HBASE = "hbase"; + + public static final String STORAGE_HOSTNAME = "storage.hostname"; + + public static final String STORAGE_HBASE_TABLE = "storage.hbase.table"; + + public static final String STORAGE_HBASE_EXT_ZOOKEEPER_ZNODE_PARENT = + "storage.hbase.ext.zookeeper.znode.parent"; + + public static final String INDEX_PREFIX = "index"; + + public static final String INDEX_SEARCH_BACKEND = "index.search.backend"; + + public static final String INDEX_SEARCH_BACKEND_ELASTICSEARCH = "elasticsearch"; + + public static final String INDEX_SEARCH_HOSTNAME = "index.search.hostname"; + } + + public class JanusGraphInstanceConstant { + public static final String GRAPH_UNIQUE_INSTANCE_ID = "graph.unique-instance-id"; + + public static final String GRAPH_UNIQUE_INSTANCE_PREFIX = "unique_instance_id_"; + } + + public class JanusGraphLabelPropertyKeyIndexConstant { + public static final String LABEL_VERTEX = "vertex_label"; + + public static final String LABEL_EDGE = "edge_label"; + + public static final String VERTEX_PROPERTY_UNIQ = "vertex_uniq"; + + public static final String VERTEX_PROPERTY_TYPE = "vertex_type"; + + public static final String VERTEX_PROPERTY_DELETED = "vertex_deleted"; + + public static final String VERTEX_PROPERTY_CREATE_TIME = "vertex_createTime"; + + public static final String EDGE_PROPERTY_UNIQ = "edge_uniq"; + + public static final String EDGE_PROPERTY_TYPE = "edge_type"; + + public static final String EDGE_PROPERTY_DELETED = "edge_deleted"; + + public static final String EDGE_PROPERTY_SOURCE_VERTEX_UNIQ = "edge_sourceVertexUniq"; + + public static final String EDGE_PROPERTY_TARGET_VERTEX_UNIQ = "edge_targetVertexUniq"; + + public static final String EDGE_PROPERTY_CREATE_TIME = "edge_createTime"; + } +} diff --git a/graph-common/src/main/java/org/studiox/graph/common/GraphEdge.java b/graph-common/src/main/java/org/studiox/graph/common/GraphEdge.java new file mode 100644 index 0000000000000000000000000000000000000000..242b6a49615439ca5b9061ec564d3d2b015ec94b --- /dev/null +++ b/graph-common/src/main/java/org/studiox/graph/common/GraphEdge.java @@ -0,0 +1,108 @@ +package org.studiox.graph.common; + +import java.util.Objects; + +/** The Define of Common Graph Edge */ +public class GraphEdge { + + /** the unique name of the edge */ + private String uniq; + + /** the type of the edge */ + private String type; + + /** the status of the edge */ + private Boolean deleted; + + /** the unique name of the source vertex */ + private String sourceVertexUniq; + + /** the unique name of the target vertex */ + private String targetVertexUniq; + + /** the create time of the edge */ + private Long createTime; + + public String getUniq() { + return uniq; + } + + public void setUniq(String uniq) { + this.uniq = uniq; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Boolean getDeleted() { + return deleted; + } + + public void setDeleted(Boolean deleted) { + this.deleted = deleted; + } + + public String getSourceVertexUniq() { + return sourceVertexUniq; + } + + public void setSourceVertexUniq(String sourceVertexUniq) { + this.sourceVertexUniq = sourceVertexUniq; + } + + public String getTargetVertexUniq() { + return targetVertexUniq; + } + + public void setTargetVertexUniq(String targetVertexUniq) { + this.targetVertexUniq = targetVertexUniq; + } + + public Long getCreateTime() { + return createTime; + } + + public void setCreateTime(Long createTime) { + this.createTime = createTime; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GraphEdge graphEdge = (GraphEdge) o; + return Objects.equals(uniq, graphEdge.uniq) + && Objects.equals(type, graphEdge.type) + && Objects.equals(deleted, graphEdge.deleted) + && Objects.equals(sourceVertexUniq, graphEdge.sourceVertexUniq) + && Objects.equals(targetVertexUniq, graphEdge.targetVertexUniq) + && Objects.equals(createTime, graphEdge.createTime); + } + + @Override + public int hashCode() { + return Objects.hash(uniq, type, deleted, sourceVertexUniq, targetVertexUniq, createTime); + } + + @Override + public String toString() { + final StringBuffer stringBuffer = new StringBuffer("GraphEdge{"); + stringBuffer.append("uniq='").append(uniq).append('\''); + stringBuffer.append(", type='").append(type).append('\''); + stringBuffer.append(", deleted=").append(deleted); + stringBuffer.append(", sourceVertexUniq='").append(sourceVertexUniq).append('\''); + stringBuffer.append(", targetVertexUniq='").append(targetVertexUniq).append('\''); + stringBuffer.append(", createTime=").append(createTime); + stringBuffer.append('}'); + return stringBuffer.toString(); + } +} diff --git a/graph-common/src/main/java/org/studiox/graph/common/GraphLineage.java b/graph-common/src/main/java/org/studiox/graph/common/GraphLineage.java new file mode 100644 index 0000000000000000000000000000000000000000..5f5c5664436981efdfcbbb726b9e545aed03fcbd --- /dev/null +++ b/graph-common/src/main/java/org/studiox/graph/common/GraphLineage.java @@ -0,0 +1,70 @@ +package org.studiox.graph.common; + +import java.util.List; +import java.util.Objects; + +/** The Define of Common Graph Lineage */ +public class GraphLineage { + + /** the define current vertex of graph lineage */ + private GraphVertex current; + + /** the vertices of the graph lineage */ + private List vertices; + + /** the edges of the graph lineage */ + private List edges; + + public GraphVertex getCurrent() { + return current; + } + + public void setCurrent(GraphVertex current) { + this.current = current; + } + + public List getVertices() { + return vertices; + } + + public void setVertices(List vertices) { + this.vertices = vertices; + } + + public List getEdges() { + return edges; + } + + public void setEdges(List edges) { + this.edges = edges; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GraphLineage that = (GraphLineage) o; + return Objects.equals(current, that.current) + && Objects.equals(vertices, that.vertices) + && Objects.equals(edges, that.edges); + } + + @Override + public int hashCode() { + return Objects.hash(current, vertices, edges); + } + + @Override + public String toString() { + final StringBuffer stringBuffer = new StringBuffer("GraphLineage{"); + stringBuffer.append("current=").append(current); + stringBuffer.append(", vertices=").append(vertices); + stringBuffer.append(", edges=").append(edges); + stringBuffer.append('}'); + return stringBuffer.toString(); + } +} diff --git a/graph-common/src/main/java/org/studiox/graph/common/GraphVertex.java b/graph-common/src/main/java/org/studiox/graph/common/GraphVertex.java new file mode 100644 index 0000000000000000000000000000000000000000..5d001ec914fca9eab315c1058754f573ea064497 --- /dev/null +++ b/graph-common/src/main/java/org/studiox/graph/common/GraphVertex.java @@ -0,0 +1,82 @@ +package org.studiox.graph.common; + +import java.util.Objects; + +/** The Define of Common Graph Vertex */ +public class GraphVertex { + + /** the unique name of the vertex */ + private String uniq; + + /** the type of the vertex */ + private String type; + + /** the status of the vertex */ + private Boolean deleted; + + /** the create time of the vertex */ + private Long createTime; + + public String getUniq() { + return uniq; + } + + public void setUniq(String uniq) { + this.uniq = uniq; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Boolean getDeleted() { + return deleted; + } + + public void setDeleted(Boolean deleted) { + this.deleted = deleted; + } + + public Long getCreateTime() { + return createTime; + } + + public void setCreateTime(Long createTime) { + this.createTime = createTime; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GraphVertex that = (GraphVertex) o; + return Objects.equals(uniq, that.uniq) + && Objects.equals(type, that.type) + && Objects.equals(deleted, that.deleted) + && Objects.equals(createTime, that.createTime); + } + + @Override + public int hashCode() { + return Objects.hash(uniq, type, deleted, createTime); + } + + @Override + public String toString() { + final StringBuffer stringBuffer = new StringBuffer("GraphVertex{"); + stringBuffer.append("uniq='").append(uniq).append('\''); + stringBuffer.append(", type='").append(type).append('\''); + stringBuffer.append(", deleted=").append(deleted); + stringBuffer.append(", createTime=").append(createTime); + stringBuffer.append('}'); + return stringBuffer.toString(); + } +} diff --git a/graph-common/src/main/java/org/studiox/graph/common/IGraphFactory.java b/graph-common/src/main/java/org/studiox/graph/common/IGraphFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..129b8ddf00eca08b09b4874df4957d5e6770e8d9 --- /dev/null +++ b/graph-common/src/main/java/org/studiox/graph/common/IGraphFactory.java @@ -0,0 +1,13 @@ +package org.studiox.graph.common; + +/** the interface of the common graph factory */ +public interface IGraphFactory { + + /** + * Get the define type of the graph instance + * + * @param graphType + * @return + */ + IGraphWrapper getGraphWarpper(String graphType); +} diff --git a/graph-common/src/main/java/org/studiox/graph/common/IGraphWrapper.java b/graph-common/src/main/java/org/studiox/graph/common/IGraphWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..8f9cadebccc8fe6963b3fc3044e94b98d93a0b71 --- /dev/null +++ b/graph-common/src/main/java/org/studiox/graph/common/IGraphWrapper.java @@ -0,0 +1,306 @@ +package org.studiox.graph.common; + +import org.studiox.graph.common.exception.GraphException; + +import java.util.List; +import java.util.Map; + +/** the interface of the common graph */ +public interface IGraphWrapper { + + /** init method */ + void open() throws GraphException; + + /** close method */ + void close(); + + /** + * Create new vertex + * + *

uniq is the unique name to one vertex + * + * @param uniq + * @param type + * @param createTime + * @return Vertex ID(Long) + */ + Long createVertex(String uniq, String type, Long createTime); + + /** + * Create new vertex without pre-check + * + *

uniq is the unique name to one vertex + * + * @param uniq + * @param type + * @param createTime + * @return Vertex ID(Long) + */ + Long createVertexNotPreCheck(String uniq, String type, Long createTime); + + /** + * Update the existed vertex + * + *

uniq is the unique name to one vertex + * + * @param uniq + * @param type + * @param createTime + * @return + */ + boolean updateVertex(String uniq, String type, Long createTime); + + /** + * Update the existed vertex, also update the graph edge property + * + *

oldUniq is the unique name to the old vertex + * + *

newUniq is the unique name change from the old vertex unique name + * + * @param oldUniq + * @param newUniq + * @param type + * @param createTime + * @return + */ + boolean updateVertex(String oldUniq, String newUniq, String type, Long createTime); + + /** + * Soft delete the existed vertices + * + *

uniq is the unique name to one vertex + * + *

type could point to multi vertices + * + * @param uniq + * @param type + * @return + */ + boolean softDeleteVertices(String uniq, String type); + + /** + * Recover the deleted vertices + * + *

uniq is the unique name to one vertex + * + *

type could point to multi vertices + * + * @param uniq + * @param type + * @return + */ + boolean recoverDeletedVertices(String uniq, String type); + + /** + * Hard delete the existed vertices + * + *

uniq is the unique name to one vertex + * + *

type could point to multi vertices + * + * @param uniq + * @param type + * @return + */ + boolean hardDeleteVertices(String uniq, String type); + + /** + * Create new edge + * + *

uniq is the unique name to one edge + * + * @param uniq + * @param type + * @param sourceVertexUniq + * @param targetVertexUniq + * @param createTime + * @return Edge ID(String) + */ + String createEdge( + String uniq, String type, String sourceVertexUniq, String targetVertexUniq, Long createTime); + + /** + * Create new edge without pre-check + * + *

uniq is the unique name to one edge + * + * @param uniq + * @param type + * @param sourceVertexId + * @param targetVertexId + * @param createTime + * @return Edge ID(String) + */ + String createEdgeNotPreCheck( + String uniq, String type, Long sourceVertexId, Long targetVertexId, Long createTime); + + /** + * Soft delete the existed edges + * + *

uniq is the unique name to one edge + * + *

type、sourceVertexUniq and targetVertexUniq could point to multi edges + * + * @param uniq + * @param type + * @param sourceVertexUniq + * @param targetVertexUniq + * @return + */ + boolean softDeleteEdges( + String uniq, String type, String sourceVertexUniq, String targetVertexUniq); + + /** + * Recover the deleted edges + * + *

uniq is the unique name to one edge + * + *

type、sourceVertexUniq and targetVertexUniq could point to multi edges + * + * @param uniq + * @param type + * @param sourceVertexUniq + * @param targetVertexUniq + * @return + */ + boolean recoverDeletedEdges( + String uniq, String type, String sourceVertexUniq, String targetVertexUniq); + + /** + * Hard delete the existed edges + * + *

uniq is the unique name to one edge + * + *

type、sourceVertexUniq and targetVertexUniq could point to multi edges + * + * @param uniq + * @param type + * @param sourceVertexUniq + * @param targetVertexUniq + * @return + */ + boolean hardDeleteEdges( + String uniq, String type, String sourceVertexUniq, String targetVertexUniq); + + /** + * Get the chosen vertex by uniq + * + *

uniq is the unique name to one vertex + * + * @param uniq + * @return + */ + GraphVertex getVertex(String uniq); + + /** + * Get some vertices by type property + * + *

type is the property value belong to the vertex + * + * @param type + * @return + */ + List getVerticesByType(String type); + + /** + * Get the chosen edge by uniq + * + *

uniq is the unique name to one edge + * + * @param uniq + * @return + */ + GraphEdge getEdge(String uniq); + + /** + * Get the chosen edges + * + *

type、sourceVertexUniq and targetVertexUniq could point to multi edges + * + * @param type + * @param sourceVertexUniq + * @param targetVertexUniq + * @return + */ + List getEdges(String type, String sourceVertexUniq, String targetVertexUniq); + + /** + * Get source vertices and edges + * + *

vertexUniq is the unique value to the vertex + * + * @param vertexUniq + * @return + */ + Map getSourceVertexEdgeMap(String vertexUniq); + + /** + * Get target vertices and edges + * + *

vertexUniq is the unique value to the vertex + * + * @param vertexUniq + * @return + */ + Map getTargetVertexEdgeMap(String vertexUniq); + + /** + * Get Graph Impact Info + * + *

vertexUniq is the unique value to the vertex + * + *

when depth <= 0, means traverse the entire graph + * + *

when depth > 0, means the define depth result + * + *

skipDeleted=false, return all the vertices(include the deleted vertices) + * + *

skipDeleted=true, return undeleted the vertices + * + * @param vertexUniq + * @param depth + * @param skipDeleted + * @return + */ + GraphLineage getImpact(String vertexUniq, Integer depth, boolean skipDeleted); + + /** + * Get Graph Lineage Info + * + *

vertexUniq is the unique value to the vertex + * + *

when depth <= 0, means traverse the entire graph + * + *

when depth > 0, means the define depth result + * + *

skipDeleted=false, return all the vertices(include the deleted vertices) + * + *

skipDeleted=true, return undeleted the vertices + * + * @param vertexUniq + * @param depth + * @param skipDeleted + * @return + */ + GraphLineage getLineage(String vertexUniq, Integer depth, boolean skipDeleted); + + /** + * Get Both Graph Impact and Lineage Info + * + *

vertexUniq is the unique value to the vertex + * + *

when depth <= 0, means traverse the entire graph + * + *

when depth > 0, means the define depth result + * + *

skipDeleted=false, return all the vertices(include the deleted vertices) + * + *

skipDeleted=true, return undeleted the vertices + * + * @param vertexUniq + * @param depth + * @param skipDeleted + * @return + */ + GraphLineage getFullLineage(String vertexUniq, Integer depth, boolean skipDeleted); +} diff --git a/graph-common/src/main/java/org/studiox/graph/common/exception/GraphException.java b/graph-common/src/main/java/org/studiox/graph/common/exception/GraphException.java new file mode 100644 index 0000000000000000000000000000000000000000..6deda786a4f781ae9e205b632b4764349f931669 --- /dev/null +++ b/graph-common/src/main/java/org/studiox/graph/common/exception/GraphException.java @@ -0,0 +1,52 @@ +package org.studiox.graph.common.exception; + +public class GraphException extends Exception { + + private GraphExceptionCode exceptionCode; + + public GraphException(GraphExceptionCode exceptionCode) { + super(); + this.setExceptionCode(exceptionCode); + } + + public GraphException(String message, GraphExceptionCode exceptionCode) { + super(exceptionCode.getExceptionMsg(message)); + this.setExceptionCode(exceptionCode); + } + + public GraphException(Throwable cause, GraphExceptionCode exceptionCode) { + super(cause); + this.setExceptionCode(exceptionCode); + } + + public GraphException(String message, Throwable cause, GraphExceptionCode exceptionCode) { + super(exceptionCode.getExceptionMsg(message), cause); + this.setExceptionCode(exceptionCode); + } + + public GraphException( + String message, + Throwable cause, + boolean enableSuppression, + boolean writableStackTrace, + GraphExceptionCode exceptionCode) { + super(exceptionCode.getExceptionMsg(message), cause, enableSuppression, writableStackTrace); + this.setExceptionCode(exceptionCode); + } + + public GraphExceptionCode getExceptionCode() { + return exceptionCode; + } + + public void setExceptionCode(GraphExceptionCode exceptionCode) { + this.exceptionCode = exceptionCode; + } + + @Override + public String toString() { + final StringBuffer stringBuffer = new StringBuffer("GraphException{"); + stringBuffer.append("exceptionCode=").append(exceptionCode); + stringBuffer.append('}'); + return stringBuffer.toString(); + } +} diff --git a/graph-common/src/main/java/org/studiox/graph/common/exception/GraphExceptionCode.java b/graph-common/src/main/java/org/studiox/graph/common/exception/GraphExceptionCode.java new file mode 100644 index 0000000000000000000000000000000000000000..2e9da32fa44beee1cef3249cbb01a4e49003ce2b --- /dev/null +++ b/graph-common/src/main/java/org/studiox/graph/common/exception/GraphExceptionCode.java @@ -0,0 +1,59 @@ +package org.studiox.graph.common.exception; + +public enum GraphExceptionCode { + CONFIG_FILE_NOT_EXIST("GE0001", "图库配置文件不存在,请仔细检查!"), + + CONFIG_PREFIX_NOT_EXIST("GE0002", "图库配置项前缀不存在,请仔细检查!"), + + CONFIG_FORMAT_NOT_CORRECT("GE0003", "图库配置文件格式不正确,请仔细检查!"), + + JANUS_GRAPH_EXCEPTION("GE0004", "JanusGraphs实例方法调用异常,请详细查看异常栈等日志信息!"); + + private String code; + + private String desc; + + GraphExceptionCode(String code, String desc) { + this.setCode(code); + this.setDesc(desc); + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getExceptionMsg() { + return "Graph Exception code='" + getCode() + "' and desc='" + getDesc() + "'."; + } + + public String getExceptionMsg(String detailMsg) { + return "Graph Exception code='" + + getCode() + + "' and desc='" + + getDesc() + + "', with the detail msg='" + + detailMsg + + "'."; + } + + @Override + public String toString() { + final StringBuffer stringBuffer = new StringBuffer("GraphExceptionCode{"); + stringBuffer.append("code='").append(code).append('\''); + stringBuffer.append(", desc='").append(desc).append('\''); + stringBuffer.append('}'); + return stringBuffer.toString(); + } +} diff --git a/graph-common/src/main/java/org/studiox/graph/common/exception/GraphRuntimeException.java b/graph-common/src/main/java/org/studiox/graph/common/exception/GraphRuntimeException.java new file mode 100644 index 0000000000000000000000000000000000000000..955dbe44f3e2ba9034f69be0f5733e037a4df6e0 --- /dev/null +++ b/graph-common/src/main/java/org/studiox/graph/common/exception/GraphRuntimeException.java @@ -0,0 +1,52 @@ +package org.studiox.graph.common.exception; + +public class GraphRuntimeException extends RuntimeException { + + private GraphExceptionCode exceptionCode; + + public GraphRuntimeException(GraphExceptionCode exceptionCode) { + super(); + this.exceptionCode = exceptionCode; + } + + public GraphRuntimeException(String message, GraphExceptionCode exceptionCode) { + super(exceptionCode.getExceptionMsg(message)); + this.exceptionCode = exceptionCode; + } + + public GraphRuntimeException(Throwable cause, GraphExceptionCode exceptionCode) { + super(cause); + this.exceptionCode = exceptionCode; + } + + public GraphRuntimeException(String message, Throwable cause, GraphExceptionCode exceptionCode) { + super(exceptionCode.getExceptionMsg(message), cause); + this.exceptionCode = exceptionCode; + } + + public GraphRuntimeException( + String message, + Throwable cause, + boolean enableSuppression, + boolean writableStackTrace, + GraphExceptionCode exceptionCode) { + super(exceptionCode.getExceptionMsg(message), cause, enableSuppression, writableStackTrace); + this.exceptionCode = exceptionCode; + } + + public GraphExceptionCode getExceptionCode() { + return exceptionCode; + } + + public void setExceptionCode(GraphExceptionCode exceptionCode) { + this.exceptionCode = exceptionCode; + } + + @Override + public String toString() { + final StringBuffer stringBuffer = new StringBuffer("GraphRuntimeException{"); + stringBuffer.append("exceptionCode=").append(exceptionCode); + stringBuffer.append('}'); + return stringBuffer.toString(); + } +} diff --git a/graph-common/src/test/java/org/studiox/graph/common/GraphConfigTest.java b/graph-common/src/test/java/org/studiox/graph/common/GraphConfigTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c69d962d21bef516628c8b06cfebffdd4a1d43f8 --- /dev/null +++ b/graph-common/src/test/java/org/studiox/graph/common/GraphConfigTest.java @@ -0,0 +1,32 @@ +package org.studiox.graph.common; + +import org.apache.commons.configuration.Configuration; +import org.junit.*; +import org.junit.runners.MethodSorters; +import org.studiox.graph.common.exception.GraphException; + +import static org.studiox.graph.common.GraphConstant.JanusGraphConfigConstant.GREMLIN_GRAPH; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class GraphConfigTest { + + private static Configuration config = null; + + @BeforeClass + public static void setUp() { + GraphConfig.forceReload(); + } + + @AfterClass + public static void tearDown() { + config = null; + } + + @Test + public void test_01_getDefaultGraphConfig() throws GraphException { + config = GraphConfig.getGraphConfig(); + Assert.assertNotNull(config); + String actualGremlinGraph = config.getString(GREMLIN_GRAPH); + Assert.assertEquals("org.janusgraph.core.JanusGraphFactory", actualGremlinGraph); + } +} diff --git a/graph-common/src/test/resources/graph.properties b/graph-common/src/test/resources/graph.properties new file mode 100755 index 0000000000000000000000000000000000000000..c42c92eae0e647da813529c00d0be4d4051c1ba8 --- /dev/null +++ b/graph-common/src/test/resources/graph.properties @@ -0,0 +1,30 @@ +################################################################### +graph.force-index=true +graph.schema.default=none +graph.storage.batch-loading=true +################################################################### +graph.gremlin.graph=org.janusgraph.core.JanusGraphFactory +################################################################### +#graph.storage.backend=inmemory +################################################################### +graph.cache.db-cache=false +graph.ids.block-size=20000000 +graph.ids.renew-timeout=3600000 +graph.storage.buffer-size=20240 +graph.storage.read-attempts=100 +graph.storage.write-attempts=100 +graph.storage.attempt-wait=1000 +################################################################### +graph.storage.backend=hbase +graph.storage.hostname=localhost +graph.storage.hbase.table=graph_table_1 +graph.storage.hbase.ext.zookeeper.znode.parent=/hbase1 +graph.storage.hbase.ext.hbase.rpc.timeout=300000 +graph.storage.hbase.ext.hbase.client.operation.timeout=300000 +graph.storage.hbase.ext.hbase.client.scanner.timeout.period=300000 +graph.storage.hbase.region-count=9 +graph.storage.hbase.regions-per-server=3 +################################################################### +graph.index.search.backend=elasticsearch +graph.index.search.hostname=localhost:9200 +################################################################### diff --git a/graph-janusgraph/janusgraph-common/pom.xml b/graph-janusgraph/janusgraph-common/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..c1998d975c2e0b4b989dfb76286057125ae64702 --- /dev/null +++ b/graph-janusgraph/janusgraph-common/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + + org.studiox.graph + graph-janusgraph + 1.0.5-SNAPSHOT + + org.studiox.graph.janusgraph + janusgraph-common + 1.0.5-SNAPSHOT + jar + + + junit + junit + provided + + + org.studiox.graph + graph-common + + + diff --git a/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/AbstractJanusGraph.java b/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/AbstractJanusGraph.java new file mode 100644 index 0000000000000000000000000000000000000000..4a77f1666e17564828e641fbafe0bc1f2553972e --- /dev/null +++ b/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/AbstractJanusGraph.java @@ -0,0 +1,470 @@ +package org.studiox.graph.janusgraph.common; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang.StringUtils; +import org.studiox.graph.common.GraphConfig; +import org.studiox.graph.common.IGraphWrapper; +import org.studiox.graph.common.exception.GraphException; + +import java.util.Iterator; +import java.util.concurrent.ExecutionException; + +import static org.studiox.graph.common.GraphConstant.JanusGraphConfigConstant.*; +import static org.studiox.graph.common.GraphConstant.JanusGraphInstanceConstant.GRAPH_UNIQUE_INSTANCE_ID; +import static org.studiox.graph.common.GraphConstant.JanusGraphInstanceConstant.GRAPH_UNIQUE_INSTANCE_PREFIX; +import static org.studiox.graph.common.GraphConstant.JanusGraphLabelPropertyKeyIndexConstant.*; +import static org.studiox.graph.common.GraphConstant.Separator.DOT; +import static org.studiox.graph.common.exception.GraphExceptionCode.JANUS_GRAPH_EXCEPTION; + +/** + * 遍历操作 + * + *

out()、in()、both()、outE()、inE()、bothE()、outV()、inV()、bothV()、otherV() + * + *

遍历(以定点为基础) + * + *

1.out(label):根据指定的 Edge Label 来访问顶点的 OUT 方向邻接点(可以是零个 Edge Label,代表所有类型边 + * + *

也可以一个或多个 Edge Label,代表任意给定 Edge Label 的边,下同) + * + *

2.in(label):根据指定的 Edge Label 来访问顶点的 IN 方向邻接点 + * + *

3.both(label):根据指定的 Edge Label 来访问顶点的双向邻接点 + * + *

4.outE(label): 根据指定的 Edge Label 来访问顶点的 OUT 方向邻接边 + * + *

5.inE(label):根据指定的 Edge Label 来访问顶点的 IN 方向邻接边 + * + *

6.bothE(label):根据指定的 Edge Label 来访问顶点的双向邻接边 + * + *

遍历(以边为基础) + * + *

1.outV():访问边的出顶点,出顶点是指边的起始顶点 + * + *

2.inV():访问边的入顶点,入顶点是指边的目标顶点,也就是箭头指向的顶点 + * + *

3.bothV():访问边的双向顶点 + * + *

4.otherV():访问边的伙伴顶点,即相对于基准顶点而言的另一端的顶点 + * + *

查询路径path + * + *

path()、simplePath()、cyclicPath() 路径:就是经过的顶点成为路径 + * + *

Path说明 + * + *

在使用Gremlin对图进行分析时,关注点有时并不仅仅在最终到达的顶点、边或者属性上,通过什么样的路径到达最终的顶点、边和属性同样重要 + * + *

此时可以借助path()来获取经过的路径信息。 + * + *

path()返回当前遍历过的所有路径。有时需要对路径进行过滤,只选择没有环路的路径或者选择包含环路的路径 + * + *

Gremlin针对这种需求提供了两种过滤路径的step:simplePath()和cyclicPath() + * + *

path()返回所有路径,包含有环路径和无环路径 simplePath(),过滤掉路径中含有环路的对象,只保留路径中不含有环路的对象 + * + *

cyclicPath(),过滤掉路径中不含有环路的对象,只保留路径中含有环路的对象 + * + *

循环操作 + * + *

repeat()、times()、until()、emit()、loops() + * + *

repeat+times 根据次数来重复执行语句 + * + *

repeat() + until() 根据条件来重复执行语句 + * + *

repeat() + emit() 收集执行过程中的数据 + * + *

repeat() + loops() 根据最大次数限制来重复执行语句 + */ +public abstract class AbstractJanusGraph implements IGraphWrapper { + + private static final String VERTEX_UNIQ_ONLY_INDEX = "VERTEX_UNIQ_ONLY_INDEX"; + + private static final String VERTEX_UNIQ_INDEX = "VERTEX_UNIQ_INDEX"; + + private static final String VERTEX_TYPE_INDEX = "VERTEX_TYPE_INDEX"; + + private static final String VERTEX_DELETED_INDEX = "VERTEX_DELETED_INDEX"; + + private static final String VERTEX_CREATE_TIME_INDEX = "VERTEX_CREATE_TIME_INDEX"; + + private static final String EDGE_UNIQ_INDEX = "EDGE_UNIQ_INDEX"; + + private static final String EDGE_TYPE_INDEX = "EDGE_TYPE_INDEX"; + + private static final String EDGE_DELETED_INDEX = "EDGE_DELETED_INDEX"; + + private static final String EDGE_SOURCE_UNIQ_INDEX = "EDGE_SOURCE_UNIQ_INDEX"; + + private static final String EDGE_TARGET_UNIQ_INDEX = "EDGE_TARGET_UNIQ_INDEX"; + + private static final String EDGE_CREATE_TIME_INDEX = "EDGE_CREATE_TIME_INDEX"; + + private static final String EDGE_SOURCE_TARGET_UNIQ_INDEX = "EDGE_SOURCE_TARGET_UNIQ_INDEX"; + + private static final String EDGE_TYPE_SOURCE_UNIQ_INDEX = "EDGE_TYPE_SOURCE_UNIQ_INDEX"; + + private static final String EDGE_TYPE_TARGET_UNIQ_INDEX = "EDGE_TYPE_TARGET_UNIQ_INDEX"; + + private static final String EDGE_TYPE_SOURCE_TARGET_UNIQ_INDEX = + "EDGE_TYPE_SOURCE_TARGET_UNIQ_INDEX"; + + public abstract void setBuilderConfig(String key, Object value); + + public void buildGraphConfig() throws GraphException { + Configuration graphConfig = GraphConfig.getGraphConfig(); + Iterator iterator = graphConfig.getKeys(); + while (iterator.hasNext()) { + String key = iterator.next(); + setBuilderConfig(key, graphConfig.getProperty(key)); + } + } + + public void buildGraphConfig(Configuration config, String hbaseTableName) { + if (!config.containsKey(STORAGE_BACKEND) + || config.getString(STORAGE_BACKEND).equalsIgnoreCase(BACKEND_MEMORY)) { + setBuilderConfig(STORAGE_BACKEND, BACKEND_MEMORY); + setBuilderConfig(GREMLIN_GRAPH, JANUSGRAPH_FACTORY); + return; + } + Iterator iterator = config.getKeys(); + while (iterator.hasNext()) { + String key = iterator.next(); + if (key.substring(0, key.indexOf(DOT)).equalsIgnoreCase(INDEX_PREFIX)) { + continue; + } + setBuilderConfig(key, config.getProperty(key)); + } + if (StringUtils.isNotBlank(hbaseTableName)) { + setBuilderConfig(STORAGE_HBASE_TABLE, hbaseTableName); + } + setBuilderConfig( + GRAPH_UNIQUE_INSTANCE_ID, GRAPH_UNIQUE_INSTANCE_PREFIX + System.currentTimeMillis()); + } + + public abstract boolean mgmtNotContainsVertexLabel(String vertexLabelName); + + public abstract void mgmtMakeVertexLabel(String vertexLabelName); + + public abstract boolean mgmtNotContainsEdgeLabel(String edgeLabelName); + + public abstract void mgmtMakeEdgeLabel(String edgeLabelName); + + private void buildGraphLabel() { + if (mgmtNotContainsVertexLabel(LABEL_VERTEX)) { + mgmtMakeVertexLabel(LABEL_VERTEX); + } + if (mgmtNotContainsEdgeLabel(LABEL_EDGE)) { + mgmtMakeEdgeLabel(LABEL_EDGE); + } + } + + public abstract boolean mgmtNotContainsPropertyKey(String propertyKeyName); + + public abstract void mgmtMakePropertyKey(String propertyKeyName, Class zlass); + + private void buildGraphPropertyKey() { + if (mgmtNotContainsPropertyKey(VERTEX_PROPERTY_UNIQ)) { + mgmtMakePropertyKey(VERTEX_PROPERTY_UNIQ, String.class); + } + if (mgmtNotContainsPropertyKey(VERTEX_PROPERTY_TYPE)) { + mgmtMakePropertyKey(VERTEX_PROPERTY_TYPE, String.class); + } + if (mgmtNotContainsPropertyKey(VERTEX_PROPERTY_DELETED)) { + mgmtMakePropertyKey(VERTEX_PROPERTY_DELETED, Boolean.class); + } + if (mgmtNotContainsPropertyKey(VERTEX_PROPERTY_CREATE_TIME)) { + mgmtMakePropertyKey(VERTEX_PROPERTY_CREATE_TIME, Long.class); + } + + if (mgmtNotContainsPropertyKey(EDGE_PROPERTY_UNIQ)) { + mgmtMakePropertyKey(EDGE_PROPERTY_UNIQ, String.class); + } + if (mgmtNotContainsPropertyKey(EDGE_PROPERTY_TYPE)) { + mgmtMakePropertyKey(EDGE_PROPERTY_TYPE, String.class); + } + if (mgmtNotContainsPropertyKey(EDGE_PROPERTY_DELETED)) { + mgmtMakePropertyKey(EDGE_PROPERTY_DELETED, Boolean.class); + } + if (mgmtNotContainsPropertyKey(EDGE_PROPERTY_SOURCE_VERTEX_UNIQ)) { + mgmtMakePropertyKey(EDGE_PROPERTY_SOURCE_VERTEX_UNIQ, String.class); + } + if (mgmtNotContainsPropertyKey(EDGE_PROPERTY_TARGET_VERTEX_UNIQ)) { + mgmtMakePropertyKey(EDGE_PROPERTY_TARGET_VERTEX_UNIQ, String.class); + } + if (mgmtNotContainsPropertyKey(EDGE_PROPERTY_CREATE_TIME)) { + mgmtMakePropertyKey(EDGE_PROPERTY_CREATE_TIME, Long.class); + } + } + + public abstract boolean mgmtNotContainsGraphIndex(String indexName); + + public abstract void mgmtBuildVertexIndexOnlyWithOneKeyUnique( + String indexName, String propertyKeyName, String labelName); + + public abstract void mgmtBuildVertexIndexWithOneKey(String indexName, String propertyKeyName); + + public abstract void mgmtBuildEdgeIndexWithOneKey(String indexName, String propertyKeyName); + + public abstract void mgmtBuildEdgeIndexWithTwoKey( + String indexName, String firstPropertyKeyName, String secondPropertyKeyName); + + public abstract void mgmtBuildEdgeIndexWithThreeKey( + String indexName, + String firstPropertyKeyName, + String secondPropertyKeyName, + String thirdPropertyKeyName); + + public abstract boolean mgmtNotContainsRelationIndex( + String edgeLabelName, String relationIndexName); + + public abstract void mgmtBuildRelationIndexWithOneKey( + String edgeLabelName, String relationIndexName, String propertyKeyName); + + public abstract void mgmtBuildRelationIndexWithTwoKey( + String edgeLabelName, + String relationIndexName, + String firstPropertyKeyName, + String secondPropertyKeyName); + + public abstract void mgmtBuildRelationIndexWithThreeKey( + String edgeLabelName, + String relationIndexName, + String firstPropertyKeyName, + String secondPropertyKeyName, + String thirdPropertyKeyName); + + private void buildGraphVertexIndex() { + if (mgmtNotContainsGraphIndex(VERTEX_UNIQ_ONLY_INDEX)) { + mgmtBuildVertexIndexOnlyWithOneKeyUnique( + VERTEX_UNIQ_ONLY_INDEX, VERTEX_PROPERTY_UNIQ, LABEL_VERTEX); + } + if (mgmtNotContainsGraphIndex(VERTEX_UNIQ_INDEX)) { + mgmtBuildVertexIndexWithOneKey(VERTEX_UNIQ_INDEX, VERTEX_PROPERTY_UNIQ); + } + if (mgmtNotContainsGraphIndex(VERTEX_TYPE_INDEX)) { + mgmtBuildVertexIndexWithOneKey(VERTEX_TYPE_INDEX, VERTEX_PROPERTY_TYPE); + } + if (mgmtNotContainsGraphIndex(VERTEX_DELETED_INDEX)) { + mgmtBuildVertexIndexWithOneKey(VERTEX_DELETED_INDEX, VERTEX_PROPERTY_DELETED); + } + if (mgmtNotContainsGraphIndex(VERTEX_CREATE_TIME_INDEX)) { + mgmtBuildVertexIndexWithOneKey(VERTEX_CREATE_TIME_INDEX, VERTEX_PROPERTY_CREATE_TIME); + } + } + + private void buildGraphEdgeIndex() { + if (mgmtNotContainsGraphIndex(EDGE_UNIQ_INDEX)) { + mgmtBuildEdgeIndexWithOneKey(EDGE_UNIQ_INDEX, EDGE_PROPERTY_UNIQ); + } + if (mgmtNotContainsGraphIndex(EDGE_TYPE_INDEX)) { + mgmtBuildEdgeIndexWithOneKey(EDGE_TYPE_INDEX, EDGE_PROPERTY_TYPE); + } + if (mgmtNotContainsGraphIndex(EDGE_DELETED_INDEX)) { + mgmtBuildEdgeIndexWithOneKey(EDGE_DELETED_INDEX, EDGE_PROPERTY_DELETED); + } + if (mgmtNotContainsGraphIndex(EDGE_SOURCE_UNIQ_INDEX)) { + mgmtBuildEdgeIndexWithOneKey(EDGE_SOURCE_UNIQ_INDEX, EDGE_PROPERTY_SOURCE_VERTEX_UNIQ); + } + if (mgmtNotContainsGraphIndex(EDGE_TARGET_UNIQ_INDEX)) { + mgmtBuildEdgeIndexWithOneKey(EDGE_TARGET_UNIQ_INDEX, EDGE_PROPERTY_TARGET_VERTEX_UNIQ); + } + if (mgmtNotContainsGraphIndex(EDGE_CREATE_TIME_INDEX)) { + mgmtBuildEdgeIndexWithOneKey(EDGE_CREATE_TIME_INDEX, EDGE_PROPERTY_CREATE_TIME); + } + + if (mgmtNotContainsGraphIndex(EDGE_SOURCE_TARGET_UNIQ_INDEX)) { + mgmtBuildEdgeIndexWithTwoKey( + EDGE_SOURCE_TARGET_UNIQ_INDEX, + EDGE_PROPERTY_SOURCE_VERTEX_UNIQ, + EDGE_PROPERTY_TARGET_VERTEX_UNIQ); + } + if (mgmtNotContainsGraphIndex(EDGE_TYPE_SOURCE_UNIQ_INDEX)) { + mgmtBuildEdgeIndexWithTwoKey( + EDGE_TYPE_SOURCE_UNIQ_INDEX, EDGE_PROPERTY_TYPE, EDGE_PROPERTY_SOURCE_VERTEX_UNIQ); + } + if (mgmtNotContainsGraphIndex(EDGE_TYPE_TARGET_UNIQ_INDEX)) { + mgmtBuildEdgeIndexWithTwoKey( + EDGE_TYPE_TARGET_UNIQ_INDEX, EDGE_PROPERTY_TYPE, EDGE_PROPERTY_TARGET_VERTEX_UNIQ); + } + + if (mgmtNotContainsGraphIndex(EDGE_TYPE_SOURCE_TARGET_UNIQ_INDEX)) { + mgmtBuildEdgeIndexWithThreeKey( + EDGE_TYPE_SOURCE_TARGET_UNIQ_INDEX, + EDGE_PROPERTY_TYPE, + EDGE_PROPERTY_SOURCE_VERTEX_UNIQ, + EDGE_PROPERTY_TARGET_VERTEX_UNIQ); + } + } + + private void buildGraphRelationIndex() { + if (mgmtNotContainsRelationIndex(LABEL_EDGE, EDGE_UNIQ_INDEX)) { + mgmtBuildRelationIndexWithOneKey(LABEL_EDGE, EDGE_UNIQ_INDEX, EDGE_PROPERTY_UNIQ); + } + if (mgmtNotContainsRelationIndex(LABEL_EDGE, EDGE_TYPE_INDEX)) { + mgmtBuildRelationIndexWithOneKey(LABEL_EDGE, EDGE_TYPE_INDEX, EDGE_PROPERTY_TYPE); + } + if (mgmtNotContainsRelationIndex(LABEL_EDGE, EDGE_DELETED_INDEX)) { + mgmtBuildRelationIndexWithOneKey(LABEL_EDGE, EDGE_DELETED_INDEX, EDGE_PROPERTY_DELETED); + } + if (mgmtNotContainsRelationIndex(LABEL_EDGE, EDGE_SOURCE_UNIQ_INDEX)) { + mgmtBuildRelationIndexWithOneKey( + LABEL_EDGE, EDGE_SOURCE_UNIQ_INDEX, EDGE_PROPERTY_SOURCE_VERTEX_UNIQ); + } + if (mgmtNotContainsRelationIndex(LABEL_EDGE, EDGE_TARGET_UNIQ_INDEX)) { + mgmtBuildRelationIndexWithOneKey( + LABEL_EDGE, EDGE_TARGET_UNIQ_INDEX, EDGE_PROPERTY_TARGET_VERTEX_UNIQ); + } + if (mgmtNotContainsRelationIndex(LABEL_EDGE, EDGE_CREATE_TIME_INDEX)) { + mgmtBuildRelationIndexWithOneKey( + LABEL_EDGE, EDGE_CREATE_TIME_INDEX, EDGE_PROPERTY_CREATE_TIME); + } + + if (mgmtNotContainsRelationIndex(LABEL_EDGE, EDGE_SOURCE_TARGET_UNIQ_INDEX)) { + mgmtBuildRelationIndexWithTwoKey( + LABEL_EDGE, + EDGE_SOURCE_TARGET_UNIQ_INDEX, + EDGE_PROPERTY_SOURCE_VERTEX_UNIQ, + EDGE_PROPERTY_TARGET_VERTEX_UNIQ); + } + if (mgmtNotContainsRelationIndex(LABEL_EDGE, EDGE_TYPE_SOURCE_UNIQ_INDEX)) { + mgmtBuildRelationIndexWithTwoKey( + LABEL_EDGE, + EDGE_TYPE_SOURCE_UNIQ_INDEX, + EDGE_PROPERTY_TYPE, + EDGE_PROPERTY_SOURCE_VERTEX_UNIQ); + } + if (mgmtNotContainsRelationIndex(LABEL_EDGE, EDGE_TYPE_TARGET_UNIQ_INDEX)) { + mgmtBuildRelationIndexWithTwoKey( + LABEL_EDGE, + EDGE_TYPE_TARGET_UNIQ_INDEX, + EDGE_PROPERTY_TYPE, + EDGE_PROPERTY_TARGET_VERTEX_UNIQ); + } + + if (mgmtNotContainsRelationIndex(LABEL_EDGE, EDGE_TYPE_SOURCE_TARGET_UNIQ_INDEX)) { + mgmtBuildRelationIndexWithThreeKey( + LABEL_EDGE, + EDGE_TYPE_SOURCE_TARGET_UNIQ_INDEX, + EDGE_PROPERTY_TYPE, + EDGE_PROPERTY_SOURCE_VERTEX_UNIQ, + EDGE_PROPERTY_TARGET_VERTEX_UNIQ); + } + } + + public abstract void mgmtCommit(); + + public abstract void mgmtRollback(); + + public abstract void updateGraphIndexWithReindex(String indexName) + throws ExecutionException, InterruptedException; + + public abstract void updateRelationIndexWithReindex( + String edgeLabelName, String relationIndexName) + throws ExecutionException, InterruptedException; + + private void updateGraphIndex() throws ExecutionException, InterruptedException { + try { + updateGraphIndexWithReindex(VERTEX_UNIQ_ONLY_INDEX); + updateGraphIndexWithReindex(VERTEX_UNIQ_INDEX); + updateGraphIndexWithReindex(VERTEX_TYPE_INDEX); + updateGraphIndexWithReindex(VERTEX_DELETED_INDEX); + updateGraphIndexWithReindex(VERTEX_CREATE_TIME_INDEX); + + updateGraphIndexWithReindex(EDGE_UNIQ_INDEX); + updateGraphIndexWithReindex(EDGE_TYPE_INDEX); + updateGraphIndexWithReindex(EDGE_DELETED_INDEX); + updateGraphIndexWithReindex(EDGE_SOURCE_UNIQ_INDEX); + updateGraphIndexWithReindex(EDGE_TARGET_UNIQ_INDEX); + updateGraphIndexWithReindex(EDGE_CREATE_TIME_INDEX); + updateGraphIndexWithReindex(EDGE_SOURCE_TARGET_UNIQ_INDEX); + updateGraphIndexWithReindex(EDGE_TYPE_SOURCE_UNIQ_INDEX); + updateGraphIndexWithReindex(EDGE_TYPE_TARGET_UNIQ_INDEX); + updateGraphIndexWithReindex(EDGE_TYPE_SOURCE_TARGET_UNIQ_INDEX); + + mgmtCommit(); + } catch (ExecutionException | InterruptedException e) { + mgmtRollback(); + throw e; + } + } + + private void updateGraphRelationIndex() throws ExecutionException, InterruptedException { + try { + updateRelationIndexWithReindex(LABEL_EDGE, EDGE_UNIQ_INDEX); + updateRelationIndexWithReindex(LABEL_EDGE, EDGE_TYPE_INDEX); + updateRelationIndexWithReindex(LABEL_EDGE, EDGE_DELETED_INDEX); + updateRelationIndexWithReindex(LABEL_EDGE, EDGE_SOURCE_UNIQ_INDEX); + updateRelationIndexWithReindex(LABEL_EDGE, EDGE_TARGET_UNIQ_INDEX); + updateRelationIndexWithReindex(LABEL_EDGE, EDGE_CREATE_TIME_INDEX); + updateRelationIndexWithReindex(LABEL_EDGE, EDGE_SOURCE_TARGET_UNIQ_INDEX); + updateRelationIndexWithReindex(LABEL_EDGE, EDGE_TYPE_SOURCE_UNIQ_INDEX); + updateRelationIndexWithReindex(LABEL_EDGE, EDGE_TYPE_TARGET_UNIQ_INDEX); + updateRelationIndexWithReindex(LABEL_EDGE, EDGE_TYPE_SOURCE_TARGET_UNIQ_INDEX); + + mgmtCommit(); + } catch (ExecutionException | InterruptedException e) { + mgmtRollback(); + throw e; + } + } + + public abstract void awaitGraphIndexStatusWithEnabled(String indexName) + throws InterruptedException; + + public abstract void awaitRelationIndexStatusWithEnabled( + String edgeLabelName, String relationIndexName) throws InterruptedException; + + private void awaitGraphIndexStatus() throws InterruptedException { + awaitGraphIndexStatusWithEnabled(VERTEX_UNIQ_ONLY_INDEX); + awaitGraphIndexStatusWithEnabled(VERTEX_UNIQ_INDEX); + awaitGraphIndexStatusWithEnabled(VERTEX_TYPE_INDEX); + awaitGraphIndexStatusWithEnabled(VERTEX_DELETED_INDEX); + awaitGraphIndexStatusWithEnabled(VERTEX_CREATE_TIME_INDEX); + + awaitGraphIndexStatusWithEnabled(EDGE_UNIQ_INDEX); + awaitGraphIndexStatusWithEnabled(EDGE_TYPE_INDEX); + awaitGraphIndexStatusWithEnabled(EDGE_DELETED_INDEX); + awaitGraphIndexStatusWithEnabled(EDGE_SOURCE_UNIQ_INDEX); + awaitGraphIndexStatusWithEnabled(EDGE_TARGET_UNIQ_INDEX); + awaitGraphIndexStatusWithEnabled(EDGE_CREATE_TIME_INDEX); + awaitGraphIndexStatusWithEnabled(EDGE_SOURCE_TARGET_UNIQ_INDEX); + awaitGraphIndexStatusWithEnabled(EDGE_TYPE_SOURCE_UNIQ_INDEX); + awaitGraphIndexStatusWithEnabled(EDGE_TYPE_TARGET_UNIQ_INDEX); + awaitGraphIndexStatusWithEnabled(EDGE_TYPE_SOURCE_TARGET_UNIQ_INDEX); + } + + private void awaitGraphRelationIndexStatus() throws InterruptedException { + awaitRelationIndexStatusWithEnabled(LABEL_EDGE, EDGE_UNIQ_INDEX); + awaitRelationIndexStatusWithEnabled(LABEL_EDGE, EDGE_TYPE_INDEX); + awaitRelationIndexStatusWithEnabled(LABEL_EDGE, EDGE_DELETED_INDEX); + awaitRelationIndexStatusWithEnabled(LABEL_EDGE, EDGE_SOURCE_UNIQ_INDEX); + awaitRelationIndexStatusWithEnabled(LABEL_EDGE, EDGE_TARGET_UNIQ_INDEX); + awaitRelationIndexStatusWithEnabled(LABEL_EDGE, EDGE_CREATE_TIME_INDEX); + awaitRelationIndexStatusWithEnabled(LABEL_EDGE, EDGE_SOURCE_TARGET_UNIQ_INDEX); + awaitRelationIndexStatusWithEnabled(LABEL_EDGE, EDGE_TYPE_SOURCE_UNIQ_INDEX); + awaitRelationIndexStatusWithEnabled(LABEL_EDGE, EDGE_TYPE_TARGET_UNIQ_INDEX); + awaitRelationIndexStatusWithEnabled(LABEL_EDGE, EDGE_TYPE_SOURCE_TARGET_UNIQ_INDEX); + } + + @Override + public void open() throws GraphException { + try { + buildGraphLabel(); + buildGraphPropertyKey(); + buildGraphVertexIndex(); + buildGraphEdgeIndex(); + buildGraphRelationIndex(); + mgmtCommit(); + // updateGraphIndex(); + // updateGraphRelationIndex(); + // awaitGraphIndexStatus(); + // awaitGraphRelationIndexStatus(); + } catch (Throwable t) { + throw new GraphException(t, JANUS_GRAPH_EXCEPTION); + } + } + + public abstract Long getJanusGraphVertexId(String uniq); +} diff --git a/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/JanusGraphEdge.java b/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/JanusGraphEdge.java new file mode 100644 index 0000000000000000000000000000000000000000..d125fa3a3ff7dd7a8e03c1fa3e42d83db066a7c2 --- /dev/null +++ b/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/JanusGraphEdge.java @@ -0,0 +1,76 @@ +package org.studiox.graph.janusgraph.common; + +import org.studiox.graph.common.GraphEdge; + +import java.util.Objects; + +public class JanusGraphEdge extends GraphEdge { + + private String id; + + private Long sourceVertexId; + + private Long targetVertexId; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Long getSourceVertexId() { + return sourceVertexId; + } + + public void setSourceVertexId(Long sourceVertexId) { + this.sourceVertexId = sourceVertexId; + } + + public Long getTargetVertexId() { + return targetVertexId; + } + + public void setTargetVertexId(Long targetVertexId) { + this.targetVertexId = targetVertexId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + JanusGraphEdge that = (JanusGraphEdge) o; + return Objects.equals(id, that.id) + && Objects.equals(sourceVertexId, that.sourceVertexId) + && Objects.equals(targetVertexId, that.targetVertexId); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), id, sourceVertexId, targetVertexId); + } + + @Override + public String toString() { + return "JanusGraphEdge{" + + "id='" + + id + + '\'' + + ", sourceVertexId=" + + sourceVertexId + + ", targetVertexId=" + + targetVertexId + + '}' + + ",GraphEdge{" + + super.toString() + + "}"; + } +} diff --git a/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/JanusGraphVertex.java b/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/JanusGraphVertex.java new file mode 100644 index 0000000000000000000000000000000000000000..3b2ff93247d77cd4ed9404b3e8ffc178d505dac8 --- /dev/null +++ b/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/JanusGraphVertex.java @@ -0,0 +1,43 @@ +package org.studiox.graph.janusgraph.common; + +import org.studiox.graph.common.GraphVertex; + +import java.util.Objects; + +public class JanusGraphVertex extends GraphVertex { + + private Long id; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + JanusGraphVertex that = (JanusGraphVertex) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), id); + } + + @Override + public String toString() { + return "JanusGraphVertex{" + "id=" + id + '}' + ",GraphVertex{" + super.toString() + "}"; + } +} diff --git a/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/test/JanusGraphTest.java b/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/test/JanusGraphTest.java new file mode 100644 index 0000000000000000000000000000000000000000..378a47bf1251e1a5228945c752c3d57f60f202ac --- /dev/null +++ b/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/test/JanusGraphTest.java @@ -0,0 +1,551 @@ +package org.studiox.graph.janusgraph.common.test; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; +import org.studiox.graph.common.GraphEdge; +import org.studiox.graph.common.GraphLineage; +import org.studiox.graph.common.GraphVertex; +import org.studiox.graph.common.exception.GraphException; +import org.studiox.graph.janusgraph.common.AbstractJanusGraph; + +import java.util.List; +import java.util.Map; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class JanusGraphTest { + + private static AbstractJanusGraph janusGraph; + + public AbstractJanusGraph getJanusGraph() { + return janusGraph; + } + + public void setJanusGraph(AbstractJanusGraph janusGraph) { + JanusGraphTest.janusGraph = janusGraph; + } + + public static void init(AbstractJanusGraph janusGraph) throws GraphException { + JanusGraphTest.janusGraph = janusGraph; + janusGraph.open(); + janusGraph.hardDeleteVertices(null, "type1"); + janusGraph.hardDeleteEdges(null, "type1", null, null); + } + + @AfterClass + public static void clean() { + janusGraph.close(); + } + + @Test + public void test_01_createVertexTest() { + Long vertexId1 = janusGraph.createVertex("qualifiedName1", "type1", null); + Assert.assertNotNull(vertexId1); + Long vertexId2 = janusGraph.createVertex("qualifiedName1", "type1", null); + Assert.assertNull(vertexId2); + Assert.assertEquals(janusGraph.getVerticesByType("type1").size(), 1); + } + + @Test + public void test_02_createVertexNotPreCheckTest() { + Long vertexId = janusGraph.createVertexNotPreCheck("qualifiedName2", "type1", null); + Assert.assertNotNull(vertexId); + Assert.assertEquals(janusGraph.getVerticesByType("type1").size(), 2); + } + + @Test + public void test_03_updateVertexTest() { + boolean flag1 = janusGraph.updateVertex("qualifiedName2", "type2", null); + Assert.assertEquals(flag1, true); + GraphVertex vertex = janusGraph.getVertex("qualifiedName2"); + Assert.assertEquals(vertex.getType(), "type2"); + Long vertexId = janusGraph.createVertex("qualifiedName3", "type1", null); + Assert.assertNotNull(vertexId); + boolean flag2 = janusGraph.updateVertex("qualifiedName2", "type1", null); + Assert.assertEquals(flag2, true); + } + + @Test + public void test_04_softDeleteVerticesTest() { + GraphVertex vertex = janusGraph.getVertex("qualifiedName3"); + Assert.assertEquals(vertex.getDeleted(), false); + boolean flag = janusGraph.softDeleteVertices("qualifiedName3", null); + Assert.assertEquals(flag, true); + vertex = janusGraph.getVertex("qualifiedName3"); + Assert.assertEquals(vertex.getDeleted(), true); + } + + @Test + public void test_05_recoverDeletedVerticesTest() { + boolean flag = janusGraph.recoverDeletedVertices("qualifiedName3", null); + Assert.assertEquals(flag, true); + GraphVertex vertex = janusGraph.getVertex("qualifiedName3"); + Assert.assertEquals(vertex.getDeleted(), false); + } + + @Test + public void test_06_hardDeleteVerticesTest() { + GraphVertex vertex = janusGraph.getVertex("qualifiedName3"); + Assert.assertNotNull(vertex); + boolean flag = janusGraph.hardDeleteVertices("qualifiedName3", null); + Assert.assertEquals(flag, true); + vertex = janusGraph.getVertex("qualifiedName3"); + Assert.assertNull(vertex); + } + + @Test + public void test_07_getVertexTest() { + GraphVertex vertex = janusGraph.getVertex("qualifiedName2"); + Assert.assertNotNull(vertex); + } + + @Test + public void test_08_getVerticesByTypeTest() { + List vertices = janusGraph.getVerticesByType("type1"); + Assert.assertNotNull(vertices); + Assert.assertEquals(vertices.size(), 2); + } + + @Test + public void test_09_createEdgeTest() { + String edgeId = + janusGraph.createEdge( + "edgeQualifiedName1", "type1", "qualifiedName1", "qualifiedName2", null); + Assert.assertNotNull(edgeId); + } + + @Test + public void test_10_createEdgeNotPreCheckTest() { + String edgeId = + janusGraph.createEdgeNotPreCheck( + "edgeQualifiedName2", + "type1", + janusGraph.getJanusGraphVertexId("qualifiedName1"), + janusGraph.getJanusGraphVertexId("qualifiedName2"), + null); + Assert.assertNotNull(edgeId); + } + + @Test + public void test_11_graphLineageQueryTest() { + Map sourceVertexEdgeMap = + janusGraph.getSourceVertexEdgeMap("qualifiedName1"); + Assert.assertEquals(0, sourceVertexEdgeMap.size()); + sourceVertexEdgeMap = janusGraph.getSourceVertexEdgeMap("qualifiedName2"); + Assert.assertEquals(2, sourceVertexEdgeMap.size()); + Map targetVertexEdgeMap = + janusGraph.getTargetVertexEdgeMap("qualifiedName1"); + Assert.assertEquals(2, targetVertexEdgeMap.size()); + targetVertexEdgeMap = janusGraph.getTargetVertexEdgeMap("qualifiedName2"); + Assert.assertEquals(0, targetVertexEdgeMap.size()); + GraphLineage fullLineage = janusGraph.getFullLineage("qualifiedName1", -1, true); + Assert.assertEquals(2, fullLineage.getVertices().size()); + Assert.assertEquals(2, fullLineage.getEdges().size()); + fullLineage = janusGraph.getFullLineage("qualifiedName2", -1, true); + Assert.assertEquals(2, fullLineage.getVertices().size()); + Assert.assertEquals(2, fullLineage.getEdges().size()); + fullLineage = janusGraph.getFullLineage("qualifiedName1", 1, true); + Assert.assertEquals(2, fullLineage.getVertices().size()); + Assert.assertEquals(2, fullLineage.getEdges().size()); + fullLineage = janusGraph.getFullLineage("qualifiedName2", 1, true); + Assert.assertEquals(2, fullLineage.getVertices().size()); + Assert.assertEquals(2, fullLineage.getEdges().size()); + GraphLineage impactInfo = janusGraph.getImpact("qualifiedName1", 1, true); + Assert.assertEquals(2, impactInfo.getVertices().size()); + Assert.assertEquals(2, impactInfo.getEdges().size()); + GraphLineage lineageInfo = janusGraph.getLineage("qualifiedName2", 1, true); + Assert.assertEquals(2, lineageInfo.getVertices().size()); + Assert.assertEquals(2, lineageInfo.getEdges().size()); + } + + @Test + public void test_12_getEdgeTest() { + GraphEdge graphEdge = janusGraph.getEdge("edgeQualifiedName2"); + Assert.assertNotNull(graphEdge); + Assert.assertEquals(graphEdge.getType(), "type1"); + } + + @Test + public void test_13_getEdgesTest() { + List graphEdges = janusGraph.getEdges("type1", "qualifiedName1", "qualifiedName2"); + Assert.assertNotNull(graphEdges); + Assert.assertEquals(graphEdges.size(), 2); + } + + @Test + public void test_14_deleteSourceTargetVertexTest() { + boolean flag = janusGraph.hardDeleteVertices("qualifiedName2", null); + Assert.assertEquals(true, flag); + List graphEdges = janusGraph.getEdges("type1", null, null); + Assert.assertEquals(0, graphEdges.size()); + String edgeId = + janusGraph.createEdge( + "edgeQualifiedName1", "type1", "qualifiedName1", "qualifiedName2", null); + Assert.assertNotNull(edgeId); + edgeId = + janusGraph.createEdgeNotPreCheck( + "edgeQualifiedName2", + "type1", + janusGraph.getJanusGraphVertexId("qualifiedName1"), + janusGraph.getJanusGraphVertexId("qualifiedName2"), + null); + Assert.assertNotNull(edgeId); + List vertices = janusGraph.getVerticesByType("type1"); + Assert.assertNotNull(vertices); + Assert.assertEquals(vertices.size(), 2); + graphEdges = janusGraph.getEdges("type1", "qualifiedName1", "qualifiedName2"); + Assert.assertNotNull(graphEdges); + Assert.assertEquals(graphEdges.size(), 2); + } + + @Test + public void test_15_softDeleteEdgesTest() { + boolean flag = janusGraph.softDeleteEdges("edgeQualifiedName1", null, null, null); + Assert.assertEquals(flag, true); + GraphEdge graphEdge = janusGraph.getEdge("edgeQualifiedName1"); + Assert.assertEquals(graphEdge.getDeleted(), true); + } + + @Test + public void test_16_recoverDeletedEdgesTest() { + boolean flag = janusGraph.recoverDeletedEdges("edgeQualifiedName1", null, null, null); + Assert.assertEquals(flag, true); + GraphEdge graphEdge = janusGraph.getEdge("edgeQualifiedName1"); + Assert.assertEquals(graphEdge.getDeleted(), false); + } + + @Test + public void test_17_hardDeleteEdgesTest() { + boolean flag = janusGraph.hardDeleteEdges("edgeQualifiedName2", null, null, null); + Assert.assertEquals(flag, true); + GraphEdge edge = janusGraph.getEdge("edgeQualifiedName2"); + Assert.assertNull(edge); + } + + @Test + public void test_18_getSourceVertexEdgeMapTest() { + String edgeId = + janusGraph.createEdge( + "edgeQualifiedName2", "type1", "qualifiedName1", "qualifiedName2", null); + Assert.assertNotNull(edgeId); + Map graphEdgeVertexMap = + janusGraph.getSourceVertexEdgeMap("qualifiedName2"); + Assert.assertNotNull(graphEdgeVertexMap); + Assert.assertEquals(graphEdgeVertexMap.size(), 2); + } + + @Test + public void test_19_getTargetVertexEdgeMapTest() { + Map graphEdgeVertexMap = + janusGraph.getTargetVertexEdgeMap("qualifiedName1"); + Assert.assertNotNull(graphEdgeVertexMap); + Assert.assertEquals(graphEdgeVertexMap.size(), 2); + } + + @Test + public void test_20_updateVertexOldNewUniqTest() { + Long vertexId1 = janusGraph.createVertex("qualifiedName11", "type2", null); + Assert.assertNotNull(vertexId1); + Long vertexId2 = janusGraph.createVertex("qualifiedName22", "type2", null); + Assert.assertNotNull(vertexId2); + Long vertexId3 = janusGraph.createVertex("qualifiedName33", "type2", null); + Assert.assertNotNull(vertexId3); + String edgeId1 = + janusGraph.createEdge( + "edgeQualifiedName11", "type2", "qualifiedName11", "qualifiedName22", null); + Assert.assertNotNull(edgeId1); + String edgeId2 = + janusGraph.createEdge( + "edgeQualifiedName22", "type2", "qualifiedName22", "qualifiedName33", null); + Assert.assertNotNull(edgeId2); + janusGraph.updateVertex("qualifiedName22", "qualifiedName44", null, null); + GraphVertex graphVertex = janusGraph.getVertex("qualifiedName22"); + Assert.assertNull(graphVertex); + graphVertex = janusGraph.getVertex("qualifiedName44"); + Assert.assertNotNull(graphVertex); + GraphEdge graphEdge = janusGraph.getEdge("edgeQualifiedName11"); + Assert.assertEquals("qualifiedName44", graphEdge.getTargetVertexUniq()); + graphEdge = janusGraph.getEdge("edgeQualifiedName22"); + Assert.assertEquals("qualifiedName44", graphEdge.getSourceVertexUniq()); + janusGraph.hardDeleteVertices("qualifiedName11", null); + janusGraph.hardDeleteVertices("qualifiedName44", null); + janusGraph.hardDeleteVertices("qualifiedName33", null); + graphEdge = janusGraph.getEdge("edgeQualifiedName11"); + Assert.assertNull(graphEdge); + graphEdge = janusGraph.getEdge("edgeQualifiedName22"); + Assert.assertNull(graphEdge); + } + + @Test + public void test_21_getImpact1Test() { + janusGraph.createVertex("qualifiedName3", "type1", null); + janusGraph.createVertex("qualifiedName4", "type1", null); + janusGraph.createVertex("qualifiedName5", "type1", null); + janusGraph.createEdge("edgeQualifiedName3", "type1", "qualifiedName2", "qualifiedName3", null); + janusGraph.createEdge("edgeQualifiedName4", "type1", "qualifiedName3", "qualifiedName4", null); + janusGraph.createEdge("edgeQualifiedName5", "type1", "qualifiedName4", "qualifiedName5", null); + janusGraph.createEdge("edgeQualifiedName6", "type1", "qualifiedName5", "qualifiedName1", null); + GraphLineage lineage = janusGraph.getImpact("qualifiedName2", -1, false); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 5); + Assert.assertEquals(lineage.getEdges().size(), 6); + } + + @Test + public void test_22_getImpact2Test() { + janusGraph.softDeleteEdges("edgeQualifiedName4", null, null, null); + GraphLineage lineage = janusGraph.getImpact("qualifiedName2", -1, true); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 2); + Assert.assertEquals(lineage.getEdges().size(), 1); + janusGraph.recoverDeletedEdges("edgeQualifiedName4", null, null, null); + } + + @Test + public void test_23_getImpact3Test() { + janusGraph.softDeleteVertices("qualifiedName4", null); + GraphLineage lineage = janusGraph.getImpact("qualifiedName2", -1, true); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 2); + Assert.assertEquals(lineage.getEdges().size(), 1); + janusGraph.recoverDeletedVertices("qualifiedName4", null); + } + + @Test + public void test_24_getImpact4Test() { + GraphLineage lineage = janusGraph.getImpact("qualifiedName2", 3, false); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 4); + Assert.assertEquals(lineage.getEdges().size(), 3); + } + + @Test + public void test_25_getImpact5Test() { + janusGraph.softDeleteEdges("edgeQualifiedName4", null, null, null); + GraphLineage lineage = janusGraph.getImpact("qualifiedName2", 3, true); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 2); + Assert.assertEquals(lineage.getEdges().size(), 1); + janusGraph.recoverDeletedEdges("edgeQualifiedName4", null, null, null); + } + + @Test + public void test_26_getImpact6Test() { + janusGraph.softDeleteVertices("qualifiedName4", null); + GraphLineage lineage = janusGraph.getImpact("qualifiedName2", 3, true); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 2); + Assert.assertEquals(lineage.getEdges().size(), 1); + janusGraph.recoverDeletedVertices("qualifiedName4", null); + } + + @Test + public void test_27_getImpact7Test() { + janusGraph.softDeleteVertices("qualifiedName2", null); + GraphLineage lineage = janusGraph.getImpact("qualifiedName2", -1, false); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 5); + Assert.assertEquals(lineage.getEdges().size(), 6); + } + + @Test + public void test_28_getImpact8Test() { + GraphLineage lineage = janusGraph.getImpact("qualifiedName2", 3, true); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 0); + Assert.assertEquals(lineage.getEdges().size(), 0); + janusGraph.recoverDeletedVertices("qualifiedName2", null); + } + + @Test + public void test_29_getLineage1Test() { + GraphLineage lineage = janusGraph.getLineage("qualifiedName2", -1, false); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 5); + Assert.assertEquals(lineage.getEdges().size(), 6); + } + + @Test + public void test_30_getLineage2Test() { + janusGraph.softDeleteEdges("edgeQualifiedName5", null, null, null); + GraphLineage lineage = janusGraph.getLineage("qualifiedName2", -1, true); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 3); + Assert.assertEquals(lineage.getEdges().size(), 3); + janusGraph.recoverDeletedEdges("edgeQualifiedName5", null, null, null); + } + + @Test + public void test_31_getLineage3Test() { + janusGraph.softDeleteVertices("qualifiedName5", null); + GraphLineage lineage = janusGraph.getLineage("qualifiedName2", -1, true); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 2); + Assert.assertEquals(lineage.getEdges().size(), 2); + janusGraph.recoverDeletedVertices("qualifiedName5", null); + } + + @Test + public void test_32_getLineage4Test() { + GraphLineage lineage = janusGraph.getLineage("qualifiedName2", 3, false); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 4); + Assert.assertEquals(lineage.getEdges().size(), 4); + } + + @Test + public void test_33_getLineage5Test() { + janusGraph.softDeleteEdges("edgeQualifiedName5", null, null, null); + GraphLineage lineage = janusGraph.getLineage("qualifiedName2", 3, true); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 3); + Assert.assertEquals(lineage.getEdges().size(), 3); + janusGraph.recoverDeletedEdges("edgeQualifiedName5", null, null, null); + } + + @Test + public void test_34_getLineage6Test() { + janusGraph.softDeleteVertices("qualifiedName5", null); + GraphLineage lineage = janusGraph.getLineage("qualifiedName2", 3, true); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 2); + Assert.assertEquals(lineage.getEdges().size(), 2); + janusGraph.recoverDeletedVertices("qualifiedName5", null); + } + + @Test + public void test_35_getLineage7Test() { + janusGraph.softDeleteVertices("qualifiedName2", null); + GraphLineage lineage = janusGraph.getLineage("qualifiedName2", -1, false); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 5); + Assert.assertEquals(lineage.getEdges().size(), 6); + } + + @Test + public void test_36_getLineage8Test() { + GraphLineage lineage = janusGraph.getLineage("qualifiedName2", 3, true); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 0); + Assert.assertEquals(lineage.getEdges().size(), 0); + janusGraph.recoverDeletedVertices("qualifiedName2", null); + } + + @Test + public void test_37_getFullLineage1Test() { + GraphLineage lineage = janusGraph.getFullLineage("qualifiedName2", -1, false); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 5); + Assert.assertEquals(lineage.getEdges().size(), 6); + } + + @Test + public void test_38_getFullLineage2Test() { + janusGraph.softDeleteEdges("edgeQualifiedName5", null, null, null); + GraphLineage lineage = janusGraph.getFullLineage("qualifiedName2", -1, true); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 5); + Assert.assertEquals(lineage.getEdges().size(), 5); + janusGraph.recoverDeletedEdges("edgeQualifiedName5", null, null, null); + } + + @Test + public void test_39_getFullLineage3Test() { + janusGraph.softDeleteVertices("qualifiedName5", null); + GraphLineage lineage = janusGraph.getFullLineage("qualifiedName2", -1, true); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 4); + Assert.assertEquals(lineage.getEdges().size(), 4); + janusGraph.recoverDeletedVertices("qualifiedName5", null); + } + + @Test + public void test_40_getFullLineage4Test() { + GraphLineage lineage = janusGraph.getFullLineage("qualifiedName2", 2, false); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 5); + Assert.assertEquals(lineage.getEdges().size(), 5); + } + + @Test + public void test_41_getFullLineage5Test() { + janusGraph.softDeleteEdges("edgeQualifiedName4", null, null, null); + GraphLineage lineage = janusGraph.getFullLineage("qualifiedName2", 2, true); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 4); + Assert.assertEquals(lineage.getEdges().size(), 4); + janusGraph.recoverDeletedEdges("edgeQualifiedName4", null, null, null); + } + + @Test + public void test_42_getFullLineage6Test() { + janusGraph.softDeleteVertices("qualifiedName3", null); + GraphLineage lineage = janusGraph.getFullLineage("qualifiedName2", 2, true); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 3); + Assert.assertEquals(lineage.getEdges().size(), 3); + janusGraph.recoverDeletedVertices("qualifiedName3", null); + } + + @Test + public void test_43_getFullLineage7Test() { + janusGraph.softDeleteVertices("qualifiedName2", null); + GraphLineage lineage = janusGraph.getFullLineage("qualifiedName2", -1, false); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 5); + Assert.assertEquals(lineage.getEdges().size(), 6); + } + + @Test + public void test_44_getFullLineage8Test() { + GraphLineage lineage = janusGraph.getFullLineage("qualifiedName2", 3, true); + Assert.assertNotNull(lineage); + Assert.assertEquals(lineage.getVertices().size(), 0); + Assert.assertEquals(lineage.getEdges().size(), 0); + janusGraph.recoverDeletedVertices("qualifiedName2", null); + } + + @Test + public void test_45_createVertexNotPreCheck2Test() { + Long vertexId = janusGraph.createVertexNotPreCheck("qualifiedName1", "type1", null); + Assert.assertNotNull(vertexId); + Assert.assertEquals(janusGraph.getVerticesByType("type1").size(), 6); + } + + @Test + public void test_46_createEdge2Test() { + String edgeId = + janusGraph.createEdgeNotPreCheck( + "edgeQualifiedName1", + "type1", + janusGraph.getJanusGraphVertexId("qualifiedName1"), + janusGraph.getJanusGraphVertexId("qualifiedName2"), + null); + Assert.assertNotNull(edgeId); + List graphEdges = janusGraph.getEdges("type1", "qualifiedName1", "qualifiedName2"); + Assert.assertNotNull(graphEdges); + Assert.assertEquals(graphEdges.size(), 3); + } + + @Test + public void test_47_performance1Test() { + int startNum = 1; + int endNum = 50; + for (int i = startNum; i <= endNum; i++) { + String uniq = "performance_1_" + String.valueOf(i); + janusGraph.createVertex(uniq, "type1", null); + } + } + + @Test + public void test_48_performance2Test() { + int startNum = 1; + int endNum = 50; + for (int i = startNum; i <= endNum; i++) { + String uniq = "performance_2_" + String.valueOf(i); + janusGraph.createVertexNotPreCheck(uniq, "type1", null); + } + } +} diff --git a/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/util/DateUtil.java b/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/util/DateUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..05c55d6bbea5ce6c694dfe26fab241cb9b26467b --- /dev/null +++ b/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/util/DateUtil.java @@ -0,0 +1,56 @@ +package org.studiox.graph.janusgraph.common.util; + +import org.apache.commons.lang.StringUtils; + +import java.text.SimpleDateFormat; +import java.util.Date; + +public class DateUtil { + + public static Long getCurrentTimestamp() { + return System.currentTimeMillis(); + } + + public static Date getCurrentDate() { + return new Date(); + } + + public static Date getDate(long timestamp) { + return new Date(timestamp); + } + + public static long getTimestamp(Date date) { + return date.getTime(); + } + + public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; + + public static final String DEFAULT_DAY_FORMAT = "yyyy-MM-dd"; + + public static String formatDate(String format, Date date) { + if (StringUtils.isBlank(format) || null == date) { + return null; + } + SimpleDateFormat sdf = new SimpleDateFormat(format); + return sdf.format(date); + } + + public static Date parseDate(String format, String dateStr) throws Exception { + if (StringUtils.isBlank(format) || StringUtils.isBlank(dateStr)) { + return null; + } + SimpleDateFormat sdf = new SimpleDateFormat(format); + try { + return sdf.parse(dateStr); + } catch (Throwable e) { + throw new Exception( + "parse date string='" + + dateStr + + "' by format='" + + format + + "' failed, caused by " + + e.getMessage(), + e); + } + } +} diff --git a/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/util/StringUtil.java b/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/util/StringUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..46e3646a1920bb562d7851e9476d0e68f6dc5d27 --- /dev/null +++ b/graph-janusgraph/janusgraph-common/src/main/java/org/studiox/graph/janusgraph/common/util/StringUtil.java @@ -0,0 +1,18 @@ +package org.studiox.graph.janusgraph.common.util; + +import org.apache.commons.lang.StringUtils; + +public class StringUtil { + + public static boolean checkStringParamsBlank(String... params) { + if (null == params || params.length == 0) { + return true; + } + for (String param : params) { + if (StringUtils.isBlank(param)) { + return true; + } + } + return false; + } +} diff --git a/graph-janusgraph/janusgraph-latest/pom.xml b/graph-janusgraph/janusgraph-latest/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..54683913a59d833daab81023fce73fc309c2c06a --- /dev/null +++ b/graph-janusgraph/janusgraph-latest/pom.xml @@ -0,0 +1,224 @@ + + + 4.0.0 + + org.studiox.graph + graph-janusgraph + 1.0.5-SNAPSHOT + + org.studiox.graph.janusgraph + janusgraph-latest + 1.0.5-SNAPSHOT + jar + + + + 1.0.0-SNAPSHOT + 1.3.1 + 3.5.1 + + + + 2.5.14 + + + + junit + junit + test + + + org.studiox.graph.janusgraph + janusgraph-v0.2.0 + + + org.janusgraph + * + + + org.apache.hbase + * + + + + + org.janusgraph + janusgraph-core + ${janusgraph.version} + + + org.janusgraph + janusgraph-inmemory + ${janusgraph.version} + + + org.janusgraph + janusgraph-hbase + ${janusgraph.version} + + + org.apache.hbase + hbase-client + ${hbase.version} + + + org.apache.hadoop + hadoop-mapreduce-client-core + + + + + org.janusgraph + janusgraph-server + ${janusgraph.version} + + + org.codehaus.groovy + groovy-cli-picocli + ${groovy.version} + + + org.codehaus.groovy + groovy + + + + + org.codehaus.groovy + groovy-console + ${groovy.version} + + + org.codehaus.groovy + groovy-templates + ${groovy.version} + + + org.codehaus.groovy + groovy-xml + ${groovy.version} + + + org.codehaus.groovy + groovy-swing + ${groovy.version} + + + + + + org.apache.maven.plugins + maven-shade-plugin + + false + + + + package + + shade + + + + + *:* + + properties.dtd + log4j.properties + digesterRules.xml + PropertyList-1.0.dtd + yarn-default.xml + core-default.xml + hbase-default.xml + mapred-default.xml + tp2-to-tp3-graphml.xslt + + + + + + io.netty + graph.io.netty + + + org.jboss.netty + graph.org.jboss.netty + + + org.codehaus.groovy + graph.org.codehaus.groovy + + + + + + + + + + + pl.project13.maven + git-commit-id-plugin + + + + diff --git a/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/AbstractGraphChannelizer.java b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/AbstractGraphChannelizer.java new file mode 100644 index 0000000000000000000000000000000000000000..580dae81e4340d369fcc40bccf7a8b927ab3fdf9 --- /dev/null +++ b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/AbstractGraphChannelizer.java @@ -0,0 +1,408 @@ +package org.apache.tinkerpop.gremlin.server; + +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.ssl.ClientAuth; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.SslProvider; +import io.netty.handler.timeout.IdleStateHandler; +import org.apache.tinkerpop.gremlin.driver.MessageSerializer; +import org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1; +import org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV2d0; +import org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor; +import org.apache.tinkerpop.gremlin.server.auth.Authenticator; +import org.apache.tinkerpop.gremlin.server.authz.Authorizer; +import org.apache.tinkerpop.gremlin.server.handler.AbstractAuthenticationHandler; +import org.apache.tinkerpop.gremlin.server.handler.GraphOpSelectorHandler; +import org.apache.tinkerpop.gremlin.server.handler.OpExecutorHandler; +import org.apache.tinkerpop.gremlin.server.util.GraphServerGremlinExecutor; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.javatuples.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLException; +import javax.net.ssl.TrustManagerFactory; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ScheduledExecutorService; +import java.util.stream.Stream; + +public abstract class AbstractGraphChannelizer extends ChannelInitializer + implements GraphChannelizer { + private static final Logger logger = LoggerFactory.getLogger(AbstractGraphChannelizer.class); + + protected Settings settings; + protected GremlinExecutor gremlinExecutor; + protected Optional sslContext; + protected GraphManager graphManager; + protected ExecutorService gremlinExecutorService; + protected ScheduledExecutorService scheduledExecutorService; + + public static final String PIPELINE_AUTHENTICATOR = "authenticator"; + public static final String PIPELINE_AUTHORIZER = "authorizer"; + public static final String PIPELINE_REQUEST_HANDLER = "request-handler"; + public static final String PIPELINE_HTTP_RESPONSE_ENCODER = "http-response-encoder"; + public static final String PIPELINE_HTTP_AGGREGATOR = "http-aggregator"; + public static final String PIPELINE_WEBSOCKET_SERVER_COMPRESSION = + "web-socket-server-compression-handler"; + + protected static final String PIPELINE_SSL = "ssl"; + protected static final String PIPELINE_OP_SELECTOR = "op-selector"; + protected static final String PIPELINE_OP_EXECUTOR = "op-executor"; + protected static final String PIPELINE_HTTP_REQUEST_DECODER = "http-request-decoder"; + + protected static final String GREMLIN_ENDPOINT = "/gremlin"; + + protected final Map> serializers = new HashMap<>(); + + private GraphOpSelectorHandler opSelectorHandler; + private OpExecutorHandler opExecutorHandler; + + protected Authenticator authenticator; + protected Authorizer authorizer; + + /** + * This method is called from within {@link #initChannel(SocketChannel)} just after the SSL + * handler is put in the pipeline. Modify the pipeline as needed here. + */ + public abstract void configure(final ChannelPipeline pipeline); + + /** + * This method is called after the pipeline is completely configured. It can be overridden to make + * any final changes to the pipeline before it goes into use. + */ + public void finalize(final ChannelPipeline pipeline) { + // do nothing + } + + public static final List getDefaultSerializers() { + return Arrays.asList( + new Settings.SerializerSettings( + GraphSONMessageSerializerV2d0.class.getName(), Collections.emptyMap()), + new Settings.SerializerSettings( + GraphBinaryMessageSerializerV1.class.getName(), Collections.emptyMap()), + new Settings.SerializerSettings( + GraphBinaryMessageSerializerV1.class.getName(), + new HashMap() { + { + put(GraphBinaryMessageSerializerV1.TOKEN_SERIALIZE_RESULT_TO_STRING, true); + } + })); + } + + @Override + public void init(final GraphServerGremlinExecutor graphServerGremlinExecutor) { + settings = graphServerGremlinExecutor.getSettings(); + gremlinExecutor = graphServerGremlinExecutor.getGremlinExecutor(); + graphManager = graphServerGremlinExecutor.getGraphManager(); + gremlinExecutorService = graphServerGremlinExecutor.getGremlinExecutorService(); + scheduledExecutorService = graphServerGremlinExecutor.getScheduledExecutorService(); + + // instantiate and configure the serializers that gremlin server will use - could error out here + // and fail the server startup + configureSerializers(); + + // configure ssl if present + sslContext = + settings.optionalSsl().isPresent() && settings.ssl.enabled + ? Optional.ofNullable(createSSLContext(settings)) + : Optional.empty(); + if (sslContext.isPresent()) { + logger.info("SSL enabled"); + } + + authenticator = createAuthenticator(settings.authentication); + authorizer = createAuthorizer(settings.authorization); + + // these handlers don't share any state and can thus be initialized once per pipeline + opSelectorHandler = + new GraphOpSelectorHandler( + settings, graphManager, gremlinExecutor, scheduledExecutorService, this); + opExecutorHandler = + new OpExecutorHandler(settings, graphManager, gremlinExecutor, scheduledExecutorService); + } + + @Override + public void initChannel(final SocketChannel ch) throws Exception { + final ChannelPipeline pipeline = ch.pipeline(); + + sslContext.ifPresent( + sslContext -> pipeline.addLast(PIPELINE_SSL, sslContext.newHandler(ch.alloc()))); + + // checks for no activity on a channel and triggers an event that is consumed by the + // OpSelectorHandler + // and either closes the connection or sends a ping to see if the client is still alive + if (supportsIdleMonitor()) { + final int idleConnectionTimeout = (int) (settings.idleConnectionTimeout / 1000); + final int keepAliveInterval = (int) (settings.keepAliveInterval / 1000); + pipeline.addLast(new IdleStateHandler(idleConnectionTimeout, keepAliveInterval, 0)); + } + + // the implementation provides the method by which Gremlin Server will process requests. the + // end of the + // pipeline must decode to an incoming RequestMessage instances and encode to a outgoing + // ResponseMessage + // instance + configure(pipeline); + + pipeline.addLast(PIPELINE_OP_SELECTOR, opSelectorHandler); + pipeline.addLast(PIPELINE_OP_EXECUTOR, opExecutorHandler); + + finalize(pipeline); + } + + protected AbstractAuthenticationHandler createAuthenticationHandler(final Settings settings) { + try { + final Class clazz = Class.forName(settings.authentication.authenticationHandler); + AbstractAuthenticationHandler aah; + try { + // the three arg constructor is the new form as a handler may need the authorizer in some + // cases + final Class[] threeArgForm = + new Class[] {Authenticator.class, Authorizer.class, Settings.class}; + final Constructor twoArgConstructor = clazz.getDeclaredConstructor(threeArgForm); + return (AbstractAuthenticationHandler) + twoArgConstructor.newInstance(authenticator, authorizer, settings); + } catch (Exception threeArgEx) { + try { + // the two arg constructor is the "old form" that existed prior to Authorizers. should + // probably + // deprecate this form + final Class[] twoArgForm = new Class[] {Authenticator.class, Settings.class}; + final Constructor twoArgConstructor = clazz.getDeclaredConstructor(twoArgForm); + + if (authorizer != null) { + logger.warn( + "There is an authorizer configured but the {} does not have a constructor of ({}, {}, {}) so it cannot be added", + clazz.getName(), + Authenticator.class.getSimpleName(), + Authorizer.class.getSimpleName(), + Settings.class.getSimpleName()); + } + + return (AbstractAuthenticationHandler) + twoArgConstructor.newInstance(authenticator, settings); + } catch (Exception twoArgEx) { + throw twoArgEx; + } + } + } catch (Exception ex) { + logger.warn(ex.getMessage()); + throw new IllegalStateException( + String.format( + "Could not create/configure AuthenticationHandler %s", + settings.authentication.authenticationHandler), + ex); + } + } + + private Authenticator createAuthenticator(final Settings.AuthenticationSettings config) { + final String authenticatorClass = config.authenticator; + try { + final Class clazz = Class.forName(authenticatorClass); + final Authenticator authenticator = (Authenticator) clazz.newInstance(); + authenticator.setup(config.config); + return authenticator; + } catch (Exception ex) { + logger.warn(ex.getMessage()); + throw new IllegalStateException( + String.format("Could not create/configure Authenticator %s", authenticator), ex); + } + } + + private Authorizer createAuthorizer(final Settings.AuthorizationSettings config) { + final String authorizerClass = config.authorizer; + if (null == authorizerClass) { + return null; + } + try { + final Class clazz = Class.forName(authorizerClass); + final Authorizer authorizer = (Authorizer) clazz.newInstance(); + authorizer.setup(config.config); + return authorizer; + } catch (Exception ex) { + logger.warn(ex.getMessage()); + throw new IllegalStateException( + String.format("Could not create/configure Authorizer %s", authorizer), ex); + } + } + + private void configureSerializers() { + // grab some sensible defaults if no serializers are present in the config + final List serializerSettings = + (null == this.settings.serializers || this.settings.serializers.isEmpty()) + ? getDefaultSerializers() + : settings.serializers; + + serializerSettings.stream() + .map( + config -> { + try { + final Class clazz = Class.forName(config.className); + if (!MessageSerializer.class.isAssignableFrom(clazz)) { + logger.warn( + "The {} serialization class does not implement {} - it will not be available.", + config.className, + MessageSerializer.class.getCanonicalName()); + return Optional.empty(); + } + + if (clazz.getAnnotation(Deprecated.class) != null) + logger.warn("The {} serialization class is deprecated.", config.className); + + final MessageSerializer serializer = (MessageSerializer) clazz.newInstance(); + final Map graphsDefinedAtStartup = new HashMap<>(); + for (String graphName : settings.graphs.keySet()) { + graphsDefinedAtStartup.put(graphName, graphManager.getGraph(graphName)); + } + + if (config.config != null) + serializer.configure(config.config, graphsDefinedAtStartup); + + return Optional.ofNullable(serializer); + } catch (ClassNotFoundException cnfe) { + logger.warn( + "Could not find configured serializer class - {} - it will not be available", + config.className); + return Optional.empty(); + } catch (Exception ex) { + logger.warn( + "Could not instantiate configured serializer class - {} - it will not be available. {}", + config.className, + ex.getMessage()); + return Optional.empty(); + } + }) + .filter(Optional::isPresent) + .map(Optional::get) + .flatMap( + serializer -> + Stream.of(serializer.mimeTypesSupported()) + .map(mimeType -> Pair.with(mimeType, serializer))) + .forEach( + pair -> { + final String mimeType = pair.getValue0(); + final MessageSerializer serializer = pair.getValue1(); + if (serializers.containsKey(mimeType)) + logger.info( + "{} already has {} configured - it will not be replaced by {}, change order of serialization configuration if this is not desired.", + mimeType, + serializers.get(mimeType).getClass().getName(), + serializer.getClass().getName()); + else { + logger.info( + "Configured {} with {}", mimeType, pair.getValue1().getClass().getName()); + serializers.put(mimeType, serializer); + } + }); + + if (serializers.size() == 0) { + logger.error("No serializers were successfully configured - server will not start."); + throw new RuntimeException("Serialization configuration error."); + } + } + + private SslContext createSSLContext(final Settings settings) { + final Settings.SslSettings sslSettings = settings.ssl; + + if (sslSettings.getSslContext().isPresent()) { + logger.info("Using the SslContext override"); + return sslSettings.getSslContext().get(); + } + + final SslProvider provider = SslProvider.JDK; + + final SslContextBuilder builder; + + // Build JSSE SSLContext + try { + final KeyManagerFactory kmf = + KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + + // Load private key and signed cert + if (null != sslSettings.keyStore) { + final String keyStoreType = + null == sslSettings.keyStoreType ? KeyStore.getDefaultType() : sslSettings.keyStoreType; + final KeyStore keystore = KeyStore.getInstance(keyStoreType); + final char[] password = + null == sslSettings.keyStorePassword + ? null + : sslSettings.keyStorePassword.toCharArray(); + try (final InputStream in = new FileInputStream(sslSettings.keyStore)) { + keystore.load(in, password); + } + kmf.init(keystore, password); + } else { + throw new IllegalStateException("keyStore must be configured when SSL is enabled."); + } + + builder = SslContextBuilder.forServer(kmf); + + // Load custom truststore for client auth certs + if (null != sslSettings.trustStore) { + final String trustStoreType = + null != sslSettings.trustStoreType + ? sslSettings.trustStoreType + : sslSettings.keyStoreType != null + ? sslSettings.keyStoreType + : KeyStore.getDefaultType(); + + final KeyStore truststore = KeyStore.getInstance(trustStoreType); + final char[] password = + null == sslSettings.trustStorePassword + ? null + : sslSettings.trustStorePassword.toCharArray(); + try (final InputStream in = new FileInputStream(sslSettings.trustStore)) { + truststore.load(in, password); + } + final TrustManagerFactory tmf = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(truststore); + builder.trustManager(tmf); + } + + } catch (UnrecoverableKeyException + | NoSuchAlgorithmException + | KeyStoreException + | CertificateException + | IOException e) { + logger.error(e.getMessage()); + throw new RuntimeException("There was an error enabling SSL.", e); + } + + if (null != sslSettings.sslCipherSuites && !sslSettings.sslCipherSuites.isEmpty()) { + builder.ciphers(sslSettings.sslCipherSuites); + } + + if (null != sslSettings.sslEnabledProtocols && !sslSettings.sslEnabledProtocols.isEmpty()) { + builder.protocols(sslSettings.sslEnabledProtocols.toArray(new String[] {})); + } + + if (null != sslSettings.needClientAuth && ClientAuth.OPTIONAL == sslSettings.needClientAuth) { + logger.warn("needClientAuth = OPTIONAL is not a secure configuration. Setting to REQUIRE."); + sslSettings.needClientAuth = ClientAuth.REQUIRE; + } + + builder.clientAuth(sslSettings.needClientAuth).sslProvider(provider); + + try { + return builder.build(); + } catch (SSLException ssle) { + logger.error(ssle.getMessage()); + throw new RuntimeException("There was an error enabling SSL.", ssle); + } + } +} diff --git a/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/GraphChannelizer.java b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/GraphChannelizer.java new file mode 100644 index 0000000000000000000000000000000000000000..ec8174de20ff9fe2531e64cfde2878c7ed94bbdd --- /dev/null +++ b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/GraphChannelizer.java @@ -0,0 +1,17 @@ +package org.apache.tinkerpop.gremlin.server; + +import io.netty.channel.ChannelHandler; +import org.apache.tinkerpop.gremlin.server.util.GraphServerGremlinExecutor; + +public interface GraphChannelizer extends ChannelHandler { + + public void init(final GraphServerGremlinExecutor graphServerGremlinExecutor); + + public default Object createIdleDetectionMessage() { + return null; + } + + public default boolean supportsIdleMonitor() { + return false; + } +} diff --git a/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/GraphGremlinServer.java b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/GraphGremlinServer.java new file mode 100644 index 0000000000000000000000000000000000000000..bea2dad7e9e84a36b894bf26c6c3f29055ad51cb --- /dev/null +++ b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/GraphGremlinServer.java @@ -0,0 +1,459 @@ +package org.apache.tinkerpop.gremlin.server; + +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.PooledByteBufAllocator; +import io.netty.channel.*; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.epoll.EpollServerSocketChannel; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.util.concurrent.GenericFutureListener; +import io.netty.util.internal.logging.InternalLoggerFactory; +import io.netty.util.internal.logging.Slf4JLoggerFactory; +import org.apache.commons.lang3.SystemUtils; +import org.apache.tinkerpop.gremlin.server.op.OpLoader; +import org.apache.tinkerpop.gremlin.server.util.GraphServerGremlinExecutor; +import org.apache.tinkerpop.gremlin.server.util.LifeCycleHook; +import org.apache.tinkerpop.gremlin.server.util.MetricManager; +import org.apache.tinkerpop.gremlin.server.util.ThreadFactoryUtil; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.janusgraph.core.JanusGraph; +import org.janusgraph.graphdb.grpc.JanusGraphContextHandler; +import org.janusgraph.graphdb.grpc.JanusGraphManagerServiceImpl; +import org.janusgraph.graphdb.grpc.schema.SchemaManagerImpl; +import org.janusgraph.graphdb.management.JanusGraphManager; +import org.janusgraph.graphdb.server.JanusGraphSettings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.studiox.graph.common.exception.GraphException; +import org.studiox.graph.janusgraph.latest.JanusGraphLatest; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.concurrent.*; + +public class GraphGremlinServer { + + static { + // hook slf4j up to netty internal logging + InternalLoggerFactory.setDefaultFactory(Slf4JLoggerFactory.INSTANCE); + } + + private static final String SERVER_THREAD_PREFIX = "gremlin-server-"; + + private static final Logger logger = LoggerFactory.getLogger(GraphGremlinServer.class); + + private final Settings settings; + + private Channel ch; + + private CompletableFuture serverStopped = null; + private CompletableFuture serverStarted = null; + + private final EventLoopGroup bossGroup; + private final EventLoopGroup workerGroup; + private final ExecutorService gremlinExecutorService; + + private final GraphServerGremlinExecutor graphServerGremlinExecutor; + + private final boolean isEpollEnabled; + + private GraphChannelizer graphChannelizer; + + public GraphGremlinServer(final Settings settings) { + this(settings, null, null); + } + + public GraphGremlinServer(final Settings settings, Graph graph) { + this(settings, null, graph); + } + + public GraphGremlinServer( + final Settings settings, final ExecutorService gremlinExecutorService, Graph graph) { + this.settings = settings; + + provideDefaultForGremlinPoolSize(settings); + this.isEpollEnabled = settings.useEpollEventLoop && SystemUtils.IS_OS_LINUX; + if (settings.useEpollEventLoop && !SystemUtils.IS_OS_LINUX) { + logger.warn("cannot use epoll in non-linux env, falling back to NIO"); + } + + Runtime.getRuntime() + .addShutdownHook(new Thread(() -> this.stop().join(), SERVER_THREAD_PREFIX + "shutdown")); + + final ThreadFactory threadFactoryBoss = ThreadFactoryUtil.create("boss-%d"); + + // if linux os use epoll else fallback to nio based eventloop + // epoll helps in reducing GC and has better performance + // http://netty.io/wiki/native-transports.html + if (isEpollEnabled) { + bossGroup = new EpollEventLoopGroup(settings.threadPoolBoss, threadFactoryBoss); + } else { + bossGroup = new NioEventLoopGroup(settings.threadPoolBoss, threadFactoryBoss); + } + + final ThreadFactory threadFactoryWorker = ThreadFactoryUtil.create("worker-%d"); + if (isEpollEnabled) { + workerGroup = new EpollEventLoopGroup(settings.threadPoolWorker, threadFactoryWorker); + } else { + workerGroup = new NioEventLoopGroup(settings.threadPoolWorker, threadFactoryWorker); + } + + // use the ExecutorService returned from ServerGremlinExecutor as it might be initialized there + graphServerGremlinExecutor = + new GraphServerGremlinExecutor(settings, gremlinExecutorService, workerGroup, graph); + this.gremlinExecutorService = graphServerGremlinExecutor.getGremlinExecutorService(); + + // initialize the OpLoader with configurations being passed to each OpProcessor implementation + // loaded + OpLoader.init(settings); + } + + public void start() { + Server grpcServer = createGrpcServer(); + + CompletableFuture grpcServerFuture = + CompletableFuture.runAsync( + () -> { + try { + grpcServer.start(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + }); + + CompletableFuture gremlinServerFuture = + startGremlinServer().thenAcceptAsync(GraphGremlinServer::configure); + + CompletableFuture serverStarted = + CompletableFuture.allOf(gremlinServerFuture, grpcServerFuture); + } + + public synchronized CompletableFuture startGremlinServer() { + if (serverStarted != null) { + // server already started - don't get it rolling again + return serverStarted; + } + + serverStarted = new CompletableFuture<>(); + final CompletableFuture serverReadyFuture = serverStarted; + try { + final ServerBootstrap b = new ServerBootstrap(); + + // when high value is reached then the channel becomes non-writable and stays like that until + // the + // low value is so that there is time to recover + b.childOption( + ChannelOption.WRITE_BUFFER_WATER_MARK, + new WriteBufferWaterMark( + settings.writeBufferLowWaterMark, settings.writeBufferHighWaterMark)); + b.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); + + // fire off any lifecycle scripts that were provided by the user. hooks get initialized during + // ServerGremlinExecutor initialization + graphServerGremlinExecutor + .getHooks() + .forEach( + hook -> { + logger.info("Executing start up {}", LifeCycleHook.class.getSimpleName()); + try { + hook.onStartUp(new LifeCycleHook.Context(logger)); + } catch (UnsupportedOperationException uoe) { + // if the user doesn't implement onStartUp the scriptengine will throw + // this exception. it can safely be ignored. + } + }); + + graphChannelizer = createGraphChannelizer(settings); + graphChannelizer.init(graphServerGremlinExecutor); + b.group(bossGroup, workerGroup).childHandler(graphChannelizer); + if (isEpollEnabled) { + b.channel(EpollServerSocketChannel.class); + } else { + b.channel(NioServerSocketChannel.class); + } + + // bind to host/port and wait for channel to be ready + b.bind(settings.host, settings.port) + .addListener( + (ChannelFutureListener) + channelFuture -> { + if (channelFuture.isSuccess()) { + ch = channelFuture.channel(); + logger.info( + "Gremlin Server configured with worker thread pool of {}, gremlin pool of {} and boss thread pool of {}.", + settings.threadPoolWorker, + settings.gremlinPool, + settings.threadPoolBoss); + logger.info("Channel started at port {}.", settings.port); + serverReadyFuture.complete(graphServerGremlinExecutor); + } else { + serverReadyFuture.completeExceptionally( + new IOException( + String.format( + "Could not bind to %s and %s - perhaps something else is bound to that address.", + settings.host, settings.port))); + } + }); + } catch (Exception ex) { + logger.error("Gremlin Server Error", ex); + serverReadyFuture.completeExceptionally(ex); + } + + return serverStarted; + } + + private static GraphChannelizer createGraphChannelizer(final Settings settings) { + try { + final Class clazz = Class.forName(settings.channelizer); + final Object o = clazz.newInstance(); + + final GraphChannelizer c = (GraphChannelizer) o; + if (c.supportsIdleMonitor()) { + logger.info( + "idleConnectionTimeout was set to {} which resolves to {} seconds when configuring this value - this feature will be {}", + settings.idleConnectionTimeout, + settings.idleConnectionTimeout / 1000, + settings.idleConnectionTimeout < 1000 ? "disabled" : "enabled"); + logger.info( + "keepAliveInterval was set to {} which resolves to {} seconds when configuring this value - this feature will be {}", + settings.keepAliveInterval, + settings.keepAliveInterval / 1000, + settings.keepAliveInterval < 1000 ? "disabled" : "enabled"); + } + + return c; + } catch (ClassNotFoundException cnfe) { + logger.error( + "Could not find {} implementation defined by the 'graphChannelizer' setting as: {}", + GraphChannelizer.class.getName(), + settings.channelizer); + throw new RuntimeException(cnfe); + } catch (Exception ex) { + logger.error( + "Class defined by the 'graphChannelizer' setting as: {} could not be properly instantiated as a {}", + settings.channelizer, + GraphChannelizer.class.getName()); + throw new RuntimeException(ex); + } + } + + public synchronized CompletableFuture stop() { + if (serverStopped != null) { + // shutdown has started so don't fire it off again + return serverStopped; + } + + serverStopped = new CompletableFuture<>(); + final CountDownLatch servicesLeftToShutdown = new CountDownLatch(3); + + // release resources in the OpProcessors (e.g. kill sessions) + OpLoader.getProcessors() + .forEach( + (key, value) -> { + logger.info("Shutting down OpProcessor[{}]", key); + try { + value.close(); + } catch (Exception ex) { + logger.warn( + "Shutdown will continue but, there was an error encountered while closing " + + key, + ex); + } + }); + + // it's possible that a channel might not be initialized in the first place if bind() fails + // because + // of port conflict. in that case, there's no need to wait for the channel to close. + if (null == ch) { + servicesLeftToShutdown.countDown(); + } else { + ch.close().addListener(f -> servicesLeftToShutdown.countDown()); + } + + logger.info("Shutting down thread pools."); + + try { + if (gremlinExecutorService != null) { + gremlinExecutorService.shutdown(); + } + } finally { + logger.debug("Shutdown Gremlin thread pool."); + } + + try { + workerGroup + .shutdownGracefully() + .addListener((GenericFutureListener) f -> servicesLeftToShutdown.countDown()); + } finally { + logger.debug("Shutdown Worker thread pool."); + } + try { + bossGroup + .shutdownGracefully() + .addListener((GenericFutureListener) f -> servicesLeftToShutdown.countDown()); + } finally { + logger.debug("Shutdown Boss thread pool."); + } + + // channel is shutdown as are the thread pools - time to kill graphs as nothing else should be + // acting on them + new Thread( + () -> { + if (graphServerGremlinExecutor != null) { + graphServerGremlinExecutor + .getHooks() + .forEach( + hook -> { + logger.info("Executing shutdown {}", LifeCycleHook.class.getSimpleName()); + try { + hook.onShutDown(new LifeCycleHook.Context(logger)); + } catch (UnsupportedOperationException + | UndeclaredThrowableException uoe) { + // if the user doesn't implement onShutDown the scriptengine will throw + // this exception. it can safely be ignored. + } + }); + } + + try { + if (gremlinExecutorService != null) { + if (!gremlinExecutorService.awaitTermination(30000, TimeUnit.MILLISECONDS)) { + logger.warn( + "Gremlin thread pool did not fully terminate - continuing with shutdown process"); + } + } + } catch (InterruptedException ie) { + logger.warn( + "Timeout waiting for Gremlin thread pool to shutdown - continuing with shutdown process."); + } + + try { + servicesLeftToShutdown.await(30000, TimeUnit.MILLISECONDS); + } catch (InterruptedException ie) { + logger.warn( + "Timeout waiting for boss/worker thread pools to shutdown - continuing with shutdown process."); + } + + if (graphServerGremlinExecutor != null) { + graphServerGremlinExecutor + .getGraphManager() + .getGraphNames() + .forEach( + gName -> { + logger.debug("Closing Graph instance [{}]", gName); + try { + graphServerGremlinExecutor.getGraphManager().getGraph(gName).close(); + } catch (Exception ex) { + logger.warn( + String.format("Exception while closing Graph instance [%s]", gName), + ex); + } finally { + logger.info("Closed Graph instance [{}]", gName); + } + }); + } + + // kills reporter threads. this is a last bit of cleanup that can be done. typically, + // the jvm is headed + // for shutdown which would obviously kill the reporters, but when it isn't they just + // keep reporting. + // removing them all will silent them up and release the appropriate resources. + MetricManager.INSTANCE.removeAllReporters(); + + // removing all the metrics should allow Gremlin Server to clean up the metrics + // instance so that it can be + // started again in the same JVM without those metrics initialized which generates a + // warning and won't + // reset to start values + MetricManager.INSTANCE.removeAllMetrics(); + + logger.info("Gremlin Server - shutdown complete"); + serverStopped.complete(null); + }, + SERVER_THREAD_PREFIX + "stop") + .start(); + + return serverStopped; + } + + public GraphServerGremlinExecutor getGraphServerGremlinExecutor() { + return graphServerGremlinExecutor; + } + + private static void provideDefaultForGremlinPoolSize(final Settings settings) { + if (settings.gremlinPool == 0) { + settings.gremlinPool = Runtime.getRuntime().availableProcessors(); + } + } + + private Server createGrpcServer() { + JanusGraphSettings janusGraphSettings = (JanusGraphSettings) settings; + GraphManager graphManager = getGraphServerGremlinExecutor().getGraphManager(); + JanusGraphContextHandler janusGraphContextHandler = new JanusGraphContextHandler(graphManager); + return ServerBuilder.forPort(janusGraphSettings.getGrpcServer().getPort()) + .addService(new JanusGraphManagerServiceImpl(janusGraphContextHandler)) + .addService(new SchemaManagerImpl(janusGraphContextHandler)) + .build(); + } + + private static void configure(GraphServerGremlinExecutor graphServerGremlinExecutor) { + GraphManager graphManager = graphServerGremlinExecutor.getGraphManager(); + ((JanusGraphManager) graphManager) + .configureGremlinExecutor(graphServerGremlinExecutor.getGremlinExecutor()); + } + + public static JanusGraphSettings createJanusGraphSettings() { + JanusGraphSettings janusGraphSettings = null; + InputStream inputStream = null; + try { + inputStream = + Thread.currentThread().getContextClassLoader().getResourceAsStream("gremlin-server.yaml"); + janusGraphSettings = JanusGraphSettings.read(inputStream); + } catch (Throwable t) { + logger.error(t.getMessage(), t); + } finally { + if (null != inputStream) { + try { + inputStream.close(); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + } + } + + janusGraphSettings + .scriptEngines + .get("gremlin-groovy") + .plugins + .put( + "org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin", + Collections.singletonMap( + "files", + new ArrayList<>( + Arrays.asList( + Thread.currentThread() + .getContextClassLoader() + .getResource("graph.groovy") + .getFile())))); + + return janusGraphSettings; + } + + public static void main(String[] args) throws GraphException { + JanusGraphLatest janusGraphLatest = new JanusGraphLatest(logger); + janusGraphLatest.open(); + janusGraphLatest.createVertex("uniq1", "type1", null); + JanusGraph janusGraph = janusGraphLatest.getJanusGraph(); + + JanusGraphSettings janusGraphSettings = GraphGremlinServer.createJanusGraphSettings(); + GraphGremlinServer graphGremlinServer = new GraphGremlinServer(janusGraphSettings, janusGraph); + graphGremlinServer.start(); + } +} diff --git a/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/channel/GraphHttpChannelizer.java b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/channel/GraphHttpChannelizer.java new file mode 100644 index 0000000000000000000000000000000000000000..043ea2a1c729f95c6f564c18c85fdf3a33441cf2 --- /dev/null +++ b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/channel/GraphHttpChannelizer.java @@ -0,0 +1,86 @@ +package org.apache.tinkerpop.gremlin.server.channel; + +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelPipeline; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import org.apache.tinkerpop.gremlin.server.AbstractGraphChannelizer; +import org.apache.tinkerpop.gremlin.server.Settings; +import org.apache.tinkerpop.gremlin.server.auth.AllowAllAuthenticator; +import org.apache.tinkerpop.gremlin.server.handler.AbstractAuthenticationHandler; +import org.apache.tinkerpop.gremlin.server.handler.HttpBasicAuthenticationHandler; +import org.apache.tinkerpop.gremlin.server.handler.HttpBasicAuthorizationHandler; +import org.apache.tinkerpop.gremlin.server.handler.HttpGremlinEndpointHandler; +import org.apache.tinkerpop.gremlin.server.util.GraphServerGremlinExecutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GraphHttpChannelizer extends AbstractGraphChannelizer { + private static final Logger logger = LoggerFactory.getLogger(GraphHttpChannelizer.class); + + private HttpGremlinEndpointHandler httpGremlinEndpointHandler; + + @Override + public void init(final GraphServerGremlinExecutor graphServerGremlinExecutor) { + super.init(graphServerGremlinExecutor); + httpGremlinEndpointHandler = + new HttpGremlinEndpointHandler(serializers, gremlinExecutor, graphManager, settings); + } + + @Override + public void configure(final ChannelPipeline pipeline) { + if (logger.isDebugEnabled()) { + pipeline.addLast(new LoggingHandler("log-io", LogLevel.DEBUG)); + } + + pipeline.addLast("http-server", new HttpServerCodec()); + + if (logger.isDebugEnabled()) { + pipeline.addLast(new LoggingHandler("http-io", LogLevel.DEBUG)); + } + + final HttpObjectAggregator aggregator = new HttpObjectAggregator(settings.maxContentLength); + aggregator.setMaxCumulationBufferComponents(settings.maxAccumulationBufferComponents); + pipeline.addLast(PIPELINE_HTTP_AGGREGATOR, aggregator); + + if (authenticator != null) { + // Cannot add the same handler instance multiple times unless + // it is marked as @Sharable, indicating a race condition will + // not occur. It may not be a safe assumption that the handler + // is sharable so create a new handler each time. + final AbstractAuthenticationHandler authenticationHandler = + authenticator.getClass() == AllowAllAuthenticator.class + ? null + : instantiateAuthenticationHandler(settings); + if (authenticationHandler != null) { + pipeline.addLast(PIPELINE_AUTHENTICATOR, authenticationHandler); + } + } + + if (authorizer != null) { + final ChannelInboundHandlerAdapter authorizationHandler = + new HttpBasicAuthorizationHandler(authorizer); + pipeline.addLast(PIPELINE_AUTHORIZER, authorizationHandler); + } + + pipeline.addLast("http-gremlin-handler", httpGremlinEndpointHandler); + } + + private AbstractAuthenticationHandler instantiateAuthenticationHandler(final Settings settings) { + final String authHandlerClass = settings.authentication.authenticationHandler; + if (authHandlerClass == null) { + // Keep things backwards compatible + return new HttpBasicAuthenticationHandler(authenticator, settings); + } else { + return createAuthenticationHandler(settings); + } + } + + @Override + public void finalize(final ChannelPipeline pipeline) { + pipeline.remove(PIPELINE_OP_SELECTOR); + pipeline.remove(PIPELINE_OP_EXECUTOR); + } +} diff --git a/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/channel/GraphWebSocketChannelizer.java b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/channel/GraphWebSocketChannelizer.java new file mode 100644 index 0000000000000000000000000000000000000000..0691892021fee8440d4390e93dd1c61c0c51019e --- /dev/null +++ b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/channel/GraphWebSocketChannelizer.java @@ -0,0 +1,143 @@ +package org.apache.tinkerpop.gremlin.server.channel; + +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelPipeline; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; +import io.netty.handler.codec.http.websocketx.PingWebSocketFrame; +import io.netty.handler.codec.http.websocketx.WebSocketDecoderConfig; +import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; +import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; +import org.apache.tinkerpop.gremlin.server.AbstractGraphChannelizer; +import org.apache.tinkerpop.gremlin.server.Settings; +import org.apache.tinkerpop.gremlin.server.auth.AllowAllAuthenticator; +import org.apache.tinkerpop.gremlin.server.handler.*; +import org.apache.tinkerpop.gremlin.server.util.GraphServerGremlinExecutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class GraphWebSocketChannelizer extends AbstractGraphChannelizer { + private static final Logger logger = LoggerFactory.getLogger(GraphWebSocketChannelizer.class); + + private GremlinResponseFrameEncoder gremlinResponseFrameEncoder; + private WsGremlinTextRequestDecoder wsGremlinTextRequestDecoder; + private WsGremlinBinaryRequestDecoder wsGremlinBinaryRequestDecoder; + private WsGremlinResponseFrameEncoder wsGremlinResponseFrameEncoder; + private WsGremlinCloseRequestDecoder wsGremlinCloseRequestDecoder; + private AbstractAuthenticationHandler authenticationHandler; + private ChannelInboundHandlerAdapter authorizationHandler; + + @Override + public void init(final GraphServerGremlinExecutor graphServerGremlinExecutor) { + super.init(graphServerGremlinExecutor); + + gremlinResponseFrameEncoder = new GremlinResponseFrameEncoder(); + wsGremlinTextRequestDecoder = new WsGremlinTextRequestDecoder(serializers); + wsGremlinBinaryRequestDecoder = new WsGremlinBinaryRequestDecoder(serializers); + wsGremlinCloseRequestDecoder = new WsGremlinCloseRequestDecoder(serializers); + wsGremlinResponseFrameEncoder = new WsGremlinResponseFrameEncoder(); + + // configure authentication - null means don't bother to add authentication to the pipeline + authenticationHandler = + authenticator.getClass() == AllowAllAuthenticator.class + ? null + : instantiateAuthenticationHandler(settings); + if (authorizer != null) { + authorizationHandler = new WebSocketAuthorizationHandler(authorizer); + } + } + + @Override + public void configure(final ChannelPipeline pipeline) { + + if (logger.isDebugEnabled()) { + pipeline.addLast(new LoggingHandler("log-encoder-aggregator", LogLevel.DEBUG)); + } + + pipeline.addLast(PIPELINE_HTTP_RESPONSE_ENCODER, new HttpResponseEncoder()); + + logger.debug( + "HttpRequestDecoder settings - maxInitialLineLength={}, maxHeaderSize={}, maxChunkSize={}", + settings.maxInitialLineLength, + settings.maxHeaderSize, + settings.maxChunkSize); + pipeline.addLast( + PIPELINE_HTTP_REQUEST_DECODER, + new HttpRequestDecoder( + settings.maxInitialLineLength, settings.maxHeaderSize, settings.maxChunkSize)); + + if (logger.isDebugEnabled()) { + pipeline.addLast(new LoggingHandler("log-decoder-aggregator", LogLevel.DEBUG)); + } + + logger.debug( + "HttpObjectAggregator settings - maxContentLength={}, maxAccumulationBufferComponents={}", + settings.maxContentLength, + settings.maxAccumulationBufferComponents); + final HttpObjectAggregator aggregator = new HttpObjectAggregator(settings.maxContentLength); + aggregator.setMaxCumulationBufferComponents(settings.maxAccumulationBufferComponents); + pipeline.addLast(PIPELINE_HTTP_AGGREGATOR, aggregator); + // Add compression extension for WebSocket defined in https://tools.ietf.org/html/rfc7692 + pipeline.addLast( + PIPELINE_WEBSOCKET_SERVER_COMPRESSION, new WebSocketServerCompressionHandler()); + + // setting closeOnProtocolViolation to false prevents causing all the other requests using the + // same channel + // to fail when a single request causes a protocol violation. + final WebSocketDecoderConfig wsDecoderConfig = + WebSocketDecoderConfig.newBuilder() + .closeOnProtocolViolation(false) + .allowExtensions(true) + .maxFramePayloadLength(settings.maxContentLength) + .build(); + pipeline.addLast( + PIPELINE_REQUEST_HANDLER, + new WebSocketServerProtocolHandler( + GREMLIN_ENDPOINT, null, false, false, 10000L, wsDecoderConfig)); + + if (logger.isDebugEnabled()) { + pipeline.addLast(new LoggingHandler("log-aggregator-encoder", LogLevel.DEBUG)); + } + + pipeline.addLast("ws-frame-encoder", wsGremlinResponseFrameEncoder); + pipeline.addLast("response-frame-encoder", gremlinResponseFrameEncoder); + pipeline.addLast("request-text-decoder", wsGremlinTextRequestDecoder); + pipeline.addLast("request-binary-decoder", wsGremlinBinaryRequestDecoder); + pipeline.addLast("request-close-decoder", wsGremlinCloseRequestDecoder); + + if (logger.isDebugEnabled()) { + pipeline.addLast(new LoggingHandler("log-aggregator-encoder", LogLevel.DEBUG)); + } + + if (authenticationHandler != null) { + pipeline.addLast(PIPELINE_AUTHENTICATOR, authenticationHandler); + } + + if (authorizationHandler != null) { + pipeline.addLast(PIPELINE_AUTHORIZER, authorizationHandler); + } + } + + @Override + public boolean supportsIdleMonitor() { + return true; + } + + @Override + public Object createIdleDetectionMessage() { + return new PingWebSocketFrame(); + } + + private AbstractAuthenticationHandler instantiateAuthenticationHandler(final Settings settings) { + final String authenticationHandler = settings.authentication.authenticationHandler; + if (authenticationHandler == null) { + // Keep things backwards compatible + return new SaslAuthenticationHandler(authenticator, settings); + } else { + return createAuthenticationHandler(settings); + } + } +} diff --git a/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/channel/GraphWsAndHttpChannelizer.java b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/channel/GraphWsAndHttpChannelizer.java new file mode 100644 index 0000000000000000000000000000000000000000..ed1f2bc5758479f70cedc96b6d76ea3ee4cdd0b0 --- /dev/null +++ b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/channel/GraphWsAndHttpChannelizer.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tinkerpop.gremlin.server.channel; + +import io.netty.channel.ChannelPipeline; +import org.apache.tinkerpop.gremlin.server.AbstractGraphChannelizer; +import org.apache.tinkerpop.gremlin.server.handler.GraphWsAndHttpChannelizerHandler; +import org.apache.tinkerpop.gremlin.server.handler.HttpGremlinEndpointHandler; +import org.apache.tinkerpop.gremlin.server.util.GraphServerGremlinExecutor; + +public class GraphWsAndHttpChannelizer extends AbstractGraphChannelizer { + + private GraphWsAndHttpChannelizerHandler handler; + + @Override + public void init(final GraphServerGremlinExecutor graphServerGremlinExecutor) { + super.init(graphServerGremlinExecutor); + handler = new GraphWsAndHttpChannelizerHandler(); + handler.init( + graphServerGremlinExecutor, + new HttpGremlinEndpointHandler(serializers, gremlinExecutor, graphManager, settings)); + } + + @Override + public void configure(final ChannelPipeline pipeline) { + handler.configure(pipeline); + pipeline.addAfter(PIPELINE_HTTP_REQUEST_DECODER, "WsAndHttpChannelizerHandler", handler); + } + + @Override + public boolean supportsIdleMonitor() { + return true; + } + + @Override + public Object createIdleDetectionMessage() { + return handler.getGraphWebSocketChannelizer().createIdleDetectionMessage(); + } +} diff --git a/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/handler/GraphOpSelectorHandler.java b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/handler/GraphOpSelectorHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..e4ded4a02afa2088435ea5ecb06bcc915f98ea8e --- /dev/null +++ b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/handler/GraphOpSelectorHandler.java @@ -0,0 +1,111 @@ +package org.apache.tinkerpop.gremlin.server.handler; + +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageDecoder; +import io.netty.handler.timeout.IdleState; +import io.netty.handler.timeout.IdleStateEvent; +import org.apache.tinkerpop.gremlin.driver.message.RequestMessage; +import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage; +import org.apache.tinkerpop.gremlin.driver.message.ResponseStatusCode; +import org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor; +import org.apache.tinkerpop.gremlin.server.*; +import org.apache.tinkerpop.gremlin.server.op.OpLoader; +import org.apache.tinkerpop.gremlin.server.op.OpProcessorException; +import org.javatuples.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ScheduledExecutorService; + +@ChannelHandler.Sharable +public class GraphOpSelectorHandler extends MessageToMessageDecoder { + private static final Logger logger = LoggerFactory.getLogger(GraphOpSelectorHandler.class); + + private final Settings settings; + private final GraphManager graphManager; + + private final GremlinExecutor gremlinExecutor; + private final ScheduledExecutorService scheduledExecutorService; + private final GraphChannelizer graphChannelizer; + + public GraphOpSelectorHandler( + final Settings settings, + final GraphManager graphManager, + final GremlinExecutor gremlinExecutor, + final ScheduledExecutorService scheduledExecutorService, + final GraphChannelizer graphChannelizer) { + this.settings = settings; + this.graphManager = graphManager; + this.gremlinExecutor = gremlinExecutor; + this.scheduledExecutorService = scheduledExecutorService; + this.graphChannelizer = graphChannelizer; + } + + @Override + protected void decode( + final ChannelHandlerContext ctx, final RequestMessage msg, final List objects) + throws Exception { + final Context gremlinServerContext = + new Context( + msg, ctx, settings, graphManager, gremlinExecutor, this.scheduledExecutorService); + try { + // choose a processor to do the work based on the request message. + final Optional processor = OpLoader.getProcessor(msg.getProcessor()); + + if (processor.isPresent()) + // the processor is known so use it to evaluate the message + objects.add(Pair.with(msg, processor.get().select(gremlinServerContext))); + else { + // invalid op processor selected so write back an error by way of OpProcessorException. + final String errorMessage = + String.format("Invalid OpProcessor requested [%s]", msg.getProcessor()); + throw new OpProcessorException( + errorMessage, + ResponseMessage.build(msg) + .code(ResponseStatusCode.REQUEST_ERROR_INVALID_REQUEST_ARGUMENTS) + .statusMessage(errorMessage) + .create()); + } + } catch (OpProcessorException ope) { + logger.warn(ope.getMessage(), ope); + gremlinServerContext.writeAndFlush(ope.getResponseMessage()); + } + } + + @Override + public void userEventTriggered(final ChannelHandlerContext ctx, final Object evt) + throws Exception { + // only need to handle this event if the idle monitor is on + if (!graphChannelizer.supportsIdleMonitor()) { + return; + } + + if (evt instanceof IdleStateEvent) { + final IdleStateEvent e = (IdleStateEvent) evt; + + // if no requests (reader) then close, if no writes from server to client then ping. clients + // should + // periodically ping the server, but coming from this direction allows the server to kill + // channels that + // have dead clients on the other end + if (e.state() == IdleState.READER_IDLE) { + logger.info( + "Closing channel - client is disconnected after idle period of " + + settings.idleConnectionTimeout + + " " + + ctx.channel().id().asShortText()); + ctx.close(); + } else if (e.state() == IdleState.WRITER_IDLE && settings.keepAliveInterval > 0) { + logger.info( + "Checking channel - sending ping to client after idle period of " + + settings.keepAliveInterval + + " " + + ctx.channel().id().asShortText()); + ctx.writeAndFlush(graphChannelizer.createIdleDetectionMessage()); + } + } + } +} diff --git a/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/handler/GraphWsAndHttpChannelizerHandler.java b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/handler/GraphWsAndHttpChannelizerHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..f809c0d546a209fac2d5ccd003ec2cd57c8c3f6d --- /dev/null +++ b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/handler/GraphWsAndHttpChannelizerHandler.java @@ -0,0 +1,80 @@ +package org.apache.tinkerpop.gremlin.server.handler; + +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelPipeline; +import io.netty.handler.codec.http.HttpMessage; +import org.apache.tinkerpop.gremlin.server.GraphChannelizer; +import org.apache.tinkerpop.gremlin.server.channel.GraphWebSocketChannelizer; +import org.apache.tinkerpop.gremlin.server.util.GraphServerGremlinExecutor; + +import static org.apache.tinkerpop.gremlin.server.AbstractChannelizer.PIPELINE_HTTP_AGGREGATOR; +import static org.apache.tinkerpop.gremlin.server.channel.WebSocketChannelizer.PIPELINE_AUTHENTICATOR; +import static org.apache.tinkerpop.gremlin.server.channel.WebSocketChannelizer.PIPELINE_REQUEST_HANDLER; + +@ChannelHandler.Sharable +public class GraphWsAndHttpChannelizerHandler extends ChannelInboundHandlerAdapter { + + private final GraphWebSocketChannelizer graphWebSocketChannelizer = + new GraphWebSocketChannelizer(); + private HttpGremlinEndpointHandler httpGremlinEndpointHandler; + + public void init( + final GraphServerGremlinExecutor graphServerGremlinExecutor, + final HttpGremlinEndpointHandler httpGremlinEndpointHandler) { + // WebSocketChannelizer has everything needed for the http endpoint to work + graphWebSocketChannelizer.init(graphServerGremlinExecutor); + this.httpGremlinEndpointHandler = httpGremlinEndpointHandler; + } + + public GraphChannelizer getGraphWebSocketChannelizer() { + return graphWebSocketChannelizer; + } + + public void configure(final ChannelPipeline pipeline) { + graphWebSocketChannelizer.configure(pipeline); + } + + @Override + public void channelRead(final ChannelHandlerContext ctx, final Object obj) { + final ChannelPipeline pipeline = ctx.pipeline(); + if (obj instanceof HttpMessage && !WebSocketHandlerUtil.isWebSocket((HttpMessage) obj)) { + // if the message is for HTTP and not websockets then this handler injects the endpoint + // handler in front + // of the HTTP Aggregator to intercept the HttpMessage. Therefore the pipeline looks like this + // at start: + // + // IdleStateHandler -> HttpResponseEncoder -> HttpRequestDecoder -> + // WsAndHttpChannelizerHandler -> HttpObjectAggregator -> + // WebSocketServerCompressionHandler -> WebSocketServerProtocolHandshakeHandler -> (more + // websockets) + // + // and shifts to (setting aside the authentication condition): + // + // IdleStateHandler -> HttpResponseEncoder -> HttpRequestDecoder -> + // WsAndHttpChannelizerHandler -> HttpObjectAggregator -> + // HttpGremlinEndpointHandler -> + // WebSocketServerCompressionHandler - WebSocketServerProtocolHandshakeHandler -> (more + // websockets) + if (null != pipeline.get(PIPELINE_AUTHENTICATOR)) { + pipeline.remove(PIPELINE_REQUEST_HANDLER); + final ChannelHandler authenticator = pipeline.get(PIPELINE_AUTHENTICATOR); + pipeline.remove(PIPELINE_AUTHENTICATOR); + pipeline.addAfter(PIPELINE_HTTP_AGGREGATOR, PIPELINE_AUTHENTICATOR, authenticator); + pipeline.addAfter( + PIPELINE_AUTHENTICATOR, PIPELINE_REQUEST_HANDLER, this.httpGremlinEndpointHandler); + } else { + pipeline.remove(PIPELINE_REQUEST_HANDLER); + pipeline.addAfter( + PIPELINE_HTTP_AGGREGATOR, PIPELINE_REQUEST_HANDLER, this.httpGremlinEndpointHandler); + } + } + ctx.fireChannelRead(obj); + } + + @Override + public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) { + ctx.close(); + } +} diff --git a/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/util/GraphServerGremlinExecutor.java b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/util/GraphServerGremlinExecutor.java new file mode 100644 index 0000000000000000000000000000000000000000..0d10cfdb01163da889d22d9c557c487669deb2aa --- /dev/null +++ b/graph-janusgraph/janusgraph-latest/src/main/java/org/apache/tinkerpop/gremlin/server/util/GraphServerGremlinExecutor.java @@ -0,0 +1,226 @@ +package org.apache.tinkerpop.gremlin.server.util; + +import org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor; +import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine; +import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource; +import org.apache.tinkerpop.gremlin.server.GraphManager; +import org.apache.tinkerpop.gremlin.server.Settings; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.script.SimpleBindings; +import java.lang.reflect.Constructor; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.*; +import java.util.stream.Collectors; + +import static org.studiox.graph.common.GraphConstant.JanusGraphConfigConstant.CONFIG_PREFIX; + +public class GraphServerGremlinExecutor { + private static final Logger logger = LoggerFactory.getLogger(GraphServerGremlinExecutor.class); + + private final GraphManager graphManager; + private final Settings settings; + private final List hooks; + + private final ScheduledExecutorService scheduledExecutorService; + private final ExecutorService gremlinExecutorService; + private final GremlinExecutor gremlinExecutor; + + private final Map hostOptions = new ConcurrentHashMap<>(); + + public GraphServerGremlinExecutor( + final Settings settings, + final ExecutorService gremlinExecutorService, + final ScheduledExecutorService scheduledExecutorService, + final Graph graph) { + this.settings = settings; + + try { + final Class clazz = Class.forName(settings.graphManager); + final Constructor c = clazz.getConstructor(Settings.class); + graphManager = (GraphManager) c.newInstance(settings); + } catch (ClassNotFoundException e) { + logger.error( + "Could not find GraphManager implementation " + + "defined by the 'graphManager' setting as: {}", + settings.graphManager); + throw new RuntimeException(e); + } catch (Exception e) { + logger.error( + "Could not invoke constructor on class {} (defined by " + + "the 'graphManager' setting) with one argument of " + + "class Settings", + settings.graphManager); + throw new RuntimeException(e); + } + + if (null != graphManager && null != graph) { + graphManager.putGraph(CONFIG_PREFIX, graph); + } + + if (null == gremlinExecutorService) { + final ThreadFactory threadFactoryGremlin = ThreadFactoryUtil.create("exec-%d"); + final BlockingQueue queue = new ArrayBlockingQueue<>(settings.maxWorkQueueSize); + this.gremlinExecutorService = + new ThreadPoolExecutor( + settings.gremlinPool, + settings.gremlinPool, + 0L, + TimeUnit.MILLISECONDS, + queue, + threadFactoryGremlin, + new ThreadPoolExecutor.AbortPolicy()); + } else { + this.gremlinExecutorService = gremlinExecutorService; + } + + if (null == scheduledExecutorService) { + final ThreadFactory threadFactoryGremlin = ThreadFactoryUtil.create("worker-%d"); + this.scheduledExecutorService = + Executors.newScheduledThreadPool(settings.threadPoolWorker, threadFactoryGremlin); + } else { + this.scheduledExecutorService = scheduledExecutorService; + } + + logger.info("Initialized Gremlin thread pool. Threads in pool named with pattern gremlin-*"); + + final GremlinExecutor.Builder gremlinExecutorBuilder = + GremlinExecutor.build() + .evaluationTimeout(settings.getEvaluationTimeout()) + .afterFailure((b, e) -> this.graphManager.rollbackAll()) + .beforeEval(b -> this.graphManager.rollbackAll()) + .afterTimeout(b -> this.graphManager.rollbackAll()) + .globalBindings(this.graphManager.getAsBindings()) + .executorService(this.gremlinExecutorService) + .scheduledExecutorService(this.scheduledExecutorService); + + settings.scriptEngines.forEach( + (k, v) -> { + // use plugins if they are present + if (!v.plugins.isEmpty()) { + // make sure that server related classes are available at init - new approach. the + // LifeCycleHook stuff + // will be added explicitly via configuration using GremlinServerGremlinModule in the + // yaml + gremlinExecutorBuilder.addPlugins(k, v.plugins); + } + }); + + gremlinExecutor = gremlinExecutorBuilder.create(); + + logger.info("Initialized GremlinExecutor and preparing GremlinScriptEngines instances."); + + // force each scriptengine to process something so that the init scripts will fire (this is + // necessary if + // the GremlinExecutor is using the GremlinScriptEngineManager. this is a bit of hack, but it at + // least allows + // the global bindings to become available after the init scripts are run + // (DefaultGremlinScriptEngineManager + // runs the init scripts when the GremlinScriptEngine is created. + settings + .scriptEngines + .keySet() + .forEach( + engineName -> { + try { + // use no timeout on the engine initialization - perhaps this can be a configuration + // later + final GremlinExecutor.LifeCycle lifeCycle = + GremlinExecutor.LifeCycle.build().evaluationTimeoutOverride(0L).create(); + gremlinExecutor + .eval("1+1", engineName, new SimpleBindings(Collections.emptyMap()), lifeCycle) + .join(); + registerMetrics(engineName); + logger.info( + "Initialized {} GremlinScriptEngine and registered metrics", engineName); + } catch (Exception ex) { + logger.warn( + String.format( + "Could not initialize %s GremlinScriptEngine as init script could not be evaluated", + engineName), + ex); + } + }); + + // script engine init may have altered the graph bindings or maybe even created new ones - need + // to + // re-apply those references back + gremlinExecutor.getScriptEngineManager().getBindings().entrySet().stream() + .filter(kv -> kv.getValue() instanceof Graph) + .forEach(kv -> this.graphManager.putGraph(kv.getKey(), (Graph) kv.getValue())); + + // script engine init may have constructed the TraversalSource bindings - store them in Graphs + // object + gremlinExecutor.getScriptEngineManager().getBindings().entrySet().stream() + .filter(kv -> kv.getValue() instanceof TraversalSource) + .forEach( + kv -> { + logger.info( + "A {} is now bound to [{}] with {}", + kv.getValue().getClass().getSimpleName(), + kv.getKey(), + kv.getValue()); + this.graphManager.putTraversalSource(kv.getKey(), (TraversalSource) kv.getValue()); + }); + + // determine if the initialization scripts introduced LifeCycleHook objects - if so we need to + // gather them + // up for execution + hooks = + gremlinExecutor.getScriptEngineManager().getBindings().entrySet().stream() + .filter(kv -> kv.getValue() instanceof LifeCycleHook) + .map(kv -> (LifeCycleHook) kv.getValue()) + .collect(Collectors.toList()); + } + + private void registerMetrics(final String engineName) { + final GremlinScriptEngine engine = + gremlinExecutor.getScriptEngineManager().getEngineByName(engineName); + MetricManager.INSTANCE.registerGremlinScriptEngineMetrics( + engine, engineName, "sessionless", "class-cache"); + } + + public void addHostOption(final String key, final Object value) { + hostOptions.put(key, value); + } + + public Map getHostOptions() { + return Collections.unmodifiableMap(hostOptions); + } + + public Object removeHostOption(final String key) { + return hostOptions.remove(key); + } + + public void clearHostOptions() { + hostOptions.clear(); + } + + public ScheduledExecutorService getScheduledExecutorService() { + return scheduledExecutorService; + } + + public GremlinExecutor getGremlinExecutor() { + return gremlinExecutor; + } + + public ExecutorService getGremlinExecutorService() { + return gremlinExecutorService; + } + + public GraphManager getGraphManager() { + return graphManager; + } + + public Settings getSettings() { + return settings; + } + + public List getHooks() { + return hooks; + } +} diff --git a/graph-janusgraph/janusgraph-latest/src/main/java/org/studiox/graph/janusgraph/latest/JanusGraphLatest.java b/graph-janusgraph/janusgraph-latest/src/main/java/org/studiox/graph/janusgraph/latest/JanusGraphLatest.java new file mode 100644 index 0000000000000000000000000000000000000000..5c1172b66a017dd36bcf14dc6f17a8da10cf1e3f --- /dev/null +++ b/graph-janusgraph/janusgraph-latest/src/main/java/org/studiox/graph/janusgraph/latest/JanusGraphLatest.java @@ -0,0 +1,17 @@ +package org.studiox.graph.janusgraph.latest; + +import org.apache.commons.configuration.Configuration; +import org.slf4j.Logger; +import org.studiox.graph.common.exception.GraphException; +import org.studiox.graph.janusgraph.v020.JanusGraphV020; + +public class JanusGraphLatest extends JanusGraphV020 { + + public JanusGraphLatest(Logger logger) throws GraphException { + super(logger); + } + + public JanusGraphLatest(Configuration config, String hbaseTableName, Logger logger) { + super(config, hbaseTableName, logger); + } +} diff --git a/graph-janusgraph/janusgraph-latest/src/main/resources/graph.groovy b/graph-janusgraph/janusgraph-latest/src/main/resources/graph.groovy new file mode 100644 index 0000000000000000000000000000000000000000..386298659e0a33a0c392004bcfe70f011363b239 --- /dev/null +++ b/graph-janusgraph/janusgraph-latest/src/main/resources/graph.groovy @@ -0,0 +1,17 @@ + +// an init script that returns a Map allows explicit setting of global bindings. +def globals = [:] + +// defines a sample LifeCycleHook that prints some output to the Gremlin Server console. +// note that the name of the key in the "global" map is unimportant. +globals << [hook : [ + onStartUp: { ctx -> + ctx.logger.info("Executed once at startup of Gremlin Server.") + }, + onShutDown: { ctx -> + ctx.logger.info("Executed once at shutdown of Gremlin Server.") + } +] as LifeCycleHook] + +// define the default TraversalSource to bind queries to - this one will be named "g". +globals << [g : graph.traversal()] diff --git a/graph-janusgraph/janusgraph-latest/src/main/resources/gremlin-server.yaml b/graph-janusgraph/janusgraph-latest/src/main/resources/gremlin-server.yaml new file mode 100644 index 0000000000000000000000000000000000000000..d74187b958a0729a805fdcd3d2041ff5661344ec --- /dev/null +++ b/graph-janusgraph/janusgraph-latest/src/main/resources/gremlin-server.yaml @@ -0,0 +1,69 @@ +host: 0.0.0.0 + +port: 8082 + +useEpollEventLoop: false + +gremlinPool: 1 + +evaluationTimeout: 60000 + +channelizer: org.apache.tinkerpop.gremlin.server.channel.GraphHttpChannelizer + +graphs: { +} + +scriptEngines: { + gremlin-groovy: { + plugins: + { + org.janusgraph.graphdb.tinkerpop.plugin.JanusGraphGremlinPlugin: { }, + org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: { }, + org.apache.tinkerpop.gremlin.tinkergraph.jsr223.TinkerGraphGremlinPlugin: { }, + org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: { classImports: [ java.lang.Math ], methodImports: [ java.lang.Math#* ] } + #,org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: { files: [ /xxx/xxx/graph.groovy ] } + } + } +} + +# JanusGraph sets default serializers. You need to uncomment the following lines, if you require any custom serializers. +# +# serializers: +# - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }} +# - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphBinaryMessageSerializerV1, config: { serializeResultToString: true }} +# - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }} +# - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV3d0, config: { serializeResultToString: true }} +# - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV3d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }} +# # Older serialization versions for backwards compatibility: +# - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }} +# - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoLiteMessageSerializerV1d0, config: {ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }} +# - { className: org.apache.tinkerpop.gremlin.driver.ser.GryoMessageSerializerV1d0, config: { serializeResultToString: true }} +# - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV2d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistry] }} +# - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerGremlinV1d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistryV1d0] }} +# - { className: org.apache.tinkerpop.gremlin.driver.ser.GraphSONMessageSerializerV1d0, config: { ioRegistries: [org.janusgraph.graphdb.tinkerpop.JanusGraphIoRegistryV1d0] }} + +processors: + - { + className: org.apache.tinkerpop.gremlin.server.op.session.SessionOpProcessor, + config: { + sessionTimeout: 28800000 + } + } + - { + className: org.apache.tinkerpop.gremlin.server.op.traversal.TraversalOpProcessor, + config: { + cacheExpirationTime: 600000, + cacheMaxSize: 1000 + } + } + +resultIterationBatchSize: 64 +maxInitialLineLength: 4096 +maxHeaderSize: 8192 +maxChunkSize: 8192 +maxContentLength: 65536 +maxAccumulationBufferComponents: 1024 +writeBufferHighWaterMark: 65536 +writeBufferLowWaterMark: 32768 +strictTransactionManagement: false +sessionLifetimeTimeout: 600000 diff --git a/graph-janusgraph/janusgraph-latest/src/test/java/org/studiox/graph/janusgraph/latest/GraphGremlinServerTest.java b/graph-janusgraph/janusgraph-latest/src/test/java/org/studiox/graph/janusgraph/latest/GraphGremlinServerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..134aef4db05f5dccf2a4fea80cf850c0507cdd95 --- /dev/null +++ b/graph-janusgraph/janusgraph-latest/src/test/java/org/studiox/graph/janusgraph/latest/GraphGremlinServerTest.java @@ -0,0 +1,28 @@ +package org.studiox.graph.janusgraph.latest; + +import org.apache.tinkerpop.gremlin.server.GraphGremlinServer; +import org.janusgraph.core.JanusGraph; +import org.janusgraph.graphdb.server.JanusGraphSettings; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.studiox.graph.common.exception.GraphException; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class GraphGremlinServerTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(GraphGremlinServerTest.class); + + @Test + public void test() throws GraphException { + JanusGraphLatest janusGraphLatest = new JanusGraphLatest(LOGGER); + janusGraphLatest.open(); + JanusGraph janusGraph = janusGraphLatest.getJanusGraph(); + + JanusGraphSettings janusGraphSettings = GraphGremlinServer.createJanusGraphSettings(); + GraphGremlinServer graphGremlinServer = new GraphGremlinServer(janusGraphSettings, janusGraph); + graphGremlinServer.start(); + } +} diff --git a/graph-janusgraph/janusgraph-latest/src/test/java/org/studiox/graph/janusgraph/latest/JanusGraphLatestTest.java b/graph-janusgraph/janusgraph-latest/src/test/java/org/studiox/graph/janusgraph/latest/JanusGraphLatestTest.java new file mode 100644 index 0000000000000000000000000000000000000000..661155705170071598a04918e0851cda45aece71 --- /dev/null +++ b/graph-janusgraph/janusgraph-latest/src/test/java/org/studiox/graph/janusgraph/latest/JanusGraphLatestTest.java @@ -0,0 +1,14 @@ +package org.studiox.graph.janusgraph.latest; + +import org.junit.BeforeClass; +import org.slf4j.LoggerFactory; +import org.studiox.graph.common.exception.GraphException; +import org.studiox.graph.janusgraph.common.test.JanusGraphTest; + +public class JanusGraphLatestTest extends JanusGraphTest { + + @BeforeClass + public static void init() throws GraphException { + init(new JanusGraphLatest(LoggerFactory.getLogger(JanusGraphLatestTest.class))); + } +} diff --git a/graph-janusgraph/janusgraph-latest/src/test/resources/graph.properties b/graph-janusgraph/janusgraph-latest/src/test/resources/graph.properties new file mode 100755 index 0000000000000000000000000000000000000000..028881eac35990e8878e4f2500384c658aaa54f8 --- /dev/null +++ b/graph-janusgraph/janusgraph-latest/src/test/resources/graph.properties @@ -0,0 +1,27 @@ +################################################################### +graph.schema.default=none +graph.storage.batch-loading=true +################################################################### +graph.cache.db-cache=false +graph.ids.block-size=20000000 +graph.ids.renew-timeout=3600000 +graph.storage.buffer-size=20240 +################################################################### +graph.gremlin.graph=org.janusgraph.core.JanusGraphFactory +################################################################### +graph.storage.backend=inmemory +################################################################### +#graph.storage.backend=hbase +#graph.storage.hostname=localhost +#graph.storage.hbase.table=graph_table_1 +#graph.storage.hbase.ext.zookeeper.znode.parent=/hbase1 +################################################################### +#graph.storage.hbase.region-count=9 +#graph.storage.hbase.regions-per-server=3 +#graph.storage.hbase.ext.hbase.rpc.timeout=300000 +#graph.storage.hbase.ext.hbase.client.operation.timeout=300000 +#graph.storage.hbase.ext.hbase.client.scanner.timeout.period=300000 +################################################################### +#graph.index.search.backend=elasticsearch +#graph.index.search.hostname=localhost:9200 +################################################################### diff --git a/graph-janusgraph/janusgraph-v0.2.0/pom.xml b/graph-janusgraph/janusgraph-v0.2.0/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..3c0c847189e688c479271d5965d33319388abc52 --- /dev/null +++ b/graph-janusgraph/janusgraph-v0.2.0/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + org.studiox.graph + graph-janusgraph + 1.0.5-SNAPSHOT + + org.studiox.graph.janusgraph + janusgraph-v0.2.0 + 1.0.5-SNAPSHOT + jar + + 0.2.0 + 3.2.6 + 1.3.1 + + + + junit + junit + test + + + org.studiox.graph.janusgraph + janusgraph-common + + + org.janusgraph + janusgraph-core + ${janusgraph.version} + + + org.janusgraph + janusgraph-hbase + ${janusgraph.version} + + + org.apache.hbase + hbase-client + ${hbase.version} + + + + + + pl.project13.maven + git-commit-id-plugin + + + + diff --git a/graph-janusgraph/janusgraph-v0.2.0/src/main/java/org/studiox/graph/janusgraph/v020/JanusGraphV020.java b/graph-janusgraph/janusgraph-v0.2.0/src/main/java/org/studiox/graph/janusgraph/v020/JanusGraphV020.java new file mode 100644 index 0000000000000000000000000000000000000000..9f3d22ebd133557f7064e3409d0b0d6c5e314dce --- /dev/null +++ b/graph-janusgraph/janusgraph-v0.2.0/src/main/java/org/studiox/graph/janusgraph/v020/JanusGraphV020.java @@ -0,0 +1,1211 @@ +package org.studiox.graph.janusgraph.v020; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang.StringUtils; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; +import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__; +import org.apache.tinkerpop.gremlin.structure.Direction; +import org.apache.tinkerpop.gremlin.structure.Edge; +import org.apache.tinkerpop.gremlin.structure.Graph; +import org.apache.tinkerpop.gremlin.structure.Vertex; +import org.janusgraph.core.*; +import org.janusgraph.core.schema.*; +import org.janusgraph.graphdb.database.management.ManagementSystem; +import org.slf4j.Logger; +import org.studiox.graph.common.GraphEdge; +import org.studiox.graph.common.GraphLineage; +import org.studiox.graph.common.GraphVertex; +import org.studiox.graph.common.exception.GraphException; +import org.studiox.graph.janusgraph.common.AbstractJanusGraph; +import org.studiox.graph.janusgraph.common.util.DateUtil; +import org.studiox.graph.janusgraph.common.util.StringUtil; + +import java.util.*; +import java.util.concurrent.ExecutionException; + +import static org.studiox.graph.common.GraphConstant.JanusGraphLabelPropertyKeyIndexConstant.*; + +public class JanusGraphV020 extends AbstractJanusGraph { + + private Logger logger; + + private JanusGraphFactory.Builder builder; + + public JanusGraphV020(Logger logger) throws GraphException { + this.logger = logger; + builder = JanusGraphFactory.build(); + buildGraphConfig(); + } + + public JanusGraphV020(Configuration config, String hbaseTableName, Logger logger) { + this.logger = logger; + builder = JanusGraphFactory.build(); + buildGraphConfig(config, hbaseTableName); + } + + @Override + public void setBuilderConfig(String key, Object value) { + builder.set(key, value); + } + + private JanusGraph janusGraph; + + public JanusGraph getJanusGraph() { + return janusGraph; + } + + private JanusGraphManagement mgmt; + + @Override + public boolean mgmtNotContainsVertexLabel(String vertexLabelName) { + return !mgmt.containsVertexLabel(vertexLabelName); + } + + @Override + public void mgmtMakeVertexLabel(String vertexLabelName) { + mgmt.makeVertexLabel(vertexLabelName).make(); + } + + @Override + public boolean mgmtNotContainsEdgeLabel(String edgeLabelName) { + return !mgmt.containsEdgeLabel(edgeLabelName); + } + + @Override + public void mgmtMakeEdgeLabel(String edgeLabelName) { + mgmt.makeEdgeLabel(edgeLabelName).multiplicity(Multiplicity.MULTI).make(); + } + + @Override + public boolean mgmtNotContainsPropertyKey(String propertyKeyName) { + return !mgmt.containsPropertyKey(propertyKeyName); + } + + @Override + public void mgmtMakePropertyKey(String propertyKeyName, Class zlass) { + mgmt.makePropertyKey(propertyKeyName).dataType(zlass).cardinality(Cardinality.SINGLE).make(); + } + + @Override + public boolean mgmtNotContainsGraphIndex(String indexName) { + return !mgmt.containsGraphIndex(indexName); + } + + @Override + public void mgmtBuildVertexIndexOnlyWithOneKeyUnique( + String indexName, String propertyKeyName, String labelName) { + PropertyKey propertyKey = mgmt.getPropertyKey(propertyKeyName); + VertexLabel vertexLabel = mgmt.getVertexLabel(labelName); + JanusGraphIndex janusGraphIndex = + mgmt.buildIndex(indexName, Vertex.class) + .addKey(propertyKey) + .indexOnly(vertexLabel) + .unique() + .buildCompositeIndex(); + mgmt.setConsistency(janusGraphIndex, ConsistencyModifier.LOCK); + } + + @Override + public void mgmtBuildVertexIndexWithOneKey(String indexName, String propertyKeyName) { + PropertyKey propertyKey = mgmt.getPropertyKey(propertyKeyName); + mgmt.buildIndex(indexName, Vertex.class).addKey(propertyKey).buildCompositeIndex(); + } + + @Override + public void mgmtBuildEdgeIndexWithOneKey(String indexName, String propertyKeyName) { + PropertyKey propertyKey = mgmt.getPropertyKey(propertyKeyName); + mgmt.buildIndex(indexName, Edge.class).addKey(propertyKey).buildCompositeIndex(); + } + + @Override + public void mgmtBuildEdgeIndexWithTwoKey( + String indexName, String firstPropertyKeyName, String secondPropertyKeyName) { + PropertyKey firstPropertyKey = mgmt.getPropertyKey(firstPropertyKeyName); + PropertyKey secondPropertyKey = mgmt.getPropertyKey(secondPropertyKeyName); + mgmt.buildIndex(indexName, Edge.class) + .addKey(firstPropertyKey) + .addKey(secondPropertyKey) + .buildCompositeIndex(); + } + + @Override + public void mgmtBuildEdgeIndexWithThreeKey( + String indexName, + String firstPropertyKeyName, + String secondPropertyKeyName, + String thirdPropertyKeyName) { + PropertyKey firstPropertyKey = mgmt.getPropertyKey(firstPropertyKeyName); + PropertyKey secondPropertyKey = mgmt.getPropertyKey(secondPropertyKeyName); + PropertyKey thirdPropertyKey = mgmt.getPropertyKey(thirdPropertyKeyName); + mgmt.buildIndex(indexName, Edge.class) + .addKey(firstPropertyKey) + .addKey(secondPropertyKey) + .addKey(thirdPropertyKey) + .buildCompositeIndex(); + } + + @Override + public boolean mgmtNotContainsRelationIndex(String edgeLabelName, String relationIndexName) { + EdgeLabel edgeLabel = mgmt.getEdgeLabel(edgeLabelName); + return !mgmt.containsRelationIndex(edgeLabel, relationIndexName); + } + + @Override + public void mgmtBuildRelationIndexWithOneKey( + String edgeLabelName, String relationIndexName, String propertyKeyName) { + EdgeLabel edgeLabel = mgmt.getEdgeLabel(edgeLabelName); + PropertyKey propertyKey = mgmt.getPropertyKey(propertyKeyName); + mgmt.buildEdgeIndex(edgeLabel, relationIndexName, Direction.BOTH, propertyKey); + } + + @Override + public void mgmtBuildRelationIndexWithTwoKey( + String edgeLabelName, + String relationIndexName, + String firstPropertyKeyName, + String secondPropertyKeyName) { + EdgeLabel edgeLabel = mgmt.getEdgeLabel(edgeLabelName); + PropertyKey firstPropertyKey = mgmt.getPropertyKey(firstPropertyKeyName); + PropertyKey secondPropertyKey = mgmt.getPropertyKey(secondPropertyKeyName); + mgmt.buildEdgeIndex( + edgeLabel, relationIndexName, Direction.BOTH, firstPropertyKey, secondPropertyKey); + } + + @Override + public void mgmtBuildRelationIndexWithThreeKey( + String edgeLabelName, + String relationIndexName, + String firstPropertyKeyName, + String secondPropertyKeyName, + String thirdPropertyKeyName) { + EdgeLabel edgeLabel = mgmt.getEdgeLabel(edgeLabelName); + PropertyKey firstPropertyKey = mgmt.getPropertyKey(firstPropertyKeyName); + PropertyKey secondPropertyKey = mgmt.getPropertyKey(secondPropertyKeyName); + PropertyKey thirdPropertyKey = mgmt.getPropertyKey(thirdPropertyKeyName); + mgmt.buildEdgeIndex( + edgeLabel, + relationIndexName, + Direction.BOTH, + firstPropertyKey, + secondPropertyKey, + thirdPropertyKey); + } + + @Override + public void updateGraphIndexWithReindex(String indexName) + throws ExecutionException, InterruptedException { + mgmt.updateIndex(mgmt.getGraphIndex(indexName), SchemaAction.REINDEX).get(); + } + + @Override + public void updateRelationIndexWithReindex(String edgeLabelName, String relationIndexName) + throws ExecutionException, InterruptedException { + EdgeLabel edgeLabel = mgmt.getEdgeLabel(edgeLabelName); + mgmt.updateIndex(mgmt.getRelationIndex(edgeLabel, relationIndexName), SchemaAction.REINDEX) + .get(); + } + + @Override + public void awaitGraphIndexStatusWithEnabled(String indexName) throws InterruptedException { + ManagementSystem.awaitGraphIndexStatus(janusGraph, indexName) + .status(SchemaStatus.ENABLED) + .call(); + } + + @Override + public void awaitRelationIndexStatusWithEnabled(String edgeLabelName, String relationIndexName) + throws InterruptedException { + ManagementSystem.awaitRelationIndexStatus(janusGraph, relationIndexName, edgeLabelName) + .status(SchemaStatus.ENABLED) + .call(); + } + + @Override + public void mgmtCommit() { + mgmt.commit(); + } + + @Override + public void mgmtRollback() { + mgmt.rollback(); + } + + @Override + public void open() throws GraphException { + logger.info("## JanusGraph start to create instance..."); + janusGraph = builder.open(); + mgmt = janusGraph.openManagement(); + super.open(); + logger.info("## JanusGraph create instance finish."); + } + + @Override + public void close() { + if (null != janusGraph && janusGraph.isOpen()) { + logger.info("## JanusGraph start to close..."); + try { + janusGraph.close(); + } catch (Throwable t) { + logger.info("## JanusGraph instance close failed, caused by {}", t.getMessage(), t); + return; + } + logger.info("## JanusGraph instance closed."); + } + } + + private Long createJanusGraphVertex(String uniq, String type, Long createTime) { + if (StringUtils.isBlank(uniq)) { + return null; + } + try { + JanusGraphVertex vertex = janusGraph.addVertex(LABEL_VERTEX); + vertex.property(VERTEX_PROPERTY_UNIQ, uniq); + if (StringUtils.isNotBlank(type)) { + vertex.property(VERTEX_PROPERTY_TYPE, type); + } + vertex.property(VERTEX_PROPERTY_DELETED, false); + if (null != createTime) { + vertex.property(VERTEX_PROPERTY_CREATE_TIME, createTime); + } else { + vertex.property(VERTEX_PROPERTY_CREATE_TIME, DateUtil.getCurrentTimestamp()); + } + janusGraph.tx().commit(); + return (Long) vertex.id(); + } catch (Throwable t) { + logger.error(t.getMessage(), t); + janusGraph.tx().rollback(); + return null; + } + } + + private boolean updateJanusGraphVertex( + List vertices, String uniq, String type, Long createTime) { + if (null == vertices || vertices.isEmpty()) { + return false; + } + for (Vertex vertex : vertices) { + try { + boolean skip = true; + if (StringUtils.isNotBlank(uniq)) { + if (vertex.property(VERTEX_PROPERTY_UNIQ).isPresent()) { + if (!vertex.value(VERTEX_PROPERTY_UNIQ).toString().equals(uniq)) { + vertex.property(VERTEX_PROPERTY_UNIQ, uniq); + skip = false; + } + } else { + vertex.property(VERTEX_PROPERTY_UNIQ, uniq); + skip = false; + } + } + if (StringUtils.isNotBlank(type)) { + if (vertex.property(VERTEX_PROPERTY_TYPE).isPresent()) { + if (!vertex.value(VERTEX_PROPERTY_TYPE).toString().equals(type)) { + vertex.property(VERTEX_PROPERTY_TYPE, type); + skip = false; + } + } else { + vertex.property(VERTEX_PROPERTY_TYPE, type); + skip = false; + } + } + if (null != createTime) { + if (vertex.property(VERTEX_PROPERTY_CREATE_TIME).isPresent()) { + if (Long.parseLong(vertex.value(VERTEX_PROPERTY_CREATE_TIME).toString()) + != createTime) { + vertex.property(VERTEX_PROPERTY_CREATE_TIME, createTime); + skip = false; + } + } else { + vertex.property(VERTEX_PROPERTY_CREATE_TIME, createTime); + skip = false; + } + } + if (!skip) { + janusGraph.tx().commit(); + } + } catch (Throwable t) { + logger.error(t.getMessage(), t); + janusGraph.tx().rollback(); + } + } + return true; + } + + private Vertex getJanusGraphVertexById(Long vertexId) { + if (null == vertexId || vertexId <= 0) { + return null; + } + return janusGraph.traversal().V(vertexId).next(); + } + + private List getJanusGraphVerticesByUniq(String uniq) { + if (StringUtils.isBlank(uniq)) { + return Collections.emptyList(); + } + return janusGraph.traversal().V().has(VERTEX_PROPERTY_UNIQ, uniq).toList(); + } + + private List getJanusGraphVerticesByType(String type) { + if (StringUtils.isBlank(type)) { + return janusGraph.traversal().V().toList(); + } + return janusGraph.traversal().V().has(VERTEX_PROPERTY_TYPE, type).toList(); + } + + @Override + public Long createVertex(String uniq, String type, Long createTime) { + List vertices = getJanusGraphVerticesByUniq(uniq); + if (null != vertices && !vertices.isEmpty()) { + return null; + } + return createJanusGraphVertex(uniq, type, createTime); + } + + @Override + public Long createVertexNotPreCheck(String uniq, String type, Long createTime) { + return createJanusGraphVertex(uniq, type, createTime); + } + + @Override + public boolean updateVertex(String uniq, String type, Long createTime) { + List vertices = getJanusGraphVerticesByUniq(uniq); + if (null != vertices && !vertices.isEmpty()) { + return updateJanusGraphVertex(vertices, null, type, createTime); + } + vertices = getJanusGraphVerticesByType(type); + return updateJanusGraphVertex(vertices, null, type, createTime); + } + + private boolean updateEdgeSourceTargetVertexUniq(String uniq) { + if (StringUtils.isBlank(uniq)) { + return false; + } + List inEdges = + janusGraph.traversal().V().has(VERTEX_PROPERTY_UNIQ, uniq).inE(LABEL_EDGE).toList(); + if (null != inEdges) { + for (Edge edge : inEdges) { + try { + boolean skip = true; + if (edge.property(EDGE_PROPERTY_TARGET_VERTEX_UNIQ).isPresent()) { + if (!edge.value(EDGE_PROPERTY_TARGET_VERTEX_UNIQ).toString().equals(uniq)) { + edge.property(EDGE_PROPERTY_TARGET_VERTEX_UNIQ, uniq); + skip = false; + } + } else { + edge.property(EDGE_PROPERTY_TARGET_VERTEX_UNIQ, uniq); + skip = false; + } + if (!skip) { + janusGraph.tx().commit(); + } + } catch (Throwable t) { + logger.error(t.getMessage(), t); + janusGraph.tx().rollback(); + } + } + } + List outEdges = + janusGraph.traversal().V().has(VERTEX_PROPERTY_UNIQ, uniq).outE(LABEL_EDGE).toList(); + if (null != outEdges) { + for (Edge edge : outEdges) { + try { + boolean skip = true; + if (edge.property(EDGE_PROPERTY_SOURCE_VERTEX_UNIQ).isPresent()) { + if (!edge.value(EDGE_PROPERTY_SOURCE_VERTEX_UNIQ).toString().equals(uniq)) { + edge.property(EDGE_PROPERTY_SOURCE_VERTEX_UNIQ, uniq); + skip = false; + } + } else { + edge.property(EDGE_PROPERTY_SOURCE_VERTEX_UNIQ, uniq); + skip = false; + } + if (!skip) { + janusGraph.tx().commit(); + } + } catch (Throwable t) { + logger.error(t.getMessage(), t); + janusGraph.tx().rollback(); + } + } + } + return true; + } + + @Override + public boolean updateVertex(String oldUniq, String newUniq, String type, Long createTime) { + if (StringUtils.isBlank(oldUniq) || StringUtils.isBlank(newUniq)) { + return false; + } + List vertices = getJanusGraphVerticesByUniq(newUniq); + if (null != vertices && !vertices.isEmpty()) { + return false; + } + vertices = getJanusGraphVerticesByUniq(oldUniq); + if (null == vertices || vertices.isEmpty()) { + return false; + } + boolean updateVertexFlag = updateJanusGraphVertex(vertices, newUniq, type, createTime); + boolean updateEdgeFlag = false; + if (updateVertexFlag) { + updateEdgeFlag = updateEdgeSourceTargetVertexUniq(newUniq); + } + return updateVertexFlag & updateEdgeFlag; + } + + private void updateJanusGraphVertexDeletedStatus(List vertices, boolean deleted) { + if (null == vertices || vertices.isEmpty()) { + return; + } + for (Vertex vertex : vertices) { + try { + boolean skip = true; + if (vertex.property(VERTEX_PROPERTY_DELETED).isPresent()) { + boolean current = Boolean.parseBoolean(vertex.value(VERTEX_PROPERTY_DELETED).toString()); + if (current != deleted) { + vertex.property(VERTEX_PROPERTY_DELETED, deleted); + skip = false; + } + } else { + vertex.property(VERTEX_PROPERTY_DELETED, deleted); + skip = false; + } + if (!skip) { + janusGraph.tx().commit(); + } + } catch (Throwable t) { + logger.error(t.getMessage(), t); + janusGraph.tx().rollback(); + } + } + } + + private boolean updateJanusGraphVertexDeletedStatus(String uniq, String type, boolean deleted) { + if (StringUtils.isNotBlank(uniq)) { + List vertices = getJanusGraphVerticesByUniq(uniq); + if (null != vertices && !vertices.isEmpty()) { + updateJanusGraphVertexDeletedStatus(vertices, deleted); + } + } else { + List vertices = getJanusGraphVerticesByType(type); + updateJanusGraphVertexDeletedStatus(vertices, deleted); + } + return true; + } + + @Override + public boolean softDeleteVertices(String uniq, String type) { + return updateJanusGraphVertexDeletedStatus(uniq, type, true); + } + + @Override + public boolean recoverDeletedVertices(String uniq, String type) { + return updateJanusGraphVertexDeletedStatus(uniq, type, false); + } + + @Override + public boolean hardDeleteVertices(String uniq, String type) { + if (StringUtils.isNotBlank(uniq)) { + try { + janusGraph.traversal().V().has(VERTEX_PROPERTY_UNIQ, uniq).drop().iterate(); + janusGraph.tx().commit(); + return true; + } catch (Throwable t) { + logger.error(t.getMessage(), t); + janusGraph.tx().rollback(); + return false; + } + } + if (StringUtils.isNotBlank(type)) { + try { + janusGraph.traversal().V().has(VERTEX_PROPERTY_TYPE, type).drop().iterate(); + janusGraph.tx().commit(); + return true; + } catch (Throwable t) { + logger.error(t.getMessage(), t); + janusGraph.tx().rollback(); + return false; + } + } + try { + janusGraph.traversal().V().drop().iterate(); + janusGraph.tx().commit(); + return true; + } catch (Throwable t) { + logger.error(t.getMessage(), t); + janusGraph.tx().rollback(); + return false; + } + } + + private Edge getJanusGraphEdgeById(String edgeId) { + if (StringUtils.isBlank(edgeId)) { + return null; + } + return janusGraph.traversal().E(edgeId).next(); + } + + private GraphTraversal getJanusGraphEdgeTraversal( + String uniq, String type, String sourceVertexUniq, String targetVertexUniq) { + if (StringUtils.isNotBlank(uniq)) { + return janusGraph.traversal().E().has(EDGE_PROPERTY_UNIQ, uniq); + } + if (StringUtils.isNotBlank(type)) { + if (StringUtils.isNotBlank(sourceVertexUniq) && StringUtils.isNotBlank(targetVertexUniq)) { + return janusGraph + .traversal() + .E() + .has(EDGE_PROPERTY_TYPE, type) + .has(EDGE_PROPERTY_SOURCE_VERTEX_UNIQ, sourceVertexUniq) + .has(EDGE_PROPERTY_TARGET_VERTEX_UNIQ, targetVertexUniq); + } else if (StringUtils.isNotBlank(sourceVertexUniq) + && StringUtils.isBlank(targetVertexUniq)) { + return janusGraph + .traversal() + .E() + .has(EDGE_PROPERTY_TYPE, type) + .has(EDGE_PROPERTY_SOURCE_VERTEX_UNIQ, sourceVertexUniq); + } else if (StringUtils.isBlank(sourceVertexUniq) + && StringUtils.isNotBlank(targetVertexUniq)) { + return janusGraph + .traversal() + .E() + .has(EDGE_PROPERTY_TYPE, type) + .has(EDGE_PROPERTY_TARGET_VERTEX_UNIQ, targetVertexUniq); + } else { + return janusGraph.traversal().E().has(EDGE_PROPERTY_TYPE, type); + } + } + if (StringUtils.isNotBlank(sourceVertexUniq) && StringUtils.isNotBlank(targetVertexUniq)) { + return janusGraph + .traversal() + .E() + .has(EDGE_PROPERTY_SOURCE_VERTEX_UNIQ, sourceVertexUniq) + .has(EDGE_PROPERTY_TARGET_VERTEX_UNIQ, targetVertexUniq); + } else if (StringUtils.isNotBlank(sourceVertexUniq) && StringUtils.isBlank(targetVertexUniq)) { + return janusGraph.traversal().E().has(EDGE_PROPERTY_SOURCE_VERTEX_UNIQ, sourceVertexUniq); + } else if (StringUtils.isBlank(sourceVertexUniq) && StringUtils.isNotBlank(targetVertexUniq)) { + return janusGraph.traversal().E().has(EDGE_PROPERTY_TARGET_VERTEX_UNIQ, targetVertexUniq); + } + return janusGraph.traversal().E(); + } + + private List getJanusGraphEdges( + String uniq, String type, String sourceVertexUniq, String targetVertexUniq) { + GraphTraversal janusGraphEdgeTraversal = + getJanusGraphEdgeTraversal(uniq, type, sourceVertexUniq, targetVertexUniq); + if (null != janusGraphEdgeTraversal) { + return janusGraphEdgeTraversal.toList(); + } + return Collections.emptyList(); + } + + private String createJanusGraphEdge( + String uniq, String type, Vertex sourceVertex, Vertex targetVertex, Long createTime) { + if (StringUtils.isBlank(uniq)) { + return null; + } + if (null == sourceVertex || null == targetVertex) { + return null; + } + try { + Edge edge = sourceVertex.addEdge(LABEL_EDGE, targetVertex); + edge.property(EDGE_PROPERTY_UNIQ, uniq); + if (StringUtils.isNotBlank(type)) { + edge.property(EDGE_PROPERTY_TYPE, type); + } + edge.property(EDGE_PROPERTY_DELETED, false); + if (sourceVertex.property(VERTEX_PROPERTY_UNIQ).isPresent()) { + edge.property( + EDGE_PROPERTY_SOURCE_VERTEX_UNIQ, sourceVertex.value(VERTEX_PROPERTY_UNIQ).toString()); + } + if (targetVertex.property(VERTEX_PROPERTY_UNIQ).isPresent()) { + edge.property( + EDGE_PROPERTY_TARGET_VERTEX_UNIQ, targetVertex.value(VERTEX_PROPERTY_UNIQ).toString()); + } + if (null != createTime) { + edge.property(EDGE_PROPERTY_CREATE_TIME, createTime); + } else { + edge.property(EDGE_PROPERTY_CREATE_TIME, DateUtil.getCurrentTimestamp()); + } + janusGraph.tx().commit(); + return edge.id().toString(); + } catch (Throwable t) { + logger.error(t.getMessage(), t); + janusGraph.tx().rollback(); + return null; + } + } + + @Override + public String createEdge( + String uniq, String type, String sourceVertexUniq, String targetVertexUniq, Long createTime) { + if (StringUtil.checkStringParamsBlank(uniq, sourceVertexUniq, targetVertexUniq)) { + return null; + } + List edges = getJanusGraphEdges(uniq, null, null, null); + if (null != edges && !edges.isEmpty()) { + return null; + } + List sourceVertices = getJanusGraphVerticesByUniq(sourceVertexUniq); + if (null == sourceVertices || sourceVertices.isEmpty()) { + Long vertexId = createVertex(sourceVertexUniq, type, createTime); + if (null != vertexId) { + sourceVertices = new ArrayList<>(); + sourceVertices.add(getJanusGraphVertexById(vertexId)); + } else { + logger.warn( + "Create edge uniq='{}',type='{}',sourceVertexUniq='{}',targetVertexUniq='{}' failed when create source vertex.", + uniq, + type, + sourceVertexUniq, + targetVertexUniq); + return null; + } + } + List targetVertices = getJanusGraphVerticesByUniq(targetVertexUniq); + if (null == targetVertices || targetVertices.isEmpty()) { + Long vertexId = createVertex(targetVertexUniq, type, createTime); + if (null != vertexId) { + targetVertices = new ArrayList<>(); + targetVertices.add(getJanusGraphVertexById(vertexId)); + } else { + logger.warn( + "Create edge uniq='{}',type='{}',sourceVertexUniq='{}',targetVertexUniq='{}' failed when create target vertex.", + uniq, + type, + sourceVertexUniq, + targetVertexUniq); + return null; + } + } + if (sourceVertices.size() > 1) { + logger.warn( + "## CreateEdge method query '{}' vertices by sourceVertexUniq='{}'.", + sourceVertices.size(), + sourceVertexUniq); + } + if (targetVertices.size() > 1) { + logger.warn( + "## CreateEdge method query '{}' vertices by targetVertexUniq='{}'.", + targetVertices.size(), + targetVertexUniq); + } + return createJanusGraphEdge( + uniq, type, sourceVertices.get(0), targetVertices.get(0), createTime); + } + + @Override + public String createEdgeNotPreCheck( + String uniq, String type, Long sourceVertexId, Long targetVertexId, Long createTime) { + if (StringUtils.isBlank(uniq)) { + return null; + } + Vertex sourceVertex = getJanusGraphVertexById(sourceVertexId); + if (null == sourceVertex) { + return null; + } + Vertex targetVertex = getJanusGraphVertexById(targetVertexId); + if (null == targetVertex) { + return null; + } + return createJanusGraphEdge(uniq, type, sourceVertex, targetVertex, createTime); + } + + private boolean setJanusGraphEdgesDeletedStatus(List edges, boolean deleted) { + if (null == edges || edges.isEmpty()) { + return false; + } + for (Edge edge : edges) { + try { + boolean skip = true; + if (edge.property(EDGE_PROPERTY_DELETED).isPresent()) { + boolean current = Boolean.parseBoolean(edge.value(EDGE_PROPERTY_DELETED).toString()); + if (current != deleted) { + edge.property(EDGE_PROPERTY_DELETED, deleted); + skip = false; + } + } else { + edge.property(EDGE_PROPERTY_DELETED, deleted); + skip = false; + } + if (!skip) { + janusGraph.tx().commit(); + } + } catch (Throwable t) { + logger.error(t.getMessage(), t); + janusGraph.tx().rollback(); + } + } + return true; + } + + private boolean deleteJanusGraphEdges( + String uniq, String type, String sourceVertexUniq, String targetVertexUniq, boolean deleted) { + List edges = getJanusGraphEdges(uniq, type, sourceVertexUniq, targetVertexUniq); + if (null == edges || edges.isEmpty()) { + return false; + } + if (edges.size() > 1) { + logger.warn( + "## DeleteJanusGraphEdges method query '{}' edges by uniq={},type='{}',sourceVertexUniq={},targetVertexUniq={}.", + edges.size(), + uniq, + type, + sourceVertexUniq, + targetVertexUniq); + } + return setJanusGraphEdgesDeletedStatus(edges, deleted); + } + + @Override + public boolean softDeleteEdges( + String uniq, String type, String sourceVertexUniq, String targetVertexUniq) { + return deleteJanusGraphEdges(uniq, type, sourceVertexUniq, targetVertexUniq, true); + } + + @Override + public boolean recoverDeletedEdges( + String uniq, String type, String sourceVertexUniq, String targetVertexUniq) { + return deleteJanusGraphEdges(uniq, type, sourceVertexUniq, targetVertexUniq, false); + } + + private boolean hardDeleteJanusGraphEdges( + String uniq, String type, String sourceVertexUniq, String targetVertexUniq) { + GraphTraversal janusGraphEdgeTraversal = + getJanusGraphEdgeTraversal(uniq, type, sourceVertexUniq, targetVertexUniq); + if (null == janusGraphEdgeTraversal) { + return false; + } + try { + janusGraphEdgeTraversal.drop().iterate(); + janusGraph.tx().commit(); + } catch (Throwable t) { + logger.error(t.getMessage(), t); + janusGraph.tx().rollback(); + return false; + } + return true; + } + + @Override + public boolean hardDeleteEdges( + String uniq, String type, String sourceVertexUniq, String targetVertexUniq) { + return hardDeleteJanusGraphEdges(uniq, type, sourceVertexUniq, targetVertexUniq); + } + + private GraphVertex transferToGraphVertex(Vertex v) { + if (null == v) { + return null; + } + // org.studiox.graph.janusgraph.common.JanusGraphVertex graphVertex = + // new org.studiox.graph.janusgraph.common.JanusGraphVertex(); + GraphVertex graphVertex = new GraphVertex(); + try { + // graphVertex.setId(Long.parseLong(v.id().toString())); + if (v.property(VERTEX_PROPERTY_UNIQ).isPresent()) { + graphVertex.setUniq(v.value(VERTEX_PROPERTY_UNIQ).toString()); + } + if (v.property(VERTEX_PROPERTY_TYPE).isPresent()) { + graphVertex.setType(v.value(VERTEX_PROPERTY_TYPE).toString()); + } + if (v.property(VERTEX_PROPERTY_DELETED).isPresent()) { + graphVertex.setDeleted(Boolean.valueOf(v.value(VERTEX_PROPERTY_DELETED).toString())); + } + if (v.property(VERTEX_PROPERTY_CREATE_TIME).isPresent()) { + graphVertex.setCreateTime(Long.parseLong(v.value(VERTEX_PROPERTY_CREATE_TIME).toString())); + } + } catch (Throwable t) { + logger.error(t.getMessage(), t); + } + return graphVertex; + } + + @Override + public Long getJanusGraphVertexId(String uniq) { + List vertices = getJanusGraphVerticesByUniq(uniq); + if (null == vertices || vertices.isEmpty()) { + return null; + } + if (vertices.size() > 1) { + logger.warn("## GetVertex method query '{}' vertices by uniq='{}'.", vertices.size(), uniq); + } + return Long.parseLong(vertices.get(0).id().toString()); + } + + @Override + public GraphVertex getVertex(String uniq) { + List vertices = getJanusGraphVerticesByUniq(uniq); + if (null == vertices || vertices.isEmpty()) { + return null; + } + if (vertices.size() > 1) { + logger.warn("## GetVertex method query '{}' vertices by uniq='{}'.", vertices.size(), uniq); + } + return transferToGraphVertex(vertices.get(0)); + } + + @Override + public List getVerticesByType(String type) { + List vertices = getJanusGraphVerticesByType(type); + if (null == vertices || vertices.isEmpty()) { + return Collections.emptyList(); + } + List result = new ArrayList<>(); + for (Vertex v : vertices) { + GraphVertex vertex = transferToGraphVertex(v); + if (null == vertex) { + continue; + } + result.add(vertex); + } + return result; + } + + private GraphEdge transferToGraphEdge(Edge e) { + if (null == e) { + return null; + } + // org.studiox.graph.janusgraph.common.JanusGraphEdge graphEdge = + // new org.studiox.graph.janusgraph.common.JanusGraphEdge(); + GraphEdge graphEdge = new GraphEdge(); + try { + // graphEdge.setId(((RelationIdentifier) e.id()).toString()); + // graphEdge.setSourceVertexId(Long.parseLong(e.outVertex().id().toString())); + // graphEdge.setTargetVertexId(Long.parseLong(e.inVertex().id().toString())); + if (e.property(EDGE_PROPERTY_UNIQ).isPresent()) { + graphEdge.setUniq(e.value(EDGE_PROPERTY_UNIQ).toString()); + } + if (e.property(EDGE_PROPERTY_TYPE).isPresent()) { + graphEdge.setType(e.value(EDGE_PROPERTY_TYPE).toString()); + } + if (e.property(EDGE_PROPERTY_DELETED).isPresent()) { + graphEdge.setDeleted(Boolean.valueOf(e.value(EDGE_PROPERTY_DELETED).toString())); + } + if (e.property(EDGE_PROPERTY_SOURCE_VERTEX_UNIQ).isPresent()) { + graphEdge.setSourceVertexUniq(e.value(EDGE_PROPERTY_SOURCE_VERTEX_UNIQ).toString()); + } + if (e.property(EDGE_PROPERTY_TARGET_VERTEX_UNIQ).isPresent()) { + graphEdge.setTargetVertexUniq(e.value(EDGE_PROPERTY_TARGET_VERTEX_UNIQ).toString()); + } + if (e.property(EDGE_PROPERTY_CREATE_TIME).isPresent()) { + graphEdge.setCreateTime(Long.parseLong(e.value(EDGE_PROPERTY_CREATE_TIME).toString())); + } + } catch (Throwable t) { + logger.error(t.getMessage(), t); + } + return graphEdge; + } + + private List transferToGraphEdges(List edges) { + if (null == edges || edges.isEmpty()) { + return Collections.emptyList(); + } + List result = new ArrayList<>(); + for (Edge e : edges) { + GraphEdge edge = transferToGraphEdge(e); + if (null == e) { + continue; + } + result.add(edge); + } + return result; + } + + @Override + public GraphEdge getEdge(String uniq) { + List edges = getJanusGraphEdges(uniq, null, null, null); + if (null == edges || edges.isEmpty()) { + return null; + } + return transferToGraphEdge(edges.get(0)); + } + + @Override + public List getEdges(String type, String sourceVertexUniq, String targetVertexUniq) { + List edges = getJanusGraphEdges(null, type, sourceVertexUniq, targetVertexUniq); + return transferToGraphEdges(edges); + } + + @Override + public Map getSourceVertexEdgeMap(String vertexUniq) { + if (StringUtils.isBlank(vertexUniq)) { + return Collections.emptyMap(); + } + List edges = + janusGraph.traversal().V().has(VERTEX_PROPERTY_UNIQ, vertexUniq).inE(LABEL_EDGE).toList(); + if (null == edges || edges.isEmpty()) { + return Collections.emptyMap(); + } + Map result = new HashMap<>(edges.size()); + for (Edge e : edges) { + GraphEdge graphEdge = transferToGraphEdge(e); + GraphVertex graphVertex = transferToGraphVertex(e.inVertex()); + result.put(graphEdge, graphVertex); + } + return result; + } + + @Override + public Map getTargetVertexEdgeMap(String vertexUniq) { + if (StringUtils.isBlank(vertexUniq)) { + return Collections.emptyMap(); + } + List edges = + janusGraph.traversal().V().has(VERTEX_PROPERTY_UNIQ, vertexUniq).outE(LABEL_EDGE).toList(); + if (null == edges || edges.size() == 0) { + return Collections.emptyMap(); + } + Map result = new HashMap<>(edges.size()); + for (Edge e : edges) { + GraphEdge graphEdge = transferToGraphEdge(e); + GraphVertex graphVertex = transferToGraphVertex(e.outVertex()); + result.put(graphEdge, graphVertex); + } + return result; + } + + private GraphLineage transferToGraphLineage(String vertexUniq, Graph graph) { + if (StringUtils.isBlank(vertexUniq) || null == graph) { + return null; + } + GraphVertex currentVertex = getVertex(vertexUniq); + GraphLineage graphLineage = new GraphLineage(); + graphLineage.setCurrent(currentVertex); + graphLineage.setVertices(new ArrayList<>()); + graphLineage.setEdges(new ArrayList<>()); + Iterator vertexIterator = graph.vertices(); + while (vertexIterator.hasNext()) { + Vertex v = vertexIterator.next(); + graphLineage.getVertices().add(transferToGraphVertex(v)); + } + Iterator edgeIterator = graph.edges(); + while (edgeIterator.hasNext()) { + Edge e = edgeIterator.next(); + graphLineage.getEdges().add(transferToGraphEdge(e)); + } + return graphLineage; + } + + @Override + public GraphLineage getImpact(String vertexUniq, Integer depth, boolean skipDeleted) { + if (StringUtils.isBlank(vertexUniq)) { + return null; + } + GraphTraversal graphTraversal = + janusGraph.traversal().V().has(VERTEX_PROPERTY_UNIQ, vertexUniq); + if (skipDeleted) { + graphTraversal = graphTraversal.has(VERTEX_PROPERTY_DELETED, false); + } + Graph graph; + if (depth <= 0) { + if (!skipDeleted) { + graph = + (Graph) + graphTraversal + .until(__.outE(LABEL_EDGE).count().is(0)) + .repeat(__.outE(LABEL_EDGE).subgraph("subGraph").inV().simplePath()) + .cap("subGraph") + .next(); + } else { + graph = + (Graph) + graphTraversal + .until( + __.outE(LABEL_EDGE) + .or( + __.has(EDGE_PROPERTY_DELETED, true), + __.inV().has(VERTEX_PROPERTY_DELETED, true), + __.count().is(0))) + .repeat(__.outE(LABEL_EDGE).subgraph("subGraph").inV().simplePath()) + .cap("subGraph") + .next(); + } + } else { + if (!skipDeleted) { + graph = + (Graph) + graphTraversal + .repeat(__.outE(LABEL_EDGE).subgraph("subGraph").inV().simplePath()) + .times(depth) + .cap("subGraph") + .next(); + } else { + graph = + (Graph) + graphTraversal + .repeat( + __.outE(LABEL_EDGE) + .and( + __.has(EDGE_PROPERTY_DELETED, false), + __.inV().has(VERTEX_PROPERTY_DELETED, false)) + .subgraph("subGraph") + .inV() + .simplePath()) + .times(depth) + .cap("subGraph") + .next(); + } + } + return transferToGraphLineage(vertexUniq, graph); + } + + @Override + public GraphLineage getLineage(String vertexUniq, Integer depth, boolean skipDeleted) { + if (StringUtils.isBlank(vertexUniq)) { + return null; + } + GraphTraversal graphTraversal = + janusGraph.traversal().V().has(VERTEX_PROPERTY_UNIQ, vertexUniq); + if (skipDeleted) { + graphTraversal = graphTraversal.has(VERTEX_PROPERTY_DELETED, false); + } + Graph graph; + if (depth <= 0) { + if (!skipDeleted) { + graph = + (Graph) + graphTraversal + .until(__.inE(LABEL_EDGE).count().is(0)) + .repeat(__.inE(LABEL_EDGE).subgraph("subGraph").outV().simplePath()) + .cap("subGraph") + .next(); + } else { + graph = + (Graph) + graphTraversal + .until( + __.inE(LABEL_EDGE) + .or( + __.has(EDGE_PROPERTY_DELETED, true), + __.outV().has(VERTEX_PROPERTY_DELETED, true), + __.count().is(0))) + .repeat(__.inE(LABEL_EDGE).subgraph("subGraph").outV().simplePath()) + .cap("subGraph") + .next(); + } + } else { + if (!skipDeleted) { + graph = + (Graph) + graphTraversal + .repeat(__.inE(LABEL_EDGE).subgraph("subGraph").outV().simplePath()) + .times(depth) + .cap("subGraph") + .next(); + } else { + graph = + (Graph) + graphTraversal + .repeat( + __.inE(LABEL_EDGE) + .and( + __.has(EDGE_PROPERTY_DELETED, false), + __.outV().has(VERTEX_PROPERTY_DELETED, false)) + .subgraph("subGraph") + .outV() + .simplePath()) + .times(depth) + .cap("subGraph") + .next(); + } + } + return transferToGraphLineage(vertexUniq, graph); + } + + @Override + public GraphLineage getFullLineage(String vertexUniq, Integer depth, boolean skipDeleted) { + if (StringUtils.isBlank(vertexUniq)) { + return null; + } + GraphTraversal graphTraversal = + janusGraph.traversal().V().has(VERTEX_PROPERTY_UNIQ, vertexUniq); + if (skipDeleted) { + graphTraversal = graphTraversal.has(VERTEX_PROPERTY_DELETED, false); + } + Graph graph; + if (depth <= 0) { + if (!skipDeleted) { + graph = + (Graph) + graphTraversal + .union( + __.until(__.outE(LABEL_EDGE).count().is(0)) + .repeat( + (Traversal) + __.outE(LABEL_EDGE).subgraph("subGraph").inV().simplePath()), + __.until(__.inE(LABEL_EDGE).count().is(0)) + .repeat( + (Traversal) + __.inE(LABEL_EDGE).subgraph("subGraph").outV().simplePath())) + .cap("subGraph") + .next(); + } else { + graph = + (Graph) + graphTraversal + .union( + __.until( + __.outE(LABEL_EDGE) + .or( + __.has(EDGE_PROPERTY_DELETED, true), + __.inV().has(VERTEX_PROPERTY_DELETED, true), + __.count().is(0))) + .repeat( + (Traversal) + __.outE(LABEL_EDGE).subgraph("subGraph").inV().simplePath()), + __.until( + __.inE(LABEL_EDGE) + .or( + __.has(EDGE_PROPERTY_DELETED, true), + __.outV().has(VERTEX_PROPERTY_DELETED, true), + __.count().is(0))) + .repeat( + (Traversal) + __.inE(LABEL_EDGE).subgraph("subGraph").outV().simplePath())) + .cap("subGraph") + .next(); + } + } else { + if (!skipDeleted) { + graph = + (Graph) + graphTraversal + .union( + __.repeat(__.outE(LABEL_EDGE).subgraph("subGraph").inV().simplePath()) + .times(depth), + __.repeat(__.inE(LABEL_EDGE).subgraph("subGraph").outV().simplePath()) + .times(depth)) + .cap("subGraph") + .next(); + + } else { + graph = + (Graph) + graphTraversal + .union( + __.repeat( + __.outE(LABEL_EDGE) + .and( + __.has(EDGE_PROPERTY_DELETED, false), + __.inV().has(VERTEX_PROPERTY_DELETED, false)) + .subgraph("subGraph") + .inV() + .simplePath()) + .times(depth), + __.repeat( + __.inE(LABEL_EDGE) + .and( + __.has(EDGE_PROPERTY_DELETED, false), + __.outV().has(VERTEX_PROPERTY_DELETED, false)) + .subgraph("subGraph") + .outV() + .simplePath()) + .times(depth)) + .cap("subGraph") + .next(); + } + } + return transferToGraphLineage(vertexUniq, graph); + } +} diff --git a/graph-janusgraph/janusgraph-v0.2.0/src/test/java/org/studiox/graph/janusgraph/v020/JanusGraphV020Test.java b/graph-janusgraph/janusgraph-v0.2.0/src/test/java/org/studiox/graph/janusgraph/v020/JanusGraphV020Test.java new file mode 100644 index 0000000000000000000000000000000000000000..4d45a93c303ddbd18ed4f02451bc056caacdccf7 --- /dev/null +++ b/graph-janusgraph/janusgraph-v0.2.0/src/test/java/org/studiox/graph/janusgraph/v020/JanusGraphV020Test.java @@ -0,0 +1,14 @@ +package org.studiox.graph.janusgraph.v020; + +import org.junit.BeforeClass; +import org.slf4j.LoggerFactory; +import org.studiox.graph.common.exception.GraphException; +import org.studiox.graph.janusgraph.common.test.JanusGraphTest; + +public class JanusGraphV020Test extends JanusGraphTest { + + @BeforeClass + public static void init() throws GraphException { + init(new JanusGraphV020(LoggerFactory.getLogger(JanusGraphV020Test.class))); + } +} diff --git a/graph-janusgraph/janusgraph-v0.2.0/src/test/resources/graph.properties b/graph-janusgraph/janusgraph-v0.2.0/src/test/resources/graph.properties new file mode 100755 index 0000000000000000000000000000000000000000..028881eac35990e8878e4f2500384c658aaa54f8 --- /dev/null +++ b/graph-janusgraph/janusgraph-v0.2.0/src/test/resources/graph.properties @@ -0,0 +1,27 @@ +################################################################### +graph.schema.default=none +graph.storage.batch-loading=true +################################################################### +graph.cache.db-cache=false +graph.ids.block-size=20000000 +graph.ids.renew-timeout=3600000 +graph.storage.buffer-size=20240 +################################################################### +graph.gremlin.graph=org.janusgraph.core.JanusGraphFactory +################################################################### +graph.storage.backend=inmemory +################################################################### +#graph.storage.backend=hbase +#graph.storage.hostname=localhost +#graph.storage.hbase.table=graph_table_1 +#graph.storage.hbase.ext.zookeeper.znode.parent=/hbase1 +################################################################### +#graph.storage.hbase.region-count=9 +#graph.storage.hbase.regions-per-server=3 +#graph.storage.hbase.ext.hbase.rpc.timeout=300000 +#graph.storage.hbase.ext.hbase.client.operation.timeout=300000 +#graph.storage.hbase.ext.hbase.client.scanner.timeout.period=300000 +################################################################### +#graph.index.search.backend=elasticsearch +#graph.index.search.hostname=localhost:9200 +################################################################### diff --git a/graph-janusgraph/pom.xml b/graph-janusgraph/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..0f4f371c59ab943c36914699be9dc22f631baa10 --- /dev/null +++ b/graph-janusgraph/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + org.studiox + graph + 1.0.5-SNAPSHOT + + org.studiox.graph + graph-janusgraph + 1.0.5-SNAPSHOT + pom + + janusgraph-common + janusgraph-v0.2.0 + janusgraph-latest + + diff --git a/graph-nebulagraph/pom.xml b/graph-nebulagraph/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..19d9431efda6efc188774d9db0feef1c71d8931c --- /dev/null +++ b/graph-nebulagraph/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + org.studiox + graph + 1.0.5-SNAPSHOT + + org.studiox.graph + graph-nebulagraph + 1.0.5-SNAPSHOT + pom + + diff --git a/graph-neo4j/pom.xml b/graph-neo4j/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..3e54be8d09d6fca620d2c8294bfd2647e2897ef6 --- /dev/null +++ b/graph-neo4j/pom.xml @@ -0,0 +1,16 @@ + + + 4.0.0 + + org.studiox + graph + 1.0.5-SNAPSHOT + + org.studiox.graph + graph-neo4j + 1.0.5-SNAPSHOT + pom + + diff --git a/graph-persistence/persistence-model/pom.xml b/graph-persistence/persistence-model/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..15222b886d3a592c22bb8ccdaecc04c35383553f --- /dev/null +++ b/graph-persistence/persistence-model/pom.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + org.studiox.graph + graph-persistence + 1.0.5-SNAPSHOT + + org.studiox.graph.persistence + persistence-model + 1.0.5-SNAPSHOT + jar + + + diff --git a/graph-persistence/persistence-model/src/main/java/org/studiox/graph/persistence/model/AbstractGraphDO.java b/graph-persistence/persistence-model/src/main/java/org/studiox/graph/persistence/model/AbstractGraphDO.java new file mode 100644 index 0000000000000000000000000000000000000000..43df431691333ec79479a84bf58cf789e6dba454 --- /dev/null +++ b/graph-persistence/persistence-model/src/main/java/org/studiox/graph/persistence/model/AbstractGraphDO.java @@ -0,0 +1,67 @@ +package org.studiox.graph.persistence.model; + +import java.io.Serializable; +import java.sql.Timestamp; + +public abstract class AbstractGraphDO implements Serializable { + + protected Long id; + + protected String type; + + protected String owner; + + protected Boolean deleted; + + protected Timestamp createTime; + + protected Timestamp updateTime; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public Boolean getDeleted() { + return deleted; + } + + public void setDeleted(Boolean deleted) { + this.deleted = deleted; + } + + public Timestamp getCreateTime() { + return createTime; + } + + public void setCreateTime(Timestamp createTime) { + this.createTime = createTime; + } + + public Timestamp getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Timestamp updateTime) { + this.updateTime = updateTime; + } +} diff --git a/graph-persistence/persistence-model/src/main/java/org/studiox/graph/persistence/model/GraphEdgeDO.java b/graph-persistence/persistence-model/src/main/java/org/studiox/graph/persistence/model/GraphEdgeDO.java new file mode 100644 index 0000000000000000000000000000000000000000..d8496fe189c6698ac3631936caed3c106d7ba5a6 --- /dev/null +++ b/graph-persistence/persistence-model/src/main/java/org/studiox/graph/persistence/model/GraphEdgeDO.java @@ -0,0 +1,136 @@ +package org.studiox.graph.persistence.model; + +import java.io.Serializable; + +public class GraphEdgeDO extends AbstractGraphDO implements Serializable { + + private String content; + + private String contentMd5; + + private String sourceVertexUniq; + + private String targetVertexUniq; + + private String janusEdgeId; + + private Long neo4jEdgeId; + + private String nebulaEdgeId; + + private Long sourceJanusVertexId; + + private Long targetJanusVertexId; + + private Long sourceNeo4jVertexId; + + private Long targetNeo4jVertexId; + + private Long sourceNebulaVertexId; + + private Long targetNebulaVertexId; + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getContentMd5() { + return contentMd5; + } + + public void setContentMd5(String contentMd5) { + this.contentMd5 = contentMd5; + } + + public String getSourceVertexUniq() { + return sourceVertexUniq; + } + + public void setSourceVertexUniq(String sourceVertexUniq) { + this.sourceVertexUniq = sourceVertexUniq; + } + + public String getTargetVertexUniq() { + return targetVertexUniq; + } + + public void setTargetVertexUniq(String targetVertexUniq) { + this.targetVertexUniq = targetVertexUniq; + } + + public String getJanusEdgeId() { + return janusEdgeId; + } + + public void setJanusEdgeId(String janusEdgeId) { + this.janusEdgeId = janusEdgeId; + } + + public Long getNeo4jEdgeId() { + return neo4jEdgeId; + } + + public void setNeo4jEdgeId(Long neo4jEdgeId) { + this.neo4jEdgeId = neo4jEdgeId; + } + + public String getNebulaEdgeId() { + return nebulaEdgeId; + } + + public void setNebulaEdgeId(String nebulaEdgeId) { + this.nebulaEdgeId = nebulaEdgeId; + } + + public Long getSourceJanusVertexId() { + return sourceJanusVertexId; + } + + public void setSourceJanusVertexId(Long sourceJanusVertexId) { + this.sourceJanusVertexId = sourceJanusVertexId; + } + + public Long getTargetJanusVertexId() { + return targetJanusVertexId; + } + + public void setTargetJanusVertexId(Long targetJanusVertexId) { + this.targetJanusVertexId = targetJanusVertexId; + } + + public Long getSourceNeo4jVertexId() { + return sourceNeo4jVertexId; + } + + public void setSourceNeo4jVertexId(Long sourceNeo4jVertexId) { + this.sourceNeo4jVertexId = sourceNeo4jVertexId; + } + + public Long getTargetNeo4jVertexId() { + return targetNeo4jVertexId; + } + + public void setTargetNeo4jVertexId(Long targetNeo4jVertexId) { + this.targetNeo4jVertexId = targetNeo4jVertexId; + } + + public Long getSourceNebulaVertexId() { + return sourceNebulaVertexId; + } + + public void setSourceNebulaVertexId(Long sourceNebulaVertexId) { + this.sourceNebulaVertexId = sourceNebulaVertexId; + } + + public Long getTargetNebulaVertexId() { + return targetNebulaVertexId; + } + + public void setTargetNebulaVertexId(Long targetNebulaVertexId) { + this.targetNebulaVertexId = targetNebulaVertexId; + } +} diff --git a/graph-persistence/persistence-model/src/main/java/org/studiox/graph/persistence/model/GraphVertexDO.java b/graph-persistence/persistence-model/src/main/java/org/studiox/graph/persistence/model/GraphVertexDO.java new file mode 100644 index 0000000000000000000000000000000000000000..0ab9f2e842d9038c37dee00030a133eabd588a1f --- /dev/null +++ b/graph-persistence/persistence-model/src/main/java/org/studiox/graph/persistence/model/GraphVertexDO.java @@ -0,0 +1,56 @@ +package org.studiox.graph.persistence.model; + +import java.io.Serializable; + +public class GraphVertexDO extends AbstractGraphDO implements Serializable { + + private String uniq; + + private Long janusVertexId; + + private Long neo4jVertexId; + + private Long nebulaVertexId; + + protected Boolean temp; + + public String getUniq() { + return uniq; + } + + public void setUniq(String uniq) { + this.uniq = uniq; + } + + public Long getJanusVertexId() { + return janusVertexId; + } + + public void setJanusVertexId(Long janusVertexId) { + this.janusVertexId = janusVertexId; + } + + public Long getNeo4jVertexId() { + return neo4jVertexId; + } + + public void setNeo4jVertexId(Long neo4jVertexId) { + this.neo4jVertexId = neo4jVertexId; + } + + public Long getNebulaVertexId() { + return nebulaVertexId; + } + + public void setNebulaVertexId(Long nebulaVertexId) { + this.nebulaVertexId = nebulaVertexId; + } + + public Boolean getTemp() { + return temp; + } + + public void setTemp(Boolean temp) { + this.temp = temp; + } +} diff --git a/graph-persistence/persistence-mybatis-plus/pom.xml b/graph-persistence/persistence-mybatis-plus/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..902181a596ebe946641d7ec90b84dc54fda71eb6 --- /dev/null +++ b/graph-persistence/persistence-mybatis-plus/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + org.studiox.graph + graph-persistence + 1.0.5-SNAPSHOT + + org.studiox.graph.persistence + persistence-mybatis-plus + 1.0.5-SNAPSHOT + jar + + + junit + junit + test + + + com.h2database + h2 + test + + + mysql + mysql-connector-java + test + + + org.studiox.graph.persistence + persistence-model + + + com.baomidou + mybatis-plus + + + com.baomidou + mybatis-plus-boot-starter + + + + + + pl.project13.maven + git-commit-id-plugin + + + + diff --git a/graph-persistence/persistence-mybatis-plus/src/main/java/org/studiox/graph/persistence/mybatis/plus/dao/GraphEdgeDao.java b/graph-persistence/persistence-mybatis-plus/src/main/java/org/studiox/graph/persistence/mybatis/plus/dao/GraphEdgeDao.java new file mode 100644 index 0000000000000000000000000000000000000000..3aca336b7398f30fa410a0de6fb49b22fcc3cd38 --- /dev/null +++ b/graph-persistence/persistence-mybatis-plus/src/main/java/org/studiox/graph/persistence/mybatis/plus/dao/GraphEdgeDao.java @@ -0,0 +1,5 @@ +package org.studiox.graph.persistence.mybatis.plus.dao; + +import org.studiox.graph.persistence.mybatis.plus.model.GraphEdgeDO; + +public interface GraphEdgeDao extends MybatisPlusDao {} diff --git a/graph-persistence/persistence-mybatis-plus/src/main/java/org/studiox/graph/persistence/mybatis/plus/dao/GraphVertexDao.java b/graph-persistence/persistence-mybatis-plus/src/main/java/org/studiox/graph/persistence/mybatis/plus/dao/GraphVertexDao.java new file mode 100644 index 0000000000000000000000000000000000000000..42548075475c2a15c882138e38aa154b03917ce7 --- /dev/null +++ b/graph-persistence/persistence-mybatis-plus/src/main/java/org/studiox/graph/persistence/mybatis/plus/dao/GraphVertexDao.java @@ -0,0 +1,5 @@ +package org.studiox.graph.persistence.mybatis.plus.dao; + +import org.studiox.graph.persistence.mybatis.plus.model.GraphVertexDO; + +public interface GraphVertexDao extends MybatisPlusDao {} diff --git a/graph-persistence/persistence-mybatis-plus/src/main/java/org/studiox/graph/persistence/mybatis/plus/dao/MybatisPlusDao.java b/graph-persistence/persistence-mybatis-plus/src/main/java/org/studiox/graph/persistence/mybatis/plus/dao/MybatisPlusDao.java new file mode 100644 index 0000000000000000000000000000000000000000..af605755f16a42432295922d6e76e9a87a0ec547 --- /dev/null +++ b/graph-persistence/persistence-mybatis-plus/src/main/java/org/studiox/graph/persistence/mybatis/plus/dao/MybatisPlusDao.java @@ -0,0 +1,5 @@ +package org.studiox.graph.persistence.mybatis.plus.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +public interface MybatisPlusDao extends BaseMapper {} diff --git a/graph-persistence/persistence-mybatis-plus/src/main/java/org/studiox/graph/persistence/mybatis/plus/model/GraphEdgeDO.java b/graph-persistence/persistence-mybatis-plus/src/main/java/org/studiox/graph/persistence/mybatis/plus/model/GraphEdgeDO.java new file mode 100644 index 0000000000000000000000000000000000000000..e56de5f04098cc5996ccd33dc0173c9219c4a2c3 --- /dev/null +++ b/graph-persistence/persistence-mybatis-plus/src/main/java/org/studiox/graph/persistence/mybatis/plus/model/GraphEdgeDO.java @@ -0,0 +1,22 @@ +package org.studiox.graph.persistence.mybatis.plus.model; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +@TableName("graph_edge") +public class GraphEdgeDO extends org.studiox.graph.persistence.model.GraphEdgeDO { + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + @Override + public Long getId() { + return id; + } + + @Override + public void setId(Long id) { + this.id = id; + } +} diff --git a/graph-persistence/persistence-mybatis-plus/src/main/java/org/studiox/graph/persistence/mybatis/plus/model/GraphVertexDO.java b/graph-persistence/persistence-mybatis-plus/src/main/java/org/studiox/graph/persistence/mybatis/plus/model/GraphVertexDO.java new file mode 100644 index 0000000000000000000000000000000000000000..7f4914bd179c2a2fa70941ff295ee31c8a7f467b --- /dev/null +++ b/graph-persistence/persistence-mybatis-plus/src/main/java/org/studiox/graph/persistence/mybatis/plus/model/GraphVertexDO.java @@ -0,0 +1,22 @@ +package org.studiox.graph.persistence.mybatis.plus.model; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +@TableName("graph_vertex") +public class GraphVertexDO extends org.studiox.graph.persistence.model.GraphVertexDO { + + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + @Override + public Long getId() { + return id; + } + + @Override + public void setId(Long id) { + this.id = id; + } +} diff --git a/graph-persistence/persistence-mybatis-plus/src/test/java/org/studiox/graph/persistence/mybatis/plus/test/MybatisPlusTest.java b/graph-persistence/persistence-mybatis-plus/src/test/java/org/studiox/graph/persistence/mybatis/plus/test/MybatisPlusTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4ceba2cf732622deb73c40a2587d2d2d3385eac1 --- /dev/null +++ b/graph-persistence/persistence-mybatis-plus/src/test/java/org/studiox/graph/persistence/mybatis/plus/test/MybatisPlusTest.java @@ -0,0 +1,138 @@ +package org.studiox.graph.persistence.mybatis.plus.test; + +import com.baomidou.mybatisplus.core.MybatisSqlSessionFactoryBuilder; +import org.apache.ibatis.io.Resources; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.junit.*; +import org.junit.runners.MethodSorters; +import org.studiox.graph.persistence.mybatis.plus.dao.GraphEdgeDao; +import org.studiox.graph.persistence.mybatis.plus.dao.GraphVertexDao; +import org.studiox.graph.persistence.mybatis.plus.model.GraphEdgeDO; +import org.studiox.graph.persistence.mybatis.plus.model.GraphVertexDO; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class MybatisPlusTest { + + private static SqlSession session = null; + + private static GraphVertexDao graphVertexDao; + + private static GraphEdgeDao graphEdgeDao; + + @BeforeClass + public static void init() { + InputStream inputStream = null; + try { + inputStream = Resources.getResourceAsStream("mybatis-config.xml"); + SqlSessionFactory factory = new MybatisSqlSessionFactoryBuilder().build(inputStream); + session = factory.openSession(); + graphVertexDao = session.getMapper(GraphVertexDao.class); + graphEdgeDao = session.getMapper(GraphEdgeDao.class); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (null != inputStream) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + @AfterClass + public static void clean() { + if (null != session) { + session.commit(); + session.close(); + } + } + + @Test + public void test1() { + List graphVertexDOs = graphVertexDao.selectList(null); + if (null != graphVertexDOs && graphVertexDOs.size() > 0) { + for (GraphVertexDO graphVertexDO : graphVertexDOs) { + graphVertexDao.deleteById(graphVertexDO.getId()); + } + } + session.commit(); + } + + @Test + public void test2() { + GraphVertexDO graphVertexDO1 = new GraphVertexDO(); + graphVertexDO1.setUniq("uniq1"); + graphVertexDO1.setType("type1"); + graphVertexDO1.setOwner("owner1"); + graphVertexDO1.setJanusVertexId(1L); + graphVertexDO1.setTemp(true); + graphVertexDO1.setDeleted(false); + graphVertexDao.insert(graphVertexDO1); + + session.commit(); + + long count = graphVertexDao.selectCount(null); + Assert.assertEquals(1L, count); + + List graphVertexDOs = graphVertexDao.selectList(null); + GraphVertexDO graphVertexDO2 = graphVertexDOs.get(0); + Long id = graphVertexDO2.getId(); + graphVertexDO2.setUniq("uniq2"); + graphVertexDao.updateById(graphVertexDO2); + + session.commit(); + + GraphVertexDO graphVertexDO3 = graphVertexDao.selectById(id); + Assert.assertEquals("uniq2", graphVertexDO3.getUniq()); + Assert.assertEquals(true, graphVertexDO3.getTemp()); + Assert.assertEquals(false, graphVertexDO3.getDeleted()); + } + + @Test + public void test3() { + List graphEdgeDOs = graphEdgeDao.selectList(null); + if (null != graphEdgeDOs && graphEdgeDOs.size() > 0) { + for (GraphEdgeDO graphEdgeDO : graphEdgeDOs) { + graphEdgeDao.deleteById(graphEdgeDO.getId()); + } + } + session.commit(); + } + + @Test + public void test4() { + GraphEdgeDO graphEdgeDO1 = new GraphEdgeDO(); + graphEdgeDO1.setContent("select * from sys.version"); + graphEdgeDO1.setType("typq1"); + graphEdgeDO1.setOwner("owner1"); + graphEdgeDO1.setSourceVertexUniq("uniq1"); + graphEdgeDO1.setTargetVertexUniq("uniq2"); + graphEdgeDO1.setJanusEdgeId("edge1"); + graphEdgeDO1.setSourceJanusVertexId(1L); + graphEdgeDO1.setTargetJanusVertexId(1L); + graphEdgeDao.insert(graphEdgeDO1); + + session.commit(); + + long count = graphEdgeDao.selectCount(null); + Assert.assertEquals(1, count); + + List graphEdgeDOs = graphEdgeDao.selectList(null); + GraphEdgeDO graphEdgeDO2 = graphEdgeDOs.get(0); + Long id = graphEdgeDO2.getId(); + graphEdgeDO2.setContent("select 1 from sys.version"); + graphEdgeDao.updateById(graphEdgeDO2); + + session.commit(); + + GraphEdgeDO graphEdgeDO3 = graphEdgeDao.selectById(id); + Assert.assertEquals("select 1 from sys.version", graphEdgeDO3.getContent()); + } +} diff --git a/graph-persistence/persistence-mybatis-plus/src/test/resources/jdbc.properties b/graph-persistence/persistence-mybatis-plus/src/test/resources/jdbc.properties new file mode 100644 index 0000000000000000000000000000000000000000..189dc13ead46c8f3eaa740157b8a9834d4f1ca2d --- /dev/null +++ b/graph-persistence/persistence-mybatis-plus/src/test/resources/jdbc.properties @@ -0,0 +1,13 @@ +# H2 Database +##################################### +jdbc.driver=org.h2.Driver +jdbc.url=jdbc:h2:mem:test;MODE=MYSQL;DATABASE_TO_LOWER=FALSE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;INIT=RUNSCRIPT FROM 'classpath:/mysql_ddl.sql' +jdbc.username=root +jdbc.password=123456 +##################################### +# MySQL Database +#jdbc.driver=com.mysql.jdbc.Driver +#jdbc.url=jdbc:mysql://localhost:13306/test1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai +#jdbc.username=root +#jdbc.password=123456 +##################################### diff --git a/graph-persistence/persistence-mybatis-plus/src/test/resources/mybatis-config.xml b/graph-persistence/persistence-mybatis-plus/src/test/resources/mybatis-config.xml new file mode 100644 index 0000000000000000000000000000000000000000..60d1bee15f69280afa4e520d212807e67e35c7d8 --- /dev/null +++ b/graph-persistence/persistence-mybatis-plus/src/test/resources/mybatis-config.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/graph-persistence/persistence-mybatis-plus/src/test/resources/mysql_ddl.sql b/graph-persistence/persistence-mybatis-plus/src/test/resources/mysql_ddl.sql new file mode 100644 index 0000000000000000000000000000000000000000..48cc95eeb62d757e9fe145bcf22fb758fd4eb86c --- /dev/null +++ b/graph-persistence/persistence-mybatis-plus/src/test/resources/mysql_ddl.sql @@ -0,0 +1,53 @@ +create table if not exists `graph_vertex` ( + `id` bigint(20) unsigned not null auto_increment comment '主键', + `uniq` varchar(255) not null default '' comment '顶点的唯一标识', + `type` varchar(20) not null default '' comment '顶点的类型', + `owner` varchar(50) not null default '' comment '所有者', + `janus_vertex_id` bigint(20) not null default 0 comment '顶点存储 JanusGraph Vertex Id', + `neo4j_vertex_id` bigint(20) not null default 0 comment '顶点存储 Neo4j Vertex Id', + `nebula_vertex_id` bigint(20) not null default 0 comment '顶点存储 NebulaGraph Vertex Id', + `temp` tinyint(1) not null default 0 comment '是否临时点(0:否 1:是),只存在数据库,不存在图中', + `deleted` tinyint(1) not null default 0 comment '是否删除(0:否 1:是)', + `create_time` timestamp not null default current_timestamp comment '顶点的创建时间', + `update_time` timestamp not null default current_timestamp comment '顶点的修改时间', + primary key (`id`), + unique key (`uniq`), + index (`janus_vertex_id`), + index (`neo4j_vertex_id`), + index (`nebula_vertex_id`) +) engine=innodb auto_increment=1 default charset=utf8 comment='图中顶点集合'; + +create table if not exists `graph_edge` ( + `id` bigint(20) unsigned not null auto_increment comment '主键', + `content` mediumtext not null comment '边的记录内容', + `content_md5` char(32) not null default '' comment '边的记录内容MD5值', + `type` varchar(20) not null default '' comment '边的类型', + `owner` varchar(50) not null default '' comment '所有者', + `source_vertex_uniq` varchar(255) not null default '' comment '边的输入顶点的唯一标识', + `target_vertex_uniq` varchar(255) not null default '' comment '边的输出顶点的唯一标识', + `janus_edge_id` varchar(50) not null default '' comment '边存储 JanusGraph Edge Id', + `neo4j_edge_id` bigint(20) not null default 0 comment '边存储 Neo4j Edge Id', + `nebula_edge_id` varchar(50) not null default '' comment '边存储 NebulaGraph Edge Id', + `source_janus_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 JanusGraph Vertex Id', + `target_janus_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 JanusGraph Vertex Id', + `source_neo4j_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 Neo4j Vertex Id', + `target_neo4j_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 Neo4j Vertex Id', + `source_nebula_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 NebulaGraph Vertex Id', + `target_nebula_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 NebulaGraph Vertex Id', + `deleted` tinyint(1) not null default 0 comment '是否删除(0:否 1:是)', + `create_time` timestamp not null default current_timestamp comment '边的创建时间', + `update_time` timestamp not null default current_timestamp comment '边的修改时间', + primary key (`id`), + index (`content_md5`), + index (`source_vertex_uniq`), + index (`target_vertex_uniq`), + index (`janus_edge_id`), + index (`neo4j_edge_id`), + index (`nebula_edge_id`), + index (`source_janus_vertex_id`), + index (`target_janus_vertex_id`), + index (`source_neo4j_vertex_id`), + index (`target_neo4j_vertex_id`), + index (`source_nebula_vertex_id`), + index (`target_nebula_vertex_id`) +) engine=innodb auto_increment=1 default charset=utf8 comment='图中边集合'; diff --git a/graph-persistence/persistence-mybatis-tk/pom.xml b/graph-persistence/persistence-mybatis-tk/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..7debc36bcd1f36a6ec7be04fb327cbe6f03b64a7 --- /dev/null +++ b/graph-persistence/persistence-mybatis-tk/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + org.studiox.graph + graph-persistence + 1.0.5-SNAPSHOT + + org.studiox.graph.persistence + persistence-mybatis-tk + 1.0.5-SNAPSHOT + jar + + 2.4.11 + 5.3.10 + + + + org.springframework.boot + spring-boot-starter-test + test + + + ch.vorburger.mariaDB4j + mariaDB4j + test + + + mysql + mysql-connector-java + test + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + + + tk.mybatis + mapper-spring-boot-starter + + + + + + pl.project13.maven + git-commit-id-plugin + + + + diff --git a/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/TkMybatisSpring.java b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/TkMybatisSpring.java new file mode 100644 index 0000000000000000000000000000000000000000..dff0130388018ee126b0c8a31e976086832b4e85 --- /dev/null +++ b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/TkMybatisSpring.java @@ -0,0 +1,22 @@ +package org.studiox.graph.persistence.mybatis.tk; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import tk.mybatis.spring.annotation.MapperScan; + +@SpringBootApplication +@MapperScan(basePackages = {"org.studiox.graph.persistence.mybatis.tk.mapper"}) +@ComponentScan( + basePackages = { + "org.studiox.graph.persistence.mybatis.tk.base", + "org.studiox.graph.persistence.mybatis.tk.domain", + "org.studiox.graph.persistence.mybatis.tk.dao", + "org.studiox.graph.persistence.mybatis.tk.service" + }) +public class TkMybatisSpring { + + public static void main(String[] args) { + SpringApplication.run(TkMybatisSpring.class, args); + } +} diff --git a/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/base/BaseGraphDO.java b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/base/BaseGraphDO.java new file mode 100644 index 0000000000000000000000000000000000000000..c568bebec44323d37261a4c2accec64bb1e20bac --- /dev/null +++ b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/base/BaseGraphDO.java @@ -0,0 +1,97 @@ +package org.studiox.graph.persistence.mybatis.tk.base; + +import tk.mybatis.mapper.annotation.KeySql; + +import javax.persistence.Column; +import javax.persistence.Id; +import java.sql.Timestamp; +import java.util.Objects; + +public abstract class BaseGraphDO { + + @Id + @Column(name = "id") + @KeySql(useGeneratedKeys = true) + private Long id; + + @Column(name = "type") + private String type; + + @Column(name = "owner") + private String owner; + + @Column(name = "deleted") + private Boolean deleted; + + @Column(name = "create_time") + private Timestamp createTime; + + @Column(name = "update_time") + private Timestamp updateTime; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public Boolean getDeleted() { + return deleted; + } + + public void setDeleted(Boolean deleted) { + this.deleted = deleted; + } + + public Timestamp getCreateTime() { + return createTime; + } + + public void setCreateTime(Timestamp createTime) { + this.createTime = createTime; + } + + public Timestamp getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Timestamp updateTime) { + this.updateTime = updateTime; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BaseGraphDO that = (BaseGraphDO) o; + return Objects.equals(id, that.id) + && Objects.equals(type, that.type) + && Objects.equals(owner, that.owner) + && Objects.equals(deleted, that.deleted) + && Objects.equals(createTime, that.createTime) + && Objects.equals(updateTime, that.updateTime); + } + + @Override + public int hashCode() { + return Objects.hash(id, type, owner, deleted, createTime, updateTime); + } +} diff --git a/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/base/BaseGraphDao.java b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/base/BaseGraphDao.java new file mode 100644 index 0000000000000000000000000000000000000000..e0c44a3353303872f209cf4f92a441eb4527c0fa --- /dev/null +++ b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/base/BaseGraphDao.java @@ -0,0 +1,64 @@ +package org.studiox.graph.persistence.mybatis.tk.base; + +import java.util.List; + +public abstract class BaseGraphDao { + + protected BaseGraphMapper mapper; + + public List select(T obj) { + return mapper.select(obj); + } + + public T selectOne(T obj) { + return mapper.selectOne(obj); + } + + public T selectByPrimaryKey(Long id) { + return mapper.selectByPrimaryKey(id); + } + + public List selectAll() { + return mapper.selectAll(); + } + + public int selectCount(T obj) { + return mapper.selectCount(obj); + } + + public boolean existsWithPrimaryKey(Long id) { + return mapper.existsWithPrimaryKey(id); + } + + public int insert(T obj) { + return mapper.insert(obj); + } + + public int insertSelective(T obj) { + return mapper.insertSelective(obj); + } + + public int insertUseGeneratedKeys(T obj) { + return mapper.insertUseGeneratedKeys(obj); + } + + public int insertList(List objs) { + return mapper.insertList(objs); + } + + public int updateByPrimaryKey(T obj) { + return mapper.updateByPrimaryKey(obj); + } + + public int updateByPrimaryKeySelective(T obj) { + return mapper.updateByPrimaryKeySelective(obj); + } + + public int delete(T obj) { + return mapper.delete(obj); + } + + public int deleteByPrimaryKey(Long id) { + return mapper.deleteByPrimaryKey(id); + } +} diff --git a/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/base/BaseGraphMapper.java b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/base/BaseGraphMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..dc5d89f41c2b0a2e59dd4cbcf5da5d2adb2f8732 --- /dev/null +++ b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/base/BaseGraphMapper.java @@ -0,0 +1,6 @@ +package org.studiox.graph.persistence.mybatis.tk.base; + +import tk.mybatis.mapper.common.BaseMapper; +import tk.mybatis.mapper.common.MySqlMapper; + +public interface BaseGraphMapper extends BaseMapper, MySqlMapper {} diff --git a/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/dao/GraphEdgeDao.java b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/dao/GraphEdgeDao.java new file mode 100644 index 0000000000000000000000000000000000000000..2943f2ee028cdd2cb8cae538175ef8b0d6713542 --- /dev/null +++ b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/dao/GraphEdgeDao.java @@ -0,0 +1,160 @@ +package org.studiox.graph.persistence.mybatis.tk.dao; + +import org.springframework.stereotype.Repository; +import org.studiox.graph.persistence.mybatis.tk.base.BaseGraphDao; +import org.studiox.graph.persistence.mybatis.tk.domain.GraphEdgeDO; +import org.studiox.graph.persistence.mybatis.tk.mapper.GraphEdgeMapper; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.List; + +@Repository +public class GraphEdgeDao extends BaseGraphDao { + + @Resource private GraphEdgeMapper graphEdgeMapper; + + @PostConstruct + private void init() { + mapper = graphEdgeMapper; + } + + public Integer getGraphEdgeSize() { + return graphEdgeMapper.selectCount(null); + } + + public List getGraphEdgeIds() { + return graphEdgeMapper.getGraphEdgeIds(); + } + + public List getGraphUniqueMinEdgeIds() { + return graphEdgeMapper.getGraphUniqueMinEdgeIds(); + } + + public List getGraphUniqueMaxEdgeIds() { + return graphEdgeMapper.getGraphUniqueMaxEdgeIds(); + } + + public Long getGraphEdgeContentSize(String sourceVertexUniq, String targetVertexUniq) { + return graphEdgeMapper.getGraphEdgeContentSize(sourceVertexUniq, targetVertexUniq); + } + + public List getGraphEdgeContents(String sourceVertexUniq, String targetVertexUniq) { + return graphEdgeMapper.getGraphEdgeContents(sourceVertexUniq, targetVertexUniq); + } + + public List getGraphEdgeDOs(GraphEdgeDO graphEdgeDO) { + return graphEdgeMapper.select(graphEdgeDO); + } + + public GraphEdgeDO getOneGraphEdgeDoBySourceTargetUniq( + String sourceVertexUniq, String targetVertexUniq) { + return graphEdgeMapper.getOneGraphEdgeDoBySourceTargetUniq(sourceVertexUniq, targetVertexUniq); + } + + public Long getGraphEdgeId(String content, String sourceVertexUniq, String targetVertexUniq) { + return graphEdgeMapper.getGraphEdgeId(content, sourceVertexUniq, targetVertexUniq); + } + + public void updateGraphEdgeContentMd5(Long id) { + graphEdgeMapper.updateGraphEdgeContentMd5(id); + } + + public void updateAllGraphEdgeContentMd5() { + graphEdgeMapper.updateAllGraphEdgeContentMd5(); + } + + public void updateJanusEdgeId(Long id, String janusEdgeId) { + graphEdgeMapper.updateJanusEdgeId(id, janusEdgeId); + } + + public void updateJanusEdgeIdAndSourceTargetVertexId( + Long id, String janusEdgeId, Long sourceJanusVertexId, Long targetJanusVertexId) { + graphEdgeMapper.updateJanusEdgeIdAndSourceTargetVertexId( + id, janusEdgeId, sourceJanusVertexId, targetJanusVertexId); + } + + public void updateNeo4jEdgeId(Long id, Long neo4jEdgeId) { + graphEdgeMapper.updateNeo4jEdgeId(id, neo4jEdgeId); + } + + public void updateNeo4jEdgeIdAndSourceTargetVertexId( + Long id, Long neo4jEdgeId, Long sourceNeo4jVertexId, Long targetNeo4jVertexId) { + graphEdgeMapper.updateNeo4jEdgeIdAndSourceTargetVertexId( + id, neo4jEdgeId, sourceNeo4jVertexId, targetNeo4jVertexId); + } + + public void updateNebulaEdgeId(Long id, String nebulaEdgeId) { + graphEdgeMapper.updateNebulaEdgeId(id, nebulaEdgeId); + } + + public void updateNebulaEdgeIdAndSourceTargetVertexId( + Long id, String nebulaEdgeId, Long sourceNebulaVertexId, Long targetNebulaVertexId) { + graphEdgeMapper.updateNebulaEdgeIdAndSourceTargetVertexId( + id, nebulaEdgeId, sourceNebulaVertexId, targetNebulaVertexId); + } + + public void updateSourceJanusVertexId(String sourceVertexUniq, Long sourceJanusVertexId) { + graphEdgeMapper.updateSourceJanusVertexId(sourceVertexUniq, sourceJanusVertexId); + } + + public void updateTargetJanusVertexId(String sourceVertexUniq, Long targetJanusVertexId) { + graphEdgeMapper.updateTargetJanusVertexId(sourceVertexUniq, targetJanusVertexId); + } + + public void updateAllSourceJanusVertexId() { + graphEdgeMapper.updateAllSourceJanusVertexId(); + } + + public void updateAllTargetJanusVertexId() { + graphEdgeMapper.updateAllTargetJanusVertexId(); + } + + public void updateAllSourceTargetJanusVertexId() { + graphEdgeMapper.updateAllSourceTargetJanusVertexId(); + } + + public void updateSourceNeo4jVertexId(String sourceVertexUniq, Long sourceNeo4jVertexId) { + graphEdgeMapper.updateSourceNeo4jVertexId(sourceVertexUniq, sourceNeo4jVertexId); + } + + public void updateTargetNeo4jVertexId(String sourceVertexUniq, Long targetNeo4jVertexId) { + graphEdgeMapper.updateTargetNeo4jVertexId(sourceVertexUniq, targetNeo4jVertexId); + } + + public void updateAllSourceNeo4jVertexId() { + graphEdgeMapper.updateAllSourceNeo4jVertexId(); + } + + public void updateAllTargetNeo4jVertexId() { + graphEdgeMapper.updateAllTargetNeo4jVertexId(); + } + + public void updateAllSourceTargetNeo4jVertexId() { + graphEdgeMapper.updateAllSourceTargetNeo4jVertexId(); + } + + public void updateSourceNebulaVertexId(String sourceVertexUniq, Long sourceNebulaVertexId) { + graphEdgeMapper.updateSourceNebulaVertexId(sourceVertexUniq, sourceNebulaVertexId); + } + + public void updateTargetNebulaVertexId(String sourceVertexUniq, Long targetNebulaVertexId) { + graphEdgeMapper.updateTargetNebulaVertexId(sourceVertexUniq, targetNebulaVertexId); + } + + public void updateAllSourceNebulaVertexId() { + graphEdgeMapper.updateAllSourceNebulaVertexId(); + } + + public void updateAllTargetNebulaVertexId() { + graphEdgeMapper.updateAllTargetNebulaVertexId(); + } + + public void updateAllSourceTargetNebulaVertexId() { + graphEdgeMapper.updateAllSourceTargetNebulaVertexId(); + } + + public void deleteGraphEdgeByDbId(Long dbId) { + graphEdgeMapper.deleteByPrimaryKey(dbId); + } +} diff --git a/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/dao/GraphVertexDao.java b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/dao/GraphVertexDao.java new file mode 100644 index 0000000000000000000000000000000000000000..d3db8fa3bb76d01c9a6da263ebc8a44c54df8c09 --- /dev/null +++ b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/dao/GraphVertexDao.java @@ -0,0 +1,65 @@ +package org.studiox.graph.persistence.mybatis.tk.dao; + +import org.springframework.stereotype.Repository; +import org.studiox.graph.persistence.mybatis.tk.base.BaseGraphDao; +import org.studiox.graph.persistence.mybatis.tk.domain.GraphVertexDO; +import org.studiox.graph.persistence.mybatis.tk.mapper.GraphVertexMapper; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.List; + +@Repository +public class GraphVertexDao extends BaseGraphDao { + + @Resource private GraphVertexMapper graphVertexMapper; + + @PostConstruct + private void init() { + mapper = graphVertexMapper; + } + + public Integer getGraphVertexSize() { + return graphVertexMapper.selectCount(null); + } + + public List getGraphVertexIds() { + return graphVertexMapper.getGraphVertexIds(); + } + + public List getGraphVertexUniqs() { + return graphVertexMapper.getGraphVertexUniqs(); + } + + public Long getGraphVertexIdByUniq(String uniq) { + return graphVertexMapper.getGraphVertexIdByUniq(uniq); + } + + public Long getJanusVertexIdByUniq(String uniq) { + return graphVertexMapper.getJanusVertexIdByUniq(uniq); + } + + public GraphVertexDO getGraphVertexDoByUniq(String uniq) { + return graphVertexMapper.getGraphVertexDoByUniq(uniq); + } + + public List getGraphVertexDosByUniqLike(String likeUniqStr) { + return graphVertexMapper.getGraphVertexDosByUniqLike(likeUniqStr); + } + + public void updateJanusVertexId(Long id, Long janusVertexId) { + graphVertexMapper.updateJanusVertexId(id, janusVertexId); + } + + public void updateNeo4jVertexId(Long id, Long neo4jVertexId) { + graphVertexMapper.updateNeo4jVertexId(id, neo4jVertexId); + } + + public void updateNebulaVertexId(Long id, Long nebulaVertexId) { + graphVertexMapper.updateNebulaVertexId(id, nebulaVertexId); + } + + public void deleteGraphVertexByDbId(Long dbId) { + graphVertexMapper.deleteByPrimaryKey(dbId); + } +} diff --git a/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/domain/GraphEdgeDO.java b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/domain/GraphEdgeDO.java new file mode 100644 index 0000000000000000000000000000000000000000..4f9e13820f667a147596dd141e75a1f88add0cd9 --- /dev/null +++ b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/domain/GraphEdgeDO.java @@ -0,0 +1,222 @@ +package org.studiox.graph.persistence.mybatis.tk.domain; + +import org.studiox.graph.persistence.mybatis.tk.base.BaseGraphDO; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.Objects; + +@Entity +@Table(name = "graph_edge") +public class GraphEdgeDO extends BaseGraphDO { + + @Column(name = "content") + private String content; + + @Column(name = "content_md5") + private String contentMd5; + + @Column(name = "source_vertex_uniq") + private String sourceVertexUniq; + + @Column(name = "target_vertex_uniq") + private String targetVertexUniq; + + @Column(name = "janus_edge_id") + private String janusEdgeId; + + @Column(name = "neo4j_edge_id") + private Long neo4jEdgeId; + + @Column(name = "nebula_edge_id") + private String nebulaEdgeId; + + @Column(name = "source_janus_vertex_id") + private Long sourceJanusVertexId; + + @Column(name = "target_janus_vertex_id") + private Long targetJanusVertexId; + + @Column(name = "source_neo4j_vertex_id") + private Long sourceNeo4jVertexId; + + @Column(name = "target_neo4j_vertex_id") + private Long targetNeo4jVertexId; + + @Column(name = "source_nebula_vertex_id") + private Long sourceNebulaVertexId; + + @Column(name = "target_nebula_vertex_id") + private Long targetNebulaVertexId; + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getContentMd5() { + return contentMd5; + } + + public void setContentMd5(String contentMd5) { + this.contentMd5 = contentMd5; + } + + public String getSourceVertexUniq() { + return sourceVertexUniq; + } + + public void setSourceVertexUniq(String sourceVertexUniq) { + this.sourceVertexUniq = sourceVertexUniq; + } + + public String getTargetVertexUniq() { + return targetVertexUniq; + } + + public void setTargetVertexUniq(String targetVertexUniq) { + this.targetVertexUniq = targetVertexUniq; + } + + public String getJanusEdgeId() { + return janusEdgeId; + } + + public void setJanusEdgeId(String janusEdgeId) { + this.janusEdgeId = janusEdgeId; + } + + public Long getNeo4jEdgeId() { + return neo4jEdgeId; + } + + public void setNeo4jEdgeId(Long neo4jEdgeId) { + this.neo4jEdgeId = neo4jEdgeId; + } + + public String getNebulaEdgeId() { + return nebulaEdgeId; + } + + public void setNebulaEdgeId(String nebulaEdgeId) { + this.nebulaEdgeId = nebulaEdgeId; + } + + public Long getSourceJanusVertexId() { + return sourceJanusVertexId; + } + + public void setSourceJanusVertexId(Long sourceJanusVertexId) { + this.sourceJanusVertexId = sourceJanusVertexId; + } + + public Long getTargetJanusVertexId() { + return targetJanusVertexId; + } + + public void setTargetJanusVertexId(Long targetJanusVertexId) { + this.targetJanusVertexId = targetJanusVertexId; + } + + public Long getSourceNeo4jVertexId() { + return sourceNeo4jVertexId; + } + + public void setSourceNeo4jVertexId(Long sourceNeo4jVertexId) { + this.sourceNeo4jVertexId = sourceNeo4jVertexId; + } + + public Long getTargetNeo4jVertexId() { + return targetNeo4jVertexId; + } + + public void setTargetNeo4jVertexId(Long targetNeo4jVertexId) { + this.targetNeo4jVertexId = targetNeo4jVertexId; + } + + public Long getSourceNebulaVertexId() { + return sourceNebulaVertexId; + } + + public void setSourceNebulaVertexId(Long sourceNebulaVertexId) { + this.sourceNebulaVertexId = sourceNebulaVertexId; + } + + public Long getTargetNebulaVertexId() { + return targetNebulaVertexId; + } + + public void setTargetNebulaVertexId(Long targetNebulaVertexId) { + this.targetNebulaVertexId = targetNebulaVertexId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + GraphEdgeDO that = (GraphEdgeDO) o; + return Objects.equals(content, that.content) + && Objects.equals(contentMd5, that.contentMd5) + && Objects.equals(sourceVertexUniq, that.sourceVertexUniq) + && Objects.equals(targetVertexUniq, that.targetVertexUniq) + && Objects.equals(janusEdgeId, that.janusEdgeId) + && Objects.equals(neo4jEdgeId, that.neo4jEdgeId) + && Objects.equals(nebulaEdgeId, that.nebulaEdgeId) + && Objects.equals(sourceJanusVertexId, that.sourceJanusVertexId) + && Objects.equals(targetJanusVertexId, that.targetJanusVertexId) + && Objects.equals(sourceNeo4jVertexId, that.sourceNeo4jVertexId) + && Objects.equals(targetNeo4jVertexId, that.targetNeo4jVertexId) + && Objects.equals(sourceNebulaVertexId, that.sourceNebulaVertexId) + && Objects.equals(targetNebulaVertexId, that.targetNebulaVertexId); + } + + @Override + public int hashCode() { + return Objects.hash( + super.hashCode(), + content, + contentMd5, + sourceVertexUniq, + targetVertexUniq, + janusEdgeId, + neo4jEdgeId, + nebulaEdgeId, + sourceJanusVertexId, + targetJanusVertexId, + sourceNeo4jVertexId, + targetNeo4jVertexId, + sourceNebulaVertexId, + targetNebulaVertexId); + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("GraphEdgeDO{"); + sb.append("content='").append(content).append('\''); + sb.append(", contentMd5='").append(contentMd5).append('\''); + sb.append(", sourceVertexUniq='").append(sourceVertexUniq).append('\''); + sb.append(", targetVertexUniq='").append(targetVertexUniq).append('\''); + sb.append(", janusEdgeId='").append(janusEdgeId).append('\''); + sb.append(", neo4jEdgeId=").append(neo4jEdgeId); + sb.append(", nebulaEdgeId='").append(nebulaEdgeId).append('\''); + sb.append(", sourceJanusVertexId=").append(sourceJanusVertexId); + sb.append(", targetJanusVertexId=").append(targetJanusVertexId); + sb.append(", sourceNeo4jVertexId=").append(sourceNeo4jVertexId); + sb.append(", targetNeo4jVertexId=").append(targetNeo4jVertexId); + sb.append(", sourceNebulaVertexId=").append(sourceNebulaVertexId); + sb.append(", targetNebulaVertexId=").append(targetNebulaVertexId); + sb.append('}'); + return sb.toString(); + } +} diff --git a/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/domain/GraphVertexDO.java b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/domain/GraphVertexDO.java new file mode 100644 index 0000000000000000000000000000000000000000..1ed9ef50948c92889134f54995bbd0e133cf4943 --- /dev/null +++ b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/domain/GraphVertexDO.java @@ -0,0 +1,104 @@ +package org.studiox.graph.persistence.mybatis.tk.domain; + +import org.studiox.graph.persistence.mybatis.tk.base.BaseGraphDO; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.Objects; + +@Entity +@Table(name = "graph_vertex") +public class GraphVertexDO extends BaseGraphDO { + + @Column(name = "uniq") + private String uniq; + + @Column(name = "janus_vertex_id") + private Long janusVertexId; + + @Column(name = "neo4j_vertex_id") + private Long neo4jVertexId; + + @Column(name = "nebula_vertex_id") + private Long nebulaVertexId; + + @Column(name = "temp") + private Boolean temp; + + public String getUniq() { + return uniq; + } + + public void setUniq(String uniq) { + this.uniq = uniq; + } + + public Long getJanusVertexId() { + return janusVertexId; + } + + public void setJanusVertexId(Long janusVertexId) { + this.janusVertexId = janusVertexId; + } + + public Long getNeo4jVertexId() { + return neo4jVertexId; + } + + public void setNeo4jVertexId(Long neo4jVertexId) { + this.neo4jVertexId = neo4jVertexId; + } + + public Long getNebulaVertexId() { + return nebulaVertexId; + } + + public void setNebulaVertexId(Long nebulaVertexId) { + this.nebulaVertexId = nebulaVertexId; + } + + public Boolean getTemp() { + return temp; + } + + public void setTemp(Boolean temp) { + this.temp = temp; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + GraphVertexDO that = (GraphVertexDO) o; + return Objects.equals(uniq, that.uniq) + && Objects.equals(janusVertexId, that.janusVertexId) + && Objects.equals(neo4jVertexId, that.neo4jVertexId) + && Objects.equals(nebulaVertexId, that.nebulaVertexId) + && Objects.equals(temp, that.temp); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), uniq, janusVertexId, neo4jVertexId, nebulaVertexId, temp); + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("GraphVertexDO{"); + sb.append("uniq='").append(uniq).append('\''); + sb.append(", janusVertexId=").append(janusVertexId); + sb.append(", neo4jVertexId=").append(neo4jVertexId); + sb.append(", nebulaVertexId=").append(nebulaVertexId); + sb.append(", temp=").append(temp); + sb.append('}'); + return sb.toString(); + } +} diff --git a/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/mapper/GraphEdgeMapper.java b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/mapper/GraphEdgeMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..7db46d4b78388264a7a0a84d7e59408483e5f53d --- /dev/null +++ b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/mapper/GraphEdgeMapper.java @@ -0,0 +1,187 @@ +package org.studiox.graph.persistence.mybatis.tk.mapper; + +import org.apache.ibatis.annotations.*; +import org.studiox.graph.persistence.mybatis.tk.base.BaseGraphMapper; +import org.studiox.graph.persistence.mybatis.tk.domain.GraphEdgeDO; + +import java.util.List; + +public interface GraphEdgeMapper extends BaseGraphMapper { + + @Select("select id from graph_edge") + List getGraphEdgeIds(); + + @Select("select min(id) from graph_edge group by source_vertex_uniq,target_vertex_uniq") + List getGraphUniqueMinEdgeIds(); + + @Select("select max(id) from graph_edge group by source_vertex_uniq,target_vertex_uniq") + List getGraphUniqueMaxEdgeIds(); + + @Select( + "select count(*) from graph_edge where source_vertex_uniq=#{sourceVertexUniq} and target_vertex_uniq=#{targetVertexUniq}") + Long getGraphEdgeContentSize( + @Param("sourceVertexUniq") String sourceVertexUniq, + @Param("targetVertexUniq") String targetVertexUniq); + + @Select( + "select content from graph_edge where source_vertex_uniq=#{sourceVertexUniq} and target_vertex_uniq=#{targetVertexUniq} order by create_time asc") + List getGraphEdgeContents( + @Param("sourceVertexUniq") String sourceVertexUniq, + @Param("targetVertexUniq") String targetVertexUniq); + + @Select( + "select * from graph_edge where source_vertex_uniq=#{sourceVertexUniq} and target_vertex_uniq=#{targetVertexUniq} order by create_time asc limit 1") + @Results( + id = "graphEdgeDOMap", + value = { + @Result(id = true, column = "id", property = "id"), + @Result(column = "content", property = "content"), + @Result(column = "content_md5", property = "contentMd5"), + @Result(column = "type", property = "type"), + @Result(column = "owner", property = "owner"), + @Result(column = "source_vertex_uniq", property = "sourceVertexUniq"), + @Result(column = "target_vertex_uniq", property = "targetVertexUniq"), + @Result(column = "janus_edge_id", property = "janusEdgeId"), + @Result(column = "neo4j_edge_id", property = "neo4jEdgeId"), + @Result(column = "nebula_edge_id", property = "nebulaEdgeId"), + @Result(column = "source_janus_vertex_id", property = "sourceJanusVertexId"), + @Result(column = "target_janus_vertex_id", property = "targetJanusVertexId"), + @Result(column = "source_neo4j_vertex_id", property = "sourceNeo4jVertexId"), + @Result(column = "target_neo4j_vertex_id", property = "targetNeo4jVertexId"), + @Result(column = "source_nebula_vertex_id", property = "sourceNebulaVertexId"), + @Result(column = "target_nebula_vertex_id", property = "targetNebulaVertexId"), + @Result(column = "deleted", property = "deleted"), + @Result(column = "create_time", property = "createTime"), + @Result(column = "update_time", property = "updateTime") + }) + GraphEdgeDO getOneGraphEdgeDoBySourceTargetUniq( + @Param("sourceVertexUniq") String sourceVertexUniq, + @Param("targetVertexUniq") String targetVertexUniq); + + @Select( + "select id from graph_edge where (length(trim(content_md5))=0 or content_md5=MD5(#{content})) and source_vertex_uniq=#{sourceVertexUniq} and target_vertex_uniq=#{targetVertexUniq} and content=#{content} limit 1") + Long getGraphEdgeId( + @Param("content") String content, + @Param("sourceVertexUniq") String sourceVertexUniq, + @Param("targetVertexUniq") String targetVertexUniq); + + @Update("update graph_edge set content_md5=MD5(content) where id=#{id}") + void updateGraphEdgeContentMd5(@Param("id") Long id); + + @Update("update graph_edge set content_md5=MD5(content) where length(trim(content_md5))=0") + void updateAllGraphEdgeContentMd5(); + + @Update("update graph_edge set janus_edge_id=#{janusEdgeId} where id=#{id}") + void updateJanusEdgeId(@Param("id") Long id, @Param("janusEdgeId") String janusEdgeId); + + @Update( + "update graph_edge set janus_edge_id=#{janusEdgeId},source_janus_vertex_id=#{sourceJanusVertexId},target_janus_vertex_id=#{targetJanusVertexId} where id=#{id}") + void updateJanusEdgeIdAndSourceTargetVertexId( + @Param("id") Long id, + @Param("janusEdgeId") String janusEdgeId, + @Param("sourceJanusVertexId") Long sourceJanusVertexId, + @Param("targetJanusVertexId") Long targetJanusVertexId); + + @Update("update graph_edge set neo4j_edge_id=#{neo4jEdgeId} where id=#{id}") + void updateNeo4jEdgeId(@Param("id") Long id, @Param("neo4jEdgeId") Long neo4jEdgeId); + + @Update( + "update graph_edge set neo4j_edge_id=#{neo4jEdgeId},source_neo4j_vertex_id=#{sourceNeo4jVertexId},target_neo4j_vertex_id=#{targetNeo4jVertexId} where id=#{id}") + void updateNeo4jEdgeIdAndSourceTargetVertexId( + @Param("id") Long id, + @Param("neo4jEdgeId") Long neo4jEdgeId, + @Param("sourceNeo4jVertexId") Long sourceNeo4jVertexId, + @Param("targetNeo4jVertexId") Long targetNeo4jVertexId); + + @Update("update graph_edge set nebula_edge_id=#{nebulaEdgeId} where id=#{id}") + void updateNebulaEdgeId(@Param("id") Long id, @Param("nebulaEdgeId") String nebulaEdgeId); + + @Update( + "update graph_edge set nebula_edge_id=#{nebulaEdgeId},source_nebula_vertex_id=#{sourceNebulaVertexId},target_nebula_vertex_id=#{targetNebulaVertexId} where id=#{id}") + void updateNebulaEdgeIdAndSourceTargetVertexId( + @Param("id") Long id, + @Param("nebulaEdgeId") String nebulaEdgeId, + @Param("sourceNebulaVertexId") Long sourceNebulaVertexId, + @Param("targetNebulaVertexId") Long targetNebulaVertexId); + + @Update( + "update graph_edge set source_janus_vertex_id=#{sourceJanusVertexId} where source_vertex_uniq=#{sourceVertexUniq}") + void updateSourceJanusVertexId( + @Param("sourceVertexUniq") String sourceVertexUniq, + @Param("sourceJanusVertexId") Long sourceJanusVertexId); + + @Update( + "update graph_edge set target_janus_vertex_id=#{targetJanusVertexId} where target_vertex_uniq=#{targetVertexUniq}") + void updateTargetJanusVertexId( + @Param("targetVertexUniq") String targetVertexUniq, + @Param("targetJanusVertexId") Long targetJanusVertexId); + + @Update( + "update graph_edge a set source_janus_vertex_id=(select janus_vertex_id from graph_vertex b where a.source_vertex_uniq=b.uniq);") + void updateAllSourceJanusVertexId(); + + @Update( + "update graph_edge a set target_janus_vertex_id=(select janus_vertex_id from graph_vertex b where a.target_vertex_uniq=b.uniq);") + void updateAllTargetJanusVertexId(); + + @Update( + "update graph_edge a " + + "inner join graph_vertex b on a.source_vertex_uniq=b.uniq " + + "inner join graph_vertex c on a.target_vertex_uniq=c.uniq " + + "set a.source_janus_vertex_id=b.janus_vertex_id,a.target_janus_vertex_id=c.janus_vertex_id;") + void updateAllSourceTargetJanusVertexId(); + + @Update( + "update graph_edge set source_neo4j_vertex_id=#{sourceNeo4jVertexId} where source_vertex_uniq=#{sourceVertexUniq}") + void updateSourceNeo4jVertexId( + @Param("sourceVertexUniq") String sourceVertexUniq, + @Param("sourceNeo4jVertexId") Long sourceNeo4jVertexId); + + @Update( + "update graph_edge set target_neo4j_vertex_id=#{targetNeo4jVertexId} where target_vertex_uniq=#{targetVertexUniq}") + void updateTargetNeo4jVertexId( + @Param("targetVertexUniq") String targetVertexUniq, + @Param("targetNeo4jVertexId") Long targetNeo4jVertexId); + + @Update( + "update graph_edge a set source_neo4j_vertex_id=(select neo4j_vertex_id from graph_vertex b where a.source_vertex_uniq=b.uniq);") + void updateAllSourceNeo4jVertexId(); + + @Update( + "update graph_edge a set target_neo4j_vertex_id=(select neo4j_vertex_id from graph_vertex b where a.target_vertex_uniq=b.uniq);") + void updateAllTargetNeo4jVertexId(); + + @Update( + "update graph_edge a " + + "inner join graph_vertex b on a.source_vertex_uniq=b.uniq " + + "inner join graph_vertex c on a.target_vertex_uniq=c.uniq " + + "set a.source_neo4j_vertex_id=b.neo4j_vertex_id,a.target_neo4j_vertex_id=c.neo4j_vertex_id;") + void updateAllSourceTargetNeo4jVertexId(); + + @Update( + "update graph_edge set source_nebula_vertex_id=#{sourceNebulaVertexId} where source_vertex_uniq=#{sourceVertexUniq}") + void updateSourceNebulaVertexId( + @Param("sourceVertexUniq") String sourceVertexUniq, + @Param("sourceNebulaVertexId") Long sourceNebulaVertexId); + + @Update( + "update graph_edge set target_nebula_vertex_id=#{targetNebulaVertexId} where target_vertex_uniq=#{targetVertexUniq}") + void updateTargetNebulaVertexId( + @Param("targetVertexUniq") String targetVertexUniq, + @Param("targetNebulaVertexId") Long targetNebulaVertexId); + + @Update( + "update graph_edge a set source_nebula_vertex_id=(select nebula_vertex_id from graph_vertex b where a.source_vertex_uniq=b.uniq);") + void updateAllSourceNebulaVertexId(); + + @Update( + "update graph_edge a set target_nebula_vertex_id=(select nebula_vertex_id from graph_vertex b where a.target_vertex_uniq=b.uniq);") + void updateAllTargetNebulaVertexId(); + + @Update( + "update graph_edge a " + + "inner join graph_vertex b on a.source_vertex_uniq=b.uniq " + + "inner join graph_vertex c on a.target_vertex_uniq=c.uniq " + + "set a.source_nebula_vertex_id=b.nebula_vertex_id,a.target_nebula_vertex_id=c.nebula_vertex_id;") + void updateAllSourceTargetNebulaVertexId(); +} diff --git a/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/mapper/GraphVertexMapper.java b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/mapper/GraphVertexMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..7c8b0dd2fc39b622215313225b21cf9a5dcbcd7c --- /dev/null +++ b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/mapper/GraphVertexMapper.java @@ -0,0 +1,67 @@ +package org.studiox.graph.persistence.mybatis.tk.mapper; + +import org.apache.ibatis.annotations.*; +import org.studiox.graph.persistence.mybatis.tk.base.BaseGraphMapper; +import org.studiox.graph.persistence.mybatis.tk.domain.GraphVertexDO; + +import java.util.List; + +public interface GraphVertexMapper extends BaseGraphMapper { + + @Select("select id from graph_vertex") + List getGraphVertexIds(); + + @Select("select uniq from graph_vertex") + List getGraphVertexUniqs(); + + @Select("select id from graph_vertex where uniq=#{uniq}") + Long getGraphVertexIdByUniq(@Param("uniq") String uniq); + + @Select("select janus_vertex_id from graph_vertex where uniq=#{uniq}") + Long getJanusVertexIdByUniq(@Param("uniq") String uniq); + + @Select("select * from graph_vertex where uniq=#{uniq}") + @Results( + id = "graphVertexDoByUniqMap", + value = { + @Result(id = true, column = "id", property = "id"), + @Result(column = "uniq", property = "uniq"), + @Result(column = "type", property = "type"), + @Result(column = "owner", property = "owner"), + @Result(column = "janus_vertex_id", property = "janusVertexId"), + @Result(column = "neo4j_vertex_id", property = "neo4jVertexId"), + @Result(column = "nebula_vertex_id", property = "nebulaVertexId"), + @Result(column = "temp", property = "temp"), + @Result(column = "deleted", property = "deleted"), + @Result(column = "create_time", property = "createTime"), + @Result(column = "update_time", property = "updateTime") + }) + GraphVertexDO getGraphVertexDoByUniq(@Param("uniq") String uniq); + + @Select("select * from graph_vertex where uniq like #{uniqLikeStr}") + @Results( + id = "graphVertexDosByUniqLikeMap", + value = { + @Result(id = true, column = "id", property = "id"), + @Result(column = "uniq", property = "uniq"), + @Result(column = "type", property = "type"), + @Result(column = "owner", property = "owner"), + @Result(column = "janus_vertex_id", property = "janusVertexId"), + @Result(column = "neo4j_vertex_id", property = "neo4jVertexId"), + @Result(column = "nebula_vertex_id", property = "nebulaVertexId"), + @Result(column = "temp", property = "temp"), + @Result(column = "deleted", property = "deleted"), + @Result(column = "create_time", property = "createTime"), + @Result(column = "update_time", property = "updateTime") + }) + List getGraphVertexDosByUniqLike(@Param("uniqLikeStr") String uniqLikeStr); + + @Update("update graph_vertex set janus_vertex_id=#{janusVertexId} where id=#{id}") + void updateJanusVertexId(@Param("id") Long id, @Param("janusVertexId") Long janusVertexId); + + @Update("update graph_vertex set neo4j_vertex_id=#{neo4jVertexId} where id=#{id}") + void updateNeo4jVertexId(@Param("id") Long id, @Param("neo4jVertexId") Long neo4jVertexId); + + @Update("update graph_vertex set nebula_vertex_id=#{nebulaVertexId} where id=#{id}") + void updateNebulaVertexId(@Param("id") Long id, @Param("nebulaVertexId") Long nebulaVertexId); +} diff --git a/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/service/GraphDbService.java b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/service/GraphDbService.java new file mode 100644 index 0000000000000000000000000000000000000000..d288b2c8369e080eb267caf86cb076932b44e36c --- /dev/null +++ b/graph-persistence/persistence-mybatis-tk/src/main/java/org/studiox/graph/persistence/mybatis/tk/service/GraphDbService.java @@ -0,0 +1,489 @@ +package org.studiox.graph.persistence.mybatis.tk.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.studiox.graph.persistence.mybatis.tk.dao.GraphEdgeDao; +import org.studiox.graph.persistence.mybatis.tk.dao.GraphVertexDao; +import org.studiox.graph.persistence.mybatis.tk.domain.GraphEdgeDO; +import org.studiox.graph.persistence.mybatis.tk.domain.GraphVertexDO; + +import java.util.Collections; +import java.util.List; + +@Service +public class GraphDbService { + + private static final Logger LOGGER = LoggerFactory.getLogger(GraphDbService.class); + + @Autowired private GraphVertexDao graphVertexDao; + + @Autowired private GraphEdgeDao graphEdgeDao; + + public Integer getGraphVertexSize() { + try { + return graphVertexDao.getGraphVertexSize(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return null; + } + + public List getGraphVertexIds() { + try { + return graphVertexDao.getGraphVertexIds(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return Collections.emptyList(); + } + + public List getGraphVertexUniqs() { + try { + return graphVertexDao.getGraphVertexUniqs(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return Collections.emptyList(); + } + + public Long getGraphVertexIdByUniq(String uniq) { + try { + return graphVertexDao.getGraphVertexIdByUniq(uniq); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return null; + } + + public Long getJanusVertexIdByUniq(String uniq) { + try { + return graphVertexDao.getJanusVertexIdByUniq(uniq); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return null; + } + + public GraphVertexDO getGraphVertexDoById(Long dbId) { + try { + return graphVertexDao.selectByPrimaryKey(dbId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return null; + } + + public GraphVertexDO getGraphVertexDoByUniq(String uniq) { + try { + return graphVertexDao.getGraphVertexDoByUniq(uniq); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return null; + } + + public List getGraphVertexDosByUniqLike(String likeUniqStr) { + try { + return graphVertexDao.getGraphVertexDosByUniqLike(likeUniqStr); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return Collections.emptyList(); + } + + public void insertGraphVertex(GraphVertexDO graphVertexDO) { + try { + graphVertexDao.insertSelective(graphVertexDO); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateGraphVertex(GraphVertexDO graphVertexDO) { + try { + graphVertexDao.updateByPrimaryKeySelective(graphVertexDO); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateJanusVertexId(Long id, Long janusVertexId) { + try { + graphVertexDao.updateJanusVertexId(id, janusVertexId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateNeo4jVertexId(Long id, Long neo4jVertexId) { + try { + graphVertexDao.updateNeo4jVertexId(id, neo4jVertexId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateNebulaVertexId(Long id, Long nebulaVertexId) { + try { + graphVertexDao.updateNebulaVertexId(id, nebulaVertexId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void deleteGraphVertexByDbId(Long dbId) { + try { + graphVertexDao.deleteGraphVertexByDbId(dbId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public Integer getGraphEdgeSize() { + try { + return graphEdgeDao.getGraphEdgeSize(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return null; + } + + public List getGraphEdgeIds() { + try { + return graphEdgeDao.getGraphEdgeIds(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return Collections.emptyList(); + } + + public List getGraphUniqueMinEdgeIds() { + try { + return graphEdgeDao.getGraphUniqueMinEdgeIds(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return Collections.emptyList(); + } + + public List getGraphUniqueMaxEdgeIds() { + try { + return graphEdgeDao.getGraphUniqueMaxEdgeIds(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return Collections.emptyList(); + } + + public Long getGraphEdgeContentSize(String sourceVertexUniq, String targetVertexUniq) { + try { + return graphEdgeDao.getGraphEdgeContentSize(sourceVertexUniq, targetVertexUniq); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return null; + } + + public List getGraphEdgeContents(String sourceVertexUniq, String targetVertexUniq) { + try { + return graphEdgeDao.getGraphEdgeContents(sourceVertexUniq, targetVertexUniq); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return Collections.emptyList(); + } + + public List getGraphEdgeDOs(GraphEdgeDO graphEdgeDO) { + try { + return graphEdgeDao.getGraphEdgeDOs(graphEdgeDO); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return Collections.emptyList(); + } + + public GraphEdgeDO getOneGraphEdgeDoBySourceTargetUniq( + String sourceVertexUniq, String targetVertexUniq) { + try { + return graphEdgeDao.getOneGraphEdgeDoBySourceTargetUniq(sourceVertexUniq, targetVertexUniq); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return null; + } + + public Long getGraphEdgeId(String content, String sourceVertexUniq, String targetVertexUniq) { + try { + return graphEdgeDao.getGraphEdgeId(content, sourceVertexUniq, targetVertexUniq); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return null; + } + + public GraphEdgeDO getGraphEdgeDoById(Long dbId) { + try { + return graphEdgeDao.selectByPrimaryKey(dbId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + return null; + } + + public void insertGraphEdge(GraphEdgeDO graphEdgeDO) { + try { + graphEdgeDao.insertSelective(graphEdgeDO); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateGraphEdge(GraphEdgeDO graphEdgeDO) { + try { + graphEdgeDao.updateByPrimaryKeySelective(graphEdgeDO); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateGraphEdgeContentMd5(Long dbId) { + try { + graphEdgeDao.updateGraphEdgeContentMd5(dbId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateAllGraphEdgeContentMd5() { + try { + graphEdgeDao.updateAllGraphEdgeContentMd5(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateJanusEdgeId(Long id, String janusEdgeId) { + try { + graphEdgeDao.updateJanusEdgeId(id, janusEdgeId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateJanusEdgeIdAndSourceTargetVertexId( + Long id, String janusEdgeId, Long sourceJanusVertexId, Long targetJanusVertexId) { + try { + graphEdgeDao.updateJanusEdgeIdAndSourceTargetVertexId( + id, janusEdgeId, sourceJanusVertexId, targetJanusVertexId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateNeo4jEdgeId(Long id, Long neo4jEdgeId) { + try { + graphEdgeDao.updateNeo4jEdgeId(id, neo4jEdgeId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateNeo4jEdgeIdAndSourceTargetVertexId( + Long id, Long neo4jEdgeId, Long sourceNeo4jVertexId, Long targetNeo4jVertexId) { + try { + graphEdgeDao.updateNeo4jEdgeIdAndSourceTargetVertexId( + id, neo4jEdgeId, sourceNeo4jVertexId, targetNeo4jVertexId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateNebulaEdgeId(Long id, String nebulaEdgeId) { + try { + graphEdgeDao.updateNebulaEdgeId(id, nebulaEdgeId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateNebulaEdgeIdAndSourceTargetVertexId( + Long id, String nebulaEdgeId, Long sourceNebulaVertexId, Long targetNebulaVertexId) { + try { + graphEdgeDao.updateNebulaEdgeIdAndSourceTargetVertexId( + id, nebulaEdgeId, sourceNebulaVertexId, targetNebulaVertexId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateSourceJanusVertexId(String sourceVertexUniq, Long sourceJanusVertexId) { + try { + graphEdgeDao.updateSourceJanusVertexId(sourceVertexUniq, sourceJanusVertexId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateTargetJanusVertexId(String sourceVertexUniq, Long targetJanusVertexId) { + try { + graphEdgeDao.updateTargetJanusVertexId(sourceVertexUniq, targetJanusVertexId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateAllSourceJanusVertexId() { + try { + graphEdgeDao.updateAllSourceJanusVertexId(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateAllTargetJanusVertexId() { + try { + graphEdgeDao.updateAllTargetJanusVertexId(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateAllSourceTargetJanusVertexId() { + try { + graphEdgeDao.updateAllSourceTargetJanusVertexId(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateSourceNeo4jVertexId(String sourceVertexUniq, Long sourceNeo4jVertexId) { + try { + graphEdgeDao.updateSourceNeo4jVertexId(sourceVertexUniq, sourceNeo4jVertexId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateTargetNeo4jVertexId(String sourceVertexUniq, Long targetNeo4jVertexId) { + try { + graphEdgeDao.updateTargetNeo4jVertexId(sourceVertexUniq, targetNeo4jVertexId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateAllSourceNeo4jVertexId() { + try { + graphEdgeDao.updateAllSourceNeo4jVertexId(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateAllTargetNeo4jVertexId() { + try { + graphEdgeDao.updateAllTargetNeo4jVertexId(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateAllSourceTargetNeo4jVertexId() { + try { + graphEdgeDao.updateAllSourceTargetNeo4jVertexId(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateSourceNebulaVertexId(String sourceVertexUniq, Long sourceNebulaVertexId) { + try { + graphEdgeDao.updateSourceNebulaVertexId(sourceVertexUniq, sourceNebulaVertexId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateTargetNebulaVertexId(String sourceVertexUniq, Long targetNebulaVertexId) { + try { + graphEdgeDao.updateTargetNebulaVertexId(sourceVertexUniq, targetNebulaVertexId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateAllSourceNebulaVertexId() { + try { + graphEdgeDao.updateAllSourceNebulaVertexId(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateAllTargetNebulaVertexId() { + try { + graphEdgeDao.updateAllTargetNebulaVertexId(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateAllSourceTargetNebulaVertexId() { + try { + graphEdgeDao.updateAllSourceTargetNebulaVertexId(); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateGraphVertexDbUniq(String oldUniq, String newUniq) { + try { + GraphVertexDO graphVertexDO = graphVertexDao.getGraphVertexDoByUniq(oldUniq); + if (null != graphVertexDO) { + graphVertexDO.setUniq(newUniq); + graphVertexDao.updateByPrimaryKeySelective(graphVertexDO); + } + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void updateGraphEdgeDbSourceTargetUniq(String oldUniq, String newUniq) { + GraphEdgeDO sourceGraphEdgeDO = new GraphEdgeDO(); + sourceGraphEdgeDO.setSourceVertexUniq(oldUniq); + try { + List graphEdgeDos = graphEdgeDao.select(sourceGraphEdgeDO); + if (null != graphEdgeDos) { + for (GraphEdgeDO obj : graphEdgeDos) { + obj.setSourceVertexUniq(newUniq); + graphEdgeDao.updateByPrimaryKeySelective(obj); + } + } + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + GraphEdgeDO targetGraphEdgeDO = new GraphEdgeDO(); + targetGraphEdgeDO.setTargetVertexUniq(oldUniq); + try { + List graphEdgeDos = graphEdgeDao.select(targetGraphEdgeDO); + if (null != graphEdgeDos) { + for (GraphEdgeDO obj : graphEdgeDos) { + obj.setTargetVertexUniq(newUniq); + graphEdgeDao.updateByPrimaryKeySelective(obj); + } + } + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } + + public void deleteGraphEdgeByDbId(Long dbId) { + try { + graphEdgeDao.deleteGraphEdgeByDbId(dbId); + } catch (Throwable t) { + LOGGER.error(t.getMessage(), t); + } + } +} diff --git a/graph-persistence/persistence-mybatis-tk/src/test/java/org/studiox/graph/persistence/mybatis/tk/TkMybatisSpringTest.java b/graph-persistence/persistence-mybatis-tk/src/test/java/org/studiox/graph/persistence/mybatis/tk/TkMybatisSpringTest.java new file mode 100644 index 0000000000000000000000000000000000000000..41da44904d3c4bf8ef09ff9c3297ad166ef0e0a8 --- /dev/null +++ b/graph-persistence/persistence-mybatis-tk/src/test/java/org/studiox/graph/persistence/mybatis/tk/TkMybatisSpringTest.java @@ -0,0 +1,271 @@ +package org.studiox.graph.persistence.mybatis.tk; + +import ch.vorburger.exec.ManagedProcessException; +import ch.vorburger.mariadb4j.DB; +import ch.vorburger.mariadb4j.DBConfigurationBuilder; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.studiox.graph.persistence.mybatis.tk.domain.GraphEdgeDO; +import org.studiox.graph.persistence.mybatis.tk.domain.GraphVertexDO; +import org.studiox.graph.persistence.mybatis.tk.service.GraphDbService; + +import java.util.List; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = TkMybatisSpring.class) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class TkMybatisSpringTest { + + private static DB db; + + @Autowired private GraphDbService graphDbService; + + @BeforeAll + public void beforeAll() { + DBConfigurationBuilder config = DBConfigurationBuilder.newBuilder(); + config.setPort(13306); + config.setDeletingTemporaryBaseAndDataDirsOnShutdown(true); + try { + db = DB.newEmbeddedDB(config.build()); + db.start(); + db.source("mysql_ddl.sql", "test"); + } catch (ManagedProcessException e) { + e.printStackTrace(); + } + } + + @AfterAll + public void afterAll() { + if (null != db) { + try { + db.stop(); + } catch (ManagedProcessException e) { + e.printStackTrace(); + } + } + } + + private void clean() { + List graphVertexIds = graphDbService.getGraphVertexIds(); + if (null != graphVertexIds && graphVertexIds.size() > 0) { + for (Long graphVertexId : graphVertexIds) { + graphDbService.deleteGraphVertexByDbId(graphVertexId); + } + } + List graphEdgeIds = graphDbService.getGraphEdgeIds(); + if (null != graphEdgeIds && graphEdgeIds.size() > 0) { + for (Long graphEdgeId : graphEdgeIds) { + graphDbService.deleteGraphEdgeByDbId(graphEdgeId); + } + } + } + + @BeforeEach + public void beforeEach() { + clean(); + } + + @AfterEach + public void afterEach() { + clean(); + } + + @Test + @Order(1) + @DisplayName("test1") + public void test1() { + GraphVertexDO graphVertexDO1 = new GraphVertexDO(); + graphVertexDO1.setUniq("uniq1"); + graphVertexDO1.setType("type1"); + graphVertexDO1.setOwner("owner1"); + graphVertexDO1.setJanusVertexId(1L); + graphVertexDO1.setTemp(true); + graphVertexDO1.setDeleted(false); + graphDbService.insertGraphVertex(graphVertexDO1); + + Integer count = graphDbService.getGraphVertexSize(); + Assertions.assertEquals(1, count); + + Assertions.assertEquals(1L, graphDbService.getJanusVertexIdByUniq("uniq1")); + + List graphVertexIds = graphDbService.getGraphVertexIds(); + Long graphVertexId = graphVertexIds.get(0); + GraphVertexDO graphVertexDO2 = graphDbService.getGraphVertexDoById(graphVertexId); + graphVertexDO2.setUniq("uniq2"); + graphDbService.updateGraphVertex(graphVertexDO2); + + GraphVertexDO graphVertexDO3 = graphDbService.getGraphVertexDoById(graphVertexId); + Assertions.assertEquals("uniq2", graphVertexDO3.getUniq()); + Assertions.assertEquals(true, graphVertexDO3.getTemp()); + Assertions.assertEquals(false, graphVertexDO3.getDeleted()); + } + + @Test + @Order(2) + @DisplayName("test2") + public void test2() { + GraphEdgeDO graphEdgeDO1 = new GraphEdgeDO(); + graphEdgeDO1.setContent("select * from sys.version"); + graphEdgeDO1.setType("typq1"); + graphEdgeDO1.setOwner("owner1"); + graphEdgeDO1.setSourceVertexUniq("uniq1"); + graphEdgeDO1.setTargetVertexUniq("uniq2"); + graphEdgeDO1.setJanusEdgeId("edge1"); + graphEdgeDO1.setSourceJanusVertexId(1L); + graphEdgeDO1.setTargetJanusVertexId(1L); + graphDbService.insertGraphEdge(graphEdgeDO1); + + Integer count = graphDbService.getGraphEdgeSize(); + Assertions.assertEquals(1, count); + + Assertions.assertEquals(1L, graphDbService.getGraphEdgeContentSize("uniq1", "uniq2")); + + List graphEdgeIds = graphDbService.getGraphEdgeIds(); + Long graphEdgeId = graphEdgeIds.get(0); + GraphEdgeDO graphEdgeDO2 = graphDbService.getGraphEdgeDoById(graphEdgeId); + graphEdgeDO2.setContent("select 1 from sys.version"); + graphDbService.updateGraphEdge(graphEdgeDO2); + + graphDbService.updateAllGraphEdgeContentMd5(); + + GraphEdgeDO graphEdgeDO3 = graphDbService.getGraphEdgeDoById(graphEdgeId); + Assertions.assertEquals("select 1 from sys.version", graphEdgeDO3.getContent()); + + GraphEdgeDO graphEdgeDO4 = new GraphEdgeDO(); + graphEdgeDO4.setType("typq1"); + List graphEdgeDOs = graphDbService.getGraphEdgeDOs(graphEdgeDO4); + Assertions.assertEquals(1, graphEdgeDOs.size()); + Assertions.assertEquals("select 1 from sys.version", graphEdgeDOs.get(0).getContent()); + } + + @Test + @Order(3) + @DisplayName("test3") + public void test3() { + GraphVertexDO graphVertexDO1 = new GraphVertexDO(); + graphVertexDO1.setUniq("ENTITY_TYPE_TABLE#test1.table1@service1"); + graphDbService.insertGraphVertex(graphVertexDO1); + + Long vertexId = + graphDbService.getGraphVertexIdByUniq("ENTITY_TYPE_TABLE#test1.table1@service1"); + Assertions.assertNotNull(vertexId); + + GraphVertexDO graphVertexDO2 = new GraphVertexDO(); + graphVertexDO2.setUniq("ENTITY_TYPE_TABLE#test1.table2@service1"); + graphDbService.insertGraphVertex(graphVertexDO2); + List vertexIds = graphDbService.getGraphVertexIds(); + Assertions.assertEquals(2, vertexIds.size()); + + graphDbService.updateJanusVertexId(vertexId, 111L); + graphDbService.updateNeo4jVertexId(vertexId, 222L); + graphDbService.updateNebulaVertexId(vertexId, 333L); + + GraphVertexDO graphVertexDO3 = graphDbService.getGraphVertexDoById(vertexId); + Assertions.assertNotNull(graphVertexDO3); + Assertions.assertEquals(111L, graphVertexDO3.getJanusVertexId().longValue()); + Assertions.assertEquals(222L, graphVertexDO3.getNeo4jVertexId().longValue()); + Assertions.assertEquals(333L, graphVertexDO3.getNebulaVertexId().longValue()); + + List graphVertexDOS = + graphDbService.getGraphVertexDosByUniqLike("ENTITY_TYPE_TABLE#%@service1"); + Assertions.assertNotNull(graphVertexDOS); + Assertions.assertEquals(2, graphVertexDOS.size()); + + GraphEdgeDO graphEdgeDO1 = new GraphEdgeDO(); + graphEdgeDO1.setContent("content1"); + graphEdgeDO1.setSourceVertexUniq("ENTITY_TYPE_TABLE#test1.table1@service1"); + graphEdgeDO1.setTargetVertexUniq("ENTITY_TYPE_TABLE#test1.table2@service1"); + graphDbService.insertGraphEdge(graphEdgeDO1); + + Long edgeId1 = + graphDbService.getGraphEdgeId( + "content1", + "ENTITY_TYPE_TABLE#test1.table1@service1", + "ENTITY_TYPE_TABLE#test1.table2@service1"); + Assertions.assertNotNull(edgeId1); + Long edgeId2 = + graphDbService.getGraphEdgeId( + "content2", + "ENTITY_TYPE_TABLE#test1.table1@service1", + "ENTITY_TYPE_TABLE#test1.table2@service1"); + Assertions.assertNull(edgeId2); + graphDbService.updateGraphEdgeContentMd5(edgeId1); + edgeId1 = + graphDbService.getGraphEdgeId( + "content1", + "ENTITY_TYPE_TABLE#test1.table1@service1", + "ENTITY_TYPE_TABLE#test1.table2@service1"); + Assertions.assertNotNull(edgeId1); + + List edgeIds = graphDbService.getGraphEdgeIds(); + Assertions.assertEquals(1, edgeIds.size()); + graphEdgeDO1 = graphDbService.getGraphEdgeDoById(edgeIds.get(0)); + Long edgeId = graphEdgeDO1.getId(); + graphDbService.updateJanusEdgeId(edgeId, "444"); + graphDbService.updateJanusEdgeIdAndSourceTargetVertexId( + edgeId, + "444", + graphDbService.getJanusVertexIdByUniq("ENTITY_TYPE_TABLE#test1.table1@service1"), + graphDbService.getJanusVertexIdByUniq("ENTITY_TYPE_TABLE#test1.table2@service1")); + graphDbService.updateNeo4jEdgeId(edgeId, 555L); + graphDbService.updateNeo4jEdgeIdAndSourceTargetVertexId(edgeId, 555L, null, null); + graphDbService.updateNebulaEdgeId(edgeId, "666"); + graphDbService.updateNebulaEdgeIdAndSourceTargetVertexId(edgeId, "666", null, null); + + graphEdgeDO1 = graphDbService.getGraphEdgeDoById(edgeId); + Assertions.assertEquals("444", graphEdgeDO1.getJanusEdgeId()); + Assertions.assertEquals(555L, graphEdgeDO1.getNeo4jEdgeId().longValue()); + Assertions.assertEquals("666", graphEdgeDO1.getNebulaEdgeId()); + + // For H2 Test + graphDbService.updateAllSourceTargetJanusVertexId(); + graphEdgeDO1 = graphDbService.getGraphEdgeDoById(edgeId); + Assertions.assertEquals( + graphEdgeDO1.getSourceJanusVertexId(), + graphDbService + .getGraphVertexDoByUniq(graphEdgeDO1.getSourceVertexUniq()) + .getJanusVertexId()); + Assertions.assertEquals( + graphEdgeDO1.getTargetJanusVertexId(), + graphDbService + .getGraphVertexDoByUniq(graphEdgeDO1.getTargetVertexUniq()) + .getJanusVertexId()); + graphDbService.updateAllSourceTargetNeo4jVertexId(); + graphDbService.updateAllSourceTargetNebulaVertexId(); + + GraphEdgeDO graphEdgeDO2 = new GraphEdgeDO(); + graphEdgeDO2.setContent("content2"); + graphEdgeDO2.setSourceVertexUniq("ENTITY_TYPE_TABLE#test1.table2@service1"); + graphEdgeDO2.setTargetVertexUniq("ENTITY_TYPE_TABLE#test1.table3@service1"); + graphDbService.insertGraphEdge(graphEdgeDO2); + + graphDbService.updateGraphVertexDbUniq( + "ENTITY_TYPE_TABLE#test1.table2@service1", "ENTITY_TYPE_TABLE#test1.table4@service1"); + + vertexId = graphDbService.getGraphVertexIdByUniq("ENTITY_TYPE_TABLE#test1.table2@service1"); + Assertions.assertNull(vertexId); + vertexId = graphDbService.getGraphVertexIdByUniq("ENTITY_TYPE_TABLE#test1.table4@service1"); + Assertions.assertNotNull(vertexId); + + graphDbService.updateGraphEdgeDbSourceTargetUniq( + "ENTITY_TYPE_TABLE#test1.table2@service1", "ENTITY_TYPE_TABLE#test1.table4@service1"); + + List graphEdgeDBIds = graphDbService.getGraphEdgeIds(); + Assertions.assertNotNull(graphEdgeDBIds); + for (Long graphEdgeDBId : graphEdgeDBIds) { + GraphEdgeDO graphEdgeDO = graphDbService.getGraphEdgeDoById(graphEdgeDBId); + if (graphEdgeDO.getContent().equals("content1")) { + Assertions.assertEquals( + "ENTITY_TYPE_TABLE#test1.table4@service1", graphEdgeDO.getTargetVertexUniq()); + } + if (graphEdgeDO.getContent().equals("content2")) { + Assertions.assertEquals( + "ENTITY_TYPE_TABLE#test1.table4@service1", graphEdgeDO.getSourceVertexUniq()); + } + } + } +} diff --git a/graph-persistence/persistence-mybatis-tk/src/test/resources/application.yml b/graph-persistence/persistence-mybatis-tk/src/test/resources/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..47fc76abe96399d9beaff1deea17a450f18b32d2 --- /dev/null +++ b/graph-persistence/persistence-mybatis-tk/src/test/resources/application.yml @@ -0,0 +1,15 @@ +spring: + main: + lazy-initialization: false + datasource: + url: jdbc:mysql://localhost:13306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai + username: root + password: + driver-class-name: com.mysql.jdbc.Driver + hikari: + pool-name: HikariCP + minimum-idle: 2 + maximum-pool-size: 5 + connection-test-query: SELECT 1 + max-lifetime: 1800000 + connection-timeout: 30000 diff --git a/graph-persistence/persistence-mybatis-tk/src/test/resources/mysql_ddl.sql b/graph-persistence/persistence-mybatis-tk/src/test/resources/mysql_ddl.sql new file mode 100644 index 0000000000000000000000000000000000000000..48cc95eeb62d757e9fe145bcf22fb758fd4eb86c --- /dev/null +++ b/graph-persistence/persistence-mybatis-tk/src/test/resources/mysql_ddl.sql @@ -0,0 +1,53 @@ +create table if not exists `graph_vertex` ( + `id` bigint(20) unsigned not null auto_increment comment '主键', + `uniq` varchar(255) not null default '' comment '顶点的唯一标识', + `type` varchar(20) not null default '' comment '顶点的类型', + `owner` varchar(50) not null default '' comment '所有者', + `janus_vertex_id` bigint(20) not null default 0 comment '顶点存储 JanusGraph Vertex Id', + `neo4j_vertex_id` bigint(20) not null default 0 comment '顶点存储 Neo4j Vertex Id', + `nebula_vertex_id` bigint(20) not null default 0 comment '顶点存储 NebulaGraph Vertex Id', + `temp` tinyint(1) not null default 0 comment '是否临时点(0:否 1:是),只存在数据库,不存在图中', + `deleted` tinyint(1) not null default 0 comment '是否删除(0:否 1:是)', + `create_time` timestamp not null default current_timestamp comment '顶点的创建时间', + `update_time` timestamp not null default current_timestamp comment '顶点的修改时间', + primary key (`id`), + unique key (`uniq`), + index (`janus_vertex_id`), + index (`neo4j_vertex_id`), + index (`nebula_vertex_id`) +) engine=innodb auto_increment=1 default charset=utf8 comment='图中顶点集合'; + +create table if not exists `graph_edge` ( + `id` bigint(20) unsigned not null auto_increment comment '主键', + `content` mediumtext not null comment '边的记录内容', + `content_md5` char(32) not null default '' comment '边的记录内容MD5值', + `type` varchar(20) not null default '' comment '边的类型', + `owner` varchar(50) not null default '' comment '所有者', + `source_vertex_uniq` varchar(255) not null default '' comment '边的输入顶点的唯一标识', + `target_vertex_uniq` varchar(255) not null default '' comment '边的输出顶点的唯一标识', + `janus_edge_id` varchar(50) not null default '' comment '边存储 JanusGraph Edge Id', + `neo4j_edge_id` bigint(20) not null default 0 comment '边存储 Neo4j Edge Id', + `nebula_edge_id` varchar(50) not null default '' comment '边存储 NebulaGraph Edge Id', + `source_janus_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 JanusGraph Vertex Id', + `target_janus_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 JanusGraph Vertex Id', + `source_neo4j_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 Neo4j Vertex Id', + `target_neo4j_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 Neo4j Vertex Id', + `source_nebula_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 NebulaGraph Vertex Id', + `target_nebula_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 NebulaGraph Vertex Id', + `deleted` tinyint(1) not null default 0 comment '是否删除(0:否 1:是)', + `create_time` timestamp not null default current_timestamp comment '边的创建时间', + `update_time` timestamp not null default current_timestamp comment '边的修改时间', + primary key (`id`), + index (`content_md5`), + index (`source_vertex_uniq`), + index (`target_vertex_uniq`), + index (`janus_edge_id`), + index (`neo4j_edge_id`), + index (`nebula_edge_id`), + index (`source_janus_vertex_id`), + index (`target_janus_vertex_id`), + index (`source_neo4j_vertex_id`), + index (`target_neo4j_vertex_id`), + index (`source_nebula_vertex_id`), + index (`target_nebula_vertex_id`) +) engine=innodb auto_increment=1 default charset=utf8 comment='图中边集合'; diff --git a/graph-persistence/persistence-mybatis/pom.xml b/graph-persistence/persistence-mybatis/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..ded18ee9900f38b779aadbca6c28c4b00eaa3e26 --- /dev/null +++ b/graph-persistence/persistence-mybatis/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + org.studiox.graph + graph-persistence + 1.0.5-SNAPSHOT + + org.studiox.graph.persistence + persistence-mybatis + 1.0.5-SNAPSHOT + jar + + + junit + junit + test + + + com.h2database + h2 + test + + + mysql + mysql-connector-java + test + + + org.studiox.graph.persistence + persistence-model + + + org.mybatis + mybatis + + + diff --git a/graph-persistence/persistence-mybatis/src/main/java/org/studiox/graph/persistence/mybatis/dao/GraphEdgeDao.java b/graph-persistence/persistence-mybatis/src/main/java/org/studiox/graph/persistence/mybatis/dao/GraphEdgeDao.java new file mode 100644 index 0000000000000000000000000000000000000000..3ad1f5deac99fee35d2b1b7616d9f296aac99932 --- /dev/null +++ b/graph-persistence/persistence-mybatis/src/main/java/org/studiox/graph/persistence/mybatis/dao/GraphEdgeDao.java @@ -0,0 +1,54 @@ +package org.studiox.graph.persistence.mybatis.dao; + +import org.apache.ibatis.annotations.*; +import org.studiox.graph.persistence.model.GraphEdgeDO; + +import java.util.List; + +public interface GraphEdgeDao { + + @Select("select * from graph_edge") + @Results( + id = "graphEdgeDOMap", + value = { + @Result(id = true, column = "id", property = "id"), + @Result(column = "content", property = "content"), + @Result(column = "content_md5", property = "contentMd5"), + @Result(column = "type", property = "type"), + @Result(column = "owner", property = "owner"), + @Result(column = "source_vertex_uniq", property = "sourceVertexUniq"), + @Result(column = "target_vertex_uniq", property = "targetVertexUniq"), + @Result(column = "janus_edge_id", property = "janusEdgeId"), + @Result(column = "neo4j_edge_id", property = "neo4jEdgeId"), + @Result(column = "nebula_edge_id", property = "nebulaEdgeId"), + @Result(column = "source_janus_vertex_id", property = "sourceJanusVertexId"), + @Result(column = "target_janus_vertex_id", property = "targetJanusVertexId"), + @Result(column = "source_neo4j_vertex_id", property = "sourceNeo4jVertexId"), + @Result(column = "target_neo4j_vertex_id", property = "targetNeo4jVertexId"), + @Result(column = "source_nebula_vertex_id", property = "sourceNebulaVertexId"), + @Result(column = "target_nebula_vertex_id", property = "targetNebulaVertexId"), + @Result(column = "deleted", property = "deleted"), + @Result(column = "create_time", property = "createTime"), + @Result(column = "update_time", property = "updateTime") + }) + List findAll(); + + @Insert( + "insert into graph_edge(content,type,owner,source_vertex_uniq,target_vertex_uniq,janus_edge_id,source_janus_vertex_id,target_janus_vertex_id) " + + "values (#{content},#{type},#{owner},#{sourceVertexUniq},#{targetVertexUniq},#{janusEdgeId},#{sourceJanusVertexId},#{targetJanusVertexId})") + void save(GraphEdgeDO graphEdgeDO); + + @Update( + "update graph_edge set content=#{content},type=#{type},owner=#{owner},source_vertex_uniq=#{sourceVertexUniq},target_vertex_uniq=#{targetVertexUniq},janus_edge_id=#{janusEdgeId}," + + "source_janus_vertex_id=#{sourceJanusVertexId},target_janus_vertex_id=#{targetJanusVertexId},deleted=#{deleted},update_time=#{updateTime} where id=#{id}") + void update(GraphEdgeDO graphEdgeDO); + + @Delete("delete from graph_edge where id=#{id}") + void delete(@Param("id") Long id); + + @Select("select * from graph_edge where id=#{id}") + GraphEdgeDO findById(@Param("id") Long id); + + @Select("select count(*) from graph_edge") + int findTotalNum(); +} diff --git a/graph-persistence/persistence-mybatis/src/main/java/org/studiox/graph/persistence/mybatis/dao/GraphVertexDao.java b/graph-persistence/persistence-mybatis/src/main/java/org/studiox/graph/persistence/mybatis/dao/GraphVertexDao.java new file mode 100644 index 0000000000000000000000000000000000000000..5c17d4cad7beae4ebdcde83fd1d4165213bcff7b --- /dev/null +++ b/graph-persistence/persistence-mybatis/src/main/java/org/studiox/graph/persistence/mybatis/dao/GraphVertexDao.java @@ -0,0 +1,46 @@ +package org.studiox.graph.persistence.mybatis.dao; + +import org.apache.ibatis.annotations.*; +import org.studiox.graph.persistence.model.GraphVertexDO; + +import java.util.List; + +public interface GraphVertexDao { + + @Select("select * from graph_vertex") + @Results( + id = "graphVertexDOMap", + value = { + @Result(id = true, column = "id", property = "id"), + @Result(column = "uniq", property = "uniq"), + @Result(column = "type", property = "type"), + @Result(column = "owner", property = "owner"), + @Result(column = "janus_vertex_id", property = "janusVertexId"), + @Result(column = "neo4j_vertex_id", property = "neo4jVertexId"), + @Result(column = "nebula_vertex_id", property = "nebulaVertexId"), + @Result(column = "temp", property = "temp"), + @Result(column = "deleted", property = "deleted"), + @Result(column = "create_time", property = "createTime"), + @Result(column = "update_time", property = "updateTime") + }) + List findAll(); + + @Insert( + "insert into graph_vertex(uniq,type,owner,janus_vertex_id,temp) " + + "values (#{uniq},#{type},#{owner},#{janusVertexId},#{temp})") + void save(GraphVertexDO graphVertexDO); + + @Update( + "update graph_vertex set uniq=#{uniq},type=#{type},owner=#{owner},janus_vertex_id=#{janusVertexId}," + + "temp=#{temp},deleted=#{deleted},update_time=#{updateTime} where id=#{id}") + void update(GraphVertexDO graphVertexDO); + + @Delete("delete from graph_vertex where id=#{id}") + void delete(@Param("id") Long id); + + @Select("select * from graph_vertex where id=#{id}") + GraphVertexDO findById(@Param("id") Long id); + + @Select("select count(*) from graph_vertex") + int findTotalNum(); +} diff --git a/graph-persistence/persistence-mybatis/src/test/java/org/studiox/graph/persistence/mybatis/test/MybatisTest.java b/graph-persistence/persistence-mybatis/src/test/java/org/studiox/graph/persistence/mybatis/test/MybatisTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1f129679169c8f521c17ae4801d1564a221b3dca --- /dev/null +++ b/graph-persistence/persistence-mybatis/src/test/java/org/studiox/graph/persistence/mybatis/test/MybatisTest.java @@ -0,0 +1,138 @@ +package org.studiox.graph.persistence.mybatis.test; + +import org.apache.ibatis.io.Resources; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.junit.*; +import org.junit.runners.MethodSorters; +import org.studiox.graph.persistence.model.GraphEdgeDO; +import org.studiox.graph.persistence.model.GraphVertexDO; +import org.studiox.graph.persistence.mybatis.dao.GraphEdgeDao; +import org.studiox.graph.persistence.mybatis.dao.GraphVertexDao; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class MybatisTest { + + private static SqlSession session = null; + + private static GraphVertexDao graphVertexDao; + + private static GraphEdgeDao graphEdgeDao; + + @BeforeClass + public static void init() { + InputStream inputStream = null; + try { + inputStream = Resources.getResourceAsStream("mybatis-config.xml"); + SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream); + session = factory.openSession(); + graphVertexDao = session.getMapper(GraphVertexDao.class); + graphEdgeDao = session.getMapper(GraphEdgeDao.class); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (null != inputStream) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + @AfterClass + public static void clean() { + if (null != session) { + session.commit(); + session.close(); + } + } + + @Test + public void test1() { + List graphVertexDOs = graphVertexDao.findAll(); + if (null != graphVertexDOs && graphVertexDOs.size() > 0) { + for (GraphVertexDO graphVertexDO : graphVertexDOs) { + graphVertexDao.delete(graphVertexDO.getId()); + } + } + session.commit(); + } + + @Test + public void test2() { + GraphVertexDO graphVertexDO1 = new GraphVertexDO(); + graphVertexDO1.setUniq("uniq1"); + graphVertexDO1.setType("type1"); + graphVertexDO1.setOwner("owner1"); + graphVertexDO1.setJanusVertexId(1L); + graphVertexDO1.setTemp(true); + graphVertexDO1.setDeleted(false); + graphVertexDao.save(graphVertexDO1); + + session.commit(); + + int count1 = graphVertexDao.findTotalNum(); + Assert.assertEquals(1, count1); + + List graphVertexDOs = graphVertexDao.findAll(); + GraphVertexDO graphVertexDO2 = graphVertexDOs.get(0); + Long id = graphVertexDO2.getId(); + graphVertexDO2.setUniq("uniq2"); + graphVertexDao.update(graphVertexDO2); + + session.commit(); + + GraphVertexDO graphVertexDO3 = graphVertexDao.findById(id); + Assert.assertEquals("uniq2", graphVertexDO3.getUniq()); + Assert.assertEquals(true, graphVertexDO3.getTemp()); + Assert.assertEquals(false, graphVertexDO3.getDeleted()); + } + + @Test + public void test3() { + List graphEdgeDOs = graphEdgeDao.findAll(); + if (null != graphEdgeDOs && graphEdgeDOs.size() > 0) { + for (GraphEdgeDO graphEdgeDO : graphEdgeDOs) { + graphEdgeDao.delete(graphEdgeDO.getId()); + } + } + session.commit(); + } + + @Test + public void test4() { + GraphEdgeDO graphEdgeDO1 = new GraphEdgeDO(); + graphEdgeDO1.setContent("select * from sys.version"); + graphEdgeDO1.setType("typq1"); + graphEdgeDO1.setOwner("owner1"); + graphEdgeDO1.setSourceVertexUniq("uniq1"); + graphEdgeDO1.setTargetVertexUniq("uniq2"); + graphEdgeDO1.setJanusEdgeId("edge1"); + graphEdgeDO1.setSourceJanusVertexId(1L); + graphEdgeDO1.setTargetJanusVertexId(1L); + graphEdgeDao.save(graphEdgeDO1); + + session.commit(); + + int count1 = graphEdgeDao.findTotalNum(); + Assert.assertEquals(1, count1); + + List graphEdgeDOs = graphEdgeDao.findAll(); + GraphEdgeDO graphEdgeDO2 = graphEdgeDOs.get(0); + Long id = graphEdgeDO2.getId(); + graphEdgeDO2.setContent("select 1 from sys.version"); + graphEdgeDao.update(graphEdgeDO2); + + session.commit(); + + GraphEdgeDO graphEdgeDO3 = graphEdgeDao.findById(id); + Assert.assertEquals("select 1 from sys.version", graphEdgeDO3.getContent()); + } +} diff --git a/graph-persistence/persistence-mybatis/src/test/resources/jdbc.properties b/graph-persistence/persistence-mybatis/src/test/resources/jdbc.properties new file mode 100644 index 0000000000000000000000000000000000000000..189dc13ead46c8f3eaa740157b8a9834d4f1ca2d --- /dev/null +++ b/graph-persistence/persistence-mybatis/src/test/resources/jdbc.properties @@ -0,0 +1,13 @@ +# H2 Database +##################################### +jdbc.driver=org.h2.Driver +jdbc.url=jdbc:h2:mem:test;MODE=MYSQL;DATABASE_TO_LOWER=FALSE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;INIT=RUNSCRIPT FROM 'classpath:/mysql_ddl.sql' +jdbc.username=root +jdbc.password=123456 +##################################### +# MySQL Database +#jdbc.driver=com.mysql.jdbc.Driver +#jdbc.url=jdbc:mysql://localhost:13306/test1?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai +#jdbc.username=root +#jdbc.password=123456 +##################################### diff --git a/graph-persistence/persistence-mybatis/src/test/resources/mybatis-config.xml b/graph-persistence/persistence-mybatis/src/test/resources/mybatis-config.xml new file mode 100644 index 0000000000000000000000000000000000000000..48efec8089cc39f2900e2d9af4affab6b2e497b2 --- /dev/null +++ b/graph-persistence/persistence-mybatis/src/test/resources/mybatis-config.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/graph-persistence/persistence-mybatis/src/test/resources/mysql_ddl.sql b/graph-persistence/persistence-mybatis/src/test/resources/mysql_ddl.sql new file mode 100644 index 0000000000000000000000000000000000000000..48cc95eeb62d757e9fe145bcf22fb758fd4eb86c --- /dev/null +++ b/graph-persistence/persistence-mybatis/src/test/resources/mysql_ddl.sql @@ -0,0 +1,53 @@ +create table if not exists `graph_vertex` ( + `id` bigint(20) unsigned not null auto_increment comment '主键', + `uniq` varchar(255) not null default '' comment '顶点的唯一标识', + `type` varchar(20) not null default '' comment '顶点的类型', + `owner` varchar(50) not null default '' comment '所有者', + `janus_vertex_id` bigint(20) not null default 0 comment '顶点存储 JanusGraph Vertex Id', + `neo4j_vertex_id` bigint(20) not null default 0 comment '顶点存储 Neo4j Vertex Id', + `nebula_vertex_id` bigint(20) not null default 0 comment '顶点存储 NebulaGraph Vertex Id', + `temp` tinyint(1) not null default 0 comment '是否临时点(0:否 1:是),只存在数据库,不存在图中', + `deleted` tinyint(1) not null default 0 comment '是否删除(0:否 1:是)', + `create_time` timestamp not null default current_timestamp comment '顶点的创建时间', + `update_time` timestamp not null default current_timestamp comment '顶点的修改时间', + primary key (`id`), + unique key (`uniq`), + index (`janus_vertex_id`), + index (`neo4j_vertex_id`), + index (`nebula_vertex_id`) +) engine=innodb auto_increment=1 default charset=utf8 comment='图中顶点集合'; + +create table if not exists `graph_edge` ( + `id` bigint(20) unsigned not null auto_increment comment '主键', + `content` mediumtext not null comment '边的记录内容', + `content_md5` char(32) not null default '' comment '边的记录内容MD5值', + `type` varchar(20) not null default '' comment '边的类型', + `owner` varchar(50) not null default '' comment '所有者', + `source_vertex_uniq` varchar(255) not null default '' comment '边的输入顶点的唯一标识', + `target_vertex_uniq` varchar(255) not null default '' comment '边的输出顶点的唯一标识', + `janus_edge_id` varchar(50) not null default '' comment '边存储 JanusGraph Edge Id', + `neo4j_edge_id` bigint(20) not null default 0 comment '边存储 Neo4j Edge Id', + `nebula_edge_id` varchar(50) not null default '' comment '边存储 NebulaGraph Edge Id', + `source_janus_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 JanusGraph Vertex Id', + `target_janus_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 JanusGraph Vertex Id', + `source_neo4j_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 Neo4j Vertex Id', + `target_neo4j_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 Neo4j Vertex Id', + `source_nebula_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 NebulaGraph Vertex Id', + `target_nebula_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 NebulaGraph Vertex Id', + `deleted` tinyint(1) not null default 0 comment '是否删除(0:否 1:是)', + `create_time` timestamp not null default current_timestamp comment '边的创建时间', + `update_time` timestamp not null default current_timestamp comment '边的修改时间', + primary key (`id`), + index (`content_md5`), + index (`source_vertex_uniq`), + index (`target_vertex_uniq`), + index (`janus_edge_id`), + index (`neo4j_edge_id`), + index (`nebula_edge_id`), + index (`source_janus_vertex_id`), + index (`target_janus_vertex_id`), + index (`source_neo4j_vertex_id`), + index (`target_neo4j_vertex_id`), + index (`source_nebula_vertex_id`), + index (`target_nebula_vertex_id`) +) engine=innodb auto_increment=1 default charset=utf8 comment='图中边集合'; diff --git a/graph-persistence/persistence-spring-jdbc/pom.xml b/graph-persistence/persistence-spring-jdbc/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..5dfe9a0f1862d544bab4a373dfb2ae8510d6af04 --- /dev/null +++ b/graph-persistence/persistence-spring-jdbc/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + org.studiox.graph + graph-persistence + 1.0.5-SNAPSHOT + + org.studiox.graph.persistence + persistence-spring-jdbc + 1.0.5-SNAPSHOT + jar + + + junit + junit + test + + + ch.vorburger.mariaDB4j + mariaDB4j + test + + + mysql + mysql-connector-java + test + + + org.studiox.graph.persistence + persistence-model + + + org.springframework + spring-jdbc + + + diff --git a/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/dao/GraphEdgeDao.java b/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/dao/GraphEdgeDao.java new file mode 100644 index 0000000000000000000000000000000000000000..649aa9d2ef639cec829b54c59d60fe6583aa6b8c --- /dev/null +++ b/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/dao/GraphEdgeDao.java @@ -0,0 +1,20 @@ +package org.studiox.graph.persistence.spring.jdbc.dao; + +import org.studiox.graph.persistence.model.GraphEdgeDO; + +import java.util.List; + +public interface GraphEdgeDao { + + List findAll(); + + int save(GraphEdgeDO graphEdgeDO); + + int update(GraphEdgeDO graphEdgeDO); + + int delete(Long id); + + GraphEdgeDO findById(Long id); + + int findTotalNum(); +} diff --git a/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/dao/GraphVertexDao.java b/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/dao/GraphVertexDao.java new file mode 100644 index 0000000000000000000000000000000000000000..f11807241af85e502b5bc7ba48fccfa8c2933fe7 --- /dev/null +++ b/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/dao/GraphVertexDao.java @@ -0,0 +1,20 @@ +package org.studiox.graph.persistence.spring.jdbc.dao; + +import org.studiox.graph.persistence.model.GraphVertexDO; + +import java.util.List; + +public interface GraphVertexDao { + + List findAll(); + + int save(GraphVertexDO graphVertexDO); + + int update(GraphVertexDO graphVertexDO); + + int delete(Long id); + + GraphVertexDO findById(Long id); + + int findTotalNum(); +} diff --git a/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/dao/impl/GraphEdgeDaoImpl.java b/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/dao/impl/GraphEdgeDaoImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..823d73f3c8787320a4998189effb3c76e16ccb86 --- /dev/null +++ b/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/dao/impl/GraphEdgeDaoImpl.java @@ -0,0 +1,86 @@ +package org.studiox.graph.persistence.spring.jdbc.dao.impl; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.studiox.graph.persistence.model.GraphEdgeDO; +import org.studiox.graph.persistence.spring.jdbc.dao.GraphEdgeDao; +import org.studiox.graph.persistence.spring.jdbc.rowmapper.GraphEdgeDOMapper; + +import java.util.List; + +public class GraphEdgeDaoImpl implements GraphEdgeDao { + + private JdbcTemplate jdbcTemplate; + + public GraphEdgeDaoImpl(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public List findAll() { + String sql = "select * from graph_edge"; + return jdbcTemplate.query(sql, new GraphEdgeDOMapper()); + } + + @Override + public int save(GraphEdgeDO graphEdgeDO) { + String sql = + "insert into graph_edge(content,type,owner,source_vertex_uniq,target_vertex_uniq," + + "janus_edge_id,source_janus_vertex_id,target_janus_vertex_id) values (?,?,?,?,?,?,?,?)"; + Object[] obj = + new Object[] { + graphEdgeDO.getContent(), + graphEdgeDO.getType(), + graphEdgeDO.getOwner(), + graphEdgeDO.getSourceVertexUniq(), + graphEdgeDO.getTargetVertexUniq(), + graphEdgeDO.getJanusEdgeId(), + graphEdgeDO.getSourceJanusVertexId(), + graphEdgeDO.getTargetJanusVertexId() + }; + return jdbcTemplate.update(sql, obj); + } + + @Override + public int update(GraphEdgeDO graphEdgeDO) { + String sql = + "update graph_edge set content=?,type=?,owner=?,source_vertex_uniq=?,target_vertex_uniq=?," + + "janus_edge_id=?,source_janus_vertex_id=?,target_janus_vertex_id=?,deleted=?,update_time=? where id=?"; + Object[] obj = + new Object[] { + graphEdgeDO.getContent(), + graphEdgeDO.getType(), + graphEdgeDO.getOwner(), + graphEdgeDO.getSourceVertexUniq(), + graphEdgeDO.getTargetVertexUniq(), + graphEdgeDO.getJanusEdgeId(), + graphEdgeDO.getSourceJanusVertexId(), + graphEdgeDO.getTargetJanusVertexId(), + graphEdgeDO.getDeleted(), + graphEdgeDO.getUpdateTime(), + graphEdgeDO.getId() + }; + return jdbcTemplate.update(sql, obj); + } + + @Override + public int delete(Long id) { + String sql = "delete from graph_edge where id=?"; + return jdbcTemplate.update(sql, id); + } + + @Override + public GraphEdgeDO findById(Long id) { + String sql = "select * from graph_edge where id=?"; + return jdbcTemplate.queryForObject(sql, new GraphEdgeDOMapper(), id); + } + + @Override + public int findTotalNum() { + String sql = "select count(*) from graph_edge"; + return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> rs.getInt(1)); + } +} diff --git a/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/dao/impl/GraphVertexDaoImpl.java b/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/dao/impl/GraphVertexDaoImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..0dd025c2a5880a25b8f2e5224df5cbfec662f707 --- /dev/null +++ b/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/dao/impl/GraphVertexDaoImpl.java @@ -0,0 +1,78 @@ +package org.studiox.graph.persistence.spring.jdbc.dao.impl; + +import org.springframework.jdbc.core.JdbcTemplate; +import org.studiox.graph.persistence.model.GraphVertexDO; +import org.studiox.graph.persistence.spring.jdbc.dao.GraphVertexDao; +import org.studiox.graph.persistence.spring.jdbc.rowmapper.GraphVertexDOMapper; + +import java.util.List; + +public class GraphVertexDaoImpl implements GraphVertexDao { + + private JdbcTemplate jdbcTemplate; + + public GraphVertexDaoImpl(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Override + public List findAll() { + String sql = "select * from graph_vertex"; + return jdbcTemplate.query(sql, new GraphVertexDOMapper()); + } + + @Override + public int save(GraphVertexDO graphVertexDO) { + String sql = + "insert into graph_vertex(uniq,type,owner,janus_vertex_id,temp) values (?,?,?,?,?)"; + Object[] obj = + new Object[] { + graphVertexDO.getUniq(), + graphVertexDO.getType(), + graphVertexDO.getOwner(), + graphVertexDO.getJanusVertexId(), + graphVertexDO.getTemp() + }; + return jdbcTemplate.update(sql, obj); + } + + @Override + public int update(GraphVertexDO graphVertexDO) { + String sql = + "update graph_vertex set uniq=?,type=?,owner=?,janus_vertex_id=?,temp=?,deleted=?,update_time=? where id=?"; + Object[] obj = + new Object[] { + graphVertexDO.getUniq(), + graphVertexDO.getType(), + graphVertexDO.getOwner(), + graphVertexDO.getJanusVertexId(), + graphVertexDO.getTemp(), + graphVertexDO.getDeleted(), + graphVertexDO.getUpdateTime(), + graphVertexDO.getId() + }; + return jdbcTemplate.update(sql, obj); + } + + @Override + public int delete(Long id) { + String sql = "delete from graph_vertex where id=?"; + return jdbcTemplate.update(sql, id); + } + + @Override + public GraphVertexDO findById(Long id) { + String sql = "select * from graph_vertex where id=?"; + return jdbcTemplate.queryForObject(sql, new GraphVertexDOMapper(), id); + } + + @Override + public int findTotalNum() { + String sql = "select count(*) from graph_vertex"; + return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> rs.getInt(1)); + } +} diff --git a/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/rowmapper/GraphEdgeDOMapper.java b/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/rowmapper/GraphEdgeDOMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..891bb9064f661ecd4492b10f2b3e7c750c0ca64b --- /dev/null +++ b/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/rowmapper/GraphEdgeDOMapper.java @@ -0,0 +1,35 @@ +package org.studiox.graph.persistence.spring.jdbc.rowmapper; + +import org.springframework.jdbc.core.RowMapper; +import org.studiox.graph.persistence.model.GraphEdgeDO; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class GraphEdgeDOMapper implements RowMapper { + + @Override + public GraphEdgeDO mapRow(ResultSet rs, int rowNum) throws SQLException { + GraphEdgeDO graphEdgeDO = new GraphEdgeDO(); + graphEdgeDO.setId(rs.getLong("id")); + graphEdgeDO.setContent(rs.getString("content")); + graphEdgeDO.setContentMd5(rs.getString("content_md5")); + graphEdgeDO.setType(rs.getString("type")); + graphEdgeDO.setOwner(rs.getString("owner")); + graphEdgeDO.setSourceVertexUniq(rs.getString("source_vertex_uniq")); + graphEdgeDO.setTargetVertexUniq(rs.getString("target_vertex_uniq")); + graphEdgeDO.setJanusEdgeId(rs.getString("janus_edge_id")); + graphEdgeDO.setNeo4jEdgeId(rs.getLong("neo4j_edge_id")); + graphEdgeDO.setNebulaEdgeId(rs.getString("nebula_edge_id")); + graphEdgeDO.setSourceJanusVertexId(rs.getLong("source_janus_vertex_id")); + graphEdgeDO.setTargetJanusVertexId(rs.getLong("target_janus_vertex_id")); + graphEdgeDO.setSourceNeo4jVertexId(rs.getLong("source_neo4j_vertex_id")); + graphEdgeDO.setTargetNeo4jVertexId(rs.getLong("target_neo4j_vertex_id")); + graphEdgeDO.setSourceNebulaVertexId(rs.getLong("source_nebula_vertex_id")); + graphEdgeDO.setTargetNebulaVertexId(rs.getLong("target_nebula_vertex_id")); + graphEdgeDO.setDeleted(rs.getBoolean("deleted")); + graphEdgeDO.setCreateTime(rs.getTimestamp("create_time")); + graphEdgeDO.setUpdateTime(rs.getTimestamp("update_time")); + return graphEdgeDO; + } +} diff --git a/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/rowmapper/GraphVertexDOMapper.java b/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/rowmapper/GraphVertexDOMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..37d0c23e1256aedf6d00251ee30ec313ff689168 --- /dev/null +++ b/graph-persistence/persistence-spring-jdbc/src/main/java/org/studiox/graph/persistence/spring/jdbc/rowmapper/GraphVertexDOMapper.java @@ -0,0 +1,27 @@ +package org.studiox.graph.persistence.spring.jdbc.rowmapper; + +import org.springframework.jdbc.core.RowMapper; +import org.studiox.graph.persistence.model.GraphVertexDO; + +import java.sql.ResultSet; +import java.sql.SQLException; + +public class GraphVertexDOMapper implements RowMapper { + + @Override + public GraphVertexDO mapRow(ResultSet rs, int rowNum) throws SQLException { + GraphVertexDO graphVertexDO = new GraphVertexDO(); + graphVertexDO.setId(rs.getLong("id")); + graphVertexDO.setUniq(rs.getString("uniq")); + graphVertexDO.setType(rs.getString("type")); + graphVertexDO.setOwner(rs.getString("owner")); + graphVertexDO.setJanusVertexId(rs.getLong("janus_vertex_id")); + graphVertexDO.setNeo4jVertexId(rs.getLong("neo4j_vertex_id")); + graphVertexDO.setNebulaVertexId(rs.getLong("nebula_vertex_id")); + graphVertexDO.setTemp(rs.getBoolean("temp")); + graphVertexDO.setDeleted(rs.getBoolean("deleted")); + graphVertexDO.setCreateTime(rs.getTimestamp("create_time")); + graphVertexDO.setUpdateTime(rs.getTimestamp("update_time")); + return graphVertexDO; + } +} diff --git a/graph-persistence/persistence-spring-jdbc/src/test/java/org/studiox/graph/persistence/spring/jdbc/test/SpringJdbcTest.java b/graph-persistence/persistence-spring-jdbc/src/test/java/org/studiox/graph/persistence/spring/jdbc/test/SpringJdbcTest.java new file mode 100644 index 0000000000000000000000000000000000000000..68fee8b1bc8fddcf5601a619f657c619daa1f249 --- /dev/null +++ b/graph-persistence/persistence-spring-jdbc/src/test/java/org/studiox/graph/persistence/spring/jdbc/test/SpringJdbcTest.java @@ -0,0 +1,155 @@ +package org.studiox.graph.persistence.spring.jdbc.test; + +import ch.vorburger.exec.ManagedProcessException; +import ch.vorburger.mariadb4j.DB; +import ch.vorburger.mariadb4j.DBConfigurationBuilder; +import org.junit.*; +import org.junit.runners.MethodSorters; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.studiox.graph.persistence.model.GraphEdgeDO; +import org.studiox.graph.persistence.model.GraphVertexDO; +import org.studiox.graph.persistence.spring.jdbc.dao.GraphEdgeDao; +import org.studiox.graph.persistence.spring.jdbc.dao.GraphVertexDao; +import org.studiox.graph.persistence.spring.jdbc.dao.impl.GraphEdgeDaoImpl; +import org.studiox.graph.persistence.spring.jdbc.dao.impl.GraphVertexDaoImpl; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Properties; + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class SpringJdbcTest { + + private static DB db; + + private static GraphVertexDao graphVertexDao; + + private static GraphEdgeDao graphEdgeDao; + + @BeforeClass + public static void init() { + DBConfigurationBuilder config = DBConfigurationBuilder.newBuilder(); + config.setPort(13306); + config.setDeletingTemporaryBaseAndDataDirsOnShutdown(true); + try { + db = DB.newEmbeddedDB(config.build()); + db.start(); + db.source("mysql_ddl.sql", "test"); + } catch (ManagedProcessException e) { + e.printStackTrace(); + } + + Properties properties = new Properties(); + InputStream inputStream = + Thread.currentThread().getContextClassLoader().getResourceAsStream("jdbc.properties"); + try { + properties.load(inputStream); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (null != inputStream) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource(); + driverManagerDataSource.setDriverClassName(properties.getProperty("jdbc.driver")); + driverManagerDataSource.setUrl(properties.getProperty("jdbc.url")); + driverManagerDataSource.setUsername(properties.getProperty("jdbc.username")); + driverManagerDataSource.setPassword(properties.getProperty("jdbc.password")); + + JdbcTemplate jdbcTemplate = new JdbcTemplate(driverManagerDataSource); + + graphVertexDao = new GraphVertexDaoImpl(jdbcTemplate); + graphEdgeDao = new GraphEdgeDaoImpl(jdbcTemplate); + } + + @AfterClass + public static void clean() { + if (null != db) { + try { + db.stop(); + } catch (ManagedProcessException e) { + e.printStackTrace(); + } + } + } + + @Test + public void test1() { + List graphVertexDOs = graphVertexDao.findAll(); + if (null != graphVertexDOs && graphVertexDOs.size() > 0) { + for (GraphVertexDO graphVertexDO : graphVertexDOs) { + graphVertexDao.delete(graphVertexDO.getId()); + } + } + } + + @Test + public void test2() { + GraphVertexDO graphVertexDO1 = new GraphVertexDO(); + graphVertexDO1.setUniq("uniq1"); + graphVertexDO1.setType("type1"); + graphVertexDO1.setOwner("owner1"); + graphVertexDO1.setJanusVertexId(1L); + graphVertexDO1.setTemp(true); + graphVertexDO1.setDeleted(false); + graphVertexDao.save(graphVertexDO1); + + int count1 = graphVertexDao.findTotalNum(); + Assert.assertEquals(1, count1); + + List graphVertexDOs = graphVertexDao.findAll(); + GraphVertexDO graphVertexDO2 = graphVertexDOs.get(0); + Long id = graphVertexDO2.getId(); + graphVertexDO2.setUniq("uniq2"); + graphVertexDao.update(graphVertexDO2); + + GraphVertexDO graphVertexDO3 = graphVertexDao.findById(id); + Assert.assertEquals("uniq2", graphVertexDO3.getUniq()); + Assert.assertEquals(true, graphVertexDO3.getTemp()); + Assert.assertEquals(false, graphVertexDO3.getDeleted()); + } + + @Test + public void test3() { + List graphEdgeDOs = graphEdgeDao.findAll(); + if (null != graphEdgeDOs && graphEdgeDOs.size() > 0) { + for (GraphEdgeDO graphEdgeDO : graphEdgeDOs) { + graphEdgeDao.delete(graphEdgeDO.getId()); + } + } + } + + @Test + public void test4() { + GraphEdgeDO graphEdgeDO1 = new GraphEdgeDO(); + graphEdgeDO1.setContent("select * from sys.version"); + graphEdgeDO1.setType("typq1"); + graphEdgeDO1.setOwner("owner1"); + graphEdgeDO1.setSourceVertexUniq("uniq1"); + graphEdgeDO1.setTargetVertexUniq("uniq2"); + graphEdgeDO1.setJanusEdgeId("edge1"); + graphEdgeDO1.setSourceJanusVertexId(1L); + graphEdgeDO1.setTargetJanusVertexId(1L); + graphEdgeDao.save(graphEdgeDO1); + + int count1 = graphEdgeDao.findTotalNum(); + Assert.assertEquals(1, count1); + + List graphEdgeDOs = graphEdgeDao.findAll(); + GraphEdgeDO graphEdgeDO2 = graphEdgeDOs.get(0); + Long id = graphEdgeDO2.getId(); + graphEdgeDO2.setContent("select 1 from sys.version"); + graphEdgeDao.update(graphEdgeDO2); + + GraphEdgeDO graphEdgeDO3 = graphEdgeDao.findById(id); + Assert.assertEquals("select 1 from sys.version", graphEdgeDO3.getContent()); + } +} diff --git a/graph-persistence/persistence-spring-jdbc/src/test/resources/jdbc.properties b/graph-persistence/persistence-spring-jdbc/src/test/resources/jdbc.properties new file mode 100644 index 0000000000000000000000000000000000000000..6431bc709ef22056bfa0f439f97fbe7b9079e15a --- /dev/null +++ b/graph-persistence/persistence-spring-jdbc/src/test/resources/jdbc.properties @@ -0,0 +1,18 @@ +# H2 Database +##################################### +#jdbc.driver=org.h2.Driver +#jdbc.url=jdbc:h2:mem:test;MODE=MYSQL;DATABASE_TO_LOWER=FALSE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;INIT=RUNSCRIPT FROM 'classpath:/mysql_ddl.sql' +#jdbc.username=root +#jdbc.password=123456 +##################################### +# MySQL Database +jdbc.driver=com.mysql.jdbc.Driver +jdbc.url=jdbc:mysql://localhost:13306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai +jdbc.username=root +jdbc.password= +##################################### +# MySQL uses a test database by default, and a root user with no password is also a default. +##################################### +# ubuntu下缺少libncurses* +# 解决方法:sudo apt-cache search libncurses* 搜索下,然后使用 sudo apt-get install $(libname) 安装即可 +##################################### diff --git a/graph-persistence/persistence-spring-jdbc/src/test/resources/mysql_ddl.sql b/graph-persistence/persistence-spring-jdbc/src/test/resources/mysql_ddl.sql new file mode 100644 index 0000000000000000000000000000000000000000..48cc95eeb62d757e9fe145bcf22fb758fd4eb86c --- /dev/null +++ b/graph-persistence/persistence-spring-jdbc/src/test/resources/mysql_ddl.sql @@ -0,0 +1,53 @@ +create table if not exists `graph_vertex` ( + `id` bigint(20) unsigned not null auto_increment comment '主键', + `uniq` varchar(255) not null default '' comment '顶点的唯一标识', + `type` varchar(20) not null default '' comment '顶点的类型', + `owner` varchar(50) not null default '' comment '所有者', + `janus_vertex_id` bigint(20) not null default 0 comment '顶点存储 JanusGraph Vertex Id', + `neo4j_vertex_id` bigint(20) not null default 0 comment '顶点存储 Neo4j Vertex Id', + `nebula_vertex_id` bigint(20) not null default 0 comment '顶点存储 NebulaGraph Vertex Id', + `temp` tinyint(1) not null default 0 comment '是否临时点(0:否 1:是),只存在数据库,不存在图中', + `deleted` tinyint(1) not null default 0 comment '是否删除(0:否 1:是)', + `create_time` timestamp not null default current_timestamp comment '顶点的创建时间', + `update_time` timestamp not null default current_timestamp comment '顶点的修改时间', + primary key (`id`), + unique key (`uniq`), + index (`janus_vertex_id`), + index (`neo4j_vertex_id`), + index (`nebula_vertex_id`) +) engine=innodb auto_increment=1 default charset=utf8 comment='图中顶点集合'; + +create table if not exists `graph_edge` ( + `id` bigint(20) unsigned not null auto_increment comment '主键', + `content` mediumtext not null comment '边的记录内容', + `content_md5` char(32) not null default '' comment '边的记录内容MD5值', + `type` varchar(20) not null default '' comment '边的类型', + `owner` varchar(50) not null default '' comment '所有者', + `source_vertex_uniq` varchar(255) not null default '' comment '边的输入顶点的唯一标识', + `target_vertex_uniq` varchar(255) not null default '' comment '边的输出顶点的唯一标识', + `janus_edge_id` varchar(50) not null default '' comment '边存储 JanusGraph Edge Id', + `neo4j_edge_id` bigint(20) not null default 0 comment '边存储 Neo4j Edge Id', + `nebula_edge_id` varchar(50) not null default '' comment '边存储 NebulaGraph Edge Id', + `source_janus_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 JanusGraph Vertex Id', + `target_janus_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 JanusGraph Vertex Id', + `source_neo4j_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 Neo4j Vertex Id', + `target_neo4j_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 Neo4j Vertex Id', + `source_nebula_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 NebulaGraph Vertex Id', + `target_nebula_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 NebulaGraph Vertex Id', + `deleted` tinyint(1) not null default 0 comment '是否删除(0:否 1:是)', + `create_time` timestamp not null default current_timestamp comment '边的创建时间', + `update_time` timestamp not null default current_timestamp comment '边的修改时间', + primary key (`id`), + index (`content_md5`), + index (`source_vertex_uniq`), + index (`target_vertex_uniq`), + index (`janus_edge_id`), + index (`neo4j_edge_id`), + index (`nebula_edge_id`), + index (`source_janus_vertex_id`), + index (`target_janus_vertex_id`), + index (`source_neo4j_vertex_id`), + index (`target_neo4j_vertex_id`), + index (`source_nebula_vertex_id`), + index (`target_nebula_vertex_id`) +) engine=innodb auto_increment=1 default charset=utf8 comment='图中边集合'; diff --git a/graph-persistence/pom.xml b/graph-persistence/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..e0be971754f7de6815448b42f73693981f979d3f --- /dev/null +++ b/graph-persistence/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + org.studiox + graph + 1.0.5-SNAPSHOT + + org.studiox.graph + graph-persistence + 1.0.5-SNAPSHOT + pom + + persistence-model + persistence-mybatis + persistence-mybatis-plus + persistence-mybatis-tk + persistence-spring-jdbc + + diff --git a/graph-runtime/pom.xml b/graph-runtime/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..4d7130e0ea18afda89ebff618192d1b2ea9ad918 --- /dev/null +++ b/graph-runtime/pom.xml @@ -0,0 +1,87 @@ + + + 4.0.0 + + org.studiox + graph + 1.0.5-SNAPSHOT + + org.studiox.graph + graph-runtime + 1.0.5-SNAPSHOT + jar + + + + + + + org.springframework.boot + spring-boot-starter-test + test + + + ch.vorburger.mariaDB4j + mariaDB4j + + + mysql + mysql-connector-java + + + org.springframework.boot + spring-boot-devtools + true + + + org.springframework.boot + spring-boot-starter-web + + + org.studiox.graph.persistence + persistence-mybatis-tk + + + org.springdoc + springdoc-openapi-ui + + + org.studiox.graph.janusgraph + janusgraph-latest + + + junit + junit + + + com.google.guava + guava + + + org.codehaus.groovy + * + + + + + com.google.guava + guava + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + false + + + + + diff --git a/graph-runtime/src/main/java/org/studiox/graph/runtime/GraphRuntimeServer.java b/graph-runtime/src/main/java/org/studiox/graph/runtime/GraphRuntimeServer.java new file mode 100644 index 0000000000000000000000000000000000000000..6cd27f44c24d38e43f8d4160ef74a17e949138cc --- /dev/null +++ b/graph-runtime/src/main/java/org/studiox/graph/runtime/GraphRuntimeServer.java @@ -0,0 +1,26 @@ +package org.studiox.graph.runtime; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import org.studiox.graph.runtime.config.GraphBanner; +import tk.mybatis.spring.annotation.MapperScan; + +@SpringBootApplication +@MapperScan(basePackages = {"org.studiox.graph.persistence.mybatis.tk.mapper"}) +@ComponentScan( + basePackages = { + "org.studiox.graph.persistence.mybatis.tk.base", + "org.studiox.graph.persistence.mybatis.tk.domain", + "org.studiox.graph.persistence.mybatis.tk.dao", + "org.studiox.graph.persistence.mybatis.tk.service", + "org.studiox.graph.runtime" + }) +public class GraphRuntimeServer { + + public static void main(String[] args) { + SpringApplication springApplication = new SpringApplication(GraphRuntimeServer.class); + springApplication.setBanner(new GraphBanner()); + springApplication.run(args); + } +} diff --git a/graph-runtime/src/main/java/org/studiox/graph/runtime/config/GraphBanner.java b/graph-runtime/src/main/java/org/studiox/graph/runtime/config/GraphBanner.java new file mode 100644 index 0000000000000000000000000000000000000000..c5776069023ce386cc5b23d4fe7d58d2446c6fc6 --- /dev/null +++ b/graph-runtime/src/main/java/org/studiox/graph/runtime/config/GraphBanner.java @@ -0,0 +1,28 @@ +package org.studiox.graph.runtime.config; + +import org.springframework.boot.Banner; +import org.springframework.boot.ansi.AnsiColor; +import org.springframework.boot.ansi.AnsiOutput; +import org.springframework.core.env.Environment; + +import java.io.PrintStream; + +public class GraphBanner implements Banner { + + private static final String BANNER = + " ____ _ _ _ __ __ ____ _ \n" + + " / ___|| |_ _ _ __| (_) ___\\ \\/ / / ___|_ __ __ _ _ __ | |__ \n" + + " \\___ \\| __| | | |/ _` | |/ _ \\\\ / | | _| '__/ _` | '_ \\| '_ \\ \n" + + " ___) | |_| |_| | (_| | | (_) / \\ | |_| | | | (_| | |_) | | | |\n" + + " |____/ \\__|\\__,_|\\__,_|_|\\___/_/\\_\\ \\____|_| \\__,_| .__/|_| |_|\n" + + " |_| "; + + private static final String BANNER_SITE = "http://patorjk.com/software/taag"; + + private static final String PROJECT_SITE = "https://gitee.com/studiox/graph"; + + @Override + public void printBanner(Environment environment, Class sourceClass, PrintStream printStream) { + printStream.println(AnsiOutput.toString(AnsiColor.GREEN, BANNER)); + } +} diff --git a/graph-runtime/src/main/java/org/studiox/graph/runtime/config/RuntimeConfig.java b/graph-runtime/src/main/java/org/studiox/graph/runtime/config/RuntimeConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..1a0f2a76f0e199bc15612b972d1e43d16ed34c1f --- /dev/null +++ b/graph-runtime/src/main/java/org/studiox/graph/runtime/config/RuntimeConfig.java @@ -0,0 +1,74 @@ +package org.studiox.graph.runtime.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RuntimeConfig { + + @Value("${graph.gremlin.enabled:false}") + private boolean graphGremlinEnabled; + + @Value("${graph.gremlin.port:8082}") + private Integer graphGremlinPort; + + @Value("${database.embedded.enabled:false}") + private boolean databaseEmbeddedEnabled; + + @Value("${database.embedded.port:13306}") + private Integer databaseEmbeddedPort; + + @Value("${database.embedded.file:mysql_ddl.sql}") + private String databaseEmbeddedFile; + + @Value("${database.embedded.schema:test}") + private String databaseEmbeddedSchema; + + public boolean isGraphGremlinEnabled() { + return graphGremlinEnabled; + } + + public void setGraphGremlinEnabled(boolean graphGremlinEnabled) { + this.graphGremlinEnabled = graphGremlinEnabled; + } + + public Integer getGraphGremlinPort() { + return graphGremlinPort; + } + + public void setGraphGremlinPort(Integer graphGremlinPort) { + this.graphGremlinPort = graphGremlinPort; + } + + public boolean isDatabaseEmbeddedEnabled() { + return databaseEmbeddedEnabled; + } + + public void setDatabaseEmbeddedEnabled(boolean databaseEmbeddedEnabled) { + this.databaseEmbeddedEnabled = databaseEmbeddedEnabled; + } + + public Integer getDatabaseEmbeddedPort() { + return databaseEmbeddedPort; + } + + public void setDatabaseEmbeddedPort(Integer databaseEmbeddedPort) { + this.databaseEmbeddedPort = databaseEmbeddedPort; + } + + public String getDatabaseEmbeddedFile() { + return databaseEmbeddedFile; + } + + public void setDatabaseEmbeddedFile(String databaseEmbeddedFile) { + this.databaseEmbeddedFile = databaseEmbeddedFile; + } + + public String getDatabaseEmbeddedSchema() { + return databaseEmbeddedSchema; + } + + public void setDatabaseEmbeddedSchema(String databaseEmbeddedSchema) { + this.databaseEmbeddedSchema = databaseEmbeddedSchema; + } +} diff --git a/graph-runtime/src/main/java/org/studiox/graph/runtime/config/Swagger3Config.java b/graph-runtime/src/main/java/org/studiox/graph/runtime/config/Swagger3Config.java new file mode 100644 index 0000000000000000000000000000000000000000..3899683783d2eac8da356ffb093d44f53c3bb002 --- /dev/null +++ b/graph-runtime/src/main/java/org/studiox/graph/runtime/config/Swagger3Config.java @@ -0,0 +1,45 @@ +package org.studiox.graph.runtime.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import org.springdoc.core.GroupedOpenApi; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class Swagger3Config { + + @Bean + public OpenAPI graphOpenApi() { + return new OpenAPI() + .info( + new Info() + .title("Studiox Graph Runtime OpenAPI Docs") + .description("REST API powered by OpenAPI v3.0 (Swagger3)") + .version("1.0.0-SNAPSHOT")); + } + + @Bean + public GroupedOpenApi systemApi() { + return GroupedOpenApi.builder() + .group("system") + .pathsToMatch("/studiox/api/graph/runtime/v1/system/**") + .build(); + } + + @Bean + public GroupedOpenApi graphApi() { + return GroupedOpenApi.builder() + .group("graph") + .pathsToMatch("/studiox/api/graph/runtime/v1/graph/**") + .build(); + } + + @Bean + public GroupedOpenApi lineageApi() { + return GroupedOpenApi.builder() + .group("lineage") + .pathsToMatch("/studiox/api/graph/runtime/v1/lineage/**") + .build(); + } +} diff --git a/graph-runtime/src/main/java/org/studiox/graph/runtime/controller/GraphController.java b/graph-runtime/src/main/java/org/studiox/graph/runtime/controller/GraphController.java new file mode 100644 index 0000000000000000000000000000000000000000..5b07e72fda8f721baf08f685d25f93c605715dbe --- /dev/null +++ b/graph-runtime/src/main/java/org/studiox/graph/runtime/controller/GraphController.java @@ -0,0 +1,44 @@ +package org.studiox.graph.runtime.controller; + +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.studiox.graph.runtime.service.GraphService; + +import java.util.List; + +@RestController +@RequestMapping(value = "/studiox/api/graph/runtime/v1/graph") +@Tag(name = "Graph API Interface", description = "Graph API 接口") +public class GraphController { + + @Autowired private GraphService graphService; + + @RequestMapping(value = "/vertex", method = RequestMethod.POST) + public void addGraphVertex(@RequestParam(value = "unique", required = true) String unique) { + graphService.addGraphVertex(unique); + } + + @RequestMapping(value = "/vertices", method = RequestMethod.GET) + public List getAllGraphVertexUniqueNames() { + return graphService.getAllGraphVertexUniqueNames(); + } + + @RequestMapping(value = "/edge", method = RequestMethod.POST) + public void addGraphEdge( + @RequestParam(value = "content", required = true) String content, + @RequestParam(value = "sourceUnique", required = true) String sourceUnique, + @RequestParam(value = "targetUnique", required = true) String targetUnique) { + graphService.addGraphEdge(content, sourceUnique, targetUnique); + } + + @RequestMapping(value = "/edges", method = RequestMethod.GET) + public List getGraphEdgeContentsBySourceTargetUnique( + @RequestParam(value = "sourceUnique", required = true) String sourceUnique, + @RequestParam(value = "targetUnique", required = true) String targetUnique) { + return graphService.getGraphEdgeContentsBySourceTargetUnique(sourceUnique, targetUnique); + } +} diff --git a/graph-runtime/src/main/java/org/studiox/graph/runtime/controller/LineageController.java b/graph-runtime/src/main/java/org/studiox/graph/runtime/controller/LineageController.java new file mode 100644 index 0000000000000000000000000000000000000000..ad1fc62ffb6ab9b7aa51e7e5b4e3e0ad3c3a1c8c --- /dev/null +++ b/graph-runtime/src/main/java/org/studiox/graph/runtime/controller/LineageController.java @@ -0,0 +1,73 @@ +package org.studiox.graph.runtime.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.studiox.graph.common.GraphLineage; +import org.studiox.graph.runtime.service.GraphService; + +@RestController +@RequestMapping(value = "/studiox/api/graph/runtime/v1/lineage") +@Tag(name = "Lineage API Interface", description = "Lineage API 接口") +public class LineageController { + + @Autowired private GraphService graphRuntimeService; + + @Operation( + summary = "查询血缘版本信息", + description = "根据指定条件查询并返回血缘版本信息", + parameters = { + @Parameter( + name = "version", + schema = @Schema(implementation = String.class), + description = "版本信息") + }, + responses = { + @ApiResponse( + description = "返回查询结果", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = String.class))), + @ApiResponse(responseCode = "400", description = "返回 400 Error") + }) + @RequestMapping(value = "/version", method = RequestMethod.GET) + public String getVersion(@RequestParam(value = "version", required = false) String version) { + return "Graph Runtime Server Version: " + version; + } + + @RequestMapping(value = "/lineageInfo", method = RequestMethod.GET) + public GraphLineage getLineageInfo( + @RequestParam(value = "unique", required = true) String unique, + @RequestParam(value = "depth", required = false, defaultValue = "-1") Integer depth, + @RequestParam(value = "skipDeleted", required = false, defaultValue = "true") + Boolean skipDeleted) { + return graphRuntimeService.getLineage(unique, depth, skipDeleted); + } + + @RequestMapping(value = "/impactInfo", method = RequestMethod.GET) + public GraphLineage getImpactInfo( + @RequestParam(value = "unique", required = true) String unique, + @RequestParam(value = "depth", required = false, defaultValue = "-1") Integer depth, + @RequestParam(value = "skipDeleted", required = false, defaultValue = "true") + Boolean skipDeleted) { + return graphRuntimeService.getImpact(unique, depth, skipDeleted); + } + + @RequestMapping(value = "/fullLineageInfo", method = RequestMethod.GET) + public GraphLineage getFullLineageInfo( + @RequestParam(value = "unique", required = true) String unique, + @RequestParam(value = "depth", required = false, defaultValue = "-1") Integer depth, + @RequestParam(value = "skipDeleted", required = false, defaultValue = "true") + Boolean skipDeleted) { + return graphRuntimeService.getFullLineage(unique, depth, skipDeleted); + } +} diff --git a/graph-runtime/src/main/java/org/studiox/graph/runtime/controller/SystemController.java b/graph-runtime/src/main/java/org/studiox/graph/runtime/controller/SystemController.java new file mode 100644 index 0000000000000000000000000000000000000000..4bd8dc90960e9d3e54d6d868250f1589e66f1c39 --- /dev/null +++ b/graph-runtime/src/main/java/org/studiox/graph/runtime/controller/SystemController.java @@ -0,0 +1,16 @@ +package org.studiox.graph.runtime.controller; + +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping(value = "/studiox/api/graph/runtime/v1/system") +public class SystemController { + + @GetMapping(value = "/health", produces = MediaType.APPLICATION_JSON_VALUE) + public String health() { + return "The Sun Also Rises"; + } +} diff --git a/graph-runtime/src/main/java/org/studiox/graph/runtime/service/GraphService.java b/graph-runtime/src/main/java/org/studiox/graph/runtime/service/GraphService.java new file mode 100644 index 0000000000000000000000000000000000000000..67d971f1fa3979ab0c49ae81f594ecff7dfff26b --- /dev/null +++ b/graph-runtime/src/main/java/org/studiox/graph/runtime/service/GraphService.java @@ -0,0 +1,140 @@ +package org.studiox.graph.runtime.service; + +import org.apache.commons.lang3.StringUtils; +import org.janusgraph.core.JanusGraph; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.studiox.graph.common.GraphLineage; +import org.studiox.graph.common.IGraphWrapper; +import org.studiox.graph.common.exception.GraphException; +import org.studiox.graph.janusgraph.latest.JanusGraphLatest; +import org.studiox.graph.persistence.mybatis.tk.domain.GraphEdgeDO; +import org.studiox.graph.persistence.mybatis.tk.domain.GraphVertexDO; +import org.studiox.graph.persistence.mybatis.tk.service.GraphDbService; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.List; + +@Service +public class GraphService { + + private static final Logger LOGGER = LoggerFactory.getLogger(GraphService.class); + + private IGraphWrapper graphWrapper; + + @Autowired private GraphDbService graphDbService; + + @PostConstruct + public void init() { + try { + graphWrapper = new JanusGraphLatest(LOGGER); + graphWrapper.open(); + } catch (GraphException e) { + LOGGER.error(e.getMessage(), e); + } + } + + @PreDestroy + public void clean() { + if (null != graphWrapper) { + graphWrapper.close(); + } + } + + public JanusGraph getJanusGraphInstance() { + return ((JanusGraphLatest) graphWrapper).getJanusGraph(); + } + + public void addGraphVertex(String unique) { + if (StringUtils.isBlank(unique)) { + return; + } + Long vertexDbId = graphDbService.getGraphVertexIdByUniq(unique); + if (null != vertexDbId) { + return; + } + Long vertexGraphId = graphWrapper.createVertex(unique, null, null); + GraphVertexDO graphVertexDO = new GraphVertexDO(); + graphVertexDO.setUniq(unique); + graphVertexDO.setJanusVertexId(vertexGraphId); + graphDbService.insertGraphVertex(graphVertexDO); + } + + public List getAllGraphVertexUniqueNames() { + return graphDbService.getGraphVertexUniqs(); + } + + public void addGraphEdge(String content, String sourceUnique, String targetUnique) { + if (StringUtils.isBlank(content) + || StringUtils.isBlank(sourceUnique) + || StringUtils.isBlank(targetUnique)) { + return; + } + List contents = graphDbService.getGraphEdgeContents(sourceUnique, targetUnique); + if (null == contents || contents.isEmpty()) { + Long sourceVertexGraphId; + GraphVertexDO graphVertexDO = graphDbService.getGraphVertexDoByUniq(sourceUnique); + if (null == graphVertexDO) { + sourceVertexGraphId = graphWrapper.createVertexNotPreCheck(sourceUnique, null, null); + graphVertexDO = new GraphVertexDO(); + graphVertexDO.setUniq(sourceUnique); + graphVertexDO.setJanusVertexId(sourceVertexGraphId); + graphDbService.insertGraphVertex(graphVertexDO); + } else { + sourceVertexGraphId = graphVertexDO.getJanusVertexId(); + } + Long targetVertexGraphId; + graphVertexDO = graphDbService.getGraphVertexDoByUniq(targetUnique); + if (null == graphVertexDO) { + targetVertexGraphId = graphWrapper.createVertexNotPreCheck(targetUnique, null, null); + graphVertexDO = new GraphVertexDO(); + graphVertexDO.setUniq(targetUnique); + graphVertexDO.setJanusVertexId(targetVertexGraphId); + graphDbService.insertGraphVertex(graphVertexDO); + } else { + targetVertexGraphId = graphVertexDO.getJanusVertexId(); + } + String graphEdgeId = + graphWrapper.createEdgeNotPreCheck( + content, null, sourceVertexGraphId, targetVertexGraphId, null); + GraphEdgeDO graphEdgeDO = new GraphEdgeDO(); + graphEdgeDO.setContent(content); + graphEdgeDO.setSourceVertexUniq(sourceUnique); + graphEdgeDO.setTargetVertexUniq(targetUnique); + graphEdgeDO.setJanusEdgeId(graphEdgeId); + graphEdgeDO.setSourceJanusVertexId(sourceVertexGraphId); + graphEdgeDO.setTargetJanusVertexId(targetVertexGraphId); + graphDbService.insertGraphEdge(graphEdgeDO); + graphDbService.updateGraphEdgeContentMd5(graphEdgeDO.getId()); + } else if (!contents.contains(content)) { + GraphEdgeDO graphEdgeDO = + graphDbService.getOneGraphEdgeDoBySourceTargetUniq(sourceUnique, targetUnique); + graphEdgeDO.setId(null); + graphEdgeDO.setContent(content); + graphEdgeDO.setContentMd5(null); + graphEdgeDO.setCreateTime(null); + graphEdgeDO.setUpdateTime(null); + graphDbService.insertGraphEdge(graphEdgeDO); + } + } + + public List getGraphEdgeContentsBySourceTargetUnique( + String sourceUnique, String targetUnique) { + return graphDbService.getGraphEdgeContents(sourceUnique, targetUnique); + } + + public GraphLineage getImpact(String vertexUniq, Integer depth, boolean skipDeleted) { + return graphWrapper.getImpact(vertexUniq, depth, skipDeleted); + } + + public GraphLineage getLineage(String vertexUniq, Integer depth, boolean skipDeleted) { + return graphWrapper.getLineage(vertexUniq, depth, skipDeleted); + } + + public GraphLineage getFullLineage(String vertexUniq, Integer depth, boolean skipDeleted) { + return graphWrapper.getFullLineage(vertexUniq, depth, skipDeleted); + } +} diff --git a/graph-runtime/src/main/java/org/studiox/graph/runtime/service/GremlinService.java b/graph-runtime/src/main/java/org/studiox/graph/runtime/service/GremlinService.java new file mode 100644 index 0000000000000000000000000000000000000000..423f7ab0ecb8a02db9e8090f3c672acd36179b23 --- /dev/null +++ b/graph-runtime/src/main/java/org/studiox/graph/runtime/service/GremlinService.java @@ -0,0 +1,43 @@ +package org.studiox.graph.runtime.service; + +import org.apache.tinkerpop.gremlin.server.GraphGremlinServer; +import org.janusgraph.core.JanusGraph; +import org.janusgraph.graphdb.server.JanusGraphSettings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.studiox.graph.runtime.config.RuntimeConfig; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +@Service +public class GremlinService { + + private static final Logger LOGGER = LoggerFactory.getLogger(GremlinService.class); + + @Autowired private RuntimeConfig runtimeConfig; + + @Autowired private GraphService graphService; + + private GraphGremlinServer graphGremlinServer; + + @PostConstruct + public void init() { + if (runtimeConfig.isGraphGremlinEnabled()) { + JanusGraphSettings janusGraphSettings = GraphGremlinServer.createJanusGraphSettings(); + janusGraphSettings.port = runtimeConfig.getGraphGremlinPort(); + JanusGraph janusGraph = graphService.getJanusGraphInstance(); + graphGremlinServer = new GraphGremlinServer(janusGraphSettings, janusGraph); + graphGremlinServer.start(); + } + } + + @PreDestroy + public void clean() { + if (runtimeConfig.isGraphGremlinEnabled() && null != graphGremlinServer) { + graphGremlinServer.stop(); + } + } +} diff --git a/graph-runtime/src/main/java/org/studiox/graph/runtime/service/MariaDBService.java b/graph-runtime/src/main/java/org/studiox/graph/runtime/service/MariaDBService.java new file mode 100644 index 0000000000000000000000000000000000000000..656a49f13fa852a0d88f12685485eed2bde9b7a0 --- /dev/null +++ b/graph-runtime/src/main/java/org/studiox/graph/runtime/service/MariaDBService.java @@ -0,0 +1,51 @@ +package org.studiox.graph.runtime.service; + +import ch.vorburger.exec.ManagedProcessException; +import ch.vorburger.mariadb4j.DB; +import ch.vorburger.mariadb4j.DBConfigurationBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.studiox.graph.runtime.config.RuntimeConfig; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +@Service +public class MariaDBService { + + private static final Logger LOGGER = LoggerFactory.getLogger(MariaDBService.class); + + @Autowired private RuntimeConfig runtimeConfig; + + private DB db; + + @PostConstruct + public void init() { + if (runtimeConfig.isDatabaseEmbeddedEnabled()) { + DBConfigurationBuilder config = DBConfigurationBuilder.newBuilder(); + config.setPort(runtimeConfig.getDatabaseEmbeddedPort()); + config.setDeletingTemporaryBaseAndDataDirsOnShutdown(true); + try { + db = DB.newEmbeddedDB(config.build()); + db.start(); + db.source( + runtimeConfig.getDatabaseEmbeddedFile(), runtimeConfig.getDatabaseEmbeddedSchema()); + } catch (ManagedProcessException e) { + LOGGER.error(e.getMessage(), e); + } + } + } + + @PreDestroy + public void clean() { + if (null != db) { + try { + db.stop(); + } catch (ManagedProcessException e) { + LOGGER.error(e.getMessage(), e); + } + } + } +} diff --git a/graph-runtime/src/main/resources/application.yml b/graph-runtime/src/main/resources/application.yml new file mode 100644 index 0000000000000000000000000000000000000000..2023a39b991671a5faf245830e0d058ad4cabb66 --- /dev/null +++ b/graph-runtime/src/main/resources/application.yml @@ -0,0 +1,61 @@ +server: + port: 8081 + context-path: / + use-forward-headers: true + tomcat: + remote-ip-header: X-Real-IP + protocol-header: X-Forwarded-Proto + +graph: + gremlin: + enabled: true + port: 8082 + +database: + embedded: + enabled: true + port: 13306 + file: mysql_ddl.sql + schema: test + +spring: + output: + ansi: + enabled: ALWAYS + main: + lazy-initialization: false + http: + encoding: + force: true + enabled: true + charset: UTF-8 + servlet: + multipart: + enabled: true + max-file-size: 100MB + max-request-size: 1000MB + datasource: + url: jdbc:mysql://localhost:13306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai + username: root + password: + driver-class-name: com.mysql.jdbc.Driver + hikari: + pool-name: HikariCP + minimum-idle: 2 + maximum-pool-size: 5 + connection-test-query: SELECT 1 + max-lifetime: 1800000 + connection-timeout: 30000 + +logging: + config: classpath:logback-console.xml + level: + root: INFO + +#默认为true开启 +springdoc: + api-docs: + path: /api-docs + enabled: true + swagger-ui: + path: /swagger diff --git a/graph-runtime/src/main/resources/graph.properties b/graph-runtime/src/main/resources/graph.properties new file mode 100755 index 0000000000000000000000000000000000000000..028881eac35990e8878e4f2500384c658aaa54f8 --- /dev/null +++ b/graph-runtime/src/main/resources/graph.properties @@ -0,0 +1,27 @@ +################################################################### +graph.schema.default=none +graph.storage.batch-loading=true +################################################################### +graph.cache.db-cache=false +graph.ids.block-size=20000000 +graph.ids.renew-timeout=3600000 +graph.storage.buffer-size=20240 +################################################################### +graph.gremlin.graph=org.janusgraph.core.JanusGraphFactory +################################################################### +graph.storage.backend=inmemory +################################################################### +#graph.storage.backend=hbase +#graph.storage.hostname=localhost +#graph.storage.hbase.table=graph_table_1 +#graph.storage.hbase.ext.zookeeper.znode.parent=/hbase1 +################################################################### +#graph.storage.hbase.region-count=9 +#graph.storage.hbase.regions-per-server=3 +#graph.storage.hbase.ext.hbase.rpc.timeout=300000 +#graph.storage.hbase.ext.hbase.client.operation.timeout=300000 +#graph.storage.hbase.ext.hbase.client.scanner.timeout.period=300000 +################################################################### +#graph.index.search.backend=elasticsearch +#graph.index.search.hostname=localhost:9200 +################################################################### diff --git a/graph-runtime/src/main/resources/logback-console.xml b/graph-runtime/src/main/resources/logback-console.xml new file mode 100644 index 0000000000000000000000000000000000000000..79500aec47fc44c78423e34f609b432394f8900a --- /dev/null +++ b/graph-runtime/src/main/resources/logback-console.xml @@ -0,0 +1,16 @@ + + + + + + + [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%level] [%thread] [%logger{50}] >>> [graph-runtime-server] msg=%msg%n + + + + + + + + + diff --git a/graph-runtime/src/main/resources/logback-file.xml b/graph-runtime/src/main/resources/logback-file.xml new file mode 100644 index 0000000000000000000000000000000000000000..15aa764d86123936f96d013117569f1cb98021f8 --- /dev/null +++ b/graph-runtime/src/main/resources/logback-file.xml @@ -0,0 +1,27 @@ + + + + + + + ${log_dir}/graph-runtime-server.log + + ${log_dir}/graph-runtime-server-%i.log + 1 + 9 + + + 100MB + + + + [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%level] [%thread] [%logger{50}] >>> [graph-runtime-server] msg=%msg%n + + + + + + + + + diff --git a/graph-runtime/src/main/resources/mysql_ddl.sql b/graph-runtime/src/main/resources/mysql_ddl.sql new file mode 100644 index 0000000000000000000000000000000000000000..48cc95eeb62d757e9fe145bcf22fb758fd4eb86c --- /dev/null +++ b/graph-runtime/src/main/resources/mysql_ddl.sql @@ -0,0 +1,53 @@ +create table if not exists `graph_vertex` ( + `id` bigint(20) unsigned not null auto_increment comment '主键', + `uniq` varchar(255) not null default '' comment '顶点的唯一标识', + `type` varchar(20) not null default '' comment '顶点的类型', + `owner` varchar(50) not null default '' comment '所有者', + `janus_vertex_id` bigint(20) not null default 0 comment '顶点存储 JanusGraph Vertex Id', + `neo4j_vertex_id` bigint(20) not null default 0 comment '顶点存储 Neo4j Vertex Id', + `nebula_vertex_id` bigint(20) not null default 0 comment '顶点存储 NebulaGraph Vertex Id', + `temp` tinyint(1) not null default 0 comment '是否临时点(0:否 1:是),只存在数据库,不存在图中', + `deleted` tinyint(1) not null default 0 comment '是否删除(0:否 1:是)', + `create_time` timestamp not null default current_timestamp comment '顶点的创建时间', + `update_time` timestamp not null default current_timestamp comment '顶点的修改时间', + primary key (`id`), + unique key (`uniq`), + index (`janus_vertex_id`), + index (`neo4j_vertex_id`), + index (`nebula_vertex_id`) +) engine=innodb auto_increment=1 default charset=utf8 comment='图中顶点集合'; + +create table if not exists `graph_edge` ( + `id` bigint(20) unsigned not null auto_increment comment '主键', + `content` mediumtext not null comment '边的记录内容', + `content_md5` char(32) not null default '' comment '边的记录内容MD5值', + `type` varchar(20) not null default '' comment '边的类型', + `owner` varchar(50) not null default '' comment '所有者', + `source_vertex_uniq` varchar(255) not null default '' comment '边的输入顶点的唯一标识', + `target_vertex_uniq` varchar(255) not null default '' comment '边的输出顶点的唯一标识', + `janus_edge_id` varchar(50) not null default '' comment '边存储 JanusGraph Edge Id', + `neo4j_edge_id` bigint(20) not null default 0 comment '边存储 Neo4j Edge Id', + `nebula_edge_id` varchar(50) not null default '' comment '边存储 NebulaGraph Edge Id', + `source_janus_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 JanusGraph Vertex Id', + `target_janus_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 JanusGraph Vertex Id', + `source_neo4j_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 Neo4j Vertex Id', + `target_neo4j_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 Neo4j Vertex Id', + `source_nebula_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 NebulaGraph Vertex Id', + `target_nebula_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 NebulaGraph Vertex Id', + `deleted` tinyint(1) not null default 0 comment '是否删除(0:否 1:是)', + `create_time` timestamp not null default current_timestamp comment '边的创建时间', + `update_time` timestamp not null default current_timestamp comment '边的修改时间', + primary key (`id`), + index (`content_md5`), + index (`source_vertex_uniq`), + index (`target_vertex_uniq`), + index (`janus_edge_id`), + index (`neo4j_edge_id`), + index (`nebula_edge_id`), + index (`source_janus_vertex_id`), + index (`target_janus_vertex_id`), + index (`source_neo4j_vertex_id`), + index (`target_neo4j_vertex_id`), + index (`source_nebula_vertex_id`), + index (`target_nebula_vertex_id`) +) engine=innodb auto_increment=1 default charset=utf8 comment='图中边集合'; diff --git a/graph-runtime/src/main/resources/static/css/graphStyle.css b/graph-runtime/src/main/resources/static/css/graphStyle.css new file mode 100644 index 0000000000000000000000000000000000000000..9eeb10b48b881b499137201fead75e8efe787913 --- /dev/null +++ b/graph-runtime/src/main/resources/static/css/graphStyle.css @@ -0,0 +1,80 @@ +.edge { + stroke: #999; + stroke-opacity: 0.8; +} + +.old_edge0 { + stroke: #999; + stroke-opacity: 0.6; +} + +.node circle { + stroke: #000; + stroke-width: 1.5px; +} + +.node text { + font: 10px sans-serif; +} + +.node:hover circle { + stroke-opacity: 0.6; +} + +.pinned circle { + stroke: #000; + stroke-width: 1.5px; +} + +.pinned text { + font: 10px sans-serif; +} + +.pinned:hover circle { + stroke-opacity: 0.6; +} + +.old_node0 circle { + stroke-opacity: 0.9; +} + +.old_node0 text { + font: 10px sans-serif; + opacity: 0.9; + color: #000;/* Fallback for older browsers */ + color: rgba(0,0,0,0.5); +} + +.cell { + fill: none; + pointer-events: all; +} + +.links line { + stroke: #999; + stroke-opacity: 0.6; +} + +path { + fill: none; +} + +/* + +.nodes circle { + stroke: #fff; + stroke-width: 1.5px; +} + +.text1 text{ + font: 10px sans-serif; +} +*/ +/*.table { border:0px solid black; padding:10px; width:1400px; overflow:hidden;border-spacing:0;border-collapse:collapse;} +.left { float:left; width:960px; } +.right { float:right; width:400px; } + +td, th { + padding: 1px 4px; +} +*/ \ No newline at end of file diff --git a/graph-runtime/src/main/resources/static/css/styles.css b/graph-runtime/src/main/resources/static/css/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..fe7057c36f3461f0b62a9f4254b16cc541b562f4 --- /dev/null +++ b/graph-runtime/src/main/resources/static/css/styles.css @@ -0,0 +1,348 @@ +/* ----------------------- +Base styles +------------------------*/ + +body +{ + margin: 0; + padding: 0; + color: #333; + background-color: #eee; + font: 1em/1.2 "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif; + height: 100%; +} + +h1,h2,h3,h4,h5,h6 +{ + margin: 0 0 .5em; + font-weight: 500; + line-height: 1.1; +} + +h1 { font-size: 2.25em; } /* 36px */ +h2 { font-size: 1.75em; } /* 28px */ +h3 { font-size: 1.375em; } /* 22px */ +h4 { font-size: 1.125em; } /* 18px */ +h5 { font-size: 1em; } /* 16px */ +h6 { font-size: .875em; } /* 14px */ + +p +{ + margin: 0 0 1.5em; + line-height: 1.5; +} + +blockquote +{ + padding: 1em 2em; + margin: 0 0 2em; + border-left: 5px solid #eee; +} + +hr +{ + height: 0; + margin-top: 1em; + margin-bottom: 2em; + border: 0; + border-top: 1px solid #ddd; +} + +table +{ + background-color: transparent; + border-spacing: 0; + border-collapse: collapse; + border-top: 1px solid #ddd; +} + +th, td +{ + padding: .5em 1em; + vertical-align: top; + text-align: left; + border-bottom: 1px solid #ddd; +} + +a:link { color: royalblue; } +a:visited { color: purple; } +a:focus { color: black; } +a:hover { color: green; } +a:active { color: red; } + +input[type="number"] { + width:50px; +} + +/* ----------------------- +Layout styles +------------------------*/ + +.header +{ + color: #000; + background: #ddd; + padding: 0.2em 1.25em; +} + +.header-heading { margin: 0; } + +.nav-bar +{ + color: #ddd; + background: #000; + padding: 0; +} + +.container +{ + padding: 1em 1.25em; + margin: 0 auto; +} + +.nav.container { + display: grid; + grid-template-rows: auto 15%; +} + +.nav.inputs_container_top { + grid-row: 1 / 2; + display: grid; + grid-template-columns: auto auto auto auto auto; + grid-auto-flow: row; +} + +.nav.inputs_container_bottom { + grid-row: 1 / 2; + display: grid; + grid-template-columns: auto auto auto auto; + grid-auto-flow: row; +} +.nav.input_unit_container { + padding: 5px; + align-self: center; +} + +.nav.input_label { + white-space:nowrap; +} + +.nav.controls { + grid-row: 1 / span 3; + justify-self: center; + align-self: center; +} + +.content +{ + padding: 0em 0em; + background-color: #fff; + height: 80vh; +} + +.main, .aside +{ + margin-bottom: 1em; + padding: 0em; + font: 12px sans-serif; +} + +.main +{ + position: absolute; + left: 0; + width: 100%; + height: 80vh; +} + +.left_bar +{ + position: absolute; + left: 0; + margin-left: 1em; + max-width: 275px; +} + +.right_bar +{ + position: absolute; + right: 0; + margin-right: 1em; + max-width: 275px; +} + +.footer +{ + position: absolute; + bottom: 0; + width: 100%; + color: #fff; + background: #000; +} + +/* ----------------------- +Nav +------------------------*/ + +.nav +{ + margin: 0; + padding: 0; + list-style: none; +} + +.nav li +{ + display: inline; + margin: 0; +} + +.nav a +{ + display: block; + padding: .7em 1.25em; + color: #fff; + text-decoration: none; + border-bottom: 1px solid gray; +} + +.nav a:link { color: white; } +.nav a:visited { color: white; } + +.nav a:focus +{ + color: black; + background-color: white; +} + +.nav a:hover +{ + color: white; + background-color: green; +} + +.nav a:active +{ + color: white; + background-color: red; +} + +/* ----------------------- +Side styles +------------------------*/ +.aside li +{ + padding-left: 0; + margin-left: -5px; + list-style: none; +} +.aside div +{ + margin: 1em 0 1em 0; +} +/* ----------------------- +Single styles +------------------------*/ + +.img-responsive { max-width: 100%; } + +.btn +{ + color: #fff !important; + background-color: royalblue; + border-color: #222; + display: inline-block; + padding: .5em 1em; + margin-bottom: 0; + font-weight: 400; + line-height: 1.2; + text-align: center; + white-space: nowrap; + vertical-align: middle; + cursor: pointer; + border: 1px solid transparent; + border-radius: .2em; + text-decoration: none; +} + +.btn:hover +{ + color: #fff !important; + background-color: green; +} + +.btn:focus +{ + color: #fff !important; + background-color: black; +} + +.btn:active +{ + color: #fff !important; + background-color: red; +} + +.table +{ + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} + +.list-unstyled +{ + padding-left: 0; + list-style: none; +} + +.list-inline +{ + padding-left: 0; + margin-left: -5px; + list-style: none; +} + +.list-inline > li +{ + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} + +/* ----------------------- +Wide styles +------------------------*/ + +@media (min-width: 55em) +{ + .header { padding: 0.2em 3em; } + .nav-bar { padding: 0.5em 3em; } + .content { padding: 0em 0em; } + + .main + { + margin-right: 0%; + margin-bottom: 1em; + } + + .aside + { + float: left; + margin-bottom: 1em; + margin-top: 1em; + } + + .nav li + { + display: inline; + margin: 0 1em 0 0; + } + + .nav a + { + display: inline; + padding: 0; + border-bottom: 0; + } +} \ No newline at end of file diff --git a/graph-runtime/src/main/resources/static/graph.html b/graph-runtime/src/main/resources/static/graph.html new file mode 100644 index 0000000000000000000000000000000000000000..fde2d14fd24c7e01fccb03b71f526d52e36ee6c7 --- /dev/null +++ b/graph-runtime/src/main/resources/static/graph.html @@ -0,0 +1,438 @@ + + + + + + Graphexp, graph explorer + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+
+ + Show/hide graph info +
+
+
+
+ +
+
+
+
+
+ +
+ + + + + + diff --git a/graph-runtime/src/main/resources/static/scripts/editGraph.js b/graph-runtime/src/main/resources/static/scripts/editGraph.js new file mode 100644 index 0000000000000000000000000000000000000000..70c81f174b2a07f8236be80f2a43b087690ed81a --- /dev/null +++ b/graph-runtime/src/main/resources/static/scripts/editGraph.js @@ -0,0 +1,135 @@ + function editGraph() { + var x = document.getElementById("editGraph") + if(x.style.display == "none"){ + x.style.display = "block"; + } + else{ + x.style.display ="none" ; + } + document.getElementById("addVertexForm").style.display='none'; + document.getElementById("editVertexForm").style.display='none'; + document.getElementById("addEditEdgeForm").style.display='none'; + + } + + function addVertexForm() { + document.getElementById("addVertexForm").style.display='block'; + document.getElementById("editVertexForm").style.display='none'; + document.getElementById("addEditEdgeForm").style.display='none'; + } + + function editVertexForm() { + document.getElementById("addVertexForm").style.display='none'; + document.getElementById("editVertexForm").style.display='block'; + document.getElementById("addEditEdgeForm").style.display='none'; + } + + + function addEditEdgeForm() { + document.getElementById("addVertexForm").style.display='none'; + document.getElementById("editVertexForm").style.display='none'; + document.getElementById("addEditEdgeForm").style.display='block'; + + } + + function addVertex() { + + let vertexLabel = $('#vertexLabel').val(); + let vertexPropertyName = $('#vertexPropertyName').val(); + //vertexPropertyName = vertexPropertyName.replace(/\s/g,''); + let vertexPropertyValue = $('#vertexPropertyValue').val(); + //vertexPropertyValue = vertexPropertyValue.replace(/\s/g,''); + propertyName = vertexPropertyName.split(","); + propertyValue = vertexPropertyValue.split(","); + var valueLen = propertyValue.length; + var nameLen = propertyName.length; + if(nameLen != valueLen){ + alert("Please enter same number of property name and property value") + } + else{ + document.getElementById('vertexLabel').value=''; + document.getElementById('vertexPropertyName').value=''; + document.getElementById('vertexPropertyValue').value=''; + var gremlin_query = "g.addV('"+vertexLabel+"')" + for(count =0; count 0; k--) { + var kp = k - 1; + _svg.selectAll(".old_edge" + kp).classed("old_edge" + k, true); + _svg.selectAll(".old_node" + kp).classed("old_node" + k, true); + _svg.selectAll(".old_edgepath" + kp).classed("old_edgepath" + k, true); + _svg.selectAll(".old_edgelabel" + kp).classed("old_edgelabel" + k, true); + }; + } + + function clear_old() { + old_Nodes = []; + old_Links = []; + } + + function update_data(d) { + // Save the data + var previous_nodes = _svg.selectAll("g").filter(".active_node"); + var previous_nodes_data = previous_nodes.data(); + old_Nodes = updateAdd(old_Nodes, previous_nodes_data); + var previous_links = _svg.selectAll(".active_edge"); + var previous_links_data = previous_links.data(); + old_Links = updateAdd(old_Links, previous_links_data); + + // handle the pinned nodes + var pinned_Nodes = _svg.selectAll("g").filter(".pinned"); + var pinned_nodes_data = pinned_Nodes.data(); + // get the node data and merge it with the pinned nodes + _Nodes = d.nodes; + _Nodes = updateAdd(_Nodes, pinned_nodes_data); + // add coordinates to the new active nodes that already existed in the previous step + _Nodes = transfer_coordinates(_Nodes, old_Nodes); + // retrieve the links between nodes and pinned nodes + _Links = d.links.concat(previous_links_data); // first gather the links + _Links = find_active_links(_Links, _Nodes); // then find the ones that are between active nodes + + // Sort links by source, then target, then label + // This is used to set linknum + _Links.sort(function (a, b) { + if (a.source > b.source) { return 1; } + else if (a.source < b.source) { return -1; } + else { + if (a.target > b.target) { return 1; } + if (a.target < b.target) { return -1; } + else { + if (a.label > b.label) { return 1; } + if (a.label < b.label) { return -1; } + else { return 0; } + } + } + }); + + // Any links with duplicate source and target get an incremented 'linknum' + for (var i = 0; i < _Links.length; i++) { + if (i != 0 && + _Links[i].source == _Links[i - 1].source && + _Links[i].target == _Links[i - 1].target) { + _Links[i].linknum = _Links[i - 1].linknum + 1; + } + else { _Links[i].linknum = 1; }; + }; + } + + function updateAdd(array1, array2) { + // Update lines of array1 with the ones of array2 when the elements' id match + // and add elements of array2 to array1 when they do not exist in array1 + var arraytmp = array2.slice(0); + var removeValFromIndex = []; + array1.forEach(function (d, index, thearray) { + for (var i = 0; i < arraytmp.length; i++) { + if (d.id == arraytmp[i].id) { + thearray[index] = arraytmp[i]; + removeValFromIndex.push(i); + } + } + }); + // remove the already updated values (in reverse order, not to mess up the indices) + removeValFromIndex.sort(); + for (var i = removeValFromIndex.length - 1; i >= 0; i--) + arraytmp.splice(removeValFromIndex[i], 1); + return array1.concat(arraytmp); + } + + function find_active_links(list_of_links, active_nodes) { + // find the links in the list_of_links that are between the active nodes and discard the others + var active_links = []; + list_of_links.forEach(function (row) { + for (var i = 0; i < active_nodes.length; i++) { + for (var j = 0; j < active_nodes.length; j++) { + if (active_nodes[i].id == row.source.id && active_nodes[j].id == row.target.id) { + var L_data = { source: row.source.id, target: row.target.id, type: row.type, value: row.value, id: row.id }; + var L_data = row; + L_data['source'] = row.source.id; + L_data['target'] = row.target.id; + active_links = active_links.concat(L_data); + } + else if (active_nodes[i].id == row.source && active_nodes[j].id == row.target) { + var L_data = row; + active_links = active_links.concat(L_data); + } + } + } + }); + // the active links are in active_links but there can be some duplicates + // remove duplicates links + var dic = {}; + for (var i = 0; i < active_links.length; i++) + dic[active_links[i].id] = active_links[i]; // this will remove the duplicate links (with same id) + var list_of_active_links = []; + for (var key in dic) + list_of_active_links.push(dic[key]); + return list_of_active_links; + } + + + function transfer_coordinates(Nodes, old_Nodes) { + // Transfer coordinates from old_nodes to the new nodes with the same id + for (var i = 0; i < old_Nodes.length; i++) { + var exists = 0; + for (var j = 0; j < Nodes.length; j++) { + if (Nodes[j].id == old_Nodes[i].id) { + Nodes[j].x = old_Nodes[i].x; + Nodes[j].y = old_Nodes[i].y; + Nodes[j].fx = old_Nodes[i].x; + Nodes[j].fy = old_Nodes[i].y; + Nodes[j].vx = old_Nodes[i].vx; + Nodes[j].vy = old_Nodes[i].vy; + } + } + } + return Nodes; + } + + function remove_duplicates(elem_class, elem_class_old) { + // Remove all the duplicate nodes and edges among the old_nodes and old_edges. + // A node or an edge can not be on several layers at the same time. + d3.selectAll(elem_class).each(function (d) { + var ID = d.id; + for (var n = 0; n < nb_layers; n++) { + var list_old_elements = d3.selectAll(elem_class_old + n); + //list_old_nodes_data = list_old_nodes.data(); + list_old_elements.each(function (d) { + if (d.id == ID) { + d3.select(this).remove(); + //console.log('Removed!!') + } + }) + } + }); + } + + return { + set_nb_layers: set_nb_layers, + depth: depth, + push_layers: push_layers, + clear_old: clear_old, + update_data: update_data, + remove_duplicates: remove_duplicates + } + })(); + + //////////////////////////////////////////////////////////////////////////////////// + function simulation_start(center_f) { + // Define the force applied to the nodes + _simulation = d3.forceSimulation() + .force("charge", d3.forceManyBody().strength(force_strength)) + .force("link", d3.forceLink().strength(link_strength).id(function (d) { return d.id; })); + + if (center_f == 1) { + var force_y = force_x_strength; + var force_x = force_y_strength; + _simulation.force("center", d3.forceCenter(_svg_width / 2, _svg_height / 2)); + } + else { + var force_y = 0; + var force_x = 0; + } + _simulation.force("y", d3.forceY().strength(function (d) { + return force_y; + })) + .force("x", d3.forceX().strength(function (d) { + return force_x; + })); + return _simulation; + } + + + + ////////////////////////////////////// + function refresh_data(d, center_f, with_active_node) { + // Main visualization function + var svg_graph = svg_handle(); + layers.push_layers(); + layers.update_data(d); + + ////////////////////////////////////// + // link handling + + //attach the data + var all_links = svg_graph.selectAll(".active_edge") + .data(_Links, function (d) { return d.id; }); + var all_edgepaths = svg_graph.selectAll(".active_edgepath") + .data(_Links, function (d) { return d.id; }); + var all_edgelabels = svg_graph.selectAll(".active_edgelabel") + .data(_Links, function (d) { return d.id; }); + + // links not active anymore are classified old_links + all_links.exit().classed("old_edge0", true).classed("active_edge", false); + all_edgepaths.exit().classed("old_edgepath0", true).classed("active_edgepath", false); + all_edgelabels.exit().classed("old_edgelabel0", true).classed("active_edgelabel", false); + + + // handling active links associated to the data + var edgepaths_e = all_edgepaths.enter(), + edgelabels_e = all_edgelabels.enter(), + link_e = all_links.enter(); + var decor_out = graphShapes.decorate_link(link_e, edgepaths_e, edgelabels_e); + _links = decor_out[0]; + + var edgepaths = decor_out[1], + edgelabels = decor_out[2]; + + + // previous links plus new links are merged + _links = _links.merge(all_links); + edgepaths = edgepaths.merge(all_edgepaths); + edgelabels = edgelabels.merge(all_edgelabels); + + /////////////////////////////////// + // node handling + + var all_nodes = svg_graph.selectAll("g").filter(".active_node") + .data(_Nodes, function (d) { return d.id; }); + + //console.log(data_node); + // old nodes not active any more are tagged + all_nodes.exit().classed("old_node0", true).classed("active_node", false);//;attr("class","old_node0"); + + + // nodes associated to the data are constructed + _nodes = all_nodes.enter(); + + // add node decoration + var node_deco = graphShapes.decorate_node(_nodes, with_active_node); + + var _nodes = node_deco.merge(all_nodes); + + + var focus_node_data = d3.select(".focus_node").data(); + if (focus_node_data.length > 0){ + infobox.display_info(focus_node_data[0]); + } + ////////////////////////////////// + // Additional clean up + graphShapes.decorate_old_elements(layers.depth()); + svg_graph.selectAll("g").filter(".pinned").moveToFront(); + + + layers.remove_duplicates(".active_node", ".old_node"); + layers.remove_duplicates(".active_edge", ".old_edge"); + layers.remove_duplicates(".active_edgepath", ".old_edgepath"); + layers.remove_duplicates(".active_edgelabel", ".old_edgelabel"); + + + /////////////////////////////// + // Force simulation + // simulation model and parameters + + + _simulation = simulation_start(center_f); + // Associate the simulation with the data + _simulation.nodes(_Nodes).on("tick", ticked); + _simulation.force("link").links(_Links); + _simulation.alphaTarget(0); + + //////////////////////// + // handling simulation steps + // move the nodes and links at each simulation step, following this rule: + function ticked() { + _links.attr('d', function (d) { + if (use_curved_edges) { + var dx = d.target.x - d.source.x; + var dy = d.target.y - d.source.y; + var dr = Math.sqrt((dx * dx + dy * dy) / d.linknum); + return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y; + } else { + return "M" + d.source.x + "," + d.source.y + "L" + d.target.x + "," + d.target.y; + } + }); + _nodes + .attr("transform", function (d) { return "translate(" + d.x + ", " + d.y + ")"; }); + + edgepaths.attr('d', function (d) { + if (use_curved_edges) { + var dx = d.target.x - d.source.x; + var dy = d.target.y - d.source.y; + var dr = Math.sqrt((dx * dx + dy * dy) / d.linknum); + return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y; + } else { + return "M" + d.source.x + "," + d.source.y + "L" + d.target.x + "," + d.target.y; + } + }); + + edgelabels.attr('transform', function (d) { + if (d.target.x < d.source.x) { + var bbox = this.getBBox(); + + var rx = bbox.x + bbox.width / 2; + var ry = bbox.y + bbox.height / 2; + return 'rotate(180 ' + rx + ' ' + ry + ')'; + } + else { + return 'rotate(0)'; + } + }); + } + + } + + + function get_node_edges(node_id) { + // Return the in and out edges of node with id 'node_id' + var connected_edges = d3.selectAll(".edge").filter( + function (item) { + if (item.source == node_id || item.source.id == node_id) { + return item; + } + else if (item.target == node_id || item.target.id == node_id) { + return item; + } + }); + return connected_edges; + } + + + var graph_events = (function () { + ////////////////////////////////// + // Handling mouse events + + function dragstarted(d) { + if (!d3.event.active) _simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } + + function dragged(d) { + var connected_edges = get_node_edges(d.id); + var f_connected_edges = connected_edges.filter("*:not(.active_edge)") + if (f_connected_edges._groups[0].length == 0) { + d.fx = d3.event.x; + d.fy = d3.event.y; + } + else { + f_connected_edges + .style("stroke-width", function () { return parseInt(d3.select(this).attr("stroke-width")) + 2; }) + .style("stroke-opacity", 1) + .classed("blocking", true) + } + } + + function dragended(d) { + if (!d3.event.active) _simulation.alphaTarget(0); + d3.selectAll(".blocking") + .style("stroke-width", function () { return d3.select(this).attr("stroke-width"); }) + .style("stroke-opacity", function () { return d3.select(this).attr("stroke-opacity"); }) + .classed("blocking", false) + // d.fx = null; + // d.fy = null; + } + + function clicked(d) { + d3.select(".focus_node").remove(); + var input = document.getElementById("freeze-in"); + var isChecked = input.checked; + if (isChecked) infobox.display_info(d); + else { + _simulation.stop(); + // remove the oldest links and nodes + var stop_layer = layers.depth() - 1; + _svg.selectAll(".old_node" + stop_layer).remove(); + _svg.selectAll(".old_edge" + stop_layer).remove(); + _svg.selectAll(".old_edgepath" + stop_layer).remove(); + _svg.selectAll(".old_edgelabel" + stop_layer).remove(); + graphioGremlin.click_query(d); + //infobox.display_info(d); + console.log('event!!') + } + } + + + function pin_it(d) { + d3.event.stopPropagation(); + var node_pin = d3.select(this); + var pinned_node = d3.select(this.parentNode); + //console.log('Pinned!') + //console.log(pinned_node.classed("node")); + if (pinned_node.classed("active_node")) { + if (!pinned_node.classed("pinned")) { + pinned_node.classed("pinned", true); + console.log('Pinned!'); + node_pin.attr("fill", "#000"); + pinned_node.moveToFront(); + } + else { + pinned_node.classed("pinned", false); + console.log('Unpinned!'); + node_pin.attr("fill", graphShapes.node_color); + } + } + } + + return { + dragstarted: dragstarted, + dragged: dragged, + dragended: dragended, + clicked: clicked, + pin_it: pin_it + } + + })(); + + return { + svg_handle: svg_handle, + nodes: nodes, + links: links, + nodes_data: nodes_data, + node_data: node_data, + links_data: links_data, + init: init, + create_arrows: create_arrows, + addzoom: addzoom, + clear: clear, + get_simulation_handle: get_simulation_handle, + simulation_start: simulation_start, + refresh_data: refresh_data, + layers: layers, + graph_events: graph_events + }; + +})(); diff --git a/graph-runtime/src/main/resources/static/scripts/graphioGremlin.js b/graph-runtime/src/main/resources/static/scripts/graphioGremlin.js new file mode 100644 index 0000000000000000000000000000000000000000..0da934516c87c7fb8c5bb510e9b2e0839e52d327 --- /dev/null +++ b/graph-runtime/src/main/resources/static/scripts/graphioGremlin.js @@ -0,0 +1,647 @@ +/* +Copyright 2017 Benjamin RICAUD + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Interface between the visualization and the Gremlin server. + +var traversal_source = getUrlParameter('ts'); +if (traversal_source == null) { + traversal_source = "g" +} + +var graphioGremlin = (function(){ + "use strict"; + + var _node_properties = []; + var _edge_properties = []; + + + function get_node_properties(){ + return _node_properties; + } + function get_edge_properties(){ + return _edge_properties; + } + + function create_single_command(query){ + var equalIndex = query.indexOf("="); + var semiColonIndex = query.indexOf(";"); + if( equalIndex >= 0){ + if(semiColonIndex < 0){ + query = query.substring(equalIndex+1); + } else { + query = query.substring(equalIndex+1,semiColonIndex); + } + } + var returnQuery = query.trim(); +// if(returnQuery.endsWith(".toList();")){ +// returnQuery = returnQuery+".toList();"; +// } + return returnQuery; + } + + function get_graph_info(){ + var gremlin_query_nodes = "nodes = " + traversal_source + ".V().limit(" + limit_graphinfo_request + ").groupCount().by(label);" + var gremlin_query_edges = "edges = " + traversal_source + ".E().limit(" + limit_graphinfo_request + ").groupCount().by(label);" + var gremlin_query_nodes_prop = "nodesprop = " + traversal_source + ".V().limit(" + limit_graphinfo_request + ").valueMap().select(keys).groupCount();" + var gremlin_query_edges_prop = "edgesprop = " + traversal_source + ".E().limit(" + limit_graphinfo_request + ").valueMap().select(keys).groupCount();" + + var gremlin_query = gremlin_query_nodes+gremlin_query_nodes_prop + +gremlin_query_edges+gremlin_query_edges_prop + + "[nodes.toList(),nodesprop.toList(),edges.toList(),edgesprop.toList()]" + // while busy, show we're doing something in the messageArea. + $('#messageArea').html('

(loading)

'); + var message = "" + if(SINGLE_COMMANDS_AND_NO_VARS){ + var node_label_query = create_single_command(gremlin_query_nodes); + var edge_label_query = create_single_command(gremlin_query_edges); + var node_prop_query = create_single_command(gremlin_query_nodes_prop); + var edge_prop_query = create_single_command(gremlin_query_edges_prop); + send_to_server(node_label_query, null, null, null, function(nodeLabels){ + send_to_server(edge_label_query, null, null, null, function(edgeLabels){ + send_to_server(node_prop_query, null, null, null, function(nodeProps){ + send_to_server(edge_prop_query, null, null, null, function(edgeProps){ + var combinedData = [nodeLabels, nodeProps, edgeLabels, edgeProps]; + console.log("Combined data", combinedData); + handle_server_answer(combinedData,'graphInfo',null,message); + }); + }); + }); + }); + } else { + send_to_server(gremlin_query,'graphInfo',null,message) + } + } + + + + function search_query() { + // Query sent to the server when clicking the search button + // + // Preprocess query + let input_string = $('#search_value').val(); + let input_field = $('#search_field').val(); + let label_field = $('#label_field').val(); + let limit_field = $('#limit_field').val(); + let search_type = $('#search_type').val(); + let communication_method = $('#communication_method').val(); + //console.log(input_field) + var filtered_string = input_string;//You may add .replace(/\W+/g, ''); to refuse any character not in the alphabet + if (filtered_string.length>50) filtered_string = filtered_string.substring(0,50); // limit string length + // Translate to Gremlin query + let has_str = ""; + if (label_field !== "") { + has_str = ".hasLabel('" + label_field + "')"; + } + if (input_field !== "" && input_string !== "") { + has_str += ".has('" + input_field + "',"; + switch (search_type) { + case "eq": + if (isInt(input_string)){ + has_str += filtered_string + ")" + } else { + has_str += "'" + filtered_string + "')" + } + break; + case "contains": + has_str += "textContains('" + filtered_string + "'))"; + break; + } + } else if (limit_field === "" || limit_field < 0) { + limit_field = node_limit_per_request; + } + + let gremlin_query_nodes = "nodes = " + traversal_source + ".V()" + has_str; + // Query limit + if (limit_field !== "" && isInt(limit_field) && limit_field > 0) { + gremlin_query_nodes += ".limit(" + limit_field + ")"; + } else { + console.log('Warning: no node limit set for the query. The query may fail if the graph is too big.') + } + gremlin_query_nodes += ".toList();"; + + let gremlin_query_edges = "edges = " + traversal_source + ".V(nodes).aggregate('node').outE().as('edge').inV().where(within('node')).select('edge').toList();"; + let gremlin_query_edges_no_vars = "edges = " + traversal_source + ".V()"+has_str+".aggregate('node').outE().as('edge').inV().where(within('node')).select('edge').toList();"; + //let gremlin_query_edges_no_vars = "edges = " + traversal_source + ".V()"+has_str+".bothE();"; + let gremlin_query = gremlin_query_nodes + gremlin_query_edges + "[nodes,edges]"; + console.log(gremlin_query); + + // while busy, show we're doing something in the messageArea. + $('#messageArea').html('

(loading)

'); + // To display the queries in the message area: + //var message_nodes = "

Node query: '"+gremlin_query_nodes+"'

"; + //var message_edges = "

Edge query: '"+gremlin_query_edges+"'

"; + //var message = message_nodes + message_edges; + var message = ""; + if (SINGLE_COMMANDS_AND_NO_VARS) { + var nodeQuery = create_single_command(gremlin_query_nodes); + var edgeQuery = create_single_command(gremlin_query_edges_no_vars); + console.log("Node query: "+nodeQuery); + console.log("Edge query: "+edgeQuery); + send_to_server(nodeQuery, null, null, null, function(nodeData){ + send_to_server(edgeQuery, null, null, null, function(edgeData){ + var combinedData = [nodeData,edgeData]; + handle_server_answer(combinedData, 'search', null, message); + }); + }); + } else { + send_to_server(gremlin_query,'search',null,message); + } + } + + function isInt(value) { + return !isNaN(value) && + parseInt(Number(value)) == value && + !isNaN(parseInt(value, 10)); + } + function click_query(d) { + // Query sent to the server when a node is clicked + // + var edge_filter = $('#edge_filter').val(); + var communication_method = $('#communication_method').val(); + var id = d.id; + if (typeof id === 'string' || id instanceof String) { // Add quotes if id is a string (not a number). + id = '"'+id+'"'; + } + // Gremlin query + var gremlin_query_nodes = 'nodes = ' + traversal_source + '.V('+id+').as("node").both('+(edge_filter?'"'+edge_filter+'"':'')+').as("node").select(all,"node").unfold()' + // Variant depending on the Gremlin version + if (communication_method == "GraphSON3_4") { + // Version 3.4 + gremlin_query_nodes += ".valueMap().with(WithOptions.tokens)"; + gremlin_query_nodes += '.fold().inject(' + traversal_source + '.V(' + id + ').valueMap().with(WithOptions.tokens)).unfold()'; + } else { + gremlin_query_nodes += '.fold().inject(' + traversal_source + '.V(' + id + ')).unfold()'; + } + //var gremlin_query_nodes = 'nodes = ' + traversal_source + '.V('+id+').as("node").both('+(edge_filter?'"'+edge_filter+'"':'')+').as("node").select(all,"node").unfold().valueMap()' + //gremlin_query_nodes += 'fold().inject(' + traversal_source + '.V('+id+').valueMap()).unfold()' + + // 'inject' is necessary in case of an isolated node ('both' would lead to an empty answer) + console.log('Query for the node and its neigbhors') + console.log(gremlin_query_nodes) + var gremlin_query_edges = "edges = " + traversal_source + ".V("+id+").bothE("+(edge_filter?"'"+edge_filter+"'":"")+")"; + var gremlin_query = gremlin_query_nodes+'\n'+gremlin_query_edges+'\n'+'[nodes.toList(),edges.toList()]' + // while busy, show we're doing something in the messageArea. + $('#messageArea').html('

(loading)

'); + var message = "

Query ID: "+ d.id +"

" + if(SINGLE_COMMANDS_AND_NO_VARS){ + var nodeQuery = create_single_command(gremlin_query_nodes); + var edgeQuery = create_single_command(gremlin_query_edges); + send_to_server(nodeQuery, null, null, null, function(nodeData){ + send_to_server(edgeQuery, null, null, null, function(edgeData){ + var combinedData = [nodeData,edgeData]; + handle_server_answer(combinedData, 'click', d.id, message); + }); + }); + } else { + send_to_server(gremlin_query,'click',d.id,message); + } + } + + function send_to_server(gremlin_query,query_type,active_node,message, callback){ + + let server_address = $('#server_address').val(); + let server_port = $('#server_port').val(); + let COMMUNICATION_PROTOCOL = $('#server_protocol').val(); + if (COMMUNICATION_PROTOCOL == 'REST'){ + let server_url = "" + if(REST_USE_HTTPS){ + server_url = "https://"+server_address+":"+server_port; + } else{ + server_url = "http://"+server_address+":"+server_port; + } + run_ajax_request(gremlin_query,server_url,query_type,active_node,message,callback); + } + else if (COMMUNICATION_PROTOCOL == 'websocket'){ + let server_url = "ws://"+server_address+":"+server_port+"/gremlin" + run_websocket_request(gremlin_query,server_url,query_type,active_node,message,callback); + } + else if (COMMUNICATION_PROTOCOL == 'websockets'){ + let server_url = "wss://"+server_address+":"+server_port+"/gremlin" + run_websocket_request(gremlin_query,server_url,query_type,active_node,message,callback); + } + else { + console.log('Bad communication protocol. Check configuration file. Accept "REST" or "websocket" .') + } + + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // AJAX request for the REST API + //////////////////////////////////////////////////////////////////////////////////////////////// + function run_ajax_request(gremlin_query,server_url,query_type,active_node,message, callback){ + // while busy, show we're doing something in the messageArea. + $('#messageArea').html('

(loading)

'); + + // Get the data from the server + $.ajax({ + type: "POST", + accept: "application/json", + //contentType:"application/json; charset=utf-8", + url: server_url, + //headers: GRAPH_DATABASE_AUTH, + timeout: REST_TIMEOUT, + data: JSON.stringify({"gremlin" : gremlin_query}), + success: function(data, textStatus, jqXHR){ + var Data = data.result.data; + //console.log(Data) + //console.log("Results received") + if(callback){ + callback(Data); + } else { + handle_server_answer(Data,query_type,active_node,message); + } + }, + error: function(result, status, error){ + console.log("Connection failed. "+status); + + // This will hold all error messages, to be printed in the + // output area. + let msgs = []; + + if (query_type == 'editGraph'){ + msgs.push('Problem accessing the database using REST at ' + server_url); + msgs.push('Message: ' + status + ', ' + error); + msgs.push('Possible cause: creating an edge with bad node ids ' + + '(linking nodes not existing in the DB).'); + } else { + msgs.push('Can\'t access database using REST at ' + server_url); + msgs.push('Message: ' + status + ', ' + error); + msgs.push('Check the server configuration ' + + 'or try increasing the REST_TIMEOUT value in the config file.'); + } + + // If a MalformedQueryException is received, user might be + // trying to reach an Amazon Neptune DB. Point them to the + // config file as a probable cause. + if (result.status === 400 + && SINGLE_COMMANDS_AND_NO_VARS === false + && result.hasOwnProperty('responseJSON') + && result.responseJSON.code === 'MalformedQueryException') { + msgs.push('If connecting to an Amazon Neptune databse, ensure that ' + + 'SINGLE_COMMANDS_AND_NO_VARS is set to true in the config file.'); + } + + $('#outputArea').html(msgs.map(function (i) {return '

' + i + '

'}).join('')); + $('#messageArea').html(''); + } + }); + } + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + // Websocket connection + ///////////////////////////////////////////////////////////////////////////////////////////////////// + function run_websocket_request(gremlin_query,server_url,query_type,active_node,message,callback){ + $('#messageArea').html('

(loading)

'); + + var msg = { "requestId": uuidv4(), + "op":"eval", + "processor":"", + "args":{"gremlin": gremlin_query, + "bindings":{}, + "language":"gremlin-groovy"}} + + var data = JSON.stringify(msg); + + var ws = new WebSocket(server_url); + ws.onopen = function (event){ + ws.send(data,{ mask: true}); + }; + ws.onerror = function (err){ + console.log('Connection error using websocket'); + console.log(err); + if (query_type == 'editGraph'){ + $('#outputArea').html("

Connection error using websocket

" + +"

Problem accessing "+server_url+ "

"+ + "

Possible cause: creating a edge with bad node ids "+ + "(linking nodes not existing in the DB).

"); + $('#messageArea').html(''); + } else {$('#outputArea').html("

Connection error using websocket

" + +"

Cannot connect to "+server_url+ "

"); + $('#messageArea').html(''); + } + + }; + ws.onmessage = function (event){ + var response = JSON.parse(event.data); + var code=Number(response.status.code) + if(!isInt(code) || code<200 || code>299) { + $('#outputArea').html(response.status.message); + $('#messageArea').html("Error retrieving data"); + return 1; + } + var data = response.result.data; + if (data == null){ + // No response data expected for flush query, so just validate status code. + if (query_type == 'flushGraph' && response.status.code == 204) { + $('#outputArea').html("

Successfully flushed existing DB data.

"); + $('#messageArea').html(''); + return + } + + if (query_type == 'editGraph'){ + $('#outputArea').html(response.status.message); + $('#messageArea').html('Could not write data to DB.' + + "

Possible cause: creating a edge with bad node ids "+ + "(linking nodes not existing in the DB).

"); + return 1; + } else { + //$('#outputArea').html(response.status.message); + //$('#messageArea').html('Server error. No data.'); + //return 1; + } + } + //console.log(data) + //console.log("Results received") + if(callback){ + callback(data); + } else { + handle_server_answer(data,query_type,active_node,message); + } + }; + } + + // Generate uuid for websocket requestId. Code found here: + // https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript + function uuidv4() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + } + + ////////////////////////////////////////////////////////////////////////////////////////////////// + function handle_server_answer(data,query_type,active_node,message){ + let COMMUNICATION_METHOD = $('#communication_method').val(); + if (query_type == 'editGraph'){ + //console.log(data) + $('#outputArea').html("

Data successfully written to the DB.

"); + $('#messageArea').html(''); + return // TODO handle answer to check if data has been written + } + //console.log(COMMUNICATION_METHOD) + if (COMMUNICATION_METHOD == 'GraphSON3' || COMMUNICATION_METHOD == 'GraphSON3_4'){ + //console.log(data) + data = graphson3to1(data); + var arrange_data = arrange_datav3; + } else if (COMMUNICATION_METHOD == 'GraphSON2'){ + var arrange_data = arrange_datav2; + } else { + console.log('Bad communication protocol. Accept "GraphSON2" or "GraphSON3".' + +' Using default GraphSON3.') + data = graphson3to1(data); + var arrange_data = arrange_datav3; + } + if (!(0 in data)) { + message = 'No data. Check the communication protocol. (Try changing Gremlin version to 3.3.*).' + console.log(message) + $('#outputArea').html(message); + $('#messageArea').html(''); + + } + if (query_type=='graphInfo'){ + infobox.display_graph_info(data); + _node_properties = make_properties_list(data[1][0]); + _edge_properties = make_properties_list(data[3][0]); + change_nav_bar(_node_properties,_edge_properties); + display_properties_bar(_node_properties,'nodes','Node properties:'); + display_properties_bar(_edge_properties,'edges','Edge properties:'); + display_color_choice(_node_properties,'nodes','Node color by:'); + } else { + //console.log(data); + var graph = arrange_data(data); + //console.log(graph) + if (query_type=='click') var center_f = 0; //center_f=0 mean no attraction to the center for the nodes + else if (query_type=='search') var center_f = 1; + else return; + graph_viz.refresh_data(graph,center_f,active_node); + } + + $('#outputArea').html(message); + $('#messageArea').html(''); + } + + + + ////////////////////////////////////////////////////////////////////////////////////////////////// + function make_properties_list(data){ + var prop_dic = {}; + for (var prop_str in data){ + prop_str = prop_str.replace(/[\[\ \"\'\]]/g,''); // get rid of symbols [,",',] and spaces + var prop_list = prop_str.split(','); + //prop_list = prop_list.map(function (e){e=e.slice(1); return e;}); + for (var prop_idx in prop_list){ + prop_dic[prop_list[prop_idx]] = 0; + } + } + var properties_list = []; + for (var key in prop_dic){ + properties_list.push(key); + } + return properties_list; + } + + /////////////////////////////////////////////////// + function idIndex(list,elem) { + // find the element in list with id equal to elem + // return its index or null if there is no + for (var i=0;i response = + testRestTemplate.getForEntity( + base.toString() + "/studiox/api/graph/runtime/v1/lineage/version?version={1}", + String.class, + paramTestValue); + Assertions.assertEquals("Graph Runtime Server Version: " + paramTestValue, response.getBody()); + } +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..ca66a7f4ab9e9c01f63965f648d9c3ca2bca30d1 --- /dev/null +++ b/pom.xml @@ -0,0 +1,331 @@ + + + 4.0.0 + org.studiox + graph + 1.0.5-SNAPSHOT + pom + + + The Apache Software License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + UTF-8 + UTF-8 + UTF-8 + 1.8 + 1.8 + 1.8 + 2.5.5 + 5.3.10 + + + + ${releaseStagingId} + ${releaseRepoName} + + ${releaseRepoUrl} + + + ${snapStagingId} + ${snapRepoName} + + ${snapRepoUrl} + + + + graph-common + graph-janusgraph + graph-nebulagraph + graph-neo4j + graph-persistence + graph-runtime + + + + + junit + junit + 4.13.2 + + + commons-configuration + commons-configuration + 1.10 + + + com.h2database + h2 + 1.4.200 + + + + ch.vorburger.mariaDB4j + mariaDB4j + 2.4.0 + + + mysql + mysql-connector-java + 5.1.49 + + + org.mybatis + mybatis + 3.5.7 + + + com.baomidou + mybatis-plus + 3.4.3.4 + + + com.baomidou + mybatis-plus-boot-starter + 3.4.3.4 + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.2.0 + + + tk.mybatis + mapper-spring-boot-starter + 2.1.5 + + + org.springframework + spring-framework-bom + ${spring.framework.version} + pom + import + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + org.springdoc + springdoc-openapi-ui + 1.5.11 + + + org.studiox.graph + graph-common + ${project.version} + + + org.studiox.graph.janusgraph + janusgraph-common + ${project.version} + + + org.studiox.graph.janusgraph + janusgraph-v0.2.0 + ${project.version} + + + org.studiox.graph.janusgraph + janusgraph-latest + ${project.version} + + + org.studiox.graph.persistence + persistence-model + ${project.version} + + + org.studiox.graph.persistence + persistence-mybatis-tk + ${project.version} + + + com.google.guava + guava + 31.0.1-jre + + + + + src/main/java + target/classes + src/test/java + target/test-classes + + + src/main/resources + + **/* + + false + + + + + src/test/resources + + **/* + + false + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.5.5 + + ZIP + + + + + repackage + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + true + true + UTF-8 + true + ${jdk.version} + ${jdk.version} + ${jdk.version} + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + + org.apache.maven.plugins + maven-dependency-plugin + 3.2.0 + + test + compile + runtime + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.3.1 + + + org.apache.maven.plugins + maven-resources-plugin + 3.2.0 + + + org.apache.maven.plugins + maven-clean-plugin + 3.1.0 + + + org.apache.maven.plugins + maven-install-plugin + 2.5.2 + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + org.apache.maven.plugins + maven-assembly-plugin + 3.3.0 + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + + org.apache.maven.plugins + maven-project-info-reports-plugin + 3.1.2 + + + com.spotify + docker-maven-plugin + 1.2.2 + + + pl.project13.maven + git-commit-id-plugin + 4.9.10 + + + generate + + revision + + + + + true + yyyy.MM.dd HH:mm:ss + true + true + false + + false + false + 40 + + + + + org.owasp + dependency-check-maven + 6.4.1 + + + + check + + + + + + + + + org.apache.maven.plugins + maven-source-plugin + + + compile + + jar-no-fork + + + + + + + diff --git a/scripts/build/build.sh b/scripts/build/build.sh new file mode 100755 index 0000000000000000000000000000000000000000..9389d933e579f35a53297b057c276225d4dcc172 --- /dev/null +++ b/scripts/build/build.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -ex + +export PROJECT_DIR=$(cd $(dirname $0);cd ..;cd ..;pwd) + +cd ${PROJECT_DIR} + +mvn clean install -DskipTests diff --git a/scripts/build/dependency-check.sh b/scripts/build/dependency-check.sh new file mode 100755 index 0000000000000000000000000000000000000000..7aa13879b7a5a3a4b247ba9f2601d37d3f04d94e --- /dev/null +++ b/scripts/build/dependency-check.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -ex + +export PROJECT_DIR=$(cd $(dirname $0);cd ..;cd ..;pwd) + +cd ${PROJECT_DIR} + +mvn clean org.owasp:dependency-check-maven:check -DskipTests diff --git a/scripts/build/dependency-tree.sh b/scripts/build/dependency-tree.sh new file mode 100755 index 0000000000000000000000000000000000000000..1ce06c910dbe14a75c1bbdc810f28d9b241f8da3 --- /dev/null +++ b/scripts/build/dependency-tree.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -ex + +export PROJECT_DIR=$(cd $(dirname $0);cd ..;cd ..;pwd) + +cd ${PROJECT_DIR} + +mvn verify dependency:tree dependency:analyze -DskipTests diff --git a/scripts/build/deploy.sh b/scripts/build/deploy.sh new file mode 100755 index 0000000000000000000000000000000000000000..1b11d90f527ca518b6de1edc77ef32b408bd04e3 --- /dev/null +++ b/scripts/build/deploy.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -ex + +export PROJECT_DIR=$(cd $(dirname $0);cd ..;cd ..;pwd) + +cd ${PROJECT_DIR} + +mvn clean deploy -DskipTests -Dassembly.skipAssembly=true diff --git a/scripts/build/license.sh b/scripts/build/license.sh new file mode 100755 index 0000000000000000000000000000000000000000..698b5fcff905a0d529f382732bf8e7f6ef47e1f4 --- /dev/null +++ b/scripts/build/license.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -ex + +export PROJECT_DIR=$(cd $(dirname $0);cd ..;cd ..;pwd) + +cd ${PROJECT_DIR} + +mvn clean project-info-reports:dependencies diff --git a/scripts/build/test.sh b/scripts/build/test.sh new file mode 100755 index 0000000000000000000000000000000000000000..35b298cd3658d0d23fd17f7ef72ff7a4badd9680 --- /dev/null +++ b/scripts/build/test.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -ex + +export PROJECT_DIR=$(cd $(dirname $0);cd ..;cd ..;pwd) + +cd ${PROJECT_DIR} + +mvn clean test -Dmaven.test.failure.ignore=true diff --git a/scripts/build/version.sh b/scripts/build/version.sh new file mode 100755 index 0000000000000000000000000000000000000000..0d0886ad73b61b26b50ab87a5096c111a546f751 --- /dev/null +++ b/scripts/build/version.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +set -ex + +export PROJECT_DIR=$(cd $(dirname $0);cd ..;cd ..;pwd) + +cd ${PROJECT_DIR} + +mvn versions:set -DnewVersion=$1 diff --git a/scripts/database/mysql_ddl.sql b/scripts/database/mysql_ddl.sql new file mode 100644 index 0000000000000000000000000000000000000000..48cc95eeb62d757e9fe145bcf22fb758fd4eb86c --- /dev/null +++ b/scripts/database/mysql_ddl.sql @@ -0,0 +1,53 @@ +create table if not exists `graph_vertex` ( + `id` bigint(20) unsigned not null auto_increment comment '主键', + `uniq` varchar(255) not null default '' comment '顶点的唯一标识', + `type` varchar(20) not null default '' comment '顶点的类型', + `owner` varchar(50) not null default '' comment '所有者', + `janus_vertex_id` bigint(20) not null default 0 comment '顶点存储 JanusGraph Vertex Id', + `neo4j_vertex_id` bigint(20) not null default 0 comment '顶点存储 Neo4j Vertex Id', + `nebula_vertex_id` bigint(20) not null default 0 comment '顶点存储 NebulaGraph Vertex Id', + `temp` tinyint(1) not null default 0 comment '是否临时点(0:否 1:是),只存在数据库,不存在图中', + `deleted` tinyint(1) not null default 0 comment '是否删除(0:否 1:是)', + `create_time` timestamp not null default current_timestamp comment '顶点的创建时间', + `update_time` timestamp not null default current_timestamp comment '顶点的修改时间', + primary key (`id`), + unique key (`uniq`), + index (`janus_vertex_id`), + index (`neo4j_vertex_id`), + index (`nebula_vertex_id`) +) engine=innodb auto_increment=1 default charset=utf8 comment='图中顶点集合'; + +create table if not exists `graph_edge` ( + `id` bigint(20) unsigned not null auto_increment comment '主键', + `content` mediumtext not null comment '边的记录内容', + `content_md5` char(32) not null default '' comment '边的记录内容MD5值', + `type` varchar(20) not null default '' comment '边的类型', + `owner` varchar(50) not null default '' comment '所有者', + `source_vertex_uniq` varchar(255) not null default '' comment '边的输入顶点的唯一标识', + `target_vertex_uniq` varchar(255) not null default '' comment '边的输出顶点的唯一标识', + `janus_edge_id` varchar(50) not null default '' comment '边存储 JanusGraph Edge Id', + `neo4j_edge_id` bigint(20) not null default 0 comment '边存储 Neo4j Edge Id', + `nebula_edge_id` varchar(50) not null default '' comment '边存储 NebulaGraph Edge Id', + `source_janus_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 JanusGraph Vertex Id', + `target_janus_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 JanusGraph Vertex Id', + `source_neo4j_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 Neo4j Vertex Id', + `target_neo4j_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 Neo4j Vertex Id', + `source_nebula_vertex_id` bigint(20) not null default 0 comment '边的输入顶点 NebulaGraph Vertex Id', + `target_nebula_vertex_id` bigint(20) not null default 0 comment '边的输出顶点 NebulaGraph Vertex Id', + `deleted` tinyint(1) not null default 0 comment '是否删除(0:否 1:是)', + `create_time` timestamp not null default current_timestamp comment '边的创建时间', + `update_time` timestamp not null default current_timestamp comment '边的修改时间', + primary key (`id`), + index (`content_md5`), + index (`source_vertex_uniq`), + index (`target_vertex_uniq`), + index (`janus_edge_id`), + index (`neo4j_edge_id`), + index (`nebula_edge_id`), + index (`source_janus_vertex_id`), + index (`target_janus_vertex_id`), + index (`source_neo4j_vertex_id`), + index (`target_neo4j_vertex_id`), + index (`source_nebula_vertex_id`), + index (`target_nebula_vertex_id`) +) engine=innodb auto_increment=1 default charset=utf8 comment='图中边集合';