Skip to content
松戸 一真 / kazuma matsudo edited this page Feb 4, 2024 · 36 revisions

for Developers

Contribute

Pull Request Process

  1. Ensure all test are passed.
  2. Update the README.md when you add new features.
  3. If you would like, please list your account in the Contributors field of README.md.
  4. After the author submits an approve, the pull request is merged at the author's discretion.

Environment setup

  1. check out GraphDB2RDB repository
    % gh repo clone kazumatsudo/GraphDB2RDB
  2. launch your GraphDB Server
    % docker compose up -d
  3. store test data
    % sbt "runMain GenerateTestData"

Test

% sbt test

Directory Structure

/
├ docker-compose.yml                    ... GraphDB for development
└ src/
  ├ main/
  │ ├ resources/
  │ │ ├ conf/                           ... access settings to GraphDB
  │ │ ├ application.conf                ... environment variables
  │ │ ├ using_key_list_file.json        ... use when analysis method is "using_specific_key_list"
  │ │ └ using_key_list_file_schema.json ...   schema for the above
  │ └ scala/
  │     ├ Main.scala                    ... Entrypoint
  │     ├ domain/
  │     │ ├ graph/                      ... result of loading GraphDB
  │     │ └ table/                      ... case classes for conversion SQL
  │     │     ├ ddl/                    ... case classes for conversion DDL
  │     │     │ └ attribute/            ...   DDL attributes (ex. Primary key)
  │     │     └ dml/                    ... case classes for conversion DML
  │     ├ infrastructure/               ... get vertices/edges from GraphDB
  │     ├ usecase/                      ... some methods to convert SQL from GraphDB
  │     └ utils/                        ... utility classes
  └ test/
    └ scala/
      └ GenerateTestData.scala          ... generate test data for develop

Class Diagram

domain

graph

classDiagram
    class GraphElement {
        <<trait>>
        +toDdl(): TableList
        +toDml(): RecordList
    }

    class GraphEdge {
        -value: Edge
        -inVertex: Vertex
        -inVertexId: AnyRef
        -inVertexLabel: String
        -outVertex: Vertex
        -outVertexId: AnyRef
        -outVertexLabel: String
        -config: Config
        -tableName: TableName
        -columnNamePrefixProperty: String
        -columnNamePrefixLabel: String
        -id: AnyRef
        +toDdl(): TableList
        +toDml(): RecordList
    }

    class GraphVertex {
        -value: Vertex
        -config: Config
        -tableName: TableName
        -columnNamePrefixProperty: String
        -columnNamePrefixLabel: String
        +id: AnyRef
        +toDdl(): TableList
        +toDml(): RecordList
    }

    GraphEdge ..|> GraphElement
    GraphVertex ..|> GraphElement
Loading

table

classDiagram
    class TableList {
        -value: Map[TableName, Taple2[ColumnList, TableAttributes]]
        +merge(target: TableList, checkUnique: Boolean): TableList
        +toSqlSentence(): View[String]
    }
    class TableName {
        -value: String
        +toSqlSentence(): String
    }
    class TableAttributes {
        -primaryKey: PrimaryKey
        -uniqueIndex: UniqueIndex
        +merge(target: TableAttributes, checkUnique: Boolean): TableAttributes
        +toSqlSentenceSeq(): View[String]
    }

    class PrimaryKey {
        -value: Set[ColumnName]
        +merge(target: PrimaryKey): PrimaryKey
        +toSqlSentence(): String
    }
    class ForeignKey {
        -value: Map[ColumnName, Taple2[TableName, ColumnName]]
        +merge(target: ForeignKey, checkUnique: Boolean): ForeignKey
        +toSqlSentenceView: View[String]
    }
    class UniqueIndex {
        -value: Map[UniqueIndexName, Set[ColumnName]]
        +merge(target: UniqueIndex, checkUnique: Boolean): UniqueIndex
        +toSqlSentenceView: View[String]
    }
    class UniqueIndexName {
        -value: String
        +toSqlSentence: String
    }

    class ColumnList {
        -value: Map[ColumnName, ColumnType]
        +merge(target: ColumnList): ColumnList
        +toSqlSentenceView(): View[String]
    }
    class ColumnName {
        -value: String
        +toSqlSentence(): String
    }
    class ColumnType {
        <<trait>>
        +merge(a: ColumnType, b: ColumnType): ColumnType
        +toSqlSentence(): String
    }
    class ColumnTypeBoolean {
        +length: ColumnLength
        +toSqlSentence(): String
    }
    class ColumnTypeByte {
        -length: ColumnLength
        +toSqlSentence(): String
    }
    class ColumnTypeShort {
        -length: ColumnLength
        +toSqlSentence(): String
    }
    class ColumnTypeInt {
        -length: ColumnLength
        +toSqlSentence(): String
    }
    class ColumnTypeLong {
        -length: ColumnLength
        +toSqlSentence(): String
    }
    class ColumnTypeFloat {
        -length: ColumnLength
        +toSqlSentence(): String
    }
    class ColumnTypeDouble {
        -length: ColumnLength
        +toSqlSentence(): String
    }
    class ColumnTypeUUID {
        +length: ColumnLength
        +toSqlSentence(): String
    }
    class ColumnTypeDate {
        -length: ColumnLength
        +toSqlSentence(): String
    }
    class ColumnTypeCharacter {
        -value: ColumnLength
        +toSqlSentence(): String
    }
    class ColumnTypeString {
        -value: ColumnLength
        +toSqlSentence(): String
    }
    class ColumnTypeUnknown {
        +toSqlSentence(): String
    }
    class ColumnLength {
        -value: Int
        -thresholdText: Int
        +max(target: ColumnLength): ColumnLength
        +needToUseMediumText: Boolean
        +toSqlSentence(): String
    }

    TableName "1" --* "1" TableList
    
    TableAttributes "*" --* "1" TableList
    PrimaryKey "1" --* "1" TableAttributes
    ForeignKey "*" --* "1" TableAttributes
    UniqueIndex "*" --* "1" TableAttributes
    UniqueIndexName "1" --* "1" UniqueIndex

    ColumnList "*" --* "1" TableList
    ColumnName "1" --* "1" ColumnList
    ColumnType "1" --* "1" ColumnList
    ColumnLength "0..1" --* "1" ColumnType

    ColumnTypeBoolean ..|> ColumnType
    ColumnTypeByte ..|> ColumnType
    ColumnTypeShort ..|> ColumnType
    ColumnTypeInt ..|> ColumnType
    ColumnTypeLong ..|> ColumnType
    ColumnTypeFloat ..|> ColumnType
    ColumnTypeDouble ..|> ColumnType
    ColumnTypeUUID ..|> ColumnType
    ColumnTypeDate ..|> ColumnType
    ColumnTypeCharacter ..|> ColumnType
    ColumnTypeString ..|> ColumnType
    ColumnTypeUnknown ..|> ColumnType

    class RecordList {
        -value: Map[RecordKey, RecordValue]
        +merge(target: RecordList, checkUnique: Boolean): RecordList
        +toSqlSentence(): View[String]
    }
    class RecordKey {
        -value: Tuple2[TableName, RecordId]
        +toSqlSentence(): String
    }
    class RecordId {
        -value: Any
    }
    class RecordValue {
        -value: Map[String, Any]
        +toSqlSentence(): (String, String)
        +to(value: Any, callbackBoolean: Boolean => T, callbackByte: Byte => T, callbackShort: Short => T, callbackInt: Int => T, callbackLong: Long => T, callbackFloat: Float => T, callbackDouble: Double => T, callbackUuid: UUID => T, callbackDate: Instant => T, callbackChar: Char => T, callbackString: String => T, callbackRelationIdentifier: RelationIdentifier => T, callbackUnknown: Any => T): T
    }

    RecordKey "*" --* "1" RecordList
    RecordValue "*" --* "1" RecordList
    TableName "1" --* "1" RecordKey
    RecordId "1" --* "1" RecordKey
Loading

infrastructure

classDiagram
    class EdgeQuery {
        -g: GraphTraversalSource
        -config: Config
        +countAll(): Future[Long]
        +getInEdgeList(vertex: GraphVertex): Future[Seq[GraphEdge]]
        +getList(start: Int, count: Int): Future[Seq[GraphEdge]]
        +getOutEdgeList(vertex: GraphVertex): Future[Seq[GraphEdge]]
    }
    class VertexQuery {
        -g: GraphTraversalSource
        -config: Config
        +countAll(): Future[Long]
        +getInVertexList(edge: GraphEdge): Future[Seq[GraphVertex]]
        +getList(start: Int, count: Int): Future[Seq[GraphVertex]]
        +getListByPropertyKey(label: String, key: String, value: Any): Future[Seq[GraphVertex]]
        +getOutVertexList(edge: GraphEdge): Future[Seq[GraphVertex]]
    }
Loading

usecase

classDiagram
    class UsecaseBase {
        <<trait>>
        -g: GraphTraversalSource
        -config: Config
        -toDdl(value: View[GraphElement], checkUnique: Boolean): Future[TableList]
        -toDml(value: View[GraphElement], checkUnique: Boolean): Future[RecordList]
        +execute(checkUnique: Boolean): Future[UsecaseResponse]
    }
    class ByExaustiveSearch {
        -g: GraphTraversalSource
        -config: Config
        +execute(checkUnique: Boolean): Future[UsecaseResponse]
    }
    class UsingSpecificKeyList {
        -g: GraphTraversalSource
        -config: Config
        -value: UsingSpecificKeyListRequest
        +execute(checkUnique: Boolean): Future[UsecaseResponse]
    }
    class UsecaseResponse {
        +verticesDdl: TableList,
        +verticesDml: RecordList,
        +edgesDdl: TableList,
        +edgesDml: RecordList
    }
    class UsingSpecificKeyListRequest {
        +value: Seq[UsingSpecificKeyListRequestLabel]
    }
    class UsingSpecificKeyListRequestLabel {
        +label: String
        +value: Seq[UsingSpecificKeyListRequestKey]
    }
    class UsingSpecificKeyListRequestKey {
        +key: String
        +value: Seq[Any]
    }

    UsingSpecificKeyListRequest "1" --* "1" UsingSpecificKeyList
    UsingSpecificKeyListRequestLabel "*" --* "1" UsingSpecificKeyListRequest
    UsingSpecificKeyListRequestKey "*" --* "1" UsingSpecificKeyListRequestLabel

    UsecaseResponse "1" --* "1" UsecaseBase
    UsecaseResponse "1" --* "1" ByExaustiveSearch
    UsecaseResponse "1" --* "1" UsingSpecificKeyList

    ByExaustiveSearch ..|> UsecaseBase
    UsingSpecificKeyList ..|> UsecaseBase
Loading

utility

classDiagram
    class FileUtility {
        +readJson(filePath: String): Try[String]
        +writeJson(directoryPath: String, filename: String, jsonString: String): Unit
        +writeSql(directoryPath: String, filename: String, sqlSentenceList: => View[String]): Unit
    }
    class JsonUtility {
        +readForUsingSpecificKeyListRequest(jsonString: String): Try[UsingSpecificKeyListRequest]
        +writeForUsingSpecificKeyListRequest(request: UsingSpecificKeyListRequest): String
    }
Loading

Sequence Diagram

ByExaustiveSearch

sequenceDiagram
    Main->>ByExaustiveSearch: execute ByExaustiveSearch usecase
    ByExaustiveSearch->>(Vertex|Edge)Query: get all vertices / edges
    (Vertex|Edge)Query->>ByExaustiveSearch: return all vertices / edges
    ByExaustiveSearch->>Graph(Vertex|Edge): analyze vertices / edges to convert DDL and DML
    Graph(Vertex|Edge)->>ByExaustiveSearch: return analysis result
    ByExaustiveSearch->>Main: return analysis result
    Main->>FileUtility: try to output SQL
Loading

UsingSpecificKeyList

sequenceDiagram
    Main->>UsingSpecificKeyList: execute UsingSpecificKeyList usecase

    loop get connected vertices/edges
        UsingSpecificKeyList->>VertexQuery: get specific vertices
        VertexQuery->>UsingSpecificKeyList: return vertices
        UsingSpecificKeyList->>EdgeQuery: get in edges and out edges from got vertices
        EdgeQuery->>UsingSpecificKeyList: return in edges and out edges
    end

    UsingSpecificKeyList->>Graph(Vertex|Edge): analyze vertices / edges to convert DDL and DML
    Graph(Vertex|Edge)->>UsingSpecificKeyList: return analysis result
    UsingSpecificKeyList->>Main: return analysis result
    Main->>FileUtility: try to output SQL
Loading