├── sql ├── delete-node.sql ├── insert-edge.sql ├── insert-node.sql ├── delete-incoming-edges.sql ├── delete-outgoing-edges.sql ├── update-node.sql ├── delete-edge.sql ├── delete-edges.sql ├── search-edges-inbound.sql ├── search-edges-outbound.sql ├── update-edge.sql ├── search-edges.sql ├── search-node.template ├── search-where.template ├── schema.sql └── traverse.template ├── LICENSE ├── .gitignore └── README.md /sql/delete-node.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM nodes WHERE id = ? -------------------------------------------------------------------------------- /sql/insert-edge.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO edges VALUES(?, ?, json(?)) -------------------------------------------------------------------------------- /sql/insert-node.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO nodes VALUES(json(?)) 2 | -------------------------------------------------------------------------------- /sql/delete-incoming-edges.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM edges WHERE target = ? -------------------------------------------------------------------------------- /sql/delete-outgoing-edges.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM edges WHERE source = ? -------------------------------------------------------------------------------- /sql/update-node.sql: -------------------------------------------------------------------------------- 1 | UPDATE nodes SET body = json(?) WHERE id = ? -------------------------------------------------------------------------------- /sql/delete-edge.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM edges WHERE source = ? AND target = ? -------------------------------------------------------------------------------- /sql/delete-edges.sql: -------------------------------------------------------------------------------- 1 | DELETE FROM edges WHERE source = ? OR target = ? -------------------------------------------------------------------------------- /sql/search-edges-inbound.sql: -------------------------------------------------------------------------------- 1 | SELECT * FROM edges WHERE source = ? -------------------------------------------------------------------------------- /sql/search-edges-outbound.sql: -------------------------------------------------------------------------------- 1 | SELECT * FROM edges WHERE target = ? -------------------------------------------------------------------------------- /sql/update-edge.sql: -------------------------------------------------------------------------------- 1 | UPDATE edges SET properties = json(?) WHERE source = ? AND target = ? -------------------------------------------------------------------------------- /sql/search-edges.sql: -------------------------------------------------------------------------------- 1 | SELECT * FROM edges WHERE source = ? 2 | UNION 3 | SELECT * FROM edges WHERE target = ? -------------------------------------------------------------------------------- /sql/search-node.template: -------------------------------------------------------------------------------- 1 | SELECT {{ result_column }} -- id|body 2 | FROM nodes{% if tree %}, json_tree(body{% if key %}, '$.{{ key }}'{% endif %}){% endif %}{% if search_clauses %} 3 | WHERE {% for search_clause in search_clauses %} 4 | {{ search_clause }} 5 | {% endfor %}{% endif %} -------------------------------------------------------------------------------- /sql/search-where.template: -------------------------------------------------------------------------------- 1 | {% if and_or %}{{ and_or }}{% endif %} 2 | {% if id_lookup %}id = ?{% endif %} 3 | {% if key_value %}json_extract(body, '$.{{ key }}') {{ predicate }} ?{% endif %} 4 | {% if tree %}{% if key %}(json_tree.key='{{ key }}' AND {% endif %}json_tree.value {{ predicate }} ?{% if key %}){% endif %}{% endif %} -------------------------------------------------------------------------------- /sql/schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE IF NOT EXISTS nodes ( 2 | body TEXT, 3 | id TEXT GENERATED ALWAYS AS (json_extract(body, '$.id')) VIRTUAL NOT NULL UNIQUE 4 | ); 5 | 6 | CREATE INDEX IF NOT EXISTS id_idx ON nodes(id); 7 | 8 | CREATE TABLE IF NOT EXISTS edges ( 9 | source TEXT, 10 | target TEXT, 11 | properties TEXT, 12 | UNIQUE(source, target, properties) ON CONFLICT REPLACE, 13 | FOREIGN KEY(source) REFERENCES nodes(id), 14 | FOREIGN KEY(target) REFERENCES nodes(id) 15 | ); 16 | 17 | CREATE INDEX IF NOT EXISTS source_idx ON edges(source); 18 | CREATE INDEX IF NOT EXISTS target_idx ON edges(target); 19 | -------------------------------------------------------------------------------- /sql/traverse.template: -------------------------------------------------------------------------------- 1 | WITH RECURSIVE traverse(x{% if with_bodies %}, y, obj{% endif %}) AS ( 2 | SELECT id{% if with_bodies %}, '()', body {% endif %} FROM nodes WHERE id = ? 3 | UNION 4 | SELECT id{% if with_bodies %}, '()', body {% endif %} FROM nodes JOIN traverse ON id = x 5 | {% if inbound %}UNION 6 | SELECT source{% if with_bodies %}, '<-', properties {% endif %} FROM edges JOIN traverse ON target = x{% endif %} 7 | {% if outbound %}UNION 8 | SELECT target{% if with_bodies %}, '->', properties {% endif %} FROM edges JOIN traverse ON source = x{% endif %} 9 | ) SELECT x{% if with_bodies %}, y, obj {% endif %} FROM traverse; 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) Denis Papathanasiou 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a 5 | copy of this software and associated documentation files (the "Software"), 6 | to deal in the Software without restriction, including without limitation 7 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | and/or sell copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 | OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/linux,macos,windows,visualstudiocode 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=linux,macos,windows,visualstudiocode 3 | 4 | ### Linux ### 5 | *~ 6 | 7 | # temporary files which can be created if a process still has a handle open of a deleted file 8 | .fuse_hidden* 9 | 10 | # KDE directory preferences 11 | .directory 12 | 13 | # Linux trash folder which might appear on any partition or disk 14 | .Trash-* 15 | 16 | # .nfs files are created when an open file is removed but is still being accessed 17 | .nfs* 18 | 19 | ### macOS ### 20 | # General 21 | .DS_Store 22 | .AppleDouble 23 | .LSOverride 24 | 25 | # Icon must end with two \r 26 | Icon 27 | 28 | 29 | # Thumbnails 30 | ._* 31 | 32 | # Files that might appear in the root of a volume 33 | .DocumentRevisions-V100 34 | .fseventsd 35 | .Spotlight-V100 36 | .TemporaryItems 37 | .Trashes 38 | .VolumeIcon.icns 39 | .com.apple.timemachine.donotpresent 40 | 41 | # Directories potentially created on remote AFP share 42 | .AppleDB 43 | .AppleDesktop 44 | Network Trash Folder 45 | Temporary Items 46 | .apdisk 47 | 48 | ### macOS Patch ### 49 | # iCloud generated files 50 | *.icloud 51 | 52 | ### VisualStudioCode ### 53 | .vscode/* 54 | !.vscode/settings.json 55 | !.vscode/tasks.json 56 | !.vscode/launch.json 57 | !.vscode/extensions.json 58 | !.vscode/*.code-snippets 59 | 60 | # Local History for Visual Studio Code 61 | .history/ 62 | 63 | # Built Visual Studio Code Extensions 64 | *.vsix 65 | 66 | ### VisualStudioCode Patch ### 67 | # Ignore all local history of files 68 | .history 69 | .ionide 70 | 71 | ### Windows ### 72 | # Windows thumbnail cache files 73 | Thumbs.db 74 | Thumbs.db:encryptable 75 | ehthumbs.db 76 | ehthumbs_vista.db 77 | 78 | # Dump file 79 | *.stackdump 80 | 81 | # Folder config file 82 | [Dd]esktop.ini 83 | 84 | # Recycle Bin used on file shares 85 | $RECYCLE.BIN/ 86 | 87 | # Windows Installer files 88 | *.cab 89 | *.msi 90 | *.msix 91 | *.msm 92 | *.msp 93 | 94 | # Windows shortcuts 95 | *.lnk 96 | 97 | # End of https://www.toptal.com/developers/gitignore/api/linux,macos,windows,visualstudiocode 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | This is a simple [graph database](https://en.wikipedia.org/wiki/Graph_database) in [SQLite](https://www.sqlite.org/), inspired by "[SQLite as a document database](https://dgl.cx/2020/06/sqlite-json-support)". 4 | 5 | # Structure 6 | 7 | The [schema](sql/schema.sql) consists of just two structures: 8 | 9 | * Nodes - these are any [json](https://www.json.org/) objects, with the only constraint being that they each contain a unique `id` value 10 | * Edges - these are pairs of node `id` values, specifying the direction, with an optional json object as connection properties 11 | 12 | The create, read, update, and delete functions ([.sql files](sql)) are complete statements with [qmark](https://docs.python.org/3/library/sqlite3.html#sqlite3.paramstyle) bindings. 13 | 14 | Search templates ([.template files](sql)) are in [Jinja2](https://pypi.org/project/Jinja2/) format, which can be converted to other template syntaxes relatively easily, with a bit of [regex magic](https://github.com/dpapathanasiou/simple-graph-go/blob/main/generate-constants.sh) (though it would be nice if they could be expressed in a more language-agnostic way). 15 | 16 | There are also traversal function templates as native SQLite [Common Table Expressions](https://www.sqlite.org/lang_with.html) which produce lists of identifiers or return all objects along the path. 17 | 18 | # Applications 19 | 20 | * [Social networks](https://en.wikipedia.org/wiki/Social_graph) 21 | * [Interest maps/recommendation finders](https://en.wikipedia.org/wiki/Interest_graph) 22 | * [To-do / task lists](https://en.wikipedia.org/wiki/Task_list) 23 | * [Bug trackers](https://en.wikipedia.org/wiki/Open-source_software_development#Bug_trackers_and_task_lists) 24 | * [Customer relationship management (CRM)](https://en.wikipedia.org/wiki/Customer_relationship_management) 25 | * [Gantt chart](https://en.wikipedia.org/wiki/Gantt_chart) 26 | 27 | # Usage 28 | 29 | ## RESTful API (paid) 30 | 31 | The [Banrai Simple Graph Database and Document Store](https://banrai.com/) wraps this core logic with an API service, creating a managed graph database and document store, with additional features not found in any of the public bindings. 32 | 33 | ## Importable library (free) 34 | 35 | Choose an implementation: 36 | 37 | * [Python](https://github.com/dpapathanasiou/simple-graph-pypi) (now [available in PyPI](https://pypi.org/project/simple-graph-sqlite/)) 38 | * [Go](https://github.com/dpapathanasiou/simple-graph-go) 39 | * [Julia](https://github.com/JuliaComputing/SQLiteGraph.jl) (courtesy of [Josh Day](https://github.com/joshday)) 40 | * [R](https://github.com/mikeasilva/simplegraphdb) (courtesy of [Michael Silva](https://github.com/mikeasilva)) 41 | * [Flutter and Dart](https://github.com/rodydavis/flutter_graph_database) (courtesy of [Rody Davis](https://github.com/rodydavis)) 42 | * [Swift](https://swiftpackageindex.com/Jomy10/SimpleGraph) (Courtesy of [Jonas Everaert](https://github.com/jomy10) 43 | 44 | Want to contribute an implementation in your preferred programming language? 45 | 46 | The [schema and prepared sql statements](sql) can be used by programs in *any* programming language with [SQLite bindings](https://en.wikipedia.org/wiki/SQLite#Programming_language_support). 47 | 48 | [Pull requests](https://help.github.com/articles/about-pull-requests/) are welcome! 49 | --------------------------------------------------------------------------------