1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
//! Database query builder type utilities //! //! Querying the database happens via a query object that is //! constrained in one parameter of metadata. What this means is that //! you can either query for a direct or recursive path, a tag set, or //! the exact Id of a record. //! //! This module contains types and helpers to build these queries, and //! deal with return values that are yielded by [`query()`][query] and the //! [`QueryIterator`][query_iter]. //! //! [query]: ../struct.Library.html#method.query //! [query_iter]: struct.QueryIterator.html mod iter; pub use iter::QueryIterator; mod sub; pub(crate) use sub::SubHub; pub use sub::Subscription; use crate::{ record::RecordRef, utils::{Id, Path, TagSet}, }; /// A one-dimentional database query /// /// It's recomended to use the builder-style constructor API, instead /// of building a query by hand, because the internals may change at a /// faster pace than the functions. #[derive(Clone, Debug, PartialEq, Eq)] pub enum Query { /// Return a record by exact Id Id(Id), /// Get a record by it's path Path(Path), /// Make a query for the tag set Tag(SetQuery<TagSet>), /// A fake query for tests #[cfg(test)] #[doc(hidden)] Fake, } impl Query { /// Create a direct record Id query pub fn id(id: Id) -> Self { Self::Id(id) } /// Create a path query pub fn path<P: Into<Path>>(p: P) -> Self { Self::Path(p.into()) } /// Create a tag query pub fn tags() -> TagQuery { TagQuery } } /// An API type to build tag queries /// /// Following is an overview of the type constraints available. /// /// | SetQuery type | Constraints model | /// |----------------------|-------------------| /// | SetQuery::Intersect | A ∩ B | /// | SetQuery::Subset | A ⊆ B | /// | SetQuery::Equals | A = B | /// | SetQuery::Not | ¬(A ∩ B) | pub struct TagQuery; impl TagQuery { /// Build a simple intersect query /// /// An [intersection] is defined by set theory as any overlap /// between sets A and B, meaning that neither A nor B needs to be /// contained in the other (A ∩ B). This is the weakest tag /// constraint, as it doesn't filter sub-, or equality sets. /// /// [intersection]: https://en.wikipedia.org/wiki/Intersection_(set_theory) pub fn intersect<T: Into<TagSet>>(&self, t: T) -> Query { Query::Tag(SetQuery::Intersect(t.into())) } /// Build a subset query /// /// A [subset] is defined by set theory as a set A, which is /// contained in it's entirety by a set B. The two sets may be /// equals: A ⊆ B. This is the most common set query to use /// because it allows constrained tags, without disallowing /// additional tags that are irrelevant to the query program. /// /// [subset]: https://en.wikipedia.org/wiki/Set_(mathematics)#Subsets pub fn subset<T: Into<TagSet>>(&self, t: T) -> Query { Query::Tag(SetQuery::Subset(t.into())) } /// Build an equality set query /// /// An equality set checks for an exact match of tags in the /// query, meaning that additional tags will make the comparison /// fail. /// /// This query type is most likely a lot less useful than /// `subset()`, but can still be used as part of a processing /// pipeline that adds tags to records over time. pub fn equals<T: Into<TagSet>>(&self, t: T) -> Query { Query::Tag(SetQuery::Equals(t.into())) } /// Build an exclusion set query /// /// This query type can be considered the opposite of /// `intersect()`, because it will fail the comparison of sets /// if even a single tag is shared between them. pub fn not<T: Into<TagSet>>(&self, t: T) -> Query { Query::Tag(SetQuery::Not(t.into())) } } /// The result of a query to the database #[derive(Clone, Debug)] pub enum QueryResult { /// There was a single matching item Single(RecordRef), /// There were many matching items Many(Vec<RecordRef>), } impl QueryResult { /// A simple check if the result only contains one record pub fn single(&self) -> bool { match self { Self::Single(_) => true, Self::Many(_) => false, } } pub fn merge(self, o: QueryResult) -> Self { use self::QueryResult::*; match (self, o) { (Single(r1), Single(r2)) => Self::Many(vec![r1, r2]), (Single(r), Many(mut vec)) => { vec.push(r); Self::Many(vec) } (Many(mut vec), Single(r)) => { vec.push(r); Self::Many(vec) } (Many(mut vec1), Many(mut vec2)) => { vec1.append(&mut vec2); Self::Many(vec1) } } } } /// a special type of query on a set /// /// It's highly recomended to read the function descriptions on /// [`TagQuery`][tagquery] for an explanation of the differences between these /// set query constraints. /// /// [tagquery]: struct.TagQuery.html #[derive(Clone, Debug, PartialEq, Eq)] pub enum SetQuery<T> { Intersect(T), Subset(T), Equals(T), Not(T), }