@@ -28,20 +28,14 @@ struct Node
28
28
props:: Config
29
29
end
30
30
Node (id:: Int , labels:: String... ; props... ) = Node (id, collect (labels), Config (props))
31
- Node (row:: SQLite.Row ) = Node (row. id, split (row. labels, ' ;' ), JSON3. read (row. props, Config))
31
+ Node (row:: SQLite.Row ) = Node (row. id, split (row. labels, ' ;' , keepempty = false ), JSON3. read (row. props, Config))
32
32
function Base. show (io:: IO , o:: Node )
33
- printstyled (io, " Node(" , color= :light_cyan )
34
- printstyled (io, join (repr .(o. labels), " , " ), color= :light_yellow )
35
- printstyled (io, " , " , o. id, color= :light_black )
36
- printstyled (io, " )" , color= :light_cyan )
37
- if isempty (o. props)
38
- printstyled (io, " with no props" , color= :light_black )
39
- else
40
- printstyled (io, " with props: " , color= :light_black )
41
- printstyled (io, join (keys (o. props), " , " ), color= :light_green )
42
- end
33
+ print (io, " Node($(o. id) " )
34
+ ! isempty (o. labels) && print (io, " , " , join (repr .(o. labels), " , " ))
35
+ ! isempty (o. props) && print (io, " ; " , (" $k =$v " for (k,v) in pairs (o. props)). .. )
36
+ print (io, ' )' )
43
37
end
44
- args (n:: Node ) = (n. id, join (n. labels, ' ;' ), JSON3. write (n. props))
38
+ args (n:: Node ) = (n. id, isempty (n . labels) ? " " : join (n. labels, ' ;' ), JSON3. write (n. props))
45
39
Base.:(== )(a:: Node , b:: Node ) = all (getfield (a,f) == getfield (b,f) for f in fieldnames (Node))
46
40
47
41
54
48
Edge (src:: Int , tgt:: Int , type:: String ; props... ) = Edge (src, tgt, type, Config (props))
55
49
Edge (row:: SQLite.Row ) = Edge (row. source, row. target, row. type, JSON3. read (row. props, Config))
56
50
function Base. show (io:: IO , o:: Edge )
57
- printstyled (io, " Edge(" , color= :light_cyan )
58
- printstyled (io, repr (o. type), color= :light_yellow )
59
- printstyled (io, " , $(o. source) → $(o. target) " , color= :light_black )
60
- printstyled (io, " )" , color= :light_cyan )
61
- if isempty (o. props)
62
- printstyled (io, " with no props" , color= :light_black )
63
- else
64
- printstyled (io, " with props: " , color= :light_black )
65
- printstyled (io, join (keys (o. props), " , " ), color= :light_green )
66
- end
51
+ print (io, " Edge($(o. source) , $(o. target) , " , repr (o. type))
52
+ ! isempty (o. props) && print (io, " ; " , (" $k =$v " for (k,v) in pairs (o. props)). .. )
53
+ print (io, ' )' )
67
54
end
68
55
args (e:: Edge ) = (e. source, e. target, e. type, JSON3. write (e. props))
69
56
Base.:(== )(a:: Edge , b:: Edge ) = all (getfield (a,f) == getfield (b,f) for f in fieldnames (Edge))
@@ -76,30 +63,25 @@ struct DB
76
63
function DB (file:: String = " :memory:" )
77
64
db = SQLite. DB (file)
78
65
foreach (x -> execute (db, x), [
66
+ " PRAGMA foreign_keys = ON;" ,
79
67
# nodes
80
68
" CREATE TABLE IF NOT EXISTS nodes (
81
69
id INTEGER NOT NULL UNIQUE PRIMARY KEY,
82
- labels TEXT,
83
- props TEXT,
84
- UNIQUE(id) ON CONFLICT REPLACE
70
+ labels TEXT NOT NULL,
71
+ props TEXT NOT NULL
85
72
);" ,
86
- " CREATE INDEX IF NOT EXISTS id_idx ON nodes(id);" ,
87
- " CREATE INDEX IF NOT EXISTS labels_idx ON nodes(labels);" ,
88
73
89
74
# edges
90
75
" CREATE TABLE IF NOT EXISTS edges (
91
- source INTEGER NOT NULL,
92
- target INTEGER NOT NULL,
76
+ source INTEGER NOT NULL REFERENCES nodes(id) ,
77
+ target INTEGER NOT NULL REFERENCES nodes(id) ,
93
78
type TEXT NOT NULL,
94
- props TEXT,
95
- FOREIGN KEY(source) REFERENCES nodes(id),
96
- FOREIGN KEY(target) REFERENCES nodes(id),
97
- UNIQUE(source, target, type)
79
+ props TEXT NOT NULL,
80
+ PRIMARY KEY (source, target, type)
98
81
);" ,
99
82
" CREATE INDEX IF NOT EXISTS source_idx ON edges(source);" ,
100
83
" CREATE INDEX IF NOT EXISTS target_idx ON edges(target);" ,
101
84
" CREATE INDEX IF NOT EXISTS type_idx ON edges(type);" ,
102
- " CREATE INDEX IF NOT EXISTS source_target_type_idx ON edges(source, target, type);"
103
85
])
104
86
new (db)
105
87
end
@@ -114,16 +96,33 @@ n_nodes(db::DB) = single_result_execute(db, "SELECT Count(*) FROM nodes")
114
96
n_edges (db:: DB ) = single_result_execute (db, " SELECT Count(*) FROM edges" )
115
97
116
98
Base. length (db:: DB ) = n_nodes (db)
117
- Base. size (db:: DB ) = (n_nodes= n_nodes (db), n_edges= n_edges (db))
99
+ Base. size (db:: DB ) = (nodes= n_nodes (db), edges= n_edges (db))
100
+ Base. lastindex (db:: DB ) = length (db)
101
+ Base. axes (db:: DB , i) = size (db)[i]
102
+
103
+ Broadcast. broadcastable (db:: DB ) = Ref (db)
104
+
105
+ # -----------------------------------------------------------------------------# insert!
106
+ function Base. insert! (db:: DB , node:: Node )
107
+ execute (db, " INSERT INTO nodes VALUES(?, ?, json(?))" , args (node))
108
+ db
109
+ end
110
+ function Base. insert! (db:: DB , edge:: Edge )
111
+ execute (db, " INSERT INTO edges VALUES(?, ?, ?, json(?))" , args (edge))
112
+ db
113
+ end
118
114
119
- # -----------------------------------------------------------------------------# nodes
120
- function Base. push! (db:: DB , node:: Node ; upsert= false )
121
- upsert ?
122
- execute (db, " INSERT INTO nodes VALUES(?, ?, json(?)) ON CONFLICT(id) DO UPDATE SET labels=excluded.labels, props=excluded.props" , args (node)) :
123
- execute (db, " INSERT INTO nodes VALUES(?, ?, json(?))" , args (node))
115
+ # -----------------------------------------------------------------------------# replace!
116
+ function Base. replace! (db:: DB , node:: Node )
117
+ execute (db, " INSERT INTO nodes VALUES(?, ?, json(?)) ON CONFLICT(id) DO UPDATE SET labels=excluded.labels, props=excluded.props" , args (node))
118
+ db
119
+ end
120
+ function Base. replace! (db:: DB , edge:: Edge )
121
+ execute (db, " INSERT INTO edges VALUES(?, ?, ?, json(?)) ON CONFLICT(source,target,type) DO UPDATE SET props=excluded.props" , args (edge))
124
122
db
125
123
end
126
124
125
+ # -----------------------------------------------------------------------------# getindex (Node)
127
126
function Base. getindex (db:: DB , id:: Integer )
128
127
res = execute (db, " SELECT * FROM nodes WHERE id = ?" , (id,))
129
128
isempty (res) ? error (" Node $id does not exist." ) : Node (first (res))
@@ -133,52 +132,45 @@ function Base.getindex(db::DB, ::Colon)
133
132
isempty (res) ? error (" No nodes exist yet." ) : (Node (row) for row in res)
134
133
end
135
134
136
-
137
- # -----------------------------------------------------------------------------# edges
138
- function Base. push! (db:: DB , edge:: Edge ; upsert= false )
139
- i, j = edge. source, edge. target
140
- check = single_result_execute (db, " SELECT COUNT(*) FROM nodes WHERE id=? OR id=?" , (i, j))
141
- (isnothing (check) || check < 2 ) && error (" Nodes $i and $j must exist in order for an edge to connect them." )
142
- upsert ?
143
- execute (db, " INSERT INTO edges VALUES(?, ?, ?, json(?)) ON CONFLICT(source,target,type) DO UPDATE SET props=excluded.props" , args (edge)) :
144
- execute (db, " INSERT INTO edges VALUES(?, ?, ?, json(?))" , args (edge))
145
- db
146
- end
147
-
135
+ # -----------------------------------------------------------------------------# getindex (Edge)
136
+ # all specified
148
137
function Base. getindex (db:: DB , i:: Integer , j:: Integer , type:: AbstractString )
149
138
res = execute (db, " SELECT * FROM edges WHERE source=? AND target=? AND type=?" , (i,j,type))
150
139
isempty (res) ? error (" Edge $i → $type → $j does not exist." ) : Edge (first (res))
151
140
end
152
- function Base. getindex (db:: DB , i:: Integer , j:: Integer , :: Colon = Colon ())
141
+
142
+ # one colon
143
+ function Base. getindex (db:: DB , i:: Integer , j:: Integer , :: Colon )
153
144
res = execute (db, " SELECT * FROM edges WHERE source=? AND target=?" , (i, j))
154
145
isempty (res) ? error (" No edges connect nodes $i → $j ." ) : (Edge (row) for row in res)
155
146
end
156
-
147
+ function Base. getindex (db:: DB , i:: Integer , :: Colon , type:: AbstractString )
148
+ res = execute (db, " SELECT * FROM edges WHERE source=? AND type=?" , (i,type))
149
+ isempty (res) ? error (" No outgoing edges $type → $i " ) : (Edge (row) for row in res)
150
+ end
157
151
function Base. getindex (db:: DB , :: Colon , j:: Integer , type:: AbstractString )
158
152
res = execute (db, " SELECT * FROM edges WHERE target=? AND type=?" , (j, type))
159
153
isempty (res) ? error (" No incoming edges $type → $j " ) : (Edge (row) for row in res)
160
154
end
161
- function Base. getindex (db:: DB , i:: Colon , j:: Integer , :: Colon = Colon ())
162
- res = execute (db, " SELECT * FROM edges WHERE target=?" , (j,))
163
- isempty (res) ? error (" No incoming edges into node $j " ) : (Edge (row) for row in res)
164
- end
165
155
166
- function Base. getindex (db:: DB , i:: Integer , :: Colon , type:: AbstractString )
167
- res = execute (db, " SELECT * FROM edges WHERE source=? AND type=?" , (i,type))
168
- isempty (res) ? error (" No outgoing edges $type → $i " ) : (Edge (row) for row in res)
169
- end
170
- function Base. getindex (db:: DB , i:: Integer , :: Colon , :: Colon = Colon ())
156
+ # two colons
157
+ function Base. getindex (db:: DB , i:: Integer , :: Colon , :: Colon )
171
158
res = execute (db, " SELECT * FROM edges WHERE source=?" , (i,))
172
159
isempty (res) ? error (" No outgoing edges from node $i " ) : (Edge (row) for row in res)
173
160
end
161
+ function Base. getindex (db:: DB , i:: Colon , j:: Integer , :: Colon )
162
+ res = execute (db, " SELECT * FROM edges WHERE target=?" , (j,))
163
+ isempty (res) ? error (" No incoming edges into node $j " ) : (Edge (row) for row in res)
164
+ end
165
+ function Base. getindex (db:: DB , :: Colon , :: Colon , type:: AbstractString )
166
+ res = execute (db, " SELECT * FROM edges WHERE type=?" , (type,))
167
+ isempty (res) ? error (" No edges with type $type " ) : (Edge (row) for row in res)
168
+ end
174
169
175
-
176
- # -----------------------------------------------------------------------------# interfaces
177
- Base. length (db:: DB ) = n_nodes (db)
178
- Base. size (db:: DB ) = (nodes= n_nodes (db), edges= n_edges (db))
179
- Base. lastindex (db:: DB ) = length (db)
180
- Base. axes (db:: DB , i) = size (db)[i]
181
-
182
- Broadcast. broadcastable (db:: DB ) = Ref (db)
170
+ # all colons
171
+ function Base. getindex (db:: DB , :: Colon , :: Colon , :: Colon )
172
+ res = execute (db, " SELECT * FROM edges" )
173
+ isempty (res) ? error (" No edges exist yet." ) : (Edge (row) for row in res)
174
+ end
183
175
184
176
end
0 commit comments