1use crate::algebra::{
7 Aggregate, Algebra, Expression, GroupCondition, Iri, Literal, OrderCondition, Term,
8 TriplePattern, Variable,
9};
10use anyhow::{anyhow, Result};
11use std::collections::HashMap;
12
13#[derive(Debug, Clone)]
15pub struct InteractiveQueryBuilder {
16 query_type: Option<QueryType>,
17 variables: Vec<Variable>,
18 patterns: Vec<PatternBuilder>,
19 filters: Vec<Expression>,
20 optional_patterns: Vec<Vec<PatternBuilder>>,
21 unions: Vec<Vec<PatternBuilder>>,
22 bindings: Vec<(Variable, Expression)>,
23 order_by: Vec<OrderCondition>,
24 group_by: Vec<GroupCondition>,
25 aggregates: Vec<(Variable, Aggregate)>,
26 having: Option<Expression>,
27 limit: Option<usize>,
28 offset: Option<usize>,
29 distinct: bool,
30 reduced: bool,
31 prefixes: HashMap<String, String>,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum QueryType {
37 Select,
38 Ask,
39 Construct,
40 Describe,
41}
42
43#[derive(Debug, Clone)]
45pub struct PatternBuilder {
46 subject: Option<Term>,
47 predicate: Option<Term>,
48 object: Option<Term>,
49}
50
51impl InteractiveQueryBuilder {
52 pub fn new() -> Self {
54 Self {
55 query_type: None,
56 variables: Vec::new(),
57 patterns: Vec::new(),
58 filters: Vec::new(),
59 optional_patterns: Vec::new(),
60 unions: Vec::new(),
61 bindings: Vec::new(),
62 order_by: Vec::new(),
63 group_by: Vec::new(),
64 aggregates: Vec::new(),
65 having: None,
66 limit: None,
67 offset: None,
68 distinct: false,
69 reduced: false,
70 prefixes: HashMap::new(),
71 }
72 }
73
74 pub fn select(mut self, variables: Vec<&str>) -> Result<Self> {
76 self.query_type = Some(QueryType::Select);
77 self.variables = variables
78 .into_iter()
79 .map(Variable::new)
80 .collect::<std::result::Result<Vec<_>, _>>()?;
81 Ok(self)
82 }
83
84 pub fn select_all(mut self) -> Self {
86 self.query_type = Some(QueryType::Select);
87 self.variables = Vec::new(); self
89 }
90
91 pub fn ask(mut self) -> Self {
93 self.query_type = Some(QueryType::Ask);
94 self
95 }
96
97 pub fn construct(mut self, variables: Vec<&str>) -> Result<Self> {
99 self.query_type = Some(QueryType::Construct);
100 self.variables = variables
101 .into_iter()
102 .map(Variable::new)
103 .collect::<std::result::Result<Vec<_>, _>>()?;
104 Ok(self)
105 }
106
107 pub fn describe(mut self, resources: Vec<&str>) -> Result<Self> {
109 self.query_type = Some(QueryType::Describe);
110 self.variables = resources
111 .into_iter()
112 .map(Variable::new)
113 .collect::<std::result::Result<Vec<_>, _>>()?;
114 Ok(self)
115 }
116
117 pub fn r#where(mut self, pattern: PatternBuilder) -> Self {
119 self.patterns.push(pattern);
120 self
121 }
122
123 pub fn where_all(mut self, patterns: Vec<PatternBuilder>) -> Self {
125 self.patterns.extend(patterns);
126 self
127 }
128
129 pub fn filter(mut self, condition: Expression) -> Self {
131 self.filters.push(condition);
132 self
133 }
134
135 pub fn optional(mut self, patterns: Vec<PatternBuilder>) -> Self {
137 self.optional_patterns.push(patterns);
138 self
139 }
140
141 pub fn union(mut self, patterns: Vec<PatternBuilder>) -> Self {
143 self.unions.push(patterns);
144 self
145 }
146
147 pub fn bind(mut self, var: &str, expr: Expression) -> Result<Self> {
149 self.bindings.push((Variable::new(var)?, expr));
150 Ok(self)
151 }
152
153 pub fn order_by(mut self, conditions: Vec<OrderCondition>) -> Self {
155 self.order_by = conditions;
156 self
157 }
158
159 pub fn group_by(mut self, conditions: Vec<GroupCondition>) -> Self {
161 self.group_by = conditions;
162 self
163 }
164
165 pub fn having(mut self, condition: Expression) -> Self {
167 self.having = Some(condition);
168 self
169 }
170
171 pub fn count(mut self, var: &str, expr: Option<Expression>, distinct: bool) -> Result<Self> {
173 let variable = Variable::new(var)?;
174 self.aggregates
175 .push((variable, Aggregate::Count { distinct, expr }));
176 Ok(self)
177 }
178
179 pub fn sum(mut self, var: &str, expr: Expression, distinct: bool) -> Result<Self> {
181 let variable = Variable::new(var)?;
182 self.aggregates
183 .push((variable, Aggregate::Sum { distinct, expr }));
184 Ok(self)
185 }
186
187 pub fn min(mut self, var: &str, expr: Expression, distinct: bool) -> Result<Self> {
189 let variable = Variable::new(var)?;
190 self.aggregates
191 .push((variable, Aggregate::Min { distinct, expr }));
192 Ok(self)
193 }
194
195 pub fn max(mut self, var: &str, expr: Expression, distinct: bool) -> Result<Self> {
197 let variable = Variable::new(var)?;
198 self.aggregates
199 .push((variable, Aggregate::Max { distinct, expr }));
200 Ok(self)
201 }
202
203 pub fn avg(mut self, var: &str, expr: Expression, distinct: bool) -> Result<Self> {
205 let variable = Variable::new(var)?;
206 self.aggregates
207 .push((variable, Aggregate::Avg { distinct, expr }));
208 Ok(self)
209 }
210
211 pub fn sample(mut self, var: &str, expr: Expression, distinct: bool) -> Result<Self> {
213 let variable = Variable::new(var)?;
214 self.aggregates
215 .push((variable, Aggregate::Sample { distinct, expr }));
216 Ok(self)
217 }
218
219 pub fn group_concat(
221 mut self,
222 var: &str,
223 expr: Expression,
224 distinct: bool,
225 separator: Option<String>,
226 ) -> Result<Self> {
227 let variable = Variable::new(var)?;
228 self.aggregates.push((
229 variable,
230 Aggregate::GroupConcat {
231 distinct,
232 expr,
233 separator,
234 },
235 ));
236 Ok(self)
237 }
238
239 pub fn limit(mut self, limit: usize) -> Self {
241 self.limit = Some(limit);
242 self
243 }
244
245 pub fn offset(mut self, offset: usize) -> Self {
247 self.offset = Some(offset);
248 self
249 }
250
251 pub fn distinct(mut self) -> Self {
253 self.distinct = true;
254 self
255 }
256
257 pub fn reduced(mut self) -> Self {
259 self.reduced = true;
260 self
261 }
262
263 pub fn prefix(mut self, prefix: &str, uri: &str) -> Self {
265 self.prefixes.insert(prefix.to_string(), uri.to_string());
266 self
267 }
268
269 pub fn build_string(&self) -> Result<String> {
271 let mut query = String::new();
272
273 for (prefix, uri) in &self.prefixes {
275 query.push_str(&format!("PREFIX {}: <{}>\n", prefix, uri));
276 }
277 if !self.prefixes.is_empty() {
278 query.push('\n');
279 }
280
281 let query_type = self
283 .query_type
284 .ok_or_else(|| anyhow!("Query type not set"))?;
285
286 match query_type {
287 QueryType::Select => {
288 query.push_str("SELECT ");
289 if self.distinct {
290 query.push_str("DISTINCT ");
291 }
292 if self.reduced {
293 query.push_str("REDUCED ");
294 }
295 if self.variables.is_empty() {
296 query.push('*');
297 } else {
298 for (i, var) in self.variables.iter().enumerate() {
299 if i > 0 {
300 query.push(' ');
301 }
302 query.push_str(&format!("{}", var));
303 }
304 }
305 query.push('\n');
306 }
307 QueryType::Ask => {
308 query.push_str("ASK\n");
309 }
310 QueryType::Construct => {
311 query.push_str("CONSTRUCT {\n");
312 for pattern in &self.patterns {
313 query.push_str(&format!(" {}\n", pattern.to_string()?));
314 }
315 query.push_str("}\n");
316 }
317 QueryType::Describe => {
318 query.push_str("DESCRIBE ");
319 for (i, var) in self.variables.iter().enumerate() {
320 if i > 0 {
321 query.push(' ');
322 }
323 query.push_str(&format!("{}", var));
324 }
325 query.push('\n');
326 }
327 }
328
329 query.push_str("WHERE {\n");
331
332 for pattern in &self.patterns {
334 query.push_str(&format!(" {} .\n", pattern.to_string()?));
335 }
336
337 for filter in &self.filters {
339 query.push_str(&format!(" FILTER ({:?})\n", filter));
340 }
341
342 for optional in &self.optional_patterns {
344 query.push_str(" OPTIONAL {\n");
345 for pattern in optional {
346 query.push_str(&format!(" {} .\n", pattern.to_string()?));
347 }
348 query.push_str(" }\n");
349 }
350
351 if !self.unions.is_empty() {
353 query.push_str(" {\n");
354 for (i, union_patterns) in self.unions.iter().enumerate() {
355 if i > 0 {
356 query.push_str(" } UNION {\n");
357 }
358 for pattern in union_patterns {
359 query.push_str(&format!(" {} .\n", pattern.to_string()?));
360 }
361 }
362 query.push_str(" }\n");
363 }
364
365 for (var, expr) in &self.bindings {
367 query.push_str(&format!(" BIND ({:?} AS {})\n", expr, var));
368 }
369
370 query.push_str("}\n");
371
372 if !self.group_by.is_empty() {
374 query.push_str("GROUP BY ");
375 for (i, condition) in self.group_by.iter().enumerate() {
376 if i > 0 {
377 query.push(' ');
378 }
379 query.push_str(&format!("({:?})", condition.expr));
380 if let Some(alias) = &condition.alias {
381 query.push_str(&format!(" AS {}", alias));
382 }
383 }
384 query.push('\n');
385 }
386
387 if let Some(having) = &self.having {
389 query.push_str(&format!("HAVING ({:?})\n", having));
390 }
391
392 if !self.order_by.is_empty() {
394 query.push_str("ORDER BY ");
395 for (i, condition) in self.order_by.iter().enumerate() {
396 if i > 0 {
397 query.push(' ');
398 }
399 if condition.ascending {
400 query.push_str(&format!("ASC({:?})", condition.expr));
401 } else {
402 query.push_str(&format!("DESC({:?})", condition.expr));
403 }
404 }
405 query.push('\n');
406 }
407
408 if let Some(limit) = self.limit {
410 query.push_str(&format!("LIMIT {}\n", limit));
411 }
412
413 if let Some(offset) = self.offset {
415 query.push_str(&format!("OFFSET {}\n", offset));
416 }
417
418 Ok(query)
419 }
420
421 pub fn build_algebra(&self) -> Result<Algebra> {
423 let query_type = self
424 .query_type
425 .ok_or_else(|| anyhow!("Query type not set"))?;
426
427 let mut algebra = if !self.patterns.is_empty() {
429 let triple_patterns: Result<Vec<TriplePattern>> = self
430 .patterns
431 .iter()
432 .map(|p| p.to_triple_pattern())
433 .collect();
434 Algebra::Bgp(triple_patterns?)
435 } else {
436 Algebra::Bgp(Vec::new())
437 };
438
439 for filter in &self.filters {
441 algebra = Algebra::Filter {
442 pattern: Box::new(algebra),
443 condition: filter.clone(),
444 };
445 }
446
447 for optional in &self.optional_patterns {
449 let optional_patterns: Result<Vec<TriplePattern>> =
450 optional.iter().map(|p| p.to_triple_pattern()).collect();
451 let optional_algebra = Algebra::Bgp(optional_patterns?);
452 algebra = Algebra::LeftJoin {
453 left: Box::new(algebra),
454 right: Box::new(optional_algebra),
455 filter: None,
456 };
457 }
458
459 if !self.unions.is_empty() {
461 let mut union_algebra = None;
462 for union_patterns in &self.unions {
463 let patterns: Result<Vec<TriplePattern>> = union_patterns
464 .iter()
465 .map(|p| p.to_triple_pattern())
466 .collect();
467 let union_bgp = Algebra::Bgp(patterns?);
468 union_algebra = Some(match union_algebra {
469 None => union_bgp,
470 Some(left) => Algebra::Union {
471 left: Box::new(left),
472 right: Box::new(union_bgp),
473 },
474 });
475 }
476 if let Some(union_alg) = union_algebra {
477 algebra = Algebra::Join {
478 left: Box::new(algebra),
479 right: Box::new(union_alg),
480 };
481 }
482 }
483
484 for (var, expr) in &self.bindings {
486 algebra = Algebra::Extend {
487 pattern: Box::new(algebra),
488 variable: var.clone(),
489 expr: expr.clone(),
490 };
491 }
492
493 if !self.group_by.is_empty() || !self.aggregates.is_empty() {
495 algebra = Algebra::Group {
496 pattern: Box::new(algebra),
497 variables: self.group_by.clone(),
498 aggregates: self.aggregates.clone(),
499 };
500 }
501
502 if let Some(having) = &self.having {
504 algebra = Algebra::Having {
505 pattern: Box::new(algebra),
506 condition: having.clone(),
507 };
508 }
509
510 if !self.order_by.is_empty() {
512 algebra = Algebra::OrderBy {
513 pattern: Box::new(algebra),
514 conditions: self.order_by.clone(),
515 };
516 }
517
518 if query_type == QueryType::Select && !self.variables.is_empty() {
520 algebra = Algebra::Project {
521 pattern: Box::new(algebra),
522 variables: self.variables.clone(),
523 };
524 }
525
526 if self.distinct {
528 algebra = Algebra::Distinct {
529 pattern: Box::new(algebra),
530 };
531 }
532
533 if self.reduced {
535 algebra = Algebra::Reduced {
536 pattern: Box::new(algebra),
537 };
538 }
539
540 if self.limit.is_some() || self.offset.is_some() {
542 algebra = Algebra::Slice {
543 pattern: Box::new(algebra),
544 offset: self.offset,
545 limit: self.limit,
546 };
547 }
548
549 Ok(algebra)
550 }
551}
552
553impl Default for InteractiveQueryBuilder {
554 fn default() -> Self {
555 Self::new()
556 }
557}
558
559impl PatternBuilder {
560 pub fn new() -> Self {
562 Self {
563 subject: None,
564 predicate: None,
565 object: None,
566 }
567 }
568
569 pub fn subject(mut self, subject: Term) -> Self {
571 self.subject = Some(subject);
572 self
573 }
574
575 pub fn subject_var(self, var: &str) -> Result<Self> {
577 Ok(self.subject(Term::Variable(Variable::new(var)?)))
578 }
579
580 pub fn subject_iri(self, iri: &str) -> Result<Self> {
582 Ok(self.subject(Term::Iri(Iri::new(iri)?)))
583 }
584
585 pub fn predicate(mut self, predicate: Term) -> Self {
587 self.predicate = Some(predicate);
588 self
589 }
590
591 pub fn predicate_var(self, var: &str) -> Result<Self> {
593 Ok(self.predicate(Term::Variable(Variable::new(var)?)))
594 }
595
596 pub fn predicate_iri(self, iri: &str) -> Result<Self> {
598 Ok(self.predicate(Term::Iri(Iri::new(iri)?)))
599 }
600
601 pub fn object(mut self, object: Term) -> Self {
603 self.object = Some(object);
604 self
605 }
606
607 pub fn object_var(self, var: &str) -> Result<Self> {
609 Ok(self.object(Term::Variable(Variable::new(var)?)))
610 }
611
612 pub fn object_iri(self, iri: &str) -> Result<Self> {
614 Ok(self.object(Term::Iri(Iri::new(iri)?)))
615 }
616
617 pub fn object_literal(self, value: &str) -> Self {
619 self.object(Term::Literal(Literal {
620 value: value.to_string(),
621 language: None,
622 datatype: None,
623 }))
624 }
625
626 pub fn to_triple_pattern(&self) -> Result<TriplePattern> {
628 Ok(TriplePattern {
629 subject: self
630 .subject
631 .clone()
632 .ok_or_else(|| anyhow!("Subject not set"))?,
633 predicate: self
634 .predicate
635 .clone()
636 .ok_or_else(|| anyhow!("Predicate not set"))?,
637 object: self
638 .object
639 .clone()
640 .ok_or_else(|| anyhow!("Object not set"))?,
641 })
642 }
643
644 pub fn to_string(&self) -> Result<String> {
646 let subject = self
647 .subject
648 .as_ref()
649 .ok_or_else(|| anyhow!("Subject not set"))?;
650 let predicate = self
651 .predicate
652 .as_ref()
653 .ok_or_else(|| anyhow!("Predicate not set"))?;
654 let object = self
655 .object
656 .as_ref()
657 .ok_or_else(|| anyhow!("Object not set"))?;
658
659 Ok(format!("{} {} {}", subject, predicate, object))
660 }
661}
662
663impl Default for PatternBuilder {
664 fn default() -> Self {
665 Self::new()
666 }
667}
668
669pub mod helpers {
671 use super::*;
672
673 pub fn triple(s: &str, p: &str, o: &str) -> Result<PatternBuilder> {
675 PatternBuilder::new()
676 .subject_var(s)?
677 .predicate_var(p)?
678 .object_var(o)
679 }
680
681 pub fn triple_with_iri_predicate(s: &str, p: &str, o: &str) -> Result<PatternBuilder> {
683 PatternBuilder::new()
684 .subject_var(s)?
685 .predicate_iri(p)?
686 .object_var(o)
687 }
688
689 pub fn triple_with_iri_subject_predicate(s: &str, p: &str, o: &str) -> Result<PatternBuilder> {
691 PatternBuilder::new()
692 .subject_iri(s)?
693 .predicate_iri(p)?
694 .object_var(o)
695 }
696
697 pub fn triple_with_literal(s: &str, p: &str, literal: &str) -> Result<PatternBuilder> {
699 Ok(PatternBuilder::new()
700 .subject_var(s)?
701 .predicate_iri(p)?
702 .object_literal(literal))
703 }
704}
705
706#[cfg(test)]
707mod tests {
708 use super::*;
709
710 #[test]
711 fn test_simple_select() -> Result<()> {
712 let query = InteractiveQueryBuilder::new()
713 .select(vec!["s", "p", "o"])?
714 .r#where(
715 PatternBuilder::new()
716 .subject_var("s")?
717 .predicate_var("p")?
718 .object_var("o")?,
719 )
720 .build_string()?;
721
722 assert!(query.contains("SELECT ?s ?p ?o"));
723 assert!(query.contains("WHERE"));
724 Ok(())
725 }
726
727 #[test]
728 fn test_select_with_filter() -> Result<()> {
729 let builder = InteractiveQueryBuilder::new()
730 .select(vec!["name"])?
731 .r#where(
732 PatternBuilder::new()
733 .subject_var("person")?
734 .predicate_iri("http://xmlns.com/foaf/0.1/name")?
735 .object_var("name")?,
736 )
737 .limit(10);
738
739 let query = builder.build_string()?;
740 assert!(query.contains("SELECT ?name"));
741 assert!(query.contains("LIMIT 10"));
742 Ok(())
743 }
744
745 #[test]
746 fn test_ask_query() -> Result<()> {
747 let query = InteractiveQueryBuilder::new()
748 .ask()
749 .r#where(
750 PatternBuilder::new()
751 .subject_var("s")?
752 .predicate_var("p")?
753 .object_var("o")?,
754 )
755 .build_string()?;
756
757 assert!(query.contains("ASK"));
758 assert!(query.contains("WHERE"));
759 Ok(())
760 }
761
762 #[test]
763 fn test_distinct_modifier() -> Result<()> {
764 let query = InteractiveQueryBuilder::new()
765 .select(vec!["s"])?
766 .distinct()
767 .r#where(
768 PatternBuilder::new()
769 .subject_var("s")?
770 .predicate_var("p")?
771 .object_var("o")?,
772 )
773 .build_string()?;
774
775 assert!(query.contains("SELECT DISTINCT"));
776 Ok(())
777 }
778
779 #[test]
780 fn test_with_prefixes() -> Result<()> {
781 let query = InteractiveQueryBuilder::new()
782 .prefix("foaf", "http://xmlns.com/foaf/0.1/")
783 .prefix("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#")
784 .select(vec!["name"])?
785 .r#where(
786 PatternBuilder::new()
787 .subject_var("person")?
788 .predicate_iri("http://xmlns.com/foaf/0.1/name")?
789 .object_var("name")?,
790 )
791 .build_string()?;
792
793 assert!(query.contains("PREFIX foaf:"));
794 assert!(query.contains("PREFIX rdf:"));
795 Ok(())
796 }
797
798 #[test]
799 fn test_algebra_generation() -> Result<()> {
800 let algebra = InteractiveQueryBuilder::new()
801 .select(vec!["s", "p", "o"])?
802 .r#where(
803 PatternBuilder::new()
804 .subject_var("s")?
805 .predicate_var("p")?
806 .object_var("o")?,
807 )
808 .limit(10)
809 .build_algebra()?;
810
811 match algebra {
813 Algebra::Slice { .. } => {}
814 _ => panic!("Expected Slice algebra"),
815 }
816
817 Ok(())
818 }
819
820 #[test]
821 fn test_helper_functions() -> Result<()> {
822 let pattern = helpers::triple("s", "p", "o")?;
823 let triple = pattern.to_triple_pattern()?;
824
825 assert!(matches!(triple.subject, Term::Variable(_)));
826 assert!(matches!(triple.predicate, Term::Variable(_)));
827 assert!(matches!(triple.object, Term::Variable(_)));
828
829 Ok(())
830 }
831
832 #[test]
833 fn test_optional_pattern() -> Result<()> {
834 let query = InteractiveQueryBuilder::new()
835 .select(vec!["name", "email"])?
836 .r#where(
837 PatternBuilder::new()
838 .subject_var("person")?
839 .predicate_iri("http://xmlns.com/foaf/0.1/name")?
840 .object_var("name")?,
841 )
842 .optional(vec![PatternBuilder::new()
843 .subject_var("person")?
844 .predicate_iri("http://xmlns.com/foaf/0.1/mbox")?
845 .object_var("email")?])
846 .build_string()?;
847
848 assert!(query.contains("OPTIONAL"));
849 Ok(())
850 }
851
852 #[test]
853 fn test_limit_offset() -> Result<()> {
854 let query = InteractiveQueryBuilder::new()
855 .select(vec!["s"])?
856 .r#where(
857 PatternBuilder::new()
858 .subject_var("s")?
859 .predicate_var("p")?
860 .object_var("o")?,
861 )
862 .limit(10)
863 .offset(20)
864 .build_string()?;
865
866 assert!(query.contains("LIMIT 10"));
867 assert!(query.contains("OFFSET 20"));
868 Ok(())
869 }
870
871 #[test]
872 fn test_count_aggregate() -> Result<()> {
873 let algebra = InteractiveQueryBuilder::new()
874 .select_all()
875 .r#where(
876 PatternBuilder::new()
877 .subject_var("s")?
878 .predicate_var("p")?
879 .object_var("o")?,
880 )
881 .count("count", None, false)?
882 .build_algebra()?;
883
884 match algebra {
886 Algebra::Group { aggregates, .. } => {
887 assert_eq!(aggregates.len(), 1);
888 let (var, agg) = &aggregates[0];
889 assert_eq!(var.name(), "count");
890 matches!(agg, Aggregate::Count { .. });
891 }
892 _ => panic!("Expected Group algebra"),
893 }
894
895 Ok(())
896 }
897
898 #[test]
899 fn test_sum_aggregate() -> Result<()> {
900 let algebra = InteractiveQueryBuilder::new()
901 .select_all()
902 .r#where(
903 PatternBuilder::new()
904 .subject_var("s")?
905 .predicate_var("p")?
906 .object_var("o")?,
907 )
908 .sum(
909 "total",
910 Expression::Variable(Variable::new_unchecked("value")),
911 false,
912 )?
913 .build_algebra()?;
914
915 match algebra {
916 Algebra::Group { aggregates, .. } => {
917 assert_eq!(aggregates.len(), 1);
918 let (var, agg) = &aggregates[0];
919 assert_eq!(var.name(), "total");
920 matches!(agg, Aggregate::Sum { .. });
921 }
922 _ => panic!("Expected Group algebra"),
923 }
924
925 Ok(())
926 }
927
928 #[test]
929 fn test_multiple_aggregates() -> Result<()> {
930 let algebra = InteractiveQueryBuilder::new()
931 .select_all()
932 .r#where(
933 PatternBuilder::new()
934 .subject_var("s")?
935 .predicate_var("p")?
936 .object_var("o")?,
937 )
938 .count("cnt", None, false)?
939 .sum(
940 "total",
941 Expression::Variable(Variable::new_unchecked("value")),
942 false,
943 )?
944 .avg(
945 "avg",
946 Expression::Variable(Variable::new_unchecked("value")),
947 false,
948 )?
949 .build_algebra()?;
950
951 match algebra {
952 Algebra::Group { aggregates, .. } => {
953 assert_eq!(aggregates.len(), 3);
954 assert_eq!(aggregates[0].0.name(), "cnt");
955 assert_eq!(aggregates[1].0.name(), "total");
956 assert_eq!(aggregates[2].0.name(), "avg");
957 }
958 _ => panic!("Expected Group algebra"),
959 }
960
961 Ok(())
962 }
963
964 #[test]
965 fn test_group_concat_aggregate() -> Result<()> {
966 let algebra = InteractiveQueryBuilder::new()
967 .select_all()
968 .r#where(
969 PatternBuilder::new()
970 .subject_var("s")?
971 .predicate_var("p")?
972 .object_var("o")?,
973 )
974 .group_concat(
975 "concat",
976 Expression::Variable(Variable::new_unchecked("name")),
977 false,
978 Some(", ".to_string()),
979 )?
980 .build_algebra()?;
981
982 match algebra {
983 Algebra::Group { aggregates, .. } => {
984 assert_eq!(aggregates.len(), 1);
985 match &aggregates[0].1 {
986 Aggregate::GroupConcat { separator, .. } => {
987 assert_eq!(separator.as_deref(), Some(", "));
988 }
989 _ => panic!("Expected GroupConcat aggregate"),
990 }
991 }
992 _ => panic!("Expected Group algebra"),
993 }
994
995 Ok(())
996 }
997}