@@ -28,20 +28,14 @@ struct Node
2828 props:: Config
2929end
3030Node (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))
3232function 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, ' )' )
4337end
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))
4539Base.:(== )(a:: Node , b:: Node ) = all (getfield (a,f) == getfield (b,f) for f in fieldnames (Node))
4640
4741
5448Edge (src:: Int , tgt:: Int , type:: String ; props... ) = Edge (src, tgt, type, Config (props))
5549Edge (row:: SQLite.Row ) = Edge (row. source, row. target, row. type, JSON3. read (row. props, Config))
5650function 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, ' )' )
6754end
6855args (e:: Edge ) = (e. source, e. target, e. type, JSON3. write (e. props))
6956Base.:(== )(a:: Edge , b:: Edge ) = all (getfield (a,f) == getfield (b,f) for f in fieldnames (Edge))
@@ -76,30 +63,25 @@ struct DB
7663 function DB (file:: String = " :memory:" )
7764 db = SQLite. DB (file)
7865 foreach (x -> execute (db, x), [
66+ " PRAGMA foreign_keys = ON;" ,
7967 # nodes
8068 " CREATE TABLE IF NOT EXISTS nodes (
8169 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
8572 );" ,
86- " CREATE INDEX IF NOT EXISTS id_idx ON nodes(id);" ,
87- " CREATE INDEX IF NOT EXISTS labels_idx ON nodes(labels);" ,
8873
8974 # edges
9075 " 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) ,
9378 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)
9881 );" ,
9982 " CREATE INDEX IF NOT EXISTS source_idx ON edges(source);" ,
10083 " CREATE INDEX IF NOT EXISTS target_idx ON edges(target);" ,
10184 " 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);"
10385 ])
10486 new (db)
10587 end
@@ -114,16 +96,33 @@ n_nodes(db::DB) = single_result_execute(db, "SELECT Count(*) FROM nodes")
11496n_edges (db:: DB ) = single_result_execute (db, " SELECT Count(*) FROM edges" )
11597
11698Base. 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
118114
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))
124122 db
125123end
126124
125+ # -----------------------------------------------------------------------------# getindex (Node)
127126function Base. getindex (db:: DB , id:: Integer )
128127 res = execute (db, " SELECT * FROM nodes WHERE id = ?" , (id,))
129128 isempty (res) ? error (" Node $id does not exist." ) : Node (first (res))
@@ -133,52 +132,45 @@ function Base.getindex(db::DB, ::Colon)
133132 isempty (res) ? error (" No nodes exist yet." ) : (Node (row) for row in res)
134133end
135134
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
148137function Base. getindex (db:: DB , i:: Integer , j:: Integer , type:: AbstractString )
149138 res = execute (db, " SELECT * FROM edges WHERE source=? AND target=? AND type=?" , (i,j,type))
150139 isempty (res) ? error (" Edge $i → $type → $j does not exist." ) : Edge (first (res))
151140end
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 )
153144 res = execute (db, " SELECT * FROM edges WHERE source=? AND target=?" , (i, j))
154145 isempty (res) ? error (" No edges connect nodes $i → $j ." ) : (Edge (row) for row in res)
155146end
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
157151function Base. getindex (db:: DB , :: Colon , j:: Integer , type:: AbstractString )
158152 res = execute (db, " SELECT * FROM edges WHERE target=? AND type=?" , (j, type))
159153 isempty (res) ? error (" No incoming edges $type → $j " ) : (Edge (row) for row in res)
160154end
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
165155
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 )
171158 res = execute (db, " SELECT * FROM edges WHERE source=?" , (i,))
172159 isempty (res) ? error (" No outgoing edges from node $i " ) : (Edge (row) for row in res)
173160end
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
174169
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
183175
184176end
0 commit comments