simple_triplestore/lib.rs
1//! A [triplestore](https://en.wikipedia.org/wiki/Triplestore) implementation which can be used as a flexible graph database with support for custom node and edge properties.
2//!
3//! ## Data Model
4//! Each vertex and edge (collectively called `nodes`) are associated with an id (i.e. `u64` or [Ulid](https://docs.rs/ulid/latest/ulid/struct.Ulid.html)).
5//!
6//! Property data is stored as
7//! * `Id -> NodeProps`
8//! * `Id -> EdgeProps`.
9//!
10//! Graph relationships are stored three times as <code>(Id, Id, Id) -> Id</code> with the following sort orders:
11//! * Subject, Predicate, Object
12//! * Predicate, Object, Subject
13//! * Object, Subject, Predicate
14//!
15//! This allows for any graph query to be decomposed into a range query on the lookup with the ideal ordering. For example,
16//!
17//! * `query!{ a -b-> ? }` becomes a query on the subject-predicate-object table.
18//! * `query!{ ? -a-> b }` becomes a query on the position-object-subject table.
19//! * `query!{ a -?-> b }` becomes a query on the object-subject-position table.
20//!
21//! ## Supported Key-Value Backends
22//! * [Memory](https://docs.rs/simple-triplestore/latest/simple_triplestore/struct.MemTripleStore.html)
23//! * [Sled](https://docs.rs/simple-triplestore/latest/simple_triplestore/struct.SledTripleStore.html) ( with the `sled` feature )
24
25use std::collections::HashSet;
26
27pub mod id;
28pub mod mem;
29pub mod prelude;
30#[cfg(feature = "sled")]
31pub mod sled;
32
33#[cfg(test)]
34mod conformance;
35pub mod traits;
36pub mod triple;
37
38pub use crate::{
39 id::ulid::UlidIdGenerator,
40 mem::MemTripleStore,
41 traits::{ExtendError, IdGenerator, MergeError, Mergeable, QueryError, SetOpsError},
42 triple::{PropsTriple, Triple},
43};
44
45#[cfg(feature = "sled")]
46pub use crate::sled::{SledTripleStore, SledTripleStoreError};
47
48/// The order for edges which should be returned.
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub enum EdgeOrder {
51 /// Subject, Predicate, Object
52 SPO,
53
54 /// Predicate, Object, Subject,
55 POS,
56
57 /// Object, Subject, Predicate
58 OSP,
59}
60
61impl Default for EdgeOrder {
62 fn default() -> Self {
63 Self::SPO
64 }
65}
66
67/// Represents a query which can be executed on a [TripleStore][crate::TripleStore].
68///
69/// These are most easily created using teh [query][crate::query] macro.
70#[derive(Debug, PartialEq, Eq, Clone)]
71pub enum Query<Id: traits::IdType> {
72 /// Fetch the NodeProps for the given set of ids.
73 NodeProps(HashSet<Id>),
74
75 /// Fetch the edges for the given set of triples.
76 SPO(HashSet<(Id, Id, Id)>),
77
78 /// Fetch all edges which point to one of the given set of ids.
79 O(HashSet<Id>),
80
81 /// Fetch all edges which start at one of the given set of ids.
82 S(HashSet<Id>),
83
84 /// Fetch all edges which have one of the given set of edge ids.
85 P(HashSet<Id>),
86
87 /// Fetch all edges which have one of the given tuples as predicate and object.
88 PO(HashSet<(Id, Id)>),
89
90 /// Fetch all edges which have one of the given tuples as subject and object.
91 SO(HashSet<(Id, Id)>),
92
93 /// Fetch all edges which have one of the given tuples as subject and predicate.
94 SP(HashSet<(Id, Id)>),
95}
96
97impl<Id: traits::IdType, I: IntoIterator<Item = Triple<Id>>> From<I> for Query<Id> {
98 fn from(value: I) -> Self {
99 Query::SPO(value.into_iter().map(|t| (t.sub, t.pred, t.obj)).collect())
100 }
101}
102
103/// Syntactic sugar macro for constructing [Query] objects which can be used in [crate::TripleStoreQuery::run()].
104///
105/// # Examples
106///
107/// The following is used throught all of the examples below:
108/// ```
109/// use ulid::Ulid;
110/// use simple_triplestore::{prelude::*, Query, query};
111/// let a = Ulid(1);
112/// let b = Ulid(2);
113/// let c = Ulid(3);
114/// let d = Ulid(4);
115/// let e = Ulid(5);
116/// let f = Ulid(6);
117/// ```
118///
119/// ### Node Properties
120/// To fetch node properties for a collection of vertices:
121///
122/// ```
123/// # use ulid::Ulid;
124/// # use simple_triplestore::{prelude::*, Query, query};
125/// # let a = Ulid(1);
126/// # let b = Ulid(2);
127/// assert_eq!(
128/// query! { node props for [a, b]},
129/// Query::NodeProps([a, b].into_iter().collect())
130/// );
131/// ```
132///
133/// ### S
134/// To find all edges for a list of subjects:
135///
136/// ```
137/// # use ulid::Ulid;
138/// # use simple_triplestore::{prelude::*, Query, query};
139/// # let a = Ulid(1);
140/// # let b = Ulid(2);
141/// assert_eq!(
142/// query! { [a, b] -?-> ? },
143/// Query::S([a, b].into_iter().collect())
144/// );
145/// ```
146///
147/// ### P
148/// To find all edges for a list of predicates:
149///
150/// ```
151/// # use ulid::Ulid;
152/// # use simple_triplestore::{prelude::*, Query, query};
153/// # let b = Ulid(2);
154/// # let c = Ulid(3);
155/// assert_eq!(
156/// query! { ? -[b, c]-> ? },
157/// Query::P([b, c].into_iter().collect())
158/// );
159/// ```
160///
161/// ### PO
162/// To find all edges for all combinations of predicates and objects:
163///
164/// ```
165/// # use ulid::Ulid;
166/// # use simple_triplestore::{prelude::*, Query, query};
167/// # let a = Ulid(1);
168/// # let b = Ulid(2);
169/// # let c = Ulid(3);
170/// # let d = Ulid(4);
171/// assert_eq!(
172/// query! { ? -[a,b]-> [c, d] },
173/// Query::PO([(a, c), (a, d), (b, c), (b, d)].into_iter().collect())
174/// );
175/// ```
176/// ### SO
177/// To find all edges for all combinations of subjects and objects:
178///
179/// ```
180/// # use ulid::Ulid;
181/// # use simple_triplestore::{prelude::*, Query, query};
182/// # let a = Ulid(1);
183/// # let b = Ulid(2);
184/// # let c = Ulid(3);
185/// # let d = Ulid(4);
186/// assert_eq!(
187/// query! { [a, b] -?-> [c, d] },
188/// Query::SO([(a, c), (a, d), (b, c), (b, d)].into_iter().collect())
189/// );
190/// ```
191///
192/// ### O
193/// To find all edges for a list of objects:
194///
195/// ```
196/// # use ulid::Ulid;
197/// # use simple_triplestore::{prelude::*, Query, query};
198/// # let a = Ulid(1);
199/// # let b = Ulid(2);
200/// assert_eq!(
201/// query! { ? -?-> [a, b] },
202/// Query::O([a, b].into_iter().collect())
203/// );
204/// ```
205///
206/// ### SP
207/// To find all edges for all combinations of subjects and predicates:
208///
209/// ```
210/// # use ulid::Ulid;
211/// # use simple_triplestore::{prelude::*, Query, query};
212/// # let a = Ulid(1);
213/// # let b = Ulid(2);
214/// # let c = Ulid(3);
215/// # let d = Ulid(4);
216/// assert_eq!(
217/// query! { [a, b] -[c, d]-> ? },
218/// Query::SP([(a, c), (a, d), (b, c), (b, d)].into_iter().collect())
219/// );
220/// ```
221///
222/// ### SPO
223/// To find all edges for all combinations of subjects, predicates, and objects:
224///
225/// ```
226/// # use ulid::Ulid;
227/// # use simple_triplestore::{prelude::*, Query, query};
228/// # let a = Ulid(1);
229/// # let b = Ulid(2);
230/// # let c = Ulid(3);
231/// # let d = Ulid(4);
232/// # let e = Ulid(5);
233/// # let f = Ulid(6);
234/// assert_eq!(
235/// query! { [a, b] -[c, d]-> [e, f] },
236/// Query::SPO(
237/// [
238/// (a, c, e),
239/// (a, c, f),
240/// (a, d, e),
241/// (a, d, f),
242/// (b, c, e),
243/// (b, c, f),
244/// (b, d, e),
245/// (b, d, f)
246/// ]
247/// .into_iter()
248/// .collect()
249/// )
250/// );
251/// ```
252#[macro_export]
253macro_rules! query {
254 // Match specific source, edge, and destination
255 (node props for $nodes:tt) => {{
256 $crate::Query::NodeProps($nodes.into_iter().collect())
257 }};
258
259 ($subs:tt -?-> ?) => {{
260 $crate::Query::S($subs.into_iter().collect())
261 }};
262
263 (? -$preds:tt-> ?) => {{
264 $crate::Query::P($preds.into_iter().collect())
265 }};
266
267 ($subs:tt -$preds:tt-> ?) => {{
268 use std::collections::HashSet;
269 let mut items = HashSet::new();
270 for sub in $subs {
271 for pred in $preds {
272 items.insert((sub, pred));
273 }
274 }
275 $crate::Query::SP(items)
276 }};
277
278 (? -?-> $objs:tt) => {{
279 $crate::Query::O($objs.into_iter().collect())
280 }};
281
282 ($subs:tt -?-> $objs:tt) => {{
283 use std::collections::HashSet;
284 let mut items = HashSet::new();
285 for sub in $subs {
286 for obj in $objs {
287 items.insert((sub, obj));
288 }
289 }
290 $crate::Query::SO(items)
291 }};
292
293 (? -$preds:tt-> $objs:tt) => {{
294 use std::collections::HashSet;
295 let mut items = HashSet::new();
296 for sub in $preds {
297 for obj in $objs {
298 items.insert((sub, obj));
299 }
300 }
301 $crate::Query::PO(items)
302 }};
303
304 ($subs:tt -$preds:tt-> $objs:tt) => {{
305 use std::collections::HashSet;
306 let mut items = HashSet::new();
307 for sub in $subs {
308 for pred in $preds {
309 for obj in $objs {
310 items.insert((sub, pred, obj));
311 }
312 }
313 }
314 $crate::Query::SPO(items)
315 }};
316}