11//! In-memory store of Atomic data.
22//! This provides many methods for finding, changing, serializing and parsing Atomic Data.
33
4+ use crate :: storelike:: QueryResult ;
5+ use crate :: Value ;
46use crate :: { atoms:: Atom , storelike:: Storelike } ;
57use crate :: { errors:: AtomicResult , Resource } ;
68use std:: { collections:: HashMap , sync:: Arc , sync:: Mutex } ;
@@ -24,6 +26,97 @@ impl Store {
2426 crate :: populate:: populate_base_models ( & store) ?;
2527 Ok ( store)
2628 }
29+
30+ /// Triple Pattern Fragments interface.
31+ /// Use this for most queries, e.g. finding all items with some property / value combination.
32+ /// Returns an empty array if nothing is found.
33+ ///
34+ /// # Example
35+ ///
36+ /// For example, if I want to view all Resources that are instances of the class "Property", I'd do:
37+ ///
38+ /// ```
39+ /// use atomic_lib::Storelike;
40+ /// let mut store = atomic_lib::Store::init().unwrap();
41+ /// store.populate();
42+ /// let atoms = store.tpf(
43+ /// None,
44+ /// Some("https://atomicdata.dev/properties/isA"),
45+ /// Some(&atomic_lib::Value::AtomicUrl("https://atomicdata.dev/classes/Class".into())),
46+ /// true
47+ /// ).unwrap();
48+ /// assert!(atoms.len() > 11)
49+ /// ```
50+ // Very costly, slow implementation.
51+ // Does not assume any indexing.
52+ fn tpf (
53+ & self ,
54+ q_subject : Option < & str > ,
55+ q_property : Option < & str > ,
56+ q_value : Option < & Value > ,
57+ // Whether resources from outside the store should be searched through
58+ include_external : bool ,
59+ ) -> AtomicResult < Vec < Atom > > {
60+ let mut vec: Vec < Atom > = Vec :: new ( ) ;
61+
62+ let hassub = q_subject. is_some ( ) ;
63+ let hasprop = q_property. is_some ( ) ;
64+ let hasval = q_value. is_some ( ) ;
65+
66+ // Simply return all the atoms
67+ if !hassub && !hasprop && !hasval {
68+ for resource in self . all_resources ( include_external) {
69+ for ( property, value) in resource. get_propvals ( ) {
70+ vec. push ( Atom :: new (
71+ resource. get_subject ( ) . clone ( ) ,
72+ property. clone ( ) ,
73+ value. clone ( ) ,
74+ ) )
75+ }
76+ }
77+ return Ok ( vec) ;
78+ }
79+
80+ // Find atoms matching the TPF query in a single resource
81+ let mut find_in_resource = |resource : & Resource | {
82+ let subj = resource. get_subject ( ) ;
83+ for ( prop, val) in resource. get_propvals ( ) . iter ( ) {
84+ if hasprop && q_property. as_ref ( ) . unwrap ( ) == prop {
85+ if hasval {
86+ if val. contains_value ( q_value. unwrap ( ) ) {
87+ vec. push ( Atom :: new ( subj. into ( ) , prop. into ( ) , val. clone ( ) ) )
88+ }
89+ break ;
90+ } else {
91+ vec. push ( Atom :: new ( subj. into ( ) , prop. into ( ) , val. clone ( ) ) )
92+ }
93+ break ;
94+ } else if hasval && !hasprop && val. contains_value ( q_value. unwrap ( ) ) {
95+ vec. push ( Atom :: new ( subj. into ( ) , prop. into ( ) , val. clone ( ) ) )
96+ }
97+ }
98+ } ;
99+
100+ match q_subject {
101+ Some ( sub) => match self . get_resource ( sub) {
102+ Ok ( resource) => {
103+ if hasprop | hasval {
104+ find_in_resource ( & resource) ;
105+ Ok ( vec)
106+ } else {
107+ Ok ( resource. to_atoms ( ) )
108+ }
109+ }
110+ Err ( _) => Ok ( vec) ,
111+ } ,
112+ None => {
113+ for resource in self . all_resources ( include_external) {
114+ find_in_resource ( & resource) ;
115+ }
116+ Ok ( vec)
117+ }
118+ }
119+ }
27120}
28121
29122impl Storelike for Store {
@@ -126,6 +219,62 @@ impl Storelike for Store {
126219 fn set_default_agent ( & self , agent : crate :: agents:: Agent ) {
127220 self . default_agent . lock ( ) . unwrap ( ) . replace ( agent) ;
128221 }
222+
223+ fn query ( & self , q : & crate :: storelike:: Query ) -> AtomicResult < crate :: storelike:: QueryResult > {
224+ let atoms = self . tpf (
225+ None ,
226+ q. property . as_deref ( ) ,
227+ q. value . as_ref ( ) ,
228+ q. include_external ,
229+ ) ?;
230+
231+ // Remove duplicate subjects
232+ let mut subjects_deduplicated: Vec < String > = atoms
233+ . iter ( )
234+ . map ( |atom| atom. subject . clone ( ) )
235+ . collect :: < std:: collections:: HashSet < String > > ( )
236+ . into_iter ( )
237+ . collect ( ) ;
238+
239+ // Sort by subject, better than no sorting
240+ subjects_deduplicated. sort ( ) ;
241+
242+ // WARNING: Entering expensive loop!
243+ // This is needed for sorting, authorization and including nested resources.
244+ // It could be skipped if there is no authorization and sorting requirement.
245+ let mut resources = Vec :: new ( ) ;
246+ for subject in subjects_deduplicated. iter ( ) {
247+ // These nested resources are not fully calculated - they will be presented as -is
248+ match self . get_resource_extended ( subject, true , q. for_agent . as_deref ( ) ) {
249+ Ok ( resource) => {
250+ resources. push ( resource) ;
251+ }
252+ Err ( e) => match & e. error_type {
253+ crate :: AtomicErrorType :: NotFoundError => { }
254+ crate :: AtomicErrorType :: UnauthorizedError => { }
255+ _other => {
256+ return Err (
257+ format ! ( "Error when getting resource in collection: {}" , e) . into ( )
258+ )
259+ }
260+ } ,
261+ }
262+ }
263+
264+ if let Some ( sort) = & q. sort_by {
265+ resources = crate :: collections:: sort_resources ( resources, sort, q. sort_desc ) ;
266+ }
267+ let mut subjects = Vec :: new ( ) ;
268+ for r in resources. iter ( ) {
269+ subjects. push ( r. get_subject ( ) . clone ( ) )
270+ }
271+
272+ Ok ( QueryResult {
273+ count : atoms. len ( ) ,
274+ subjects,
275+ resources,
276+ } )
277+ }
129278}
130279
131280#[ cfg( test) ]
0 commit comments