5 unstable releases
0.2.0-alpha.1 | Aug 1, 2024 |
---|---|
0.1.1 | Aug 1, 2024 |
0.1.0 | Jul 31, 2024 |
0.0.2 | Jul 29, 2024 |
0.0.1 | Jul 26, 2024 |
#311 in Database implementations
250KB
6K
SLoC
simple-triplestore
A triplestore implementation which can be used as a flexible graph database with support for custom node and edge properties.
Data Model
Each vertex and edge (collectively called nodes
) are associated with an id (i.e. u64
or Ulid).
Property data is stored as
Id -> NodeProps
Id -> EdgeProps
.
Graph relationships are stored three times as (Id, Id, Id) -> Id
with the following sort orders:
- Subject, Predicate, Object
- Predicate, Object, Subject
- Object, Subject, Predicate
This allows for any graph query to be decomposed into a range query on the lookup with the ideal ordering. For example,
query!{ a -b-> ? }
becomes a query on the subject-predicate-object table.query!{ ? -a-> b }
becomes a query on the position-object-subject table.query!{ a -?-> b }
becomes a query on the object-subject-position table.
Supported Key-Value Backends
Example
Pull in various includes we need:
use ulid::Ulid;
use simple_triplestore::prelude::*;
let mut db = MemTripleStore::new(UlidIdGenerator::new());
Get some identifiers. In real applications these will come from an index or another lookup table.
let node_1 = Ulid(123);
let node_2 = Ulid(456);
let node_3 = Ulid(789);
let edge = Ulid(999);
Insert nodes and edges with user-defined property types. For a given TripleStore we can have one type for Nodes and one for Edges.
db.insert_node(node_1, "foo".to_string())?;
db.insert_node(node_2, "bar".to_string())?;
db.insert_node(node_3, "baz".to_string())?;
db.insert_edge(Triple{sub: node_1, pred: edge, obj: node_2}, Vec::from([1,2,3]))?;
db.insert_edge(Triple{sub: node_1, pred: edge, obj: node_3}, Vec::from([4,5,6]))?;
We can now query for edges which end at node_3
, and find that there is only one.
assert_eq!(
db.run(query!{ ? -?-> [node_3] })?
.iter_edges(EdgeOrder::default())
.map(|r| r.expect("ok"))
.collect::<Vec<_>>(),
[
(Triple{sub: node_1, pred: edge, obj: node_3}, Vec::from([4,5,6])),
]
);
We can also query for all edges which have the predicate edge
, and find both of the edges we added:
assert_eq!(
db.run(query!{ ? -[edge]-> ? })?
.iter_edges(EdgeOrder::default())
.map(|r| r.expect("ok"))
.collect::<Vec<_>>(),
[
(Triple{sub: node_1, pred: edge, obj: node_2}, Vec::from([1,2,3])),
(Triple{sub: node_1, pred: edge, obj: node_3}, Vec::from([4,5,6])),
]
);
Dependencies
~1.7–2.7MB
~45K SLoC