@@ -5,7 +5,7 @@ import DBInterface: execute
55using JSON3: JSON3
66using EasyConfig
77
8- export DB, Node, Edge, find_nodes, find_edges
8+ export DB, Node, Edge
99
1010# -----------------------------------------------------------------------------# utils
1111function 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+
3338struct Node
3439 id:: Int
3540 labels:: Vector{String}
3641 props:: Config
3742end
3843Node (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))
3945function 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
4156end
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
4965end
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)" )
53117end
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