materialized_view/engine/
datalog.rs

1use std::error::Error;
2use std::fmt::{Debug, Display, Formatter};
3use std::time::Instant;
4use crate::engine::storage::{RelationIdentifier, StorageLayer};
5use crate::builders::fact::Fact;
6use crate::builders::goal::{Goal, pattern_match};
7use crate::builders::rule::Rule;
8use crate::engine::compute::{ComputeLayer, Weight};
9use crate::interning::hash::reproducible_hash_one;
10use crate::interning::herbrand_universe::InternmentLayer;
11use crate::rewriting::atom::{decode_fact, encode_fact};
12
13/// A incrementally maintained view of a graph over a property graph
14pub struct MaterializedDatalogView {
15    compute_layer: ComputeLayer,
16    internment_layer: InternmentLayer,
17    storage_layer: StorageLayer,
18    safe: bool,
19}
20
21pub const EMPTY_PROGRAM: Vec<Rule> = vec![];
22
23type RelationSymbol<'a> = &'a str;
24
25pub struct PollingError;
26
27impl Display for PollingError {
28    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
29        f.write_str("polling is needed to obtain correct results")
30    }
31}
32
33impl Debug for PollingError {
34    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
35        f.write_str("polling is needed to obtain correct results")
36    }
37}
38
39impl Error for PollingError {}
40
41impl MaterializedDatalogView {
42    /// Pushes a fact into the materialized view.
43    ///
44    /// # Examples
45    ///
46    /// ```
47    /// use materialized_view::*;
48    ///
49    /// let query = program! {
50    /// reaches(?x, ?y) <- [edge(?x, ?y)],
51    /// };
52    /// let mut dynamic_view = MaterializedDatalogView::new(query);
53    ///
54    /// dynamic_view.push_fact("edge", (1, 2));
55    /// dynamic_view.poll();
56    ///
57    /// assert!(dynamic_view.contains("edge", (1, 2)).unwrap());
58    /// assert!(dynamic_view.contains("reaches", (1, 2)).unwrap());
59    ///
60    /// ```
61    pub fn push_fact(&mut self, relation_symbol: RelationSymbol, fact: impl Into<Fact>) -> bool {
62        let hashed_relation_symbol = reproducible_hash_one(&relation_symbol);
63        let interned_fact = self.internment_layer.intern_fact(fact.into());
64
65        let encoded_fact = encode_fact(&interned_fact);
66        self.compute_layer.send_fact(hashed_relation_symbol, encoded_fact);
67        self.safe = false;
68
69        self.storage_layer.contains(&hashed_relation_symbol, &encoded_fact)
70    }
71    /// Retracts facts from the materialized view.
72    ///
73    /// # Examples
74    ///
75    /// ```
76    /// use materialized_view::*;
77    ///
78    /// let query = program! {
79    /// reaches(?x, ?y) <- [edge(?x, ?y)],
80    /// };
81    /// let mut dynamic_view = MaterializedDatalogView::new(query);
82    ///
83    /// dynamic_view.push_fact("edge", (1, 2));
84    /// dynamic_view.poll();
85    /// assert!(dynamic_view.contains("edge", (1, 2)).unwrap());
86    /// assert!(dynamic_view.contains("reaches", (1, 2)).unwrap());
87    ///
88    /// dynamic_view.retract_fact("edge", (1, 2));
89    /// dynamic_view.poll();
90    /// assert!(!dynamic_view.contains("edge", (1, 2)).unwrap());
91    /// assert!(!dynamic_view.contains("reaches", (1, 2)).unwrap());
92    ///
93    /// let expected_update = vec![(-1, (1, 2))];
94    /// let actual_update_edge: Vec<((isize, (i32, i32)))> = dynamic_view
95    ///     .query_frontier_binary("edge", (ANY_VALUE, ANY_VALUE))
96    ///     .unwrap()
97    ///     .map(|((x, y), weight)| (weight, (*x, *y)))
98    ///     .collect();
99    /// assert_eq!(expected_update, actual_update_edge);
100    ///
101    /// let actual_update_reaches: Vec<((isize, (i32, i32)))> = dynamic_view
102    ///     .query_frontier_binary("reaches", (ANY_VALUE, ANY_VALUE))
103    ///     .unwrap()
104    ///     .map(|((x, y), weight)| (weight, (*x, *y)))
105    ///     .collect();
106    /// assert_eq!(expected_update, actual_update_reaches);
107    /// ```
108    pub fn retract_fact(&mut self, relation_symbol: RelationSymbol, fact: impl Into<Fact>) -> bool {
109        let hashed_relation_symbol = reproducible_hash_one(&relation_symbol);
110        if let Some(resolved_fact) = self.internment_layer.resolve_fact(fact.into()) {
111            let encoded_resolved_fact = encode_fact(&resolved_fact);
112
113            self.compute_layer.retract_fact(hashed_relation_symbol, encoded_resolved_fact);
114            self.safe = false;
115
116            return self.storage_layer.contains(&hashed_relation_symbol, &encoded_resolved_fact)
117        };
118
119        false
120    }
121    fn ensure_relation_exists(&mut self, relation_identifier: &RelationIdentifier) {
122        self.storage_layer.inner.entry(*relation_identifier).or_default();
123    }
124    fn ensure_rule_relations_exist(&mut self, rule: &Rule) {
125        self.ensure_relation_exists(&rule.head.symbol);
126
127        rule.body.iter().for_each(|body_atom| {
128            self.ensure_relation_exists(&body_atom.symbol);
129        });
130    }
131    /// Pushes a rule into the materialized view.
132    ///
133    /// # Examples
134    ///
135    /// ```
136    /// use materialized_view::*;
137    ///
138    /// let query = program! {
139    /// reaches(?x, ?y) <- [edge(?x, ?y)],
140    /// };
141    /// let mut dynamic_view = MaterializedDatalogView::new(query);
142    ///
143    /// dynamic_view.push_fact("edge", (1, 2));
144    /// dynamic_view.push_fact("edge", (2, 3));
145    /// dynamic_view.poll();
146    /// assert!(!dynamic_view.contains("reaches", (1, 3)).unwrap());
147    ///
148    /// dynamic_view.push_rule(rule!{ reaches(?x, ?z) <- [edge(?x, ?y), reaches(?y, ?z)] });
149    /// dynamic_view.poll();
150    /// assert!(dynamic_view.contains("reaches", (1, 3)).unwrap());
151    ///
152    /// ```
153    pub fn push_rule(&mut self, rule: impl Into<Rule>) {
154        let rule = rule.into();
155        self.ensure_rule_relations_exist(&rule);
156        self.compute_layer.send_rule(self.internment_layer.intern_rule(rule));
157
158        self.safe = false;
159    }
160    /// Retracts a rule from the materialized view.
161    ///
162    /// # Examples
163    ///
164    /// ```
165    /// use materialized_view::*;
166    ///
167    /// let query = program! {
168    /// reaches(?x, ?y) <- [edge(?x, ?y)],
169    /// reaches(?x, ?z) <- [edge(?x, ?y), reaches(?y, ?z)]
170    /// };
171    /// let mut dynamic_view = MaterializedDatalogView::new(query);
172    ///
173    /// dynamic_view.push_fact("edge", (1, 2));
174    /// dynamic_view.push_fact("edge", (2, 3));
175    /// dynamic_view.poll();
176    /// assert!(dynamic_view.contains("reaches", (1, 3)).unwrap());
177    ///
178    /// dynamic_view.retract_rule(rule!{ reaches(?x, ?z) <- [edge(?x, ?y), reaches(?y, ?z)] });
179    /// dynamic_view.poll();
180    /// assert!(!dynamic_view.contains("reaches", (1, 3)).unwrap());
181    /// ```
182    pub fn retract_rule(&mut self, rule: impl Into<Rule>) {
183        let rule = rule.into();
184        if let Some(resolved_rule) = &self.internment_layer.resolve_rule(rule) {
185            self.compute_layer.retract_rule(resolved_rule);
186        }
187
188        self.safe = false;
189    }
190    /// Checks whether a fact is true in the consolidated view. Throws an error if a poll is
191    /// needed to obtain correct results.
192    ///
193    /// # Examples
194    ///
195    /// ```
196    /// use materialized_view::*;
197    ///
198    /// let query = program! {
199    /// reaches(?x, ?y) <- [edge(?x, ?y)],
200    /// };
201    /// let mut dynamic_view = MaterializedDatalogView::new(query);
202    ///
203    /// dynamic_view.push_fact("edge", (1, 2));
204    /// dynamic_view.push_fact("edge", (2, 3));
205    /// println!("{}", dynamic_view.contains("reaches", (2, 3)).unwrap_err());
206    /// dynamic_view.poll();
207    /// println!("{}", dynamic_view.contains("reaches", (2, 3)).unwrap());
208    /// ```
209    pub fn contains(
210        &self,
211        relation_symbol: RelationSymbol,
212        fact: impl Into<Fact>,
213    ) -> Result<bool, PollingError> {
214        if !self.safe() {
215            return Err(PollingError)
216        }
217
218        let hashed_relation_symbol = reproducible_hash_one(&relation_symbol);
219        if let Some(interned_fact) = self.internment_layer.resolve_fact(fact.into()) {
220            return Ok(self.storage_layer.contains(&hashed_relation_symbol, &encode_fact(&interned_fact)))
221        }
222
223        Ok(false)
224    }
225    /// Queries the consolidated view of a unary relation.
226    ///
227    /// # Examples
228    ///
229    /// ```
230    /// use materialized_view::*;
231    ///
232    /// let query = program! {
233    /// rdfType(?o) <- [RDF(?s, "rdf:type", ?o)],
234    /// };
235    /// let mut dynamic_view = MaterializedDatalogView::new(query);
236    ///
237    /// dynamic_view.push_fact("RDF", ("rust", "rdf:type", "programmingLanguage".to_string()));
238    /// dynamic_view.poll();
239    /// let query_result: Vec<((String,))> = dynamic_view
240    ///     .query_unary::<String>("rdfType", (ANY_VALUE,))
241    ///     .unwrap()
242    ///     .map(|((x,))| (x.clone(),))
243    ///     .collect();
244    /// let expected_query_result = vec![("programmingLanguage".to_string(),)];
245    /// assert_eq!(expected_query_result, query_result)
246    /// ```
247    pub fn query_unary<'a, T: 'static>(
248        &self,
249        relation_symbol: RelationSymbol,
250        goal: impl Into<Goal>,
251    ) -> Result<impl Iterator<Item=(&T, )>, PollingError> {
252        if !self.safe() {
253            return Err(PollingError)
254        }
255
256        let goal = goal.into();
257        let resolved_goal = self.internment_layer.resolve_goal(goal).unwrap();
258        let hashed_relation_symbol = reproducible_hash_one(&relation_symbol);
259        let fact_storage = &self.storage_layer.get_relations(&hashed_relation_symbol).1;
260
261        Ok(fact_storage
262            .iter()
263            .filter(move |interned_constant_terms| pattern_match(&resolved_goal, &interned_constant_terms))
264            .map(|encoded_fact| {
265                let resolved_interned_constant_term = self.internment_layer.resolve_interned_constant::<T>(decode_fact(*encoded_fact)[0]).unwrap();
266
267                (resolved_interned_constant_term,)
268            }))
269    }
270    /// Queries the frontier view of a unary relation.
271    ///
272    /// # Examples
273    ///
274    /// ```
275    /// use materialized_view::*;
276    ///
277    /// let query = program! {
278    /// rdfType(?o) <- [RDF(?s, "rdf:type", ?o)],
279    /// };
280    /// let mut dynamic_view = MaterializedDatalogView::new(query);
281    ///
282    /// dynamic_view.push_fact("RDF", ("rust", "rdf:type", "programmingLanguage".to_string()));
283    /// dynamic_view.poll();
284    /// dynamic_view.push_fact("RDF", ("emacs", "rdf:type", "operatingSystem".to_string()));
285    /// dynamic_view.poll();
286    /// let query_result: Vec<(isize, (String,))> = dynamic_view
287    ///     .query_frontier_unary::<String>("rdfType", (ANY_VALUE,))
288    ///     .unwrap()
289    ///     .map(|((x,), weight)| (weight, (x.clone(),)))
290    ///     .collect();
291    /// let expected_query_result = vec![(1, ("operatingSystem".to_string(),))];
292    /// assert_eq!(expected_query_result, query_result)
293    /// ```
294    pub fn query_frontier_unary<'a, T: 'static>(
295        &self,
296        relation_symbol: RelationSymbol,
297        goal: impl Into<Goal>,
298    ) -> Result<impl Iterator<Item=((&T, ), Weight)>, PollingError> {
299        if !self.safe() {
300            return Err(PollingError)
301        }
302
303        let goal = goal.into();
304        let resolved_goal = self.internment_layer.resolve_goal(goal).unwrap();
305        let hashed_relation_symbol = reproducible_hash_one(&relation_symbol);
306        let frontier = &self.storage_layer.get_relations(&hashed_relation_symbol).0;
307
308        Ok(frontier
309            .iter()
310            .filter(move |interned_constant_terms| pattern_match(&resolved_goal, &interned_constant_terms.0))
311            .map(|(encoded_fact, weight)| {
312                let resolved_interned_constant_term = self.internment_layer.resolve_interned_constant::<T>(decode_fact(*encoded_fact)[0]).unwrap();
313
314                ((resolved_interned_constant_term,), *weight)
315            }))
316    }
317    /// Queries the consolidated view of a binary relation.
318    ///
319    /// # Examples
320    ///
321    /// ```
322    /// use std::collections::HashSet;
323    /// use materialized_view::*;
324    ///
325    /// let query = program! {
326    /// subClassOf(?s, ?o) <- [RDF(?s, "rdfs:subClassOf", ?o)],
327    /// subClassOf(?s, ?t) <- [subClassOf(?s, ?o), subClassOf(?o, ?t)],
328    /// };
329    /// let mut dynamic_view = MaterializedDatalogView::new(query);
330    ///
331    /// dynamic_view.push_fact("RDF", ("animal".to_string(), "rdfs:subClassOf", "livingForm".to_string()));
332    /// dynamic_view.push_fact("RDF", ("bird".to_string(), "rdfs:subClassOf", "animal".to_string()));
333    /// dynamic_view.poll();
334    /// let query_result: HashSet<((String, String))> = dynamic_view
335    ///     .query_binary::<String, String>("subClassOf", (ANY_VALUE, ANY_VALUE))
336    ///     .unwrap()
337    ///     .map(|((x, y))| (x.clone(), y.clone()))
338    ///     .collect();
339    /// let expected_query_result: HashSet<_> = vec![
340    ///     ("animal".to_string(), "livingForm".to_string()),
341    ///     ("bird".to_string(), "animal".to_string()),
342    ///     ("bird".to_string(), "livingForm".to_string())].into_iter().collect();
343    /// assert_eq!(expected_query_result, query_result);
344    /// ```
345    pub fn query_binary<'a, T: 'static, R: 'static>(
346        &self,
347        relation_symbol: RelationSymbol,
348        goal: impl Into<Goal>,
349    ) -> Result<impl Iterator<Item=(&T, &R)>, PollingError> {
350        if !self.safe() {
351            return Err(PollingError);
352        }
353
354        let goal = goal.into();
355        let resolved_goal = self.internment_layer.resolve_goal(goal).unwrap();
356        let hashed_relation_symbol = reproducible_hash_one(&relation_symbol);
357        let fact_storage = &self.storage_layer.get_relations(&hashed_relation_symbol).1;
358
359        Ok(fact_storage
360            .iter()
361            .filter(move |interned_constant_terms| pattern_match(&resolved_goal, &interned_constant_terms))
362            .map(|encoded_fact| {
363                let resolved_interned_constant_term_one = self.internment_layer.resolve_interned_constant::<T>(decode_fact(*encoded_fact)[0]).unwrap();
364                let resolved_interned_constant_term_two = self.internment_layer.resolve_interned_constant::<R>(decode_fact(*encoded_fact)[1]).unwrap();
365
366                (resolved_interned_constant_term_one, resolved_interned_constant_term_two)
367            }))
368    }
369    /// Queries the frontier of a binary relation.
370    ///
371    /// # Examples
372    ///
373    /// ```
374    /// use std::collections::HashSet;
375    /// use materialized_view::*;
376    ///
377    /// let query = program! {
378    /// subClassOf(?s, ?o) <- [RDF(?s, "rdfs:subClassOf", ?o)],
379    /// subClassOf(?s, ?t) <- [subClassOf(?s, ?o), subClassOf(?o, ?t)],
380    /// };
381    /// let mut dynamic_view = MaterializedDatalogView::new(query);
382    ///
383    /// dynamic_view.push_fact("RDF", ("animal".to_string(), "rdfs:subClassOf", "livingForm".to_string()));
384    /// dynamic_view.push_fact("RDF", ("bird".to_string(), "rdfs:subClassOf", "animal".to_string()));
385    /// dynamic_view.poll();
386    /// dynamic_view.retract_fact("RDF", ("animal".to_string(), "rdfs:subClassOf", "livingForm".to_string()));
387    /// dynamic_view.poll();
388    /// let query_result: HashSet<((isize, (String, String)))> = dynamic_view
389    ///     .query_frontier_binary::<String, String>("subClassOf", (ANY_VALUE, ANY_VALUE))
390    ///     .unwrap()
391    ///     .map(|((x, y), weight)| (weight, (x.clone(), y.clone())))
392    ///     .collect();
393    /// let expected_query_result: HashSet<_> = vec![
394    ///     (-1, ("animal".to_string(), "livingForm".to_string())),
395    ///     (-1, ("bird".to_string(), "livingForm".to_string()))].into_iter().collect();
396    /// assert_eq!(expected_query_result, query_result);
397    /// ```
398    pub fn query_frontier_binary<'a, T: 'static, R: 'static>(
399        &self,
400        relation_symbol: RelationSymbol,
401        goal: impl Into<Goal>,
402    ) -> Result<impl Iterator<Item=((&T, &R), Weight)>, PollingError> {
403        if !self.safe() {
404            return Err(PollingError)
405        }
406
407        let goal = goal.into();
408        let resolved_goal = self.internment_layer.resolve_goal(goal).unwrap();
409        let hashed_relation_symbol = reproducible_hash_one(&relation_symbol);
410        let frontier = &self.storage_layer.get_relations(&hashed_relation_symbol).0;
411
412        Ok(frontier
413            .iter()
414            .filter(move |interned_constant_terms| pattern_match(&resolved_goal, &interned_constant_terms.0))
415            .map(|(encoded_fact, weight)| {
416                let resolved_interned_constant_term_one = self.internment_layer.resolve_interned_constant::<T>(decode_fact(*encoded_fact)[0]).unwrap();
417                let resolved_interned_constant_term_two = self.internment_layer.resolve_interned_constant::<R>(decode_fact(*encoded_fact)[1]).unwrap();
418
419                ((resolved_interned_constant_term_one, resolved_interned_constant_term_two), *weight)
420            }))
421    }
422    /// Queries the consolidated view of a ternary relation.
423    ///
424    /// # Examples
425    ///
426    /// ```
427    /// use std::collections::HashSet;
428    /// use materialized_view::*;
429    ///
430    /// let query = program! {
431    /// triangle(?a, ?b, ?c) <- [edge(?a, ?b), edge(?b, ?c), edge(?c, ?a)],
432    /// };
433    /// let mut dynamic_view = MaterializedDatalogView::new(query);
434    ///
435    /// dynamic_view.push_fact("edge", (1, 2));
436    /// dynamic_view.push_fact("edge", (2, 3));
437    /// dynamic_view.push_fact("edge", (3, 1));
438    /// dynamic_view.push_fact("edge", (4, 5));
439    /// dynamic_view.push_fact("edge", (5, 6));
440    /// dynamic_view.poll();
441    /// let query_result: HashSet<((i32, i32, i32))> = dynamic_view
442    ///     .query_ternary::<i32, i32, i32>("triangle", (ANY_VALUE, ANY_VALUE, ANY_VALUE))
443    ///     .unwrap()
444    ///     .map(|((a, b, c))| (*a, *b, *c))
445    ///     .collect();
446    /// let expected_query_result: HashSet<_> = vec![(1, 2, 3), (2, 3, 1), (3, 1, 2)].into_iter().collect();
447    /// assert_eq!(expected_query_result, query_result);
448    /// ```
449    pub fn query_ternary<'a, T: 'static, R: 'static, S: 'static>(
450        &self,
451        relation_symbol: RelationSymbol,
452        goal: impl Into<Goal>,
453    ) -> Result<impl Iterator<Item=(&T, &R, &S)>, PollingError> {
454        if !self.safe() {
455            return Err(PollingError);
456        }
457
458        let goal = goal.into();
459        let resolved_goal = self.internment_layer.resolve_goal(goal).unwrap();
460        let hashed_relation_symbol = reproducible_hash_one(&relation_symbol);
461        let fact_storage = &self.storage_layer.get_relations(&hashed_relation_symbol).1;
462
463        Ok(fact_storage
464            .iter()
465            .filter(move |interned_constant_terms| pattern_match(&resolved_goal, &interned_constant_terms))
466            .map(|encoded_fact| {
467                let resolved_interned_constant_term_one = self.internment_layer.resolve_interned_constant::<T>(decode_fact(*encoded_fact)[0]).unwrap();
468                let resolved_interned_constant_term_two = self.internment_layer.resolve_interned_constant::<R>(decode_fact(*encoded_fact)[1]).unwrap();
469                let resolved_interned_constant_term_three = self.internment_layer.resolve_interned_constant::<S>(decode_fact(*encoded_fact)[2]).unwrap();
470
471                (resolved_interned_constant_term_one, resolved_interned_constant_term_two, resolved_interned_constant_term_three)
472            }))
473    }
474    /// Queries the frontier of a ternary relation.
475    ///
476    /// # Examples
477    ///
478    /// ```
479    /// use std::collections::HashSet;
480    /// use materialized_view::*;
481    ///
482    /// let query = program! {
483    /// triangle(?a, ?b, ?c) <- [edge(?a, ?b), edge(?b, ?c), edge(?c, ?a)],
484    /// };
485    /// let mut dynamic_view = MaterializedDatalogView::new(query);
486    ///
487    /// dynamic_view.push_fact("edge", (1, 2));
488    /// dynamic_view.push_fact("edge", (2, 3));
489    /// dynamic_view.push_fact("edge", (3, 1));
490    /// dynamic_view.push_fact("edge", (4, 5));
491    /// dynamic_view.push_fact("edge", (5, 6));
492    /// dynamic_view.poll();
493    /// let query_result: HashSet<((isize, (i32, i32, i32)))> = dynamic_view
494    ///     .query_frontier_ternary::<i32, i32, i32>("triangle", (ANY_VALUE, ANY_VALUE, ANY_VALUE))
495    ///     .unwrap()
496    ///     .map(|(((a, b, c), weight))| (weight, (*a, *b, *c)))
497    ///     .collect();
498    /// let expected_query_result: HashSet<_> = vec![(1, (1, 2, 3)), (1, (2, 3, 1)), (1, (3, 1, 2))].into_iter().collect();
499    /// assert_eq!(expected_query_result, query_result);
500    pub fn query_frontier_ternary<'a, T: 'static, R: 'static, S: 'static>(
501        &self,
502        relation_symbol: RelationSymbol,
503        goal: impl Into<Goal>,
504    ) -> Result<impl Iterator<Item=((&T, &R, &S), Weight)>, PollingError> {
505        if !self.safe() {
506            return Err(PollingError)
507        }
508
509        let goal = goal.into();
510        let resolved_goal = self.internment_layer.resolve_goal(goal).unwrap();
511        let hashed_relation_symbol = reproducible_hash_one(&relation_symbol);
512        let frontier = &self.storage_layer.get_relations(&hashed_relation_symbol).0;
513
514        Ok(frontier
515            .iter()
516            .filter(move |interned_constant_terms| pattern_match(&resolved_goal, &interned_constant_terms.0))
517            .map(|(encoded_fact, weight)| {
518                let resolved_interned_constant_term_one = self.internment_layer.resolve_interned_constant::<T>(decode_fact(*encoded_fact)[0]).unwrap();
519                let resolved_interned_constant_term_two = self.internment_layer.resolve_interned_constant::<R>(decode_fact(*encoded_fact)[1]).unwrap();
520                let resolved_interned_constant_term_three = self.internment_layer.resolve_interned_constant::<S>(decode_fact(*encoded_fact)[2]).unwrap();
521
522                ((resolved_interned_constant_term_one, resolved_interned_constant_term_two, resolved_interned_constant_term_three), *weight)
523            }))
524    }
525    fn step(&mut self) {
526        let now = Instant::now();
527        self.compute_layer.step();
528        println!("Step: {} ms", now.elapsed().as_millis());
529    }
530    fn consolidate(&mut self) {
531        self.compute_layer.consolidate_into_storage_layer(&mut self.storage_layer)
532    }
533    /// Triggers a incremental materialization update
534    pub fn poll(&mut self) {
535        self.storage_layer.move_frontier();
536        self.step();
537        self.consolidate();
538        self.safe = true;
539    }
540    /// Creates a new dynamic materialised view
541    pub fn new(program: Vec<impl Into<Rule>>) -> Self {
542        let storage_layer: StorageLayer = Default::default();
543        let herbrand_universe = InternmentLayer::default();
544        let compute_layer = ComputeLayer::new();
545        let mut materialized_datalog_view = Self { compute_layer, internment_layer: herbrand_universe, storage_layer, safe: true };
546
547        program.into_iter().for_each(|rule| {
548            materialized_datalog_view.push_rule(rule);
549        });
550
551        materialized_datalog_view
552    }
553    /// Returns whether all incoming updates have been taken into account
554    pub fn safe(&self) -> bool {
555        self.safe
556    }
557    /// Returns the number of consolidated updates
558    pub fn len(&self) -> usize {
559        self.storage_layer.len()
560    }
561}
562
563impl<'a, R> Extend<(&'a str, R)> for MaterializedDatalogView
564where R: Into<Fact> {
565    fn extend<T: IntoIterator<Item=(&'a str, R)>>(&mut self, iter: T) {
566        iter
567            .into_iter()
568            .for_each(|(relation_name, fact)| { self.push_fact(relation_name, fact); });
569    }
570}
571
572impl<R> Extend<R> for MaterializedDatalogView
573    where R: Into<Rule> {
574    fn extend<T: IntoIterator<Item=R>>(&mut self, iter: T) {
575        iter
576            .into_iter()
577            .for_each(|rule| { self.push_rule(rule); });
578    }
579}
580
581#[cfg(test)]
582mod tests {
583    use crate::engine::datalog::{EMPTY_PROGRAM, MaterializedDatalogView};
584    use datalog_syntax_macros::{program, rule};
585    use datalog_syntax::*;
586    use std::collections::HashSet;
587    use crate::builders::goal::ANY_VALUE;
588    use crate::builders::rule;
589    use crate::builders::rule::{Const, Var};
590
591    type NodeIndex = usize;
592    type Edge = (NodeIndex, NodeIndex);
593
594    fn assemble_tc_program() -> Vec<Rule> {
595        program! {
596            tc(?x, ?y) <- [e(?x, ?y)],
597            tc(?x, ?z) <- [e(?x, ?y), tc(?y, ?z)]
598        }
599    }
600
601    #[test]
602    fn integration_test_push_fact() {
603        let tc_program = assemble_tc_program();
604
605        let mut materialized_datalog_view = MaterializedDatalogView::new(tc_program);
606        vec![
607            (1, 2),
608            (2, 3),
609            (3, 4),
610        ]
611        .into_iter()
612        .for_each(|edge: Edge| {
613            materialized_datalog_view.push_fact("e", edge);
614        });
615
616        materialized_datalog_view.poll();
617        assert_eq!(materialized_datalog_view.len(), 9);
618
619        let actual_all: HashSet<Edge> =
620            materialized_datalog_view
621                .query_binary("tc", (ANY_VALUE, ANY_VALUE))
622                .unwrap()
623                .map(|(x, y)| (*x, *y))
624                .collect();
625        let expected_all: HashSet<Edge> = vec![
626            (1, 2),
627            (2, 3),
628            (3, 4),
629            // Second iter
630            (1, 3),
631            (2, 4),
632            // Third iter
633            (1, 4),
634        ]
635        .into_iter()
636        .collect();
637        assert_eq!(expected_all, actual_all);
638
639        let actual_all_from_a: HashSet<Edge> =
640            materialized_datalog_view.query_binary("tc", (Some(1), ANY_VALUE)).unwrap().map(|(x, y)| (*x, *y)).collect();
641        let expected_all_from_a: HashSet<Edge> = vec![
642            (1, 2),
643            (1, 3),
644            (1, 4),
645        ]
646        .into_iter()
647        .collect();
648        assert_eq!(expected_all_from_a, actual_all_from_a);
649
650        expected_all.iter().for_each(|fact| {
651            assert!(materialized_datalog_view.contains("tc", *fact).unwrap());
652        });
653
654        expected_all_from_a.iter().for_each(|fact| {
655            assert!(materialized_datalog_view.contains("tc", *fact).unwrap());
656        });
657
658        // Update
659        materialized_datalog_view.push_fact("e", (4usize, 5usize));
660        assert!(!materialized_datalog_view.safe());
661        materialized_datalog_view.poll();
662        assert!(materialized_datalog_view.safe());
663
664        let actual_all_after_update: HashSet<Edge> =
665            materialized_datalog_view
666                .query_binary("tc", (ANY_VALUE, ANY_VALUE))
667                .unwrap()
668                .map(|(x, y)| (*x, *y))
669                .collect();
670        let expected_all_after_update: HashSet<Edge> = vec![
671            (1, 2),
672            (2, 3),
673            (3, 4),
674            // Second iter
675            (1, 3),
676            (2, 4),
677            // Third iter
678            (1, 4),
679            // Update
680            (4, 5),
681            (3, 5),
682            (2, 5),
683            (1, 5),
684        ]
685        .into_iter()
686        .collect();
687        assert_eq!(expected_all_after_update, actual_all_after_update);
688
689        let actual_all_from_a_after_update: HashSet<Edge> = materialized_datalog_view
690            .query_binary("tc", (Some(1), ANY_VALUE))
691            .unwrap()
692            .map(|(x, y)| (*x, *y))
693            .collect();
694        let expected_all_from_a_after_update: HashSet<Edge> = vec![
695            (1, 2),
696            (1, 3),
697            (1, 4),
698            (1, 5),
699        ]
700        .into_iter()
701        .collect();
702        assert_eq!(
703            expected_all_from_a_after_update,
704            actual_all_from_a_after_update
705        );
706    }
707    #[test]
708    fn integration_test_retract_fact() {
709        let tc_program = assemble_tc_program();
710
711        let mut materialized_datalog_view = MaterializedDatalogView::new(tc_program);
712        vec![
713            (1, 2),
714            // this extra atom will help with testing that rederivation works
715            (1, 5),
716            (2, 3),
717            (3, 4),
718            (4, 5),
719        ]
720        .into_iter()
721        .for_each(|edge: Edge| {
722            materialized_datalog_view.push_fact("e", edge);
723        });
724        materialized_datalog_view.poll();
725
726        let actual_all: HashSet<Edge> =
727            materialized_datalog_view.query_binary("tc", (ANY_VALUE, ANY_VALUE)).unwrap().map(|(x, y)| (*x, *y)).collect();
728        let expected_all: HashSet<Edge> = vec![
729            (1, 2),
730            (1, 5),
731            (2, 3),
732            (3, 4),
733            // Second iter
734            (1, 3),
735            (2, 4),
736            // Third iter
737            (1, 4),
738            // Fourth iter
739            (4, 5),
740            (3, 5),
741            (2, 5),
742        ]
743        .into_iter()
744        .collect();
745        assert_eq!(expected_all, actual_all);
746
747        let actual_all_from_a: HashSet<Edge> = materialized_datalog_view
748            .query_binary("tc", (Some(1), ANY_VALUE))
749            .unwrap()
750            .map(|(x, y)| (*x, *y))
751            .collect();
752        let expected_all_from_a: HashSet<Edge> = vec![
753            (1, 2),
754            (1, 3),
755            (1, 4),
756            (1, 5),
757        ]
758        .into_iter()
759        .collect();
760        assert_eq!(expected_all_from_a, actual_all_from_a);
761
762        // Update
763        materialized_datalog_view.retract_fact("e", (4usize, 5usize));
764        assert!(!materialized_datalog_view.safe());
765        materialized_datalog_view.poll();
766        assert!(materialized_datalog_view.safe());
767
768        let actual_all_after_update: HashSet<Edge> =
769            materialized_datalog_view.query_binary("tc", (ANY_VALUE, ANY_VALUE)).unwrap().map(|(x, y)| (*x, *y)).collect();
770        let expected_all_after_update: HashSet<Edge> = vec![
771            (1, 2),
772            (2, 3),
773            (3, 4),
774            // Second iter
775            (1, 3),
776            (2, 4),
777            // Third iter
778            (1, 4),
779            // This remains
780            (1, 5),
781        ]
782        .into_iter()
783        .collect();
784        assert_eq!(expected_all_after_update, actual_all_after_update);
785
786        let actual_all_from_a_after_update: HashSet<Edge> = materialized_datalog_view
787            .query_binary("tc", (Some(1), ANY_VALUE))
788            .unwrap()
789            .map(|(x, y)| (*x, *y))
790            .collect();
791        let expected_all_from_a_after_update: HashSet<Edge> = vec![
792            (1, 2),
793            (1, 3),
794            (1, 4),
795            (1, 5),
796        ]
797        .into_iter()
798        .collect();
799        assert_eq!(
800            expected_all_from_a_after_update,
801            actual_all_from_a_after_update
802        );
803    }
804
805    #[test]
806    fn integration_test_push_rule() {
807        let tc_program = assemble_tc_program();
808
809        let mut materialized_datalog_view = MaterializedDatalogView::new(tc_program);
810        vec![
811            (1, 2),
812            (2, 3),
813            (3, 4),
814        ]
815            .into_iter()
816            .for_each(|edge: Edge| {
817                materialized_datalog_view.push_fact("e", edge);
818            });
819
820        let actual_all_from_1_rule_dyn = rule::Rule::from((("tc1", (Const(1usize), Var("x"))), vec![("tc", (Const(1usize), Var("x")))]));
821        let actual_all_from_1_rule_compiled = rule::Rule::from(rule!{ tc1(1usize, ?x) <- [tc(1usize, ?x)] });
822        assert_eq!(actual_all_from_1_rule_dyn, actual_all_from_1_rule_compiled);
823        materialized_datalog_view.push_rule(actual_all_from_1_rule_dyn);
824        assert!(!materialized_datalog_view.safe());
825        materialized_datalog_view.poll();
826        assert!(materialized_datalog_view.safe());
827
828        let actual_all_from_1: HashSet<Edge> =
829            materialized_datalog_view.query_binary("tc1", (ANY_VALUE, ANY_VALUE)).unwrap().map(|(x, y)| (*x, *y)).collect();
830        let expected_all_from_1: HashSet<Edge> = vec![
831            (1, 2),
832            (1, 3),
833            (1, 4),
834        ]
835            .into_iter()
836            .collect();
837        assert_eq!(expected_all_from_1, actual_all_from_1);
838
839        expected_all_from_1.iter().for_each(|fact| {
840            assert!(materialized_datalog_view.contains("tc", *fact).unwrap());
841            assert!(materialized_datalog_view.contains("tc1", *fact).unwrap());
842        });
843
844        materialized_datalog_view.push_fact("e", (4usize, 5usize));
845        materialized_datalog_view.poll();
846
847        let actual_all_from_1_after_update: HashSet<Edge> = materialized_datalog_view
848            .query_binary("tc1", (ANY_VALUE, ANY_VALUE))
849            .unwrap()
850            .map(|(x, y)| (*x, *y))
851            .collect();
852        let expected_all_from_1_after_update: HashSet<Edge> = vec![
853            (1, 2),
854            (1, 3),
855            (1, 4),
856            (1, 5),
857        ]
858            .into_iter()
859            .collect();
860        assert_eq!(
861            expected_all_from_1_after_update,
862            actual_all_from_1_after_update
863        );
864    }
865    #[test]
866    fn integration_test_retract_rule() {
867        let tc_program = assemble_tc_program();
868
869        let mut materialized_datalog_view = MaterializedDatalogView::new(EMPTY_PROGRAM);
870        vec![
871            (1, 2),
872            (2, 3),
873            (3, 4),
874        ]
875            .into_iter()
876            .for_each(|edge: Edge| {
877                materialized_datalog_view.push_fact("e", edge);
878            });
879        materialized_datalog_view.poll();
880        assert_eq!(materialized_datalog_view.len(), 3);
881        materialized_datalog_view.push_rule(tc_program[0].clone());
882        materialized_datalog_view.poll();
883        assert_eq!(materialized_datalog_view.len(), 6);
884        materialized_datalog_view.push_rule(tc_program[1].clone());
885        materialized_datalog_view.poll();
886
887        assert_eq!(materialized_datalog_view.len(), 9);
888
889        materialized_datalog_view.push_rule(rule!{ tc1(1usize, ?x) <- [tc(1usize, ?x)] });
890        materialized_datalog_view.poll();
891        assert_eq!(materialized_datalog_view.len(), 12);
892
893        vec![
894            (1, 2),
895            (1, 3),
896            (1, 4),
897        ]
898            .into_iter()
899            .for_each(|fact: Edge| {
900            assert!(materialized_datalog_view.contains("tc", fact).unwrap());
901            assert!(materialized_datalog_view.contains("tc1", fact).unwrap());
902        });
903
904        materialized_datalog_view.retract_rule(rule!{ tc1(1usize, ?x) <- [tc(1usize, ?x)] });
905        assert!(!materialized_datalog_view.safe);
906        materialized_datalog_view.poll();
907        assert!(materialized_datalog_view.safe);
908        assert_eq!(materialized_datalog_view.len(), 9);
909        materialized_datalog_view.retract_rule(rule!{ tc(?x, ?z) <- [e(?x, ?y), tc(?y, ?z)] });
910        materialized_datalog_view.poll();
911        assert_eq!(materialized_datalog_view.len(), 6);
912        materialized_datalog_view.retract_rule(rule!{ tc(?x, ?y) <- [e(?x, ?y)] });
913        materialized_datalog_view.poll();
914        assert_eq!(materialized_datalog_view.len(), 3);
915    }
916
917    #[test]
918    fn integration_test_triangle_query() {
919        let tc_program = program! {
920            t(?a, ?b, ?c) <- [e(?a, ?b), e(?b, ?c), e(?c, ?a)],
921        };
922
923        let mut materialized_datalog_view = MaterializedDatalogView::new(tc_program);
924        vec![
925            (1, 2),
926            (2, 3),
927            (3, 1),
928            (4, 5),
929            (5, 6)
930        ]
931            .into_iter()
932            .for_each(|edge: Edge| {
933                materialized_datalog_view.push_fact("e", edge);
934            });
935        materialized_datalog_view.poll();
936        assert_eq!(materialized_datalog_view.len(), 8);
937
938        type Triangle = (NodeIndex, NodeIndex, NodeIndex);
939        let actual_all: HashSet<Triangle> =
940            materialized_datalog_view
941                .query_ternary("t", (ANY_VALUE, ANY_VALUE, ANY_VALUE))
942                .unwrap()
943                .map(|(x, y, z)| (*x, *y, *z))
944                .collect();
945        let expected_all: HashSet<Triangle> = vec![
946            (1, 2, 3),
947            (2, 3, 1),
948            (3, 1, 2),
949        ]
950            .into_iter()
951            .collect();
952        assert_eq!(expected_all, actual_all);
953    }
954}