1use std::collections::HashMap;
19
20#[derive(Debug, Clone, PartialEq, Eq, Hash)]
22pub struct TripleFact {
23 pub subject: String,
24 pub predicate: String,
25 pub object: String,
26}
27
28impl TripleFact {
29 pub fn new(
30 subject: impl Into<String>,
31 predicate: impl Into<String>,
32 object: impl Into<String>,
33 ) -> Self {
34 Self {
35 subject: subject.into(),
36 predicate: predicate.into(),
37 object: object.into(),
38 }
39 }
40}
41
42#[derive(Debug, Clone)]
44pub enum GraphPattern {
45 Triple(TripleFact),
47 And(Vec<GraphPattern>),
49 Optional(Box<GraphPattern>),
51 Filter {
53 pattern: Box<GraphPattern>,
54 condition: String,
55 },
56 Union(Box<GraphPattern>, Box<GraphPattern>),
58}
59
60pub type SolutionMapping = HashMap<String, String>;
62
63#[derive(Debug, Default)]
65pub struct ExistsEvaluator;
66
67impl ExistsEvaluator {
68 pub fn new() -> Self {
70 Self
71 }
72
73 pub fn evaluate_exists(
75 &self,
76 facts: &[TripleFact],
77 pattern: &GraphPattern,
78 input: &SolutionMapping,
79 ) -> bool {
80 let solutions = self.inner_match(facts, pattern, input.clone());
81 !solutions.is_empty()
82 }
83
84 pub fn evaluate_not_exists(
86 &self,
87 facts: &[TripleFact],
88 pattern: &GraphPattern,
89 input: &SolutionMapping,
90 ) -> bool {
91 !self.evaluate_exists(facts, pattern, input)
92 }
93
94 pub fn inner_match(
98 &self,
99 facts: &[TripleFact],
100 pattern: &GraphPattern,
101 bindings: SolutionMapping,
102 ) -> Vec<SolutionMapping> {
103 match pattern {
104 GraphPattern::Triple(triple) => self.match_triple(facts, triple, bindings),
105
106 GraphPattern::And(patterns) => {
107 let mut current_solutions = vec![bindings];
110 for sub_pattern in patterns {
111 let mut next_solutions = Vec::new();
112 for sol in current_solutions {
113 let results = self.inner_match(facts, sub_pattern, sol);
114 next_solutions.extend(results);
115 }
116 current_solutions = next_solutions;
117 }
118 current_solutions
119 }
120
121 GraphPattern::Optional(sub_pattern) => {
122 let results = self.inner_match(facts, sub_pattern, bindings.clone());
125 if results.is_empty() {
126 vec![bindings]
127 } else {
128 results
129 }
130 }
131
132 GraphPattern::Filter {
133 pattern: sub_pattern,
134 condition,
135 } => {
136 let solutions = self.inner_match(facts, sub_pattern, bindings);
137 solutions
138 .into_iter()
139 .filter(|sol| self.evaluate_filter_condition(sol, condition))
140 .collect()
141 }
142
143 GraphPattern::Union(left, right) => {
144 let mut left_solutions = self.inner_match(facts, left, bindings.clone());
145 let right_solutions = self.inner_match(facts, right, bindings);
146 left_solutions.extend(right_solutions);
147 left_solutions
148 }
149 }
150 }
151
152 fn match_triple(
154 &self,
155 facts: &[TripleFact],
156 triple: &TripleFact,
157 bindings: SolutionMapping,
158 ) -> Vec<SolutionMapping> {
159 let mut results = Vec::new();
160 for fact in facts {
161 if let Some(new_bindings) = self.try_bind_triple(triple, fact, bindings.clone()) {
162 results.push(new_bindings);
163 }
164 }
165 results
166 }
167
168 fn try_bind_triple(
172 &self,
173 pattern: &TripleFact,
174 fact: &TripleFact,
175 mut bindings: SolutionMapping,
176 ) -> Option<SolutionMapping> {
177 bindings = self.try_bind_term(&pattern.subject, &fact.subject, bindings)?;
179 bindings = self.try_bind_term(&pattern.predicate, &fact.predicate, bindings)?;
180 bindings = self.try_bind_term(&pattern.object, &fact.object, bindings)?;
181 Some(bindings)
182 }
183
184 fn try_bind_term(
186 &self,
187 term: &str,
188 value: &str,
189 mut bindings: SolutionMapping,
190 ) -> Option<SolutionMapping> {
191 if Self::is_variable(term) {
192 let var_name = term.trim_start_matches('?').to_string();
193 if let Some(existing) = bindings.get(&var_name) {
194 if existing != value {
195 return None; }
197 } else {
199 bindings.insert(var_name, value.to_string());
200 }
201 Some(bindings)
202 } else {
203 if term == value {
205 Some(bindings)
206 } else {
207 None
208 }
209 }
210 }
211
212 fn evaluate_filter_condition(&self, bindings: &SolutionMapping, condition: &str) -> bool {
218 if let Some(pos) = condition.find("!=") {
219 let lhs = condition[..pos].trim();
220 let rhs = condition[pos + 2..].trim();
221 if Self::is_variable(lhs) {
222 let var_name = lhs.trim_start_matches('?');
223 if let Some(val) = bindings.get(var_name) {
224 return val != rhs;
225 }
226 return false;
228 }
229 } else if let Some(pos) = condition.find('=') {
230 let lhs = condition[..pos].trim();
231 let rhs = condition[pos + 1..].trim();
232 if Self::is_variable(lhs) {
233 let var_name = lhs.trim_start_matches('?');
234 if let Some(val) = bindings.get(var_name) {
235 return val == rhs;
236 }
237 return false;
238 }
239 }
240 true
242 }
243
244 pub fn is_variable(term: &str) -> bool {
246 term.starts_with('?')
247 }
248
249 pub fn compatible(m1: &SolutionMapping, m2: &SolutionMapping) -> bool {
254 for (var, val1) in m1 {
255 if let Some(val2) = m2.get(var) {
256 if val1 != val2 {
257 return false;
258 }
259 }
260 }
261 true
262 }
263
264 pub fn merge(mut m1: SolutionMapping, m2: SolutionMapping) -> SolutionMapping {
268 for (k, v) in m2 {
269 m1.entry(k).or_insert(v);
270 }
271 m1
272 }
273}
274
275#[cfg(test)]
278mod tests {
279 use super::*;
280
281 fn evaluator() -> ExistsEvaluator {
282 ExistsEvaluator::new()
283 }
284
285 fn empty_input() -> SolutionMapping {
286 SolutionMapping::new()
287 }
288
289 fn facts_abc() -> Vec<TripleFact> {
290 vec![
291 TripleFact::new(":Alice", "knows", ":Bob"),
292 TripleFact::new(":Bob", "knows", ":Carol"),
293 TripleFact::new(":Alice", "age", "30"),
294 TripleFact::new(":Bob", "age", "25"),
295 ]
296 }
297
298 #[test]
301 fn test_is_variable_true() {
302 assert!(ExistsEvaluator::is_variable("?x"));
303 }
304
305 #[test]
306 fn test_is_variable_false_constant() {
307 assert!(!ExistsEvaluator::is_variable(":Alice"));
308 }
309
310 #[test]
311 fn test_is_variable_empty_string() {
312 assert!(!ExistsEvaluator::is_variable(""));
313 }
314
315 #[test]
316 fn test_is_variable_question_mark_only() {
317 assert!(ExistsEvaluator::is_variable("?"));
318 }
319
320 #[test]
323 fn test_compatible_empty_maps() {
324 let m1 = SolutionMapping::new();
325 let m2 = SolutionMapping::new();
326 assert!(ExistsEvaluator::compatible(&m1, &m2));
327 }
328
329 #[test]
330 fn test_compatible_same_bindings() {
331 let m1: SolutionMapping = [("x".into(), "1".into())].into();
332 let m2: SolutionMapping = [("x".into(), "1".into())].into();
333 assert!(ExistsEvaluator::compatible(&m1, &m2));
334 }
335
336 #[test]
337 fn test_compatible_different_vars() {
338 let m1: SolutionMapping = [("x".into(), "1".into())].into();
339 let m2: SolutionMapping = [("y".into(), "2".into())].into();
340 assert!(ExistsEvaluator::compatible(&m1, &m2));
341 }
342
343 #[test]
344 fn test_compatible_conflict() {
345 let m1: SolutionMapping = [("x".into(), "1".into())].into();
346 let m2: SolutionMapping = [("x".into(), "2".into())].into();
347 assert!(!ExistsEvaluator::compatible(&m1, &m2));
348 }
349
350 #[test]
353 fn test_merge_disjoint() {
354 let m1: SolutionMapping = [("x".into(), "1".into())].into();
355 let m2: SolutionMapping = [("y".into(), "2".into())].into();
356 let merged = ExistsEvaluator::merge(m1, m2);
357 assert_eq!(merged.get("x").map(|s| s.as_str()), Some("1"));
358 assert_eq!(merged.get("y").map(|s| s.as_str()), Some("2"));
359 }
360
361 #[test]
362 fn test_merge_m1_wins_on_overlap() {
363 let m1: SolutionMapping = [("x".into(), "1".into())].into();
364 let m2: SolutionMapping = [("x".into(), "99".into())].into();
365 let merged = ExistsEvaluator::merge(m1, m2);
366 assert_eq!(merged.get("x").map(|s| s.as_str()), Some("1"));
367 }
368
369 #[test]
372 fn test_exists_simple_triple_found() {
373 let e = evaluator();
374 let facts = facts_abc();
375 let pattern = GraphPattern::Triple(TripleFact::new(":Alice", "knows", ":Bob"));
376 assert!(e.evaluate_exists(&facts, &pattern, &empty_input()));
377 }
378
379 #[test]
380 fn test_exists_simple_triple_not_found() {
381 let e = evaluator();
382 let facts = facts_abc();
383 let pattern = GraphPattern::Triple(TripleFact::new(":Alice", "knows", ":Dave"));
384 assert!(!e.evaluate_exists(&facts, &pattern, &empty_input()));
385 }
386
387 #[test]
388 fn test_exists_variable_subject() {
389 let e = evaluator();
390 let facts = facts_abc();
391 let pattern = GraphPattern::Triple(TripleFact::new("?x", "knows", ":Bob"));
392 assert!(e.evaluate_exists(&facts, &pattern, &empty_input()));
393 }
394
395 #[test]
396 fn test_exists_all_variables() {
397 let e = evaluator();
398 let facts = facts_abc();
399 let pattern = GraphPattern::Triple(TripleFact::new("?s", "?p", "?o"));
400 assert!(e.evaluate_exists(&facts, &pattern, &empty_input()));
401 }
402
403 #[test]
404 fn test_exists_empty_graph() {
405 let e = evaluator();
406 let facts: Vec<TripleFact> = vec![];
407 let pattern = GraphPattern::Triple(TripleFact::new("?s", "?p", "?o"));
408 assert!(!e.evaluate_exists(&facts, &pattern, &empty_input()));
409 }
410
411 #[test]
412 fn test_exists_with_pre_bound_input_matching() {
413 let e = evaluator();
414 let facts = facts_abc();
415 let pattern = GraphPattern::Triple(TripleFact::new("?x", "knows", "?y"));
416 let mut input = SolutionMapping::new();
417 input.insert("x".into(), ":Alice".into());
418 assert!(e.evaluate_exists(&facts, &pattern, &input));
419 }
420
421 #[test]
422 fn test_exists_with_pre_bound_input_not_matching() {
423 let e = evaluator();
424 let facts = facts_abc();
425 let pattern = GraphPattern::Triple(TripleFact::new("?x", "knows", "?y"));
426 let mut input = SolutionMapping::new();
428 input.insert("x".into(), ":Dave".into());
429 assert!(!e.evaluate_exists(&facts, &pattern, &input));
430 }
431
432 #[test]
433 fn test_exists_scoped_binding_not_leaked() {
434 let e = evaluator();
436 let facts = facts_abc();
437 let pattern = GraphPattern::Triple(TripleFact::new("?x", "knows", ":Bob"));
438 let input = empty_input();
439 e.evaluate_exists(&facts, &pattern, &input);
440 assert!(input.is_empty());
442 }
443
444 #[test]
447 fn test_not_exists_simple_triple_absent() {
448 let e = evaluator();
449 let facts = facts_abc();
450 let pattern = GraphPattern::Triple(TripleFact::new(":Nobody", "knows", ":Bob"));
451 assert!(e.evaluate_not_exists(&facts, &pattern, &empty_input()));
452 }
453
454 #[test]
455 fn test_not_exists_simple_triple_present() {
456 let e = evaluator();
457 let facts = facts_abc();
458 let pattern = GraphPattern::Triple(TripleFact::new(":Alice", "knows", ":Bob"));
459 assert!(!e.evaluate_not_exists(&facts, &pattern, &empty_input()));
460 }
461
462 #[test]
463 fn test_not_exists_all_variables_nonempty_graph() {
464 let e = evaluator();
465 let facts = facts_abc();
466 let pattern = GraphPattern::Triple(TripleFact::new("?s", "?p", "?o"));
467 assert!(!e.evaluate_not_exists(&facts, &pattern, &empty_input()));
468 }
469
470 #[test]
471 fn test_not_exists_empty_graph() {
472 let e = evaluator();
473 let facts: Vec<TripleFact> = vec![];
474 let pattern = GraphPattern::Triple(TripleFact::new("?s", "?p", "?o"));
475 assert!(e.evaluate_not_exists(&facts, &pattern, &empty_input()));
476 }
477
478 #[test]
481 fn test_and_both_match() {
482 let e = evaluator();
483 let facts = facts_abc();
484 let pattern = GraphPattern::And(vec![
485 GraphPattern::Triple(TripleFact::new("?x", "knows", ":Bob")),
486 GraphPattern::Triple(TripleFact::new("?x", "age", "?a")),
487 ]);
488 assert!(e.evaluate_exists(&facts, &pattern, &empty_input()));
489 }
490
491 #[test]
492 fn test_and_second_fails() {
493 let e = evaluator();
494 let facts = facts_abc();
495 let pattern = GraphPattern::And(vec![
496 GraphPattern::Triple(TripleFact::new("?x", "knows", ":Bob")),
497 GraphPattern::Triple(TripleFact::new("?x", "flies", "?a")),
498 ]);
499 assert!(!e.evaluate_exists(&facts, &pattern, &empty_input()));
500 }
501
502 #[test]
503 fn test_and_variable_join() {
504 let e = evaluator();
505 let facts = facts_abc();
506 let pattern = GraphPattern::And(vec![
508 GraphPattern::Triple(TripleFact::new("?x", "knows", "?y")),
509 GraphPattern::Triple(TripleFact::new("?y", "knows", "?z")),
510 ]);
511 assert!(e.evaluate_exists(&facts, &pattern, &empty_input()));
512 }
513
514 #[test]
515 fn test_and_empty_pattern_list() {
516 let e = evaluator();
517 let facts = facts_abc();
518 let pattern = GraphPattern::And(vec![]);
519 assert!(e.evaluate_exists(&facts, &pattern, &empty_input()));
521 }
522
523 #[test]
526 fn test_optional_pattern_present() {
527 let e = evaluator();
528 let facts = facts_abc();
529 let pattern = GraphPattern::Optional(Box::new(GraphPattern::Triple(TripleFact::new(
530 ":Alice", "knows", ":Bob",
531 ))));
532 assert!(e.evaluate_exists(&facts, &pattern, &empty_input()));
533 }
534
535 #[test]
536 fn test_optional_pattern_absent_still_exists() {
537 let e = evaluator();
538 let facts = facts_abc();
539 let pattern = GraphPattern::Optional(Box::new(GraphPattern::Triple(TripleFact::new(
541 ":Nobody", "knows", ":Bob",
542 ))));
543 let solutions = e.inner_match(&facts, &pattern, empty_input());
545 assert_eq!(solutions.len(), 1);
546 assert!(solutions[0].is_empty());
547 }
548
549 #[test]
550 fn test_optional_extends_bindings_when_found() {
551 let e = evaluator();
552 let facts = facts_abc();
553 let pattern = GraphPattern::Optional(Box::new(GraphPattern::Triple(TripleFact::new(
554 "?s", "age", "?a",
555 ))));
556 let solutions = e.inner_match(&facts, &pattern, empty_input());
557 assert!(solutions.iter().any(|sol| sol.contains_key("a")));
559 }
560
561 #[test]
564 fn test_union_left_matches() {
565 let e = evaluator();
566 let facts = facts_abc();
567 let pattern = GraphPattern::Union(
568 Box::new(GraphPattern::Triple(TripleFact::new(
569 ":Alice", "knows", ":Bob",
570 ))),
571 Box::new(GraphPattern::Triple(TripleFact::new(
572 ":Nobody", "knows", ":Bob",
573 ))),
574 );
575 assert!(e.evaluate_exists(&facts, &pattern, &empty_input()));
576 }
577
578 #[test]
579 fn test_union_right_matches() {
580 let e = evaluator();
581 let facts = facts_abc();
582 let pattern = GraphPattern::Union(
583 Box::new(GraphPattern::Triple(TripleFact::new(
584 ":Nobody", "knows", ":Bob",
585 ))),
586 Box::new(GraphPattern::Triple(TripleFact::new(
587 ":Alice", "knows", ":Bob",
588 ))),
589 );
590 assert!(e.evaluate_exists(&facts, &pattern, &empty_input()));
591 }
592
593 #[test]
594 fn test_union_neither_matches() {
595 let e = evaluator();
596 let facts = facts_abc();
597 let pattern = GraphPattern::Union(
598 Box::new(GraphPattern::Triple(TripleFact::new(
599 ":Nobody", "knows", ":Bob",
600 ))),
601 Box::new(GraphPattern::Triple(TripleFact::new(
602 ":Noone", "knows", ":Bob",
603 ))),
604 );
605 assert!(!e.evaluate_exists(&facts, &pattern, &empty_input()));
606 }
607
608 #[test]
609 fn test_union_both_match_returns_all_solutions() {
610 let e = evaluator();
611 let facts = facts_abc();
612 let pattern = GraphPattern::Union(
614 Box::new(GraphPattern::Triple(TripleFact::new(
615 ":Alice", "knows", ":Bob",
616 ))),
617 Box::new(GraphPattern::Triple(TripleFact::new(
618 ":Bob", "knows", ":Carol",
619 ))),
620 );
621 let solutions = e.inner_match(&facts, &pattern, empty_input());
622 assert_eq!(solutions.len(), 2);
623 }
624
625 #[test]
628 fn test_filter_passing() {
629 let e = evaluator();
630 let facts = facts_abc();
631 let pattern = GraphPattern::Filter {
632 pattern: Box::new(GraphPattern::Triple(TripleFact::new("?x", "age", "?a"))),
633 condition: "?a=30".to_string(),
634 };
635 let solutions = e.inner_match(&facts, &pattern, empty_input());
636 assert_eq!(solutions.len(), 1);
637 assert_eq!(solutions[0].get("x").map(|s| s.as_str()), Some(":Alice"));
638 }
639
640 #[test]
641 fn test_filter_not_equals() {
642 let e = evaluator();
643 let facts = facts_abc();
644 let pattern = GraphPattern::Filter {
645 pattern: Box::new(GraphPattern::Triple(TripleFact::new("?x", "age", "?a"))),
646 condition: "?a!=30".to_string(),
647 };
648 let solutions = e.inner_match(&facts, &pattern, empty_input());
649 assert_eq!(solutions.len(), 1);
650 assert_eq!(solutions[0].get("x").map(|s| s.as_str()), Some(":Bob"));
651 }
652
653 #[test]
654 fn test_filter_removes_all() {
655 let e = evaluator();
656 let facts = facts_abc();
657 let pattern = GraphPattern::Filter {
658 pattern: Box::new(GraphPattern::Triple(TripleFact::new("?x", "age", "?a"))),
659 condition: "?a=999".to_string(),
660 };
661 let solutions = e.inner_match(&facts, &pattern, empty_input());
662 assert!(solutions.is_empty());
663 }
664
665 #[test]
668 fn test_inner_match_returns_multiple_solutions() {
669 let e = evaluator();
670 let facts = facts_abc();
671 let pattern = GraphPattern::Triple(TripleFact::new("?x", "knows", "?y"));
673 let solutions = e.inner_match(&facts, &pattern, empty_input());
674 assert_eq!(solutions.len(), 2);
675 }
676
677 #[test]
678 fn test_inner_match_pre_bound_narrows_results() {
679 let e = evaluator();
680 let facts = facts_abc();
681 let pattern = GraphPattern::Triple(TripleFact::new("?x", "knows", "?y"));
682 let mut input = SolutionMapping::new();
683 input.insert("x".into(), ":Alice".into());
684 let solutions = e.inner_match(&facts, &pattern, input);
685 assert_eq!(solutions.len(), 1);
686 assert_eq!(solutions[0].get("y").map(|s| s.as_str()), Some(":Bob"));
687 }
688
689 #[test]
690 fn test_inner_match_constant_predicate() {
691 let e = evaluator();
692 let facts = facts_abc();
693 let pattern = GraphPattern::Triple(TripleFact::new("?s", "age", "?o"));
694 let solutions = e.inner_match(&facts, &pattern, empty_input());
695 assert_eq!(solutions.len(), 2);
696 }
697
698 #[test]
699 fn test_inner_match_no_matching_facts() {
700 let e = evaluator();
701 let facts = facts_abc();
702 let pattern = GraphPattern::Triple(TripleFact::new("?s", "flies", "?o"));
703 let solutions = e.inner_match(&facts, &pattern, empty_input());
704 assert!(solutions.is_empty());
705 }
706
707 #[test]
710 fn test_exists_bindings_scoped_does_not_modify_input() {
711 let e = evaluator();
712 let facts = facts_abc();
713 let pattern = GraphPattern::Triple(TripleFact::new("?newvar", "knows", ":Bob"));
714 let input = SolutionMapping::new();
715 let result = e.evaluate_exists(&facts, &pattern, &input);
716 assert!(result);
717 assert!(input.is_empty());
719 }
720
721 #[test]
724 fn test_and_within_union() {
725 let e = evaluator();
726 let facts = facts_abc();
727 let knows_bob = GraphPattern::Triple(TripleFact::new("?x", "knows", ":Bob"));
728 let bob_old = GraphPattern::And(vec![
729 GraphPattern::Triple(TripleFact::new("?x", "knows", ":Carol")),
730 GraphPattern::Triple(TripleFact::new("?x", "age", "?a")),
731 ]);
732 let pattern = GraphPattern::Union(Box::new(knows_bob), Box::new(bob_old));
733 assert!(e.evaluate_exists(&facts, &pattern, &empty_input()));
734 }
735
736 #[test]
737 fn test_optional_within_and() {
738 let e = evaluator();
739 let facts = facts_abc();
740 let pattern = GraphPattern::And(vec![
741 GraphPattern::Triple(TripleFact::new("?x", "knows", "?y")),
742 GraphPattern::Optional(Box::new(GraphPattern::Triple(TripleFact::new(
743 "?x", "email", "?e",
744 )))),
745 ]);
746 let solutions = e.inner_match(&facts, &pattern, empty_input());
748 assert!(!solutions.is_empty());
749 }
750
751 #[test]
752 fn test_filter_within_and() {
753 let e = evaluator();
754 let facts = facts_abc();
755 let pattern = GraphPattern::And(vec![
756 GraphPattern::Triple(TripleFact::new("?x", "knows", "?y")),
757 GraphPattern::Filter {
758 pattern: Box::new(GraphPattern::Triple(TripleFact::new("?x", "age", "?a"))),
759 condition: "?a=30".to_string(),
760 },
761 ]);
762 let solutions = e.inner_match(&facts, &pattern, empty_input());
763 assert_eq!(solutions.len(), 1);
764 assert_eq!(solutions[0].get("x").map(|s| s.as_str()), Some(":Alice"));
765 }
766
767 #[test]
768 fn test_not_exists_with_bound_variable() {
769 let e = evaluator();
770 let facts = facts_abc();
771 let pattern = GraphPattern::Triple(TripleFact::new("?x", "flies", "?z"));
772 let mut input = SolutionMapping::new();
773 input.insert("x".into(), ":Alice".into());
774 assert!(e.evaluate_not_exists(&facts, &pattern, &input));
775 }
776
777 #[test]
778 fn test_triple_fact_new_constructor() {
779 let t = TripleFact::new(":s", ":p", ":o");
780 assert_eq!(t.subject, ":s");
781 assert_eq!(t.predicate, ":p");
782 assert_eq!(t.object, ":o");
783 }
784
785 #[test]
786 fn test_merge_empty_maps() {
787 let m1 = SolutionMapping::new();
788 let m2 = SolutionMapping::new();
789 let merged = ExistsEvaluator::merge(m1, m2);
790 assert!(merged.is_empty());
791 }
792
793 #[test]
794 fn test_exists_single_fact_dataset_match() {
795 let e = evaluator();
796 let facts = vec![TripleFact::new("ex:Alice", "foaf:knows", "ex:Bob")];
797 let pattern = GraphPattern::Triple(TripleFact::new("?s", "foaf:knows", "?o"));
798 assert!(e.evaluate_exists(&facts, &pattern, &empty_input()));
799 }
800
801 #[test]
802 fn test_not_exists_on_empty_dataset() {
803 let e = evaluator();
804 let pattern =
805 GraphPattern::And(vec![GraphPattern::Triple(TripleFact::new("?x", "p", "?y"))]);
806 assert!(e.evaluate_not_exists(&[], &pattern, &empty_input()));
807 }
808}