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}