Skip to content

Commit 3f6fcae

Browse files
committed
working on indexing
1 parent 9d42bee commit 3f6fcae

File tree

2 files changed

+75
-8
lines changed

2 files changed

+75
-8
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ db = DB()
2020
### Adding Nodes
2121

2222
```julia
23-
db[1] = (x=1, y=2)
2423
# properties must be `JSON3.write`-able (saved in the SQLite database as TEXT)
24+
db[1] = (x=1, y=2)
25+
2526
db[2] = (x=1, y=10)
2627

2728
db[1]

src/SQLiteGraph.jl

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,29 @@ function single_result_execute(db, stmt, args...)
1414
isempty(ex) ? nothing : values(first(ex))[1]
1515
end
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
2136
Create 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
"""
3459
struct DB
3560
sqlitedb::SQLite.DB
@@ -70,12 +95,21 @@ execute(db::DB, args...; kw...) = execute(db.sqlitedb, args...; kw...)
7095
n_nodes(db::DB) = single_result_execute(db, "SELECT Count(*) FROM nodes")
7196
n_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
73102
Base.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))
75104
Base.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
81115
struct ReadAs{T}
@@ -84,17 +118,27 @@ end
84118
ReadAs(db::DB, T::DataType=Dict{String,Any}) = ReadAs{T}(db)
85119
Base.show(io::IO, r::ReadAs{T}) where {T} = (print(io, "ReadAs{$T}: "); print(io, r.db))
86120
Base.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)
88126
Base.deleteat!(r::ReadAs, args...) = deleteat!(r.db, args...)
89127

90128
#-----------------------------------------------------------------------------# nodes
91129
function 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
94133
end
95134
function 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)]
98142
end
99143
function 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
109153
end
110154
function 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)
112157
end
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+
113179
function 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

Comments
 (0)