@@ -14,14 +14,29 @@ function single_result_execute(db, stmt, args...)
1414 isempty (ex) ? nothing : values (first (ex))[1 ]
1515end
1616
17+ struct Node{T}
18+ id:: Int
19+ props:: T
20+ end
21+ Node (id:: Integer , props= nothing ) = Node (id, props)
22+ Base. show (io:: IO , o:: Node ) = (print (io, " Node($(o. id) ) with props: " ); show (io, o. props))
23+
24+ struct Edge{T}
25+ source:: Int
26+ target:: Int
27+ props:: T
28+ end
29+ Base. show (io:: IO , o:: Edge ) = (print (io, " Edge($(o. source) → $(o. target) ) with props: " ); show (io, o. props))
30+ Edge (source:: Integer , target:: Integer , props= nothing ) = Edge (source, target, props)
31+
1732# -----------------------------------------------------------------------------# DB
1833"""
1934 SimpleGraph.DB(file = ":memory")
2035
2136Create a graph database (in memory by default). Edge and node properties are saved in the database
22- as text ( `JSON3.write(props)`) .
37+ as `TEXT` (see [https://www.sqlite.org/datatype3.html](https://www.sqlite.org/datatype3.html)) via `JSON3.write(props)`.
2338
24- ### Interal Table Structure
39+ # Interal Table Structure
2540
2641- `nodes`
2742 - `id INTEGER NOT NULL UNIQUE`
@@ -30,6 +45,16 @@ as text (`JSON3.write(props)`).
3045 - `source INTEGER`
3146 - `target INTEGER`
3247 - `props TEXT` (JSON.write)
48+
49+ # Examples
50+
51+ db = DB()
52+
53+ db[1] = (x=1, y=2) # node 1
54+
55+ db[2] = (x=1, y=3) # node 2
56+
57+ db[1,2] = (z = 4) # edge from 1 → 2
3358"""
3459struct DB
3560 sqlitedb:: SQLite.DB
@@ -70,12 +95,21 @@ execute(db::DB, args...; kw...) = execute(db.sqlitedb, args...; kw...)
7095n_nodes (db:: DB ) = single_result_execute (db, " SELECT Count(*) FROM nodes" )
7196n_edges (db:: DB ) = single_result_execute (db, " SELECT Count(*) FROM edges" )
7297
98+ init! (db:: DB , n) = (foreach (id -> setindex! (db, nothing , id), 1 : n); db)
99+
100+
101+ # -----------------------------------------------------------------------------# interfaces
73102Base. length (db:: DB ) = n_nodes (db)
74- Base. size (db:: DB ) = (n_nodes (db), n_edges (db))
103+ Base. size (db:: DB ) = (nodes = n_nodes (db), edges = n_edges (db))
75104Base. lastindex (db:: DB ) = length (db)
105+ Base. axes (db:: DB , i) = size (db)[i]
76106
77- init! (db:: DB , n ) = ( foreach (id -> setindex! (db, nothing , id), 1 : n); db)
107+ Broadcast . broadcastable (db:: DB ) = Ref ( db)
78108
109+ function Base. iterate (db:: DB , state = (length (db), 1 ))
110+ state[2 ] > state[1 ] && return nothing
111+ single_result_execute (db, " SELECT props FROM nodes WHERE id = ?" , (state[2 ],)), (state[1 ], state[2 ] + 1 )
112+ end
79113
80114# -----------------------------------------------------------------------------# ReadAs
81115struct ReadAs{T}
84118ReadAs (db:: DB , T:: DataType = Dict{String,Any}) = ReadAs {T} (db)
85119Base. show (io:: IO , r:: ReadAs{T} ) where {T} = (print (io, " ReadAs{$T }: " ); print (io, r. db))
86120Base. setindex! (r:: ReadAs , args... ) = setindex! (r. db, args... )
87- Base. getindex (r:: ReadAs{T} , args... ) where {T} = (res= r. db[args... ]; isnothing (res) ? res : JSON3. read (res, T))
121+ function Base. getindex (r:: ReadAs{T} , args... ) where {T}
122+ res= r. db[args... ]; isnothing (res) ? res : JSON3. read .(res, T)
123+ end
124+
125+ _transform (node:: Node{String} , T) = Node (node. id, JSON)
88126Base. deleteat! (r:: ReadAs , args... ) = deleteat! (r. db, args... )
89127
90128# -----------------------------------------------------------------------------# nodes
91129function Base. setindex! (db:: DB , props, id:: Integer )
130+ id ≤ length (db) + 1 || error (" Cannot add node ID=$id to DB with $(length (db)) nodes. IDs must be added sequentially." )
92131 execute (db, " INSERT INTO nodes VALUES(?, json(?))" , (id, JSON3. write (props)))
93132 db
94133end
95134function Base. getindex (db:: DB , id:: Integer )
96135 res = single_result_execute (db, " SELECT props FROM nodes WHERE id = ?" , (id,))
97- isnothing (res) ? throw (BoundsError (db, id)) : res
136+ isnothing (res) ? throw (BoundsError (db, id)) : Node (id, res)
137+ end
138+ Base. getindex (db:: DB , ids:: AbstractArray{<:Integer} ) = getindex .(db, ids)
139+ function Base. getindex (db:: DB , :: Colon )
140+ res = execute (db, " SELECT props from nodes" )
141+ [Node (i,row. props) for (i,row) in enumerate (res)]
98142end
99143function Base. deleteat! (db:: DB , id:: Integer )
100144 execute (db, " DELETE FROM nodes WHERE id = ?" , (id,))
@@ -108,8 +152,30 @@ function Base.setindex!(db::DB, props, i::Integer, j::Integer)
108152 db
109153end
110154function Base. getindex (db:: DB , i:: Integer , j:: Integer )
111- single_result_execute (db, " SELECT props FROM edges WHERE source = ? AND target = ? " , (i,j))
155+ res = single_result_execute (db, " SELECT props FROM edges WHERE source = ? AND target = ? " , (i,j))
156+ isnothing (res) ? res : Edge (i, j, res)
112157end
158+ Base. getindex (db:: DB , i:: Integer , js:: AbstractArray{<:Integer} ) = filter! (! isnothing, getindex .(db, i, js))
159+ Base. getindex (db:: DB , is:: AbstractArray{<:Integer} , j:: Integer ) = filter! (! isnothing, getindex .(db, is, j))
160+ function Base. getindex (db:: DB , is:: AbstractArray{<:Integer} , js:: AbstractArray{<:Integer} )
161+ res = vcat ((getindex (db, i, js) for i in is). .. )
162+ isempty (res) ? nothing : res
163+ end
164+ function Base. getindex (db:: DB , i:: Integer , :: Colon )
165+ res = execute (db, " SELECT * FROM edges WHERE source=?" , (i,))
166+ isempty (res) ? nothing : [Edge (row... ) for row in res]
167+ end
168+ function Base. getindex (db:: DB , :: Colon , j:: Integer )
169+ res = execute (db, " SELECT * FROM edges WHERE target=?" , (j,))
170+ isempty (res) ? nothing : [Edge (row... ) for row in res]
171+ end
172+ function Base. getindex (db:: DB , :: Colon , :: Colon )
173+ res = execute (db, " SELECT * from edges" )
174+ isempty (res) ? nothing : [Edge (row... ) for row in res]
175+ end
176+ Base. getindex (db:: DB , is:: AbstractArray{<:Integer} , :: Colon ) = filter! (! isnothing, getindex .(db, is, :))
177+ Base. getindex (db:: DB , :: Colon , js:: AbstractArray{<:Integer} ) = filter! (! isnothing, getindex .(db, :, js))
178+
113179function Base. deleteat! (db:: DB , i:: Integer , j:: Integer )
114180 execute (db, " DELETE FROM edges WHERE source = ? AND target = ?" , (i, j))
115181 db
0 commit comments