This is a simple graph database in SQLite, inspired by "SQLite as a document database".
The schema consists of just two structures:
- Nodes - these are any json objects, with the only constraint being that they each contain a unique
id
value - Edges - these are pairs of node
id
values, specifying the direction, with an optional json object as connection properties
- Social networks
- Interest maps/recommendation finders
- To-do / task lists
- Bug trackers
- Customer relationship management (CRM)
- Gantt chart
The python database script provides convenience functions for atomic transactions to add, delete, connect, and search for nodes.
Any single node or path of nodes can also be depicted graphically by using the visualize
function within the database script to generate dot files, which in turn can be converted to images with Graphviz.
Dropping into a python shell, we can create, upsert, and connect people from the early days of Apple Computer. The resulting database will be saved to a SQLite file named apple.sqlite
:
>>> apple = "apple.sqlite"
>>> import database as db
>>> db.initialize(apple)
>>> db.atomic(apple, db.add_node({'name': 'Apple Computer Company', 'type':['company', 'start-up'], 'founded': 'April 1, 1976'}, 1))
>>> db.atomic(apple, db.add_node({'name': 'Steve Wozniak', 'type':['person','engineer','founder']}, 2))
>>> db.atomic(apple, db.add_node({'name': 'Steve Jobs', 'type':['person','designer','founder']}, 3))
>>> db.atomic(apple, db.add_node({'name': 'Ronald Wayne', 'type':['person','administrator','founder']}, 4))
>>> db.atomic(apple, db.add_node({'name': 'Mike Markkula', 'type':['person','investor']}, 5))
>>> db.atomic(apple, db.connect_nodes(2, 1, {'action': 'founded'}))
>>> db.atomic(apple, db.connect_nodes(3, 1, {'action': 'founded'}))
>>> db.atomic(apple, db.connect_nodes(4, 1, {'action': 'founded'}))
>>> db.atomic(apple, db.connect_nodes(5, 1, {'action': 'invested', 'equity': 80000, 'debt': 170000}))
>>> db.atomic(apple, db.connect_nodes(1, 4, {'action': 'divested', 'amount': 800, 'date': 'April 12, 1976'}))
>>> db.atomic(apple, db.connect_nodes(2, 3))
>>> db.atomic(apple, db.upsert_node(2, {'nickname': 'Woz'}))
The nodes can be searched by their ids or any other combination of attributes (either as strict equality, or using _search_like
in combination with _search_starts_with
or _search_contains
):
>>> db.atomic(apple, db.find_node(1))
{'name': 'Apple Computer Company', 'type': ['company', 'start-up'], 'founded': 'April 1, 1976', 'id': 1}
>>> db.atomic(apple, db.find_nodes({'name': 'Steve'}, db._search_like, db._search_starts_with))
[{'name': 'Steve Wozniak', 'type': ['person', 'engineer', 'founder'], 'id': 2, 'nickname': 'Woz'}, {'name': 'Steve Jobs', 'type': ['person', 'designer', 'founder'], 'id': 3}]
Paths through the graph can be discovered with a starting node id, and an optional ending id; the default neighbor expansion is nodes connected nodes in either direction, but that can changed by specifying either find_outbound_neighbors
or find_inbound_neighbors
instead:
>>> db.traverse(apple, 2, 3)
[2, 3]
>>> db.traverse(apple, 4, 5)
[4, 1, 5]
>>> db.traverse(apple, 5, neighbors_fn=db.find_inbound_neighbors)
[5]
>>> db.traverse(apple, 5, neighbors_fn=db.find_outbound_neighbors)
[5, 1, 4]
>>> db.traverse(apple, 5, neighbors_fn=db.find_neighbors)
[5, 1, 4, 3, 2]
Any path or list of nodes can rendered graphically by using the visualize
function. This command produces dot files, which in turn can be converted to images with Graphviz:
>>> db.visualize(apple, 'apple.dot', [4, 1, 5])
The resuling file can produce a png image, using this command line instruction:
dot -Tpng apple.dot -o apple.png
The default options include every key/value pair (excluding the id) in the node and edge objects:
There are display options to help refine what is produced:
>>> db.visualize(apple, 'apple.dot', [4, 1, 5], exclude_node_keys=['type'], hide_edge_key=True)
The resulting dot file can be edited further as needed; the dot guide has more options and examples.