Skip to content

Commit 468422d

Browse files
committed
wip rewrite
1 parent 3a856ff commit 468422d

File tree

2 files changed

+95
-76
lines changed

2 files changed

+95
-76
lines changed

Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ version = "0.1.0"
55

66
[deps]
77
DBInterface = "a10d1c49-ce27-4219-8d33-6db1a4562965"
8+
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
89
EasyConfig = "acab07b0-f158-46d4-8913-50acef6d41fe"
910
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
1011
SQLite = "0aa819cd-b072-5ff4-a722-6bc24af294d9"

src/SQLiteGraph.jl

Lines changed: 94 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import DBInterface: execute
55
using JSON3: JSON3
66
using EasyConfig
77

8-
export DB, Node, Edge, find_nodes, find_edges
8+
export DB, Node, Edge
99

1010
#-----------------------------------------------------------------------------# utils
1111
function single_result_execute(db, stmt, args...)
@@ -30,102 +30,120 @@ end
3030
# Relationships always has a direction (one direction).
3131
# Relationships must have a type (one type) to define (classify) what type of relationship they are.
3232

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+
3338
struct Node
3439
id::Int
3540
labels::Vector{String}
3641
props::Config
3742
end
3843
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))
3945
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
4156
end
57+
args(n::Node) = (n.id, join(n.labels, ';'), JSON3.write(n.props))
4258

4359

44-
struct Relationship
45-
source_id::Int
46-
target_id::Int
60+
struct Edge
61+
source::Int
62+
target::Int
4763
type::String
4864
props::Config
4965
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)")
53117
end
54118

119+
execute(db::DB, args...; kw...) = execute(db.sqlitedb, args...; kw...)
55120

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")
56123

124+
Base.length(db::DB) = n_nodes(db)
125+
Base.size(db::DB) = (n_nodes=n_nodes(db), n_edges=n_edges(db))
57126

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
122144

123-
# execute(db::DB, args...; kw...) = execute(db.sqlitedb, args...; kw...)
124145

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")
127146

128-
# init!(db::DB, n::Integer) = (foreach(id -> setindex!(db, nothing, id), 1:n); db)
129147

130148
# #-----------------------------------------------------------------------------# get/set nodes
131149
# function Base.setindex!(db::DB, props, id::Integer)

0 commit comments

Comments
 (0)