@@ -5,7 +5,7 @@ import DBInterface: execute
5
5
using JSON3: JSON3
6
6
using EasyConfig
7
7
8
- export DB, Node, Edge, find_nodes, find_edges
8
+ export DB, Node, Edge
9
9
10
10
# -----------------------------------------------------------------------------# utils
11
11
function single_result_execute (db, stmt, args... )
@@ -30,102 +30,120 @@ end
30
30
# Relationships always has a direction (one direction).
31
31
# Relationships must have a type (one type) to define (classify) what type of relationship they are.
32
32
33
+ # Nouns-nodes, Adjectives-properties, Verbs-relationship, Adverbs-properties on relationship
34
+
35
+ # Property Graph Model on page 4
36
+ # https://s3.amazonaws.com/artifacts.opencypher.org/openCypher9.pdf
37
+
33
38
struct Node
34
39
id:: Int
35
40
labels:: Vector{String}
36
41
props:: Config
37
42
end
38
43
Node (id:: Int , labels:: String... ; props... ) = Node (id, collect (labels), Config (props))
44
+ Node (row:: SQLite.Row ) = Node (row. id, split (row. labels, ' ;' ), JSON3. read (row. props, Config))
39
45
function Base. show (io:: IO , o:: Node )
40
- print (io, " Node($(join (o. labels, " , " )) ) | $(o. id) | nprops=$(length (o. props)) " )
46
+ printstyled (io, " Node(" , color= :light_cyan )
47
+ printstyled (io, join (repr .(o. labels), " , " ), color= :light_yellow )
48
+ printstyled (io, " , " , o. id, color= :light_black )
49
+ printstyled (io, " )" , color= :light_cyan )
50
+ if isempty (o. props)
51
+ printstyled (io, " with no props" , color= :light_black )
52
+ else
53
+ printstyled (io, " with props: " , color= :light_black )
54
+ printstyled (io, join (keys (o. props), " , " ), color= :light_green )
55
+ end
41
56
end
57
+ args (n:: Node ) = (n. id, join (n. labels, ' ;' ), JSON3. write (n. props))
42
58
43
59
44
- struct Relationship
45
- source_id :: Int
46
- target_id :: Int
60
+ struct Edge
61
+ source :: Int
62
+ target :: Int
47
63
type:: String
48
64
props:: Config
49
65
end
50
- Relationship (src:: Int , tgt:: Int , type:: String ; props... ) = Relationship (src, tgt, type, Config (props))
51
- function Base. show (io:: IO , o:: Relationship )
52
- print (io, " Relationship($(o. type) ) | $(o. source_id) → $(o. target_id) | nprops=$(length (o. props)) " )
66
+ Edge (src:: Int , tgt:: Int , type:: String ; props... ) = Edge (src, tgt, type, Config (props))
67
+ Edge (row:: SQLite.Row ) = Edge (row. source, row. target, row. type, JSON3. read (row. props, Config))
68
+ function Base. show (io:: IO , o:: Edge )
69
+ printstyled (io, " Edge(" , color= :light_cyan )
70
+ printstyled (io, repr (o. type), color= :light_yellow )
71
+ printstyled (io, " , $(o. source) → $(o. target) " , color= :light_black )
72
+ printstyled (io, " )" , color= :light_cyan )
73
+ if isempty (o. props)
74
+ printstyled (io, " with no props" , color= :light_black )
75
+ else
76
+ printstyled (io, " with props: " , color= :light_black )
77
+ printstyled (io, join (keys (o. props), " , " ), color= :light_green )
78
+ end
79
+ end
80
+ args (e:: Edge ) = (e. source, e. target, join (e. type, ' ;' ), JSON3. write (e. props))
81
+
82
+ # -----------------------------------------------------------------------------# DB
83
+ struct DB
84
+ sqlitedb:: SQLite.DB
85
+
86
+ function DB (file:: String = " :memory:" )
87
+ db = SQLite. DB (file)
88
+ foreach (x -> execute (db, x), [
89
+ # nodes
90
+ " CREATE TABLE IF NOT EXISTS nodes (
91
+ id INTEGER NOT NULL UNIQUE PRIMARY KEY,
92
+ labels TEXT,
93
+ props TEXT,
94
+ UNIQUE(id) ON CONFLICT REPLACE
95
+ );" ,
96
+ " CREATE INDEX IF NOT EXISTS id_idx ON nodes(id);" ,
97
+ " CREATE INDEX IF NOT EXISTS labels_idx ON nodes(labels);" ,
98
+
99
+ # edges
100
+ " CREATE TABLE IF NOT EXISTS edges (
101
+ source INTEGER NOT NULL,
102
+ target INTEGER NOT NULL,
103
+ type TEXT NOT NULL,
104
+ props TEXT,
105
+ FOREIGN KEY(source) REFERENCES nodes(id),
106
+ FOREIGN KEY(target) REFERENCES nodes(id)
107
+ );" ,
108
+ " CREATE INDEX IF NOT EXISTS source_idx ON edges(source);" ,
109
+ " CREATE INDEX IF NOT EXISTS target_idx ON edges(target);" ,
110
+ " CREATE INDEX IF NOT EXISTS type_idx ON edges(type);" ,
111
+ ])
112
+ new (db)
113
+ end
114
+ end
115
+ function Base. show (io:: IO , db:: DB )
116
+ print (io, " SQLiteGraph.DB(\" $(db. sqlitedb. file) \" ) ($(n_nodes (db)) nodes, $(n_edges (db)) edges)" )
53
117
end
54
118
119
+ execute (db:: DB , args... ; kw... ) = execute (db. sqlitedb, args... ; kw... )
55
120
121
+ n_nodes (db:: DB ) = single_result_execute (db, " SELECT Count(*) FROM nodes" )
122
+ n_edges (db:: DB ) = single_result_execute (db, " SELECT Count(*) FROM edges" )
56
123
124
+ Base. length (db:: DB ) = n_nodes (db)
125
+ Base. size (db:: DB ) = (n_nodes= n_nodes (db), n_edges= n_edges (db))
57
126
58
- # #-----------------------------------------------------------------------------# Node
59
- # struct Node
60
- # id::Int
61
- # props::Config
62
- # end
63
- # Node(id::Integer; props...) = Node(id, Config(props))
64
- # Node(id::Integer, props::String) = Node(id, JSON3.read(props, Config))
65
- # function Base.show(io::IO, o::Node)
66
- # props = getfield(o, :props)
67
- # printstyled(io, "Node $(getfield(o, :id))", color=:light_cyan)
68
- # delete_empty!(props)
69
- # print_props(io, props)
70
- # end
71
- # Base.getproperty(o::Node, x::Symbol) = getfield(o, :props)[x]
72
- # Base.setproperty!(o::Node, x::Symbol, val) = setproperty!(getfield(o, :props), x, val)
73
-
74
- # #-----------------------------------------------------------------------------# Edge
75
- # struct Edge
76
- # source::Int
77
- # target::Int
78
- # props::Config
79
- # end
80
- # Edge(src::Integer, tgt::Integer; kw...) = Edge(src, tgt, Config(kw))
81
- # Edge(src::Integer, tgt::Integer, txt::String) = Edge(src, tgt, JSON3.read(txt, Config))
82
- # function Base.show(io::IO, o::Edge)
83
- # props = getfield(o, :props)
84
- # printstyled(io, "Edge $(getfield(o, :source)) → $(getfield(o, :target))", color=:light_cyan)
85
- # delete_empty!(props)
86
- # print_props(io, props)
87
- # end
88
- # Base.getproperty(o::Edge, x::Symbol) = getfield(o, :props)[x]
89
- # Base.setproperty!(o::Edge, x::Symbol, val) = setproperty!(getfield(o, :props), x, val)
90
-
91
-
92
- # #-----------------------------------------------------------------------------# DB
93
- # struct DB
94
- # sqlitedb::SQLite.DB
95
-
96
- # function DB(file::String = ":memory:")
97
- # db = SQLite.DB(file)
98
- # foreach(x -> execute(db, x), [
99
- # "CREATE TABLE IF NOT EXISTS nodes (
100
- # id INTEGER NOT NULL UNIQUE,
101
- # props TEXT,
102
- # UNIQUE(id) ON CONFLICT REPLACE
103
- # );",
104
- # "CREATE INDEX IF NOT EXISTS id_idx ON nodes(id);",
105
- # "CREATE TABLE IF NOT EXISTS edges (
106
- # source INTEGER,
107
- # target INTEGER,
108
- # props TEXT,
109
- # UNIQUE(source, target) ON CONFLICT REPLACE,
110
- # FOREIGN KEY(source) REFERENCES nodes(id),
111
- # FOREIGN KEY(target) REFERENCES nodes(id)
112
- # );",
113
- # "CREATE INDEX IF NOT EXISTS source_idx ON edges(source);",
114
- # "CREATE INDEX IF NOT EXISTS target_idx ON edges(target);"
115
- # ])
116
- # new(db)
117
- # end
118
- # end
119
- # function Base.show(io::IO, db::DB)
120
- # print(io, "SQLiteGraph.DB(\"$(db.sqlitedb.file)\") ($(n_nodes(db)) nodes, $(n_edges(db)) edges)")
121
- # end
127
+ # -----------------------------------------------------------------------------# nodes
128
+ function Base. getindex (db:: DB , id:: Integer )
129
+ res = execute (db, " SELECT * FROM nodes WHERE id = ?" , (id,))
130
+ isempty (res) ? throw (BoundsError (db, id)) : Node (first (res))
131
+ end
132
+
133
+ function Base. push! (db:: DB , node:: Node )
134
+ res = execute (db, " SELECT * FROM nodes WHERE id=?" , (node. id,))
135
+ isempty (res) ?
136
+ execute (db, " INSERT INTO nodes VALUES(?, ?, json(?))" , args (node)) :
137
+ error (" Node with id=$(node. id) already exists in graph. Use `insert!` to overwrite." )
138
+ db
139
+ end
140
+ function Base. insert! (db:: DB , node:: Node )
141
+ execute (db, " INSERT INTO nodes VALUES(?, ?, json(?)) ON CONFLICT(id) DO UPDATE SET labels=excluded.labels, props=excluded.props" , args (node))
142
+ db
143
+ end
122
144
123
- # execute(db::DB, args...; kw...) = execute(db.sqlitedb, args...; kw...)
124
145
125
- # n_nodes(db::DB) = single_result_execute(db, "SELECT Count(*) FROM nodes")
126
- # n_edges(db::DB) = single_result_execute(db, "SELECT Count(*) FROM edges")
127
146
128
- # init!(db::DB, n::Integer) = (foreach(id -> setindex!(db, nothing, id), 1:n); db)
129
147
130
148
# #-----------------------------------------------------------------------------# get/set nodes
131
149
# function Base.setindex!(db::DB, props, id::Integer)
0 commit comments