1use crate::sparql::{SparqlCompiler, SparqlQuery};
32use anyhow::{anyhow, Context, Result};
33use oxirs_core::model::{BlankNode, Literal, NamedNode, Object, Predicate, Subject};
34use oxirs_core::{RdfStore, Triple};
35use oxrdf::Term as OxrdfTerm;
36use oxttl::TurtleParser as OxTtlParser;
37use serde::{Deserialize, Serialize};
38use std::collections::HashMap;
39use tensorlogic_ir::TLExpr;
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
43pub enum QueryResults {
44 Select {
46 variables: Vec<String>,
48 bindings: Vec<HashMap<String, QueryValue>>,
50 },
51 Ask(bool),
53 Construct { triples: Vec<TripleResult> },
55 Describe { triples: Vec<TripleResult> },
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
61pub enum QueryValue {
62 Iri(String),
64 Literal {
66 value: String,
67 datatype: Option<String>,
68 language: Option<String>,
69 },
70 BlankNode(String),
72}
73
74impl QueryValue {
75 pub fn as_str(&self) -> &str {
77 match self {
78 QueryValue::Iri(s) => s,
79 QueryValue::Literal { value, .. } => value,
80 QueryValue::BlankNode(s) => s,
81 }
82 }
83
84 pub fn is_iri(&self) -> bool {
86 matches!(self, QueryValue::Iri(_))
87 }
88
89 pub fn is_literal(&self) -> bool {
91 matches!(self, QueryValue::Literal { .. })
92 }
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
97pub struct TripleResult {
98 pub subject: String,
99 pub predicate: String,
100 pub object: String,
101}
102
103pub struct OxirsSparqlExecutor {
108 store: RdfStore,
110 compiler: SparqlCompiler,
112 predicate_mappings: HashMap<String, String>,
114 base_iri: Option<String>,
116}
117
118impl OxirsSparqlExecutor {
119 pub fn new() -> Result<Self> {
121 Ok(Self {
122 store: RdfStore::new()?,
123 compiler: SparqlCompiler::new(),
124 predicate_mappings: HashMap::new(),
125 base_iri: None,
126 })
127 }
128
129 pub fn set_base_iri(&mut self, base: &str) {
131 self.base_iri = Some(base.to_string());
132 }
133
134 pub fn add_predicate_mapping(&mut self, iri: &str, name: &str) {
136 self.predicate_mappings
137 .insert(iri.to_string(), name.to_string());
138 self.compiler
139 .add_predicate_mapping(iri.to_string(), name.to_string());
140 }
141
142 pub fn load_turtle(&mut self, turtle: &str) -> Result<usize> {
144 let parser = OxTtlParser::new().for_slice(turtle.as_bytes());
145 let mut count = 0;
146
147 for result in parser {
148 let triple = result.context("Failed to parse Turtle")?;
149
150 let triple = self.oxttl_to_oxirs_triple(&triple)?;
152 self.store.insert_triple(triple)?;
153 count += 1;
154 }
155
156 Ok(count)
157 }
158
159 #[allow(deprecated)]
161 fn oxttl_to_oxirs_triple(&self, triple: &oxrdf::Triple) -> Result<Triple> {
162 let subject: Subject = match &triple.subject {
163 oxrdf::NamedOrBlankNode::NamedNode(n) => {
164 Subject::NamedNode(NamedNode::new(n.as_str())?)
165 }
166 oxrdf::NamedOrBlankNode::BlankNode(b) => {
167 Subject::BlankNode(BlankNode::new(b.as_str())?)
168 }
169 };
170
171 let predicate: Predicate = Predicate::NamedNode(NamedNode::new(triple.predicate.as_str())?);
172
173 let object: Object = match &triple.object {
174 OxrdfTerm::NamedNode(n) => Object::NamedNode(NamedNode::new(n.as_str())?),
175 OxrdfTerm::BlankNode(b) => Object::BlankNode(BlankNode::new(b.as_str())?),
176 OxrdfTerm::Literal(l) => Object::Literal(Literal::new(l.value())),
177 #[allow(unreachable_patterns)]
179 _ => Object::Literal(Literal::new("")),
180 };
181
182 Ok(Triple::new(subject, predicate, object))
183 }
184
185 pub fn load_ntriples(&mut self, ntriples: &str) -> Result<usize> {
187 use oxttl::NTriplesParser;
188 let parser = NTriplesParser::new().for_slice(ntriples.as_bytes());
189 let mut count = 0;
190
191 for result in parser {
192 let triple = result.context("Failed to parse N-Triples")?;
193 let triple = self.oxttl_to_oxirs_triple(&triple)?;
194 self.store.insert_triple(triple)?;
195 count += 1;
196 }
197
198 Ok(count)
199 }
200
201 pub fn num_triples(&self) -> usize {
203 self.store.len().unwrap_or(0)
204 }
205
206 fn subject_to_string(subject: &Subject) -> String {
208 match subject {
209 Subject::NamedNode(n) => n.as_str().to_string(),
210 Subject::BlankNode(b) => format!("_:{}", b.as_str()),
211 Subject::Variable(v) => format!("?{}", v.as_str()),
212 Subject::QuotedTriple(_) => "[QuotedTriple]".to_string(),
213 }
214 }
215
216 fn predicate_to_string(predicate: &Predicate) -> String {
218 match predicate {
219 Predicate::NamedNode(n) => n.as_str().to_string(),
220 Predicate::Variable(v) => format!("?{}", v.as_str()),
221 }
222 }
223
224 fn object_to_string(object: &Object) -> String {
226 match object {
227 Object::NamedNode(n) => n.as_str().to_string(),
228 Object::BlankNode(b) => format!("_:{}", b.as_str()),
229 Object::Literal(l) => l.value().to_string(),
230 Object::Variable(v) => format!("?{}", v.as_str()),
231 Object::QuotedTriple(_) => "[QuotedTriple]".to_string(),
232 }
233 }
234
235 fn get_all_triples(&self) -> Vec<Triple> {
237 self.store.triples().unwrap_or_default()
238 }
239
240 fn query_by_subject(&self, subject_str: &str) -> Vec<Triple> {
242 if let Ok(subject_node) = NamedNode::new(subject_str) {
243 let subject = Subject::NamedNode(subject_node);
244 self.store
245 .query_triples(Some(&subject), None, None)
246 .unwrap_or_default()
247 } else {
248 Vec::new()
249 }
250 }
251
252 fn query_by_predicate(&self, predicate_str: &str) -> Vec<Triple> {
254 if let Ok(predicate_node) = NamedNode::new(predicate_str) {
255 let predicate = Predicate::NamedNode(predicate_node);
256 self.store
257 .query_triples(None, Some(&predicate), None)
258 .unwrap_or_default()
259 } else {
260 Vec::new()
261 }
262 }
263
264 fn query_by_object(&self, object_str: &str) -> Vec<Triple> {
266 if let Ok(object_node) = NamedNode::new(object_str) {
267 let object = Object::NamedNode(object_node);
268 self.store
269 .query_triples(None, None, Some(&object))
270 .unwrap_or_default()
271 } else {
272 let object = Object::Literal(Literal::new(object_str));
274 self.store
275 .query_triples(None, None, Some(&object))
276 .unwrap_or_default()
277 }
278 }
279
280 pub fn execute(&self, sparql: &str) -> Result<QueryResults> {
282 let query = self.compiler.parse_query(sparql)?;
284
285 self.execute_query(&query)
287 }
288
289 fn execute_query(&self, query: &SparqlQuery) -> Result<QueryResults> {
291 use crate::sparql::QueryType;
292
293 match &query.query_type {
294 QueryType::Select {
295 select_vars,
296 distinct,
297 ..
298 } => {
299 let bindings = self.execute_pattern(&query.where_pattern)?;
300
301 let projected: Vec<HashMap<String, QueryValue>> =
303 if select_vars.contains(&"*".to_string()) {
304 bindings
305 } else {
306 bindings
307 .into_iter()
308 .map(|b| {
309 b.into_iter()
310 .filter(|(k, _)| select_vars.contains(k))
311 .collect()
312 })
313 .collect()
314 };
315
316 let results = if *distinct {
318 self.deduplicate_bindings(projected)
319 } else {
320 projected
321 };
322
323 let results = self.apply_modifiers(results, query.limit, query.offset);
325
326 Ok(QueryResults::Select {
327 variables: select_vars.clone(),
328 bindings: results,
329 })
330 }
331 QueryType::Ask => {
332 let bindings = self.execute_pattern(&query.where_pattern)?;
333 Ok(QueryResults::Ask(!bindings.is_empty()))
334 }
335 QueryType::Construct { template } => {
336 use crate::sparql::PatternElement;
337 let bindings = self.execute_pattern(&query.where_pattern)?;
338 let mut triples = Vec::new();
339
340 for binding in bindings {
341 for pattern in template {
342 let subject = match &pattern.subject {
343 PatternElement::Variable(v) => binding
344 .get(v)
345 .map(|q| q.as_str().to_string())
346 .unwrap_or_default(),
347 PatternElement::Constant(c) => c.clone(),
348 };
349 let predicate = match &pattern.predicate {
350 PatternElement::Variable(v) => binding
351 .get(v)
352 .map(|q| q.as_str().to_string())
353 .unwrap_or_default(),
354 PatternElement::Constant(c) => c.clone(),
355 };
356 let object = match &pattern.object {
357 PatternElement::Variable(v) => binding
358 .get(v)
359 .map(|q| q.as_str().to_string())
360 .unwrap_or_default(),
361 PatternElement::Constant(c) => c.clone(),
362 };
363
364 if !subject.is_empty() && !predicate.is_empty() && !object.is_empty() {
365 triples.push(TripleResult {
366 subject,
367 predicate,
368 object,
369 });
370 }
371 }
372 }
373
374 Ok(QueryResults::Construct { triples })
375 }
376 QueryType::Describe { resources } => {
377 let mut triples = Vec::new();
378
379 for resource in resources {
380 let outgoing = self.query_by_subject(resource);
382 for triple in outgoing {
383 triples.push(TripleResult {
384 subject: Self::subject_to_string(triple.subject()),
385 predicate: Self::predicate_to_string(triple.predicate()),
386 object: Self::object_to_string(triple.object()),
387 });
388 }
389
390 let incoming = self.query_by_object(resource);
392 for triple in incoming {
393 triples.push(TripleResult {
394 subject: Self::subject_to_string(triple.subject()),
395 predicate: Self::predicate_to_string(triple.predicate()),
396 object: Self::object_to_string(triple.object()),
397 });
398 }
399 }
400
401 Ok(QueryResults::Describe { triples })
402 }
403 }
404 }
405
406 fn execute_pattern(
408 &self,
409 pattern: &crate::sparql::GraphPattern,
410 ) -> Result<Vec<HashMap<String, QueryValue>>> {
411 use crate::sparql::{GraphPattern, PatternElement};
412
413 match pattern {
414 GraphPattern::Triple(triple) => {
415 let mut results = Vec::new();
416
417 let triples = match (&triple.subject, &triple.predicate, &triple.object) {
419 (PatternElement::Constant(s), _, _) => self.query_by_subject(s),
420 (_, PatternElement::Constant(p), _) => self.query_by_predicate(p),
421 (_, _, PatternElement::Constant(o)) => self.query_by_object(o),
422 _ => self.get_all_triples(),
423 };
424
425 for t in triples {
426 let mut binding = HashMap::new();
427 let t_subject = Self::subject_to_string(t.subject());
428 let t_predicate = Self::predicate_to_string(t.predicate());
429 let t_object = Self::object_to_string(t.object());
430
431 if let PatternElement::Variable(v) = &triple.subject {
433 binding.insert(v.clone(), QueryValue::Iri(t_subject.clone()));
434 } else if let PatternElement::Constant(c) = &triple.subject {
435 if t_subject != *c {
436 continue;
437 }
438 }
439
440 if let PatternElement::Variable(v) = &triple.predicate {
442 binding.insert(v.clone(), QueryValue::Iri(t_predicate.clone()));
443 } else if let PatternElement::Constant(c) = &triple.predicate {
444 if t_predicate != *c {
445 continue;
446 }
447 }
448
449 if let PatternElement::Variable(v) = &triple.object {
451 let value = if t_object.starts_with("http") {
452 QueryValue::Iri(t_object.clone())
453 } else {
454 QueryValue::Literal {
455 value: t_object.clone(),
456 datatype: None,
457 language: None,
458 }
459 };
460 binding.insert(v.clone(), value);
461 } else if let PatternElement::Constant(c) = &triple.object {
462 if t_object != *c {
463 continue;
464 }
465 }
466
467 results.push(binding);
468 }
469
470 Ok(results)
471 }
472 GraphPattern::Group(patterns) => {
473 if patterns.is_empty() {
474 return Ok(vec![HashMap::new()]);
475 }
476
477 let mut current_results = self.execute_pattern(&patterns[0])?;
478
479 for pattern in patterns.iter().skip(1) {
480 let new_results = self.execute_pattern(pattern)?;
481 current_results = self.join_bindings(current_results, new_results);
482 }
483
484 Ok(current_results)
485 }
486 GraphPattern::Optional(inner) => {
487 let inner_results = self.execute_pattern(inner)?;
489 if inner_results.is_empty() {
490 Ok(vec![HashMap::new()])
491 } else {
492 Ok(inner_results)
493 }
494 }
495 GraphPattern::Union(left, right) => {
496 let mut left_results = self.execute_pattern(left)?;
497 let right_results = self.execute_pattern(right)?;
498 left_results.extend(right_results);
499 Ok(left_results)
500 }
501 GraphPattern::Filter(_condition) => {
502 Ok(Vec::new())
505 }
506 }
507 }
508
509 fn join_bindings(
511 &self,
512 left: Vec<HashMap<String, QueryValue>>,
513 right: Vec<HashMap<String, QueryValue>>,
514 ) -> Vec<HashMap<String, QueryValue>> {
515 let mut results = Vec::new();
516
517 for l in &left {
518 for r in &right {
519 let mut compatible = true;
521 for (key, lval) in l {
522 if let Some(rval) = r.get(key) {
523 if lval.as_str() != rval.as_str() {
524 compatible = false;
525 break;
526 }
527 }
528 }
529
530 if compatible {
531 let mut merged = l.clone();
533 for (key, rval) in r {
534 merged.entry(key.clone()).or_insert_with(|| rval.clone());
535 }
536 results.push(merged);
537 }
538 }
539 }
540
541 results
542 }
543
544 fn deduplicate_bindings(
546 &self,
547 bindings: Vec<HashMap<String, QueryValue>>,
548 ) -> Vec<HashMap<String, QueryValue>> {
549 let mut seen = std::collections::HashSet::new();
550 let mut results = Vec::new();
551
552 for binding in bindings {
553 let key: Vec<_> = binding
554 .iter()
555 .map(|(k, v)| format!("{}={}", k, v.as_str()))
556 .collect();
557 let key = key.join(",");
558
559 if !seen.contains(&key) {
560 seen.insert(key);
561 results.push(binding);
562 }
563 }
564
565 results
566 }
567
568 fn apply_modifiers(
570 &self,
571 bindings: Vec<HashMap<String, QueryValue>>,
572 limit: Option<usize>,
573 offset: Option<usize>,
574 ) -> Vec<HashMap<String, QueryValue>> {
575 let offset = offset.unwrap_or(0);
576 let mut results: Vec<_> = bindings.into_iter().skip(offset).collect();
577
578 if let Some(limit) = limit {
579 results.truncate(limit);
580 }
581
582 results
583 }
584
585 pub fn execute_to_tlexpr(&self, sparql: &str) -> Result<TLExpr> {
587 let query = self.compiler.parse_query(sparql)?;
588 self.compiler.compile_to_tensorlogic(&query)
589 }
590
591 pub fn results_to_tlexpr(&self, results: &QueryResults) -> Result<TLExpr> {
593 match results {
594 QueryResults::Ask(value) => {
595 if *value {
596 Ok(TLExpr::pred("true", vec![]))
597 } else {
598 Ok(TLExpr::pred("false", vec![]))
599 }
600 }
601 QueryResults::Select { bindings, .. } => {
602 if bindings.is_empty() {
603 return Ok(TLExpr::pred("empty", vec![]));
604 }
605
606 let mut exprs = Vec::new();
608 for binding in bindings {
609 let mut terms = Vec::new();
610 for (var, value) in binding {
611 let pred_name = self
612 .predicate_mappings
613 .get(value.as_str())
614 .cloned()
615 .unwrap_or_else(|| var.clone());
616 terms.push(TLExpr::pred(
617 &pred_name,
618 vec![tensorlogic_ir::Term::constant(value.as_str())],
619 ));
620 }
621
622 if !terms.is_empty() {
623 let conjunction = terms.into_iter().reduce(TLExpr::and);
624 if let Some(expr) = conjunction {
625 exprs.push(expr);
626 }
627 }
628 }
629
630 exprs
632 .into_iter()
633 .reduce(TLExpr::or)
634 .ok_or_else(|| anyhow!("Empty results"))
635 }
636 QueryResults::Construct { triples } | QueryResults::Describe { triples } => {
637 let mut exprs = Vec::new();
639 for triple in triples {
640 let pred_name = self
641 .predicate_mappings
642 .get(&triple.predicate)
643 .cloned()
644 .unwrap_or_else(|| Self::iri_to_name(&triple.predicate));
645 let expr = TLExpr::pred(
646 &pred_name,
647 vec![
648 tensorlogic_ir::Term::constant(&triple.subject),
649 tensorlogic_ir::Term::constant(&triple.object),
650 ],
651 );
652 exprs.push(expr);
653 }
654
655 exprs
656 .into_iter()
657 .reduce(TLExpr::and)
658 .ok_or_else(|| anyhow!("Empty results"))
659 }
660 }
661 }
662
663 fn iri_to_name(iri: &str) -> String {
665 iri.split(['/', '#']).next_back().unwrap_or(iri).to_string()
666 }
667}
668
669#[cfg(test)]
670mod tests {
671 use super::*;
672
673 #[test]
674 fn test_executor_creation() {
675 let executor = OxirsSparqlExecutor::new().expect("Failed to create executor");
676 assert_eq!(executor.num_triples(), 0);
677 }
678
679 #[test]
680 fn test_load_turtle() {
681 let mut executor = OxirsSparqlExecutor::new().expect("Failed to create executor");
682 let turtle = r#"
683 @prefix ex: <http://example.org/> .
684 ex:Alice ex:knows ex:Bob .
685 ex:Bob ex:knows ex:Carol .
686 "#;
687
688 let result = executor.load_turtle(turtle);
689 assert!(result.is_ok());
690 assert_eq!(executor.num_triples(), 2);
691 }
692
693 #[test]
694 fn test_execute_select() {
695 let mut executor = OxirsSparqlExecutor::new().expect("Failed to create executor");
696 executor
697 .load_turtle(
698 r#"
699 @prefix ex: <http://example.org/> .
700 ex:Alice ex:knows ex:Bob .
701 "#,
702 )
703 .expect("Load failed");
704
705 let query = "SELECT ?s ?o WHERE { ?s <http://example.org/knows> ?o }";
706 let result = executor.execute(query);
707
708 assert!(result.is_ok());
709 match result.expect("Query failed") {
710 QueryResults::Select { bindings, .. } => {
711 assert!(!bindings.is_empty());
712 }
713 _ => panic!("Expected SELECT results"),
714 }
715 }
716
717 #[test]
718 fn test_execute_ask() {
719 let mut executor = OxirsSparqlExecutor::new().expect("Failed to create executor");
720 executor
721 .load_turtle(
722 r#"
723 @prefix ex: <http://example.org/> .
724 ex:Alice ex:knows ex:Bob .
725 "#,
726 )
727 .expect("Load failed");
728
729 let query = "ASK WHERE { ?s <http://example.org/knows> ?o }";
730 let result = executor.execute(query);
731
732 assert!(result.is_ok());
733 match result.expect("Query failed") {
734 QueryResults::Ask(value) => {
735 assert!(value);
736 }
737 _ => panic!("Expected ASK results"),
738 }
739 }
740
741 #[test]
742 fn test_predicate_mapping() {
743 let mut executor = OxirsSparqlExecutor::new().expect("Failed to create executor");
744 executor.add_predicate_mapping("http://example.org/knows", "knows");
745
746 assert!(executor
747 .predicate_mappings
748 .contains_key("http://example.org/knows"));
749 }
750
751 #[test]
752 fn test_query_value_methods() {
753 let iri = QueryValue::Iri("http://example.org/Alice".to_string());
754 assert!(iri.is_iri());
755 assert!(!iri.is_literal());
756 assert_eq!(iri.as_str(), "http://example.org/Alice");
757
758 let literal = QueryValue::Literal {
759 value: "Hello".to_string(),
760 datatype: None,
761 language: Some("en".to_string()),
762 };
763 assert!(!literal.is_iri());
764 assert!(literal.is_literal());
765 }
766
767 #[test]
768 fn test_execute_to_tlexpr() {
769 let mut executor = OxirsSparqlExecutor::new().expect("Failed to create executor");
770 executor.add_predicate_mapping("http://example.org/knows", "knows");
771 executor
772 .load_turtle(
773 r#"
774 @prefix ex: <http://example.org/> .
775 ex:Alice ex:knows ex:Bob .
776 "#,
777 )
778 .expect("Load failed");
779
780 let query = "SELECT ?s ?o WHERE { ?s <http://example.org/knows> ?o }";
781 let result = executor.execute_to_tlexpr(query);
782
783 assert!(result.is_ok());
784 }
785
786 #[test]
787 fn test_load_ntriples() {
788 let mut executor = OxirsSparqlExecutor::new().expect("Failed to create executor");
789 let ntriples = r#"
790 <http://example.org/Alice> <http://example.org/knows> <http://example.org/Bob> .
791 "#;
792
793 let result = executor.load_ntriples(ntriples);
794 assert!(result.is_ok());
795 assert_eq!(executor.num_triples(), 1);
796 }
797}