From 19bac93877f27f06ac2b3c15b958eacc4ecf5a0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Sun, 19 Jan 2025 14:04:26 +0100 Subject: [PATCH 01/20] initial version with the structs defined --- lib/rdf-query/src/lib.rs | 5 +++++ lib/rdf-query/src/pattern.rs | 7 +++++++ lib/rdf-query/src/solution.rs | 7 +++++++ lib/rdf-query/src/solutions.rs | 7 +++++++ lib/rdf-query/src/variable.rs | 7 +++++++ 5 files changed, 33 insertions(+) create mode 100644 lib/rdf-query/src/pattern.rs create mode 100644 lib/rdf-query/src/solution.rs create mode 100644 lib/rdf-query/src/solutions.rs create mode 100644 lib/rdf-query/src/variable.rs diff --git a/lib/rdf-query/src/lib.rs b/lib/rdf-query/src/lib.rs index d8284e4..ba0a805 100644 --- a/lib/rdf-query/src/lib.rs +++ b/lib/rdf-query/src/lib.rs @@ -6,3 +6,8 @@ #![no_std] #![deny(unsafe_code)] + +pub mod pattern; +pub mod solution; +pub mod solutions; +pub mod variable; diff --git a/lib/rdf-query/src/pattern.rs b/lib/rdf-query/src/pattern.rs new file mode 100644 index 0000000..1569d58 --- /dev/null +++ b/lib/rdf-query/src/pattern.rs @@ -0,0 +1,7 @@ +pub struct Pattern {} + +impl core::fmt::Debug for Pattern { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.debug_struct("Pattern").finish() + } +} diff --git a/lib/rdf-query/src/solution.rs b/lib/rdf-query/src/solution.rs new file mode 100644 index 0000000..a014ebe --- /dev/null +++ b/lib/rdf-query/src/solution.rs @@ -0,0 +1,7 @@ +pub struct Solution {} + +impl core::fmt::Debug for Solution { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.debug_struct("Solution").finish() + } +} diff --git a/lib/rdf-query/src/solutions.rs b/lib/rdf-query/src/solutions.rs new file mode 100644 index 0000000..4e7ba30 --- /dev/null +++ b/lib/rdf-query/src/solutions.rs @@ -0,0 +1,7 @@ +pub struct Solutions {} + +impl core::fmt::Debug for Solutions { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.debug_struct("Solutions").finish() + } +} diff --git a/lib/rdf-query/src/variable.rs b/lib/rdf-query/src/variable.rs new file mode 100644 index 0000000..a7e1c5e --- /dev/null +++ b/lib/rdf-query/src/variable.rs @@ -0,0 +1,7 @@ +pub struct Variable {} + +impl core::fmt::Debug for Variable { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + f.debug_struct("Variable").finish() + } +} From ce1ecfe8c2daa9d50c303ead62395601fd90e336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Sun, 19 Jan 2025 14:05:57 +0100 Subject: [PATCH 02/20] implement statement for pattern --- lib/rdf-query/src/pattern.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/rdf-query/src/pattern.rs b/lib/rdf-query/src/pattern.rs index 1569d58..29f6f24 100644 --- a/lib/rdf-query/src/pattern.rs +++ b/lib/rdf-query/src/pattern.rs @@ -1,5 +1,21 @@ +use rdf_model::Statement; + pub struct Pattern {} +impl Statement for Pattern { + fn subject(&self) -> &dyn rdf_model::Term { + todo!() + } + + fn predicate(&self) -> &dyn rdf_model::Term { + todo!() + } + + fn object(&self) -> &dyn rdf_model::Term { + todo!() + } +} + impl core::fmt::Debug for Pattern { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_struct("Pattern").finish() From 6ff72bf6f0028ac72c5aa79bda34c82a3fbb6c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Mon, 20 Jan 2025 13:16:54 +0100 Subject: [PATCH 03/20] definition of variables to be used in triple patterns --- lib/rdf-query/src/variable.rs | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/lib/rdf-query/src/variable.rs b/lib/rdf-query/src/variable.rs index a7e1c5e..da89f05 100644 --- a/lib/rdf-query/src/variable.rs +++ b/lib/rdf-query/src/variable.rs @@ -1,7 +1,30 @@ -pub struct Variable {} +extern crate alloc; -impl core::fmt::Debug for Variable { +use alloc::string::String; +use rdf_model::{HeapTerm, Term}; + +pub type Variable = GenericVariable; + +#[derive(PartialEq, Eq, PartialOrd, Ord)] +pub struct GenericVariable { + name: String, + value: Option, +} + +impl GenericVariable { + pub fn new(name: impl Into, value: Option) -> Self { + Self { + name: name.into(), + value, + } + } +} + +impl core::fmt::Debug for GenericVariable { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_struct("Variable").finish() + f.debug_struct("Variable") + .field("name", &self.name) + .field("value", &self.value) + .finish() } } From 74dae99f05f150c300500d10be01638d598b3550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Mon, 20 Jan 2025 13:17:41 +0100 Subject: [PATCH 04/20] definition of triple pattern matchers; e.g. its building blocks --- lib/rdf-query/src/lib.rs | 1 + lib/rdf-query/src/matcher.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 lib/rdf-query/src/matcher.rs diff --git a/lib/rdf-query/src/lib.rs b/lib/rdf-query/src/lib.rs index ba0a805..92dbff0 100644 --- a/lib/rdf-query/src/lib.rs +++ b/lib/rdf-query/src/lib.rs @@ -7,6 +7,7 @@ #![no_std] #![deny(unsafe_code)] +pub mod matcher; pub mod pattern; pub mod solution; pub mod solutions; diff --git a/lib/rdf-query/src/matcher.rs b/lib/rdf-query/src/matcher.rs new file mode 100644 index 0000000..65de6a0 --- /dev/null +++ b/lib/rdf-query/src/matcher.rs @@ -0,0 +1,30 @@ +use rdf_model::{HeapTerm, Term}; + +use crate::variable::GenericVariable; + +pub type Matcher = GenericMatcher; + +pub enum GenericMatcher { + Any, + Variable(GenericVariable), + Term(T), +} + +impl GenericMatcher { + pub fn matches(&self, term: &dyn Term) -> bool { + match self { + Self::Any | Self::Variable(_) => true, + Self::Term(t) => t.as_str() == term.as_str(), + } + } +} + +impl core::fmt::Debug for GenericMatcher { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + Self::Any => f.write_str("Any"), + Self::Variable(var) => write!(f, "{:?}", var), + Self::Term(t) => f.write_str(&t.as_str()), + } + } +} From 4c1d8f1451b6425c78ec39d8cff605dd15d84a7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Mon, 20 Jan 2025 13:18:06 +0100 Subject: [PATCH 05/20] definition of the triple patterns --- lib/rdf-query/src/pattern.rs | 56 +++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/lib/rdf-query/src/pattern.rs b/lib/rdf-query/src/pattern.rs index 29f6f24..b830458 100644 --- a/lib/rdf-query/src/pattern.rs +++ b/lib/rdf-query/src/pattern.rs @@ -1,23 +1,57 @@ -use rdf_model::Statement; +use rdf_model::{HeapTerm, Statement, Term}; -pub struct Pattern {} +use crate::{matcher::GenericMatcher, variable::GenericVariable}; -impl Statement for Pattern { - fn subject(&self) -> &dyn rdf_model::Term { - todo!() +pub type Pattern = GenericPattern; + +impl From> for GenericMatcher { + fn from(var: GenericVariable) -> Self { + Self::Variable(var) + } +} + +impl From for GenericMatcher { + fn from(term: T) -> Self { + Self::Term(term) } +} + +pub struct GenericPattern { + subject: GenericMatcher, + predicate: GenericMatcher, + object: GenericMatcher, + graph_name: Option, +} - fn predicate(&self) -> &dyn rdf_model::Term { - todo!() +impl GenericPattern { + pub fn new( + subject: impl Into>, + predicate: impl Into>, + object: impl Into>, + graph_name: Option, + ) -> Self { + Self { + subject: subject.into(), + predicate: predicate.into(), + object: object.into(), + graph_name, + } } - fn object(&self) -> &dyn rdf_model::Term { - todo!() + pub fn matches(&self, statement: &dyn Statement) -> bool { + self.subject.matches(statement.subject()) + && self.predicate.matches(statement.predicate()) + && self.object.matches(statement.object()) } } -impl core::fmt::Debug for Pattern { +impl core::fmt::Debug for GenericPattern { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - f.debug_struct("Pattern").finish() + f.debug_struct("Pattern") + .field("graph_name", &self.graph_name) + .field("subject", &self.subject) + .field("predicate", &self.predicate) + .field("object", &self.object) + .finish() } } From e604f8957e01b4def50257e863ba91bd676f5603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Mon, 20 Jan 2025 13:18:30 +0100 Subject: [PATCH 06/20] definition of the solution of the query and the iterator over them --- lib/rdf-query/src/solution.rs | 35 +++++++++++++++++++++++++++++++++- lib/rdf-query/src/solutions.rs | 29 ++++++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/lib/rdf-query/src/solution.rs b/lib/rdf-query/src/solution.rs index a014ebe..1db8bf7 100644 --- a/lib/rdf-query/src/solution.rs +++ b/lib/rdf-query/src/solution.rs @@ -1,4 +1,37 @@ -pub struct Solution {} +extern crate alloc; + +use alloc::collections::BTreeMap; +use rdf_model::{HeapTerm, Term}; + +use crate::variable::GenericVariable; + +pub type Solution = GenericSolution; + +pub struct GenericSolution { + bindings: BTreeMap, T>, +} + +impl GenericSolution { + pub fn new(bindings: BTreeMap, T>) -> Self { + Self { bindings } + } + + pub fn binding(&self, var: &GenericVariable) -> Option<&T> { + self.bindings.get(var) + } + + pub fn each_binding(&self) -> impl Iterator, &T)> { + self.bindings.iter() + } + + pub fn each_name(&self) -> impl Iterator> { + self.bindings.keys() + } + + pub fn each_value(&self) -> impl Iterator { + self.bindings.values() + } +} impl core::fmt::Debug for Solution { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { diff --git a/lib/rdf-query/src/solutions.rs b/lib/rdf-query/src/solutions.rs index 4e7ba30..bd9137d 100644 --- a/lib/rdf-query/src/solutions.rs +++ b/lib/rdf-query/src/solutions.rs @@ -1,6 +1,31 @@ -pub struct Solutions {} +extern crate alloc; -impl core::fmt::Debug for Solutions { +use alloc::boxed::Box; +use rdf_model::Term; + +use crate::solution::GenericSolution; + +pub struct GenericSolutions { + iter: Box>>, +} + +impl GenericSolutions { + pub fn new(iter: impl Iterator> + 'static) -> Self { + Self { + iter: Box::new(iter), + } + } +} + +impl Iterator for GenericSolutions { + type Item = GenericSolution; + + fn next(&mut self) -> Option { + self.iter.next() + } +} + +impl core::fmt::Debug for GenericSolutions { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_struct("Solutions").finish() } From 431ab2bec2c1f3db665f55d6af4b4499b614608d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Tue, 21 Jan 2025 15:43:08 +0100 Subject: [PATCH 07/20] moving queryable to rdf-query --- lib/rdf-model/src/lib.rs | 3 --- lib/rdf-model/src/traits/queryable.rs | 5 ----- lib/rdf-query/src/lib.rs | 5 +++++ lib/rdf-query/src/traits/queryable.rs | 24 ++++++++++++++++++++++++ 4 files changed, 29 insertions(+), 8 deletions(-) delete mode 100644 lib/rdf-model/src/traits/queryable.rs create mode 100644 lib/rdf-query/src/traits/queryable.rs diff --git a/lib/rdf-model/src/lib.rs b/lib/rdf-model/src/lib.rs index 74c82c0..5941d12 100644 --- a/lib/rdf-model/src/lib.rs +++ b/lib/rdf-model/src/lib.rs @@ -69,9 +69,6 @@ mod traits { mod maybe_mutable; pub use maybe_mutable::*; - - mod queryable; - pub use queryable::*; } pub use traits::*; diff --git a/lib/rdf-model/src/traits/queryable.rs b/lib/rdf-model/src/traits/queryable.rs deleted file mode 100644 index c9bd216..0000000 --- a/lib/rdf-model/src/traits/queryable.rs +++ /dev/null @@ -1,5 +0,0 @@ -// This is free and unencumbered software released into the public domain. - -use super::Enumerable; - -pub trait Queryable: Enumerable {} diff --git a/lib/rdf-query/src/lib.rs b/lib/rdf-query/src/lib.rs index 92dbff0..67ebf3b 100644 --- a/lib/rdf-query/src/lib.rs +++ b/lib/rdf-query/src/lib.rs @@ -9,6 +9,11 @@ pub mod matcher; pub mod pattern; +pub mod query; pub mod solution; pub mod solutions; pub mod variable; + +mod traits { + pub mod queryable; +} diff --git a/lib/rdf-query/src/traits/queryable.rs b/lib/rdf-query/src/traits/queryable.rs new file mode 100644 index 0000000..a34181f --- /dev/null +++ b/lib/rdf-query/src/traits/queryable.rs @@ -0,0 +1,24 @@ +// This is free and unencumbered software released into the public domain. +extern crate alloc; + +use alloc::boxed::Box; +use rdf_model::{Enumerable, Statement}; + +use crate::{pattern::Pattern, query::Query, solutions::Solutions}; + +pub trait Queryable: Enumerable + Clone { + // fn query(&self, query: GenericQuery) -> GenericSolutions; + + fn query_execute(&self, query: Query) -> Solutions { + // query.execute(self) + todo!() + } + + fn query_pattern(&self, pattern: &Pattern) -> impl Iterator> + where + Self: Sized, + { + self.clone() // TODO: check if the clone is necessary + .filter_map(move |statement| statement.ok().filter(|statement| pattern == statement)) + } +} From 50a38635f77c99df5e3c43ec5bde828ff5ae0007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Tue, 21 Jan 2025 15:44:03 +0100 Subject: [PATCH 08/20] removing the necessity of having generics, using dynamic dispatch --- lib/rdf-query/src/matcher.rs | 35 +++++++++++++-------- lib/rdf-query/src/pattern.rs | 56 ++++++++++++++++------------------ lib/rdf-query/src/solution.rs | 24 +++++++-------- lib/rdf-query/src/solutions.rs | 23 ++++++++------ lib/rdf-query/src/variable.rs | 46 +++++++++++++++++++++------- 5 files changed, 110 insertions(+), 74 deletions(-) diff --git a/lib/rdf-query/src/matcher.rs b/lib/rdf-query/src/matcher.rs index 65de6a0..7c7c7b1 100644 --- a/lib/rdf-query/src/matcher.rs +++ b/lib/rdf-query/src/matcher.rs @@ -1,28 +1,39 @@ -use rdf_model::{HeapTerm, Term}; +extern crate alloc; -use crate::variable::GenericVariable; +use alloc::boxed::Box; +use rdf_model::Term; -pub type Matcher = GenericMatcher; +use crate::variable::Variable; -pub enum GenericMatcher { - Any, - Variable(GenericVariable), - Term(T), +pub enum Matcher { + Variable(Variable), + Term(Box), } -impl GenericMatcher { - pub fn matches(&self, term: &dyn Term) -> bool { +impl PartialEq<&dyn Term> for Matcher { + fn eq(&self, term: &&dyn Term) -> bool { match self { - Self::Any | Self::Variable(_) => true, + Self::Variable(_) => true, Self::Term(t) => t.as_str() == term.as_str(), } } } -impl core::fmt::Debug for GenericMatcher { +impl From for Matcher { + fn from(var: Variable) -> Self { + Self::Variable(var) + } +} + +impl From> for Matcher { + fn from(term: Box) -> Self { + Self::Term(term) + } +} + +impl core::fmt::Debug for Matcher { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { match self { - Self::Any => f.write_str("Any"), Self::Variable(var) => write!(f, "{:?}", var), Self::Term(t) => f.write_str(&t.as_str()), } diff --git a/lib/rdf-query/src/pattern.rs b/lib/rdf-query/src/pattern.rs index b830458..47399e7 100644 --- a/lib/rdf-query/src/pattern.rs +++ b/lib/rdf-query/src/pattern.rs @@ -1,34 +1,23 @@ -use rdf_model::{HeapTerm, Statement, Term}; +extern crate alloc; -use crate::{matcher::GenericMatcher, variable::GenericVariable}; +use alloc::boxed::Box; +use rdf_model::{Statement, Term}; -pub type Pattern = GenericPattern; +use crate::{matcher::Matcher, solutions::Solutions, traits::queryable::Queryable}; -impl From> for GenericMatcher { - fn from(var: GenericVariable) -> Self { - Self::Variable(var) - } +pub struct Pattern { + subject: Matcher, + predicate: Matcher, + object: Matcher, + graph_name: Option>, } -impl From for GenericMatcher { - fn from(term: T) -> Self { - Self::Term(term) - } -} - -pub struct GenericPattern { - subject: GenericMatcher, - predicate: GenericMatcher, - object: GenericMatcher, - graph_name: Option, -} - -impl GenericPattern { +impl Pattern { pub fn new( - subject: impl Into>, - predicate: impl Into>, - object: impl Into>, - graph_name: Option, + subject: impl Into, + predicate: impl Into, + object: impl Into, + graph_name: Option>, ) -> Self { Self { subject: subject.into(), @@ -38,14 +27,21 @@ impl GenericPattern { } } - pub fn matches(&self, statement: &dyn Statement) -> bool { - self.subject.matches(statement.subject()) - && self.predicate.matches(statement.predicate()) - && self.object.matches(statement.object()) + pub(crate) fn execute(&self, queryable: &impl Queryable) -> Solutions { + // queryable.query_pattern(self) + todo!() + } +} + +impl PartialEq> for Pattern { + fn eq(&self, statement: &Box) -> bool { + self.subject == statement.subject() + && self.predicate == statement.predicate() + && self.object == statement.object() } } -impl core::fmt::Debug for GenericPattern { +impl core::fmt::Debug for Pattern { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_struct("Pattern") .field("graph_name", &self.graph_name) diff --git a/lib/rdf-query/src/solution.rs b/lib/rdf-query/src/solution.rs index 1db8bf7..a230d9f 100644 --- a/lib/rdf-query/src/solution.rs +++ b/lib/rdf-query/src/solution.rs @@ -1,34 +1,34 @@ extern crate alloc; -use alloc::collections::BTreeMap; -use rdf_model::{HeapTerm, Term}; +use alloc::{boxed::Box, collections::BTreeMap}; +use rdf_model::Term; -use crate::variable::GenericVariable; +use crate::variable::Variable; -pub type Solution = GenericSolution; +type Value = Box; -pub struct GenericSolution { - bindings: BTreeMap, T>, +pub struct Solution { + bindings: BTreeMap, } -impl GenericSolution { - pub fn new(bindings: BTreeMap, T>) -> Self { +impl Solution { + pub fn new(bindings: BTreeMap) -> Self { Self { bindings } } - pub fn binding(&self, var: &GenericVariable) -> Option<&T> { + pub fn binding(&self, var: &Variable) -> Option<&Value> { self.bindings.get(var) } - pub fn each_binding(&self) -> impl Iterator, &T)> { + pub fn each_binding(&self) -> impl Iterator { self.bindings.iter() } - pub fn each_name(&self) -> impl Iterator> { + pub fn each_name(&self) -> impl Iterator { self.bindings.keys() } - pub fn each_value(&self) -> impl Iterator { + pub fn each_value(&self) -> impl Iterator { self.bindings.values() } } diff --git a/lib/rdf-query/src/solutions.rs b/lib/rdf-query/src/solutions.rs index bd9137d..d50a86c 100644 --- a/lib/rdf-query/src/solutions.rs +++ b/lib/rdf-query/src/solutions.rs @@ -1,31 +1,36 @@ extern crate alloc; use alloc::boxed::Box; -use rdf_model::Term; -use crate::solution::GenericSolution; +use crate::solution::Solution; -pub struct GenericSolutions { - iter: Box>>, +pub struct Solutions { + iter: Box>, } -impl GenericSolutions { - pub fn new(iter: impl Iterator> + 'static) -> Self { +impl Solutions { + pub fn new(iter: impl Iterator + 'static) -> Self { Self { iter: Box::new(iter), } } + + pub fn empty() -> Self { + Self { + iter: Box::new(core::iter::empty()), + } + } } -impl Iterator for GenericSolutions { - type Item = GenericSolution; +impl Iterator for Solutions { + type Item = Solution; fn next(&mut self) -> Option { self.iter.next() } } -impl core::fmt::Debug for GenericSolutions { +impl core::fmt::Debug for Solutions { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_struct("Solutions").finish() } diff --git a/lib/rdf-query/src/variable.rs b/lib/rdf-query/src/variable.rs index da89f05..ac2c373 100644 --- a/lib/rdf-query/src/variable.rs +++ b/lib/rdf-query/src/variable.rs @@ -1,26 +1,50 @@ extern crate alloc; -use alloc::string::String; -use rdf_model::{HeapTerm, Term}; +use alloc::{boxed::Box, string::String}; +use rdf_model::Term; -pub type Variable = GenericVariable; - -#[derive(PartialEq, Eq, PartialOrd, Ord)] -pub struct GenericVariable { +pub struct Variable { name: String, - value: Option, + value: Option>, } -impl GenericVariable { - pub fn new(name: impl Into, value: Option) -> Self { +impl Variable { + pub fn bound(name: impl Into, value: impl Term + 'static) -> Self { + Self { + name: name.into(), + value: Some(Box::new(value)), + } + } + + pub fn unbound(name: impl Into) -> Self { Self { name: name.into(), - value, + value: None, } } } -impl core::fmt::Debug for GenericVariable { +impl PartialEq for Variable { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} + +impl Eq for Variable {} + +impl PartialOrd for Variable { + fn partial_cmp(&self, other: &Self) -> Option { + self.name.partial_cmp(&other.name) + } +} + +impl Ord for Variable { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.name.cmp(&other.name) + } +} + +impl core::fmt::Debug for Variable { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_struct("Variable") .field("name", &self.name) From f4b6c4afff882974805116dc19d200efbbdfb380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Tue, 21 Jan 2025 15:44:24 +0100 Subject: [PATCH 09/20] query execution --- lib/rdf-query/src/query.rs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 lib/rdf-query/src/query.rs diff --git a/lib/rdf-query/src/query.rs b/lib/rdf-query/src/query.rs new file mode 100644 index 0000000..6f1eaaa --- /dev/null +++ b/lib/rdf-query/src/query.rs @@ -0,0 +1,36 @@ +extern crate alloc; + +use alloc::vec::Vec; + +use crate::{pattern::Pattern, solutions::Solutions, traits::queryable::Queryable}; + +pub struct Query { + patterns: Vec, +} + +impl Query { + pub fn new(patterns: Vec) -> Self { + Self { patterns } + } + + /// Executes the query on the given graph. + /// + /// If the query nas no patterns, it returns a single empty solution as per + /// SPARQL 1.1 Empty Group Pattern. + pub fn execute(&self, queryable: impl Queryable) -> Solutions { + if self.empty() { + return Solutions::empty(); + } + + for pattern in &self.patterns { + let _ = pattern.execute(&queryable); + } + + Solutions::empty() + } + + /// Returns true if the query has no patterns. + pub fn empty(&self) -> bool { + self.patterns.is_empty() + } +} From 85edf200869c91286202ad23acb4637c70cfaf1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Thu, 23 Jan 2025 15:50:41 +0100 Subject: [PATCH 10/20] enumerable now has the grep method --- lib/rdf-model/src/traits/enumerable.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/rdf-model/src/traits/enumerable.rs b/lib/rdf-model/src/traits/enumerable.rs index 2c10648..2577eef 100644 --- a/lib/rdf-model/src/traits/enumerable.rs +++ b/lib/rdf-model/src/traits/enumerable.rs @@ -10,4 +10,7 @@ use core::error::Error; pub trait Enumerable: Countable + Iterator, Box>> { + fn grep(&self, pattern: &impl PartialEq>) -> Self + where + Self: Sized; } From 36281f1f278abc371e4bdaca91eb04a61306f5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Thu, 23 Jan 2025 15:51:12 +0100 Subject: [PATCH 11/20] matcher and variable can be built from a str --- lib/rdf-query/src/matcher.rs | 15 +++++++++++++++ lib/rdf-query/src/variable.rs | 10 ++++++++++ 2 files changed, 25 insertions(+) diff --git a/lib/rdf-query/src/matcher.rs b/lib/rdf-query/src/matcher.rs index 7c7c7b1..537c07a 100644 --- a/lib/rdf-query/src/matcher.rs +++ b/lib/rdf-query/src/matcher.rs @@ -10,6 +10,15 @@ pub enum Matcher { Term(Box), } +impl Matcher { + pub fn as_variable(&self) -> Option<&Variable> { + match self { + Self::Variable(var) => Some(var), + _ => None, + } + } +} + impl PartialEq<&dyn Term> for Matcher { fn eq(&self, term: &&dyn Term) -> bool { match self { @@ -25,6 +34,12 @@ impl From for Matcher { } } +impl From<&str> for Matcher { + fn from(name: &str) -> Self { + name.into() + } +} + impl From> for Matcher { fn from(term: Box) -> Self { Self::Term(term) diff --git a/lib/rdf-query/src/variable.rs b/lib/rdf-query/src/variable.rs index ac2c373..f2a8a78 100644 --- a/lib/rdf-query/src/variable.rs +++ b/lib/rdf-query/src/variable.rs @@ -22,6 +22,10 @@ impl Variable { value: None, } } + + pub fn name(&self) -> &str { + &self.name + } } impl PartialEq for Variable { @@ -44,6 +48,12 @@ impl Ord for Variable { } } +impl From<&str> for Variable { + fn from(name: &str) -> Self { + Variable::unbound(name) + } +} + impl core::fmt::Debug for Variable { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_struct("Variable") From 23ea2d3fe49499203cbb9f59c4affdebff6281d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Thu, 23 Jan 2025 15:51:29 +0100 Subject: [PATCH 12/20] query now depends on Enumerable --- lib/rdf-query/src/traits/queryable.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/rdf-query/src/traits/queryable.rs b/lib/rdf-query/src/traits/queryable.rs index a34181f..85c9cd0 100644 --- a/lib/rdf-query/src/traits/queryable.rs +++ b/lib/rdf-query/src/traits/queryable.rs @@ -1,24 +1,26 @@ // This is free and unencumbered software released into the public domain. extern crate alloc; -use alloc::boxed::Box; -use rdf_model::{Enumerable, Statement}; +use rdf_model::Enumerable; use crate::{pattern::Pattern, query::Query, solutions::Solutions}; -pub trait Queryable: Enumerable + Clone { +pub trait Queryable: Enumerable { // fn query(&self, query: GenericQuery) -> GenericSolutions; - fn query_execute(&self, query: Query) -> Solutions { - // query.execute(self) - todo!() + // TODO: is this naming correct? + fn query_execute(&self, query: Query) -> Solutions + where + Self: Sized, + { + query.execute(self) } - fn query_pattern(&self, pattern: &Pattern) -> impl Iterator> + // TODO: revisit this return type + fn query_pattern(&self, pattern: &Pattern) -> Self where Self: Sized, { - self.clone() // TODO: check if the clone is necessary - .filter_map(move |statement| statement.ok().filter(|statement| pattern == statement)) + self.grep(pattern) } } From 32ec649bdc70c23df9225c5484f189c9ec87907b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Thu, 23 Jan 2025 15:51:49 +0100 Subject: [PATCH 13/20] solution manages the binding of a variable with an statement --- lib/rdf-query/src/pattern.rs | 51 +++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/lib/rdf-query/src/pattern.rs b/lib/rdf-query/src/pattern.rs index 47399e7..95ad621 100644 --- a/lib/rdf-query/src/pattern.rs +++ b/lib/rdf-query/src/pattern.rs @@ -1,9 +1,11 @@ extern crate alloc; -use alloc::boxed::Box; -use rdf_model::{Statement, Term}; +use alloc::{boxed::Box, collections::BTreeMap}; +use rdf_model::{HeapTerm, Statement, Term}; -use crate::{matcher::Matcher, solutions::Solutions, traits::queryable::Queryable}; +use crate::{ + matcher::Matcher, solution::Solution, traits::queryable::Queryable, variable::Variable, +}; pub struct Pattern { subject: Matcher, @@ -27,9 +29,46 @@ impl Pattern { } } - pub(crate) fn execute(&self, queryable: &impl Queryable) -> Solutions { - // queryable.query_pattern(self) - todo!() + pub(crate) fn execute(&self, queryable: &Q) -> Q + where + Self: Sized, + { + queryable.query_pattern(self) + } + + /// Returns a query solution constructed by binding any variables in this + /// pattern with the corresponding terms in the given statement. + pub fn solution(&self, statement: Box) -> Solution { + let mut bindings = BTreeMap::new(); + + if let Some(subject) = self + .subject + .as_variable() + .map(Variable::name) + .map(Variable::unbound) + { + bindings.insert(subject, HeapTerm::from(statement.subject())); + } + + if let Some(predicate) = self + .predicate + .as_variable() + .map(Variable::name) + .map(Variable::unbound) + { + bindings.insert(predicate, HeapTerm::from(statement.predicate())); + } + + if let Some(object) = self + .object + .as_variable() + .map(Variable::name) + .map(Variable::unbound) + { + bindings.insert(object, HeapTerm::from(statement.object())); + } + + Solution::new(bindings) } } From 4239b49527d0bfdfe9178a19a4cdc170b5529b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Thu, 23 Jan 2025 15:52:05 +0100 Subject: [PATCH 14/20] query execute returns solutions --- lib/rdf-query/src/query.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/rdf-query/src/query.rs b/lib/rdf-query/src/query.rs index 6f1eaaa..75af92d 100644 --- a/lib/rdf-query/src/query.rs +++ b/lib/rdf-query/src/query.rs @@ -17,16 +17,21 @@ impl Query { /// /// If the query nas no patterns, it returns a single empty solution as per /// SPARQL 1.1 Empty Group Pattern. - pub fn execute(&self, queryable: impl Queryable) -> Solutions { + pub fn execute(&self, queryable: &Q) -> Solutions { if self.empty() { return Solutions::empty(); } - for pattern in &self.patterns { - let _ = pattern.execute(&queryable); - } + let solutions: Vec<_> = self + .patterns + .iter() + .flat_map(|pattern| { + let statements = pattern.execute(queryable); + statements.filter_map(|res| res.ok().map(|stmt| pattern.solution(stmt))) + }) + .collect(); - Solutions::empty() + Solutions::new(solutions.into_iter()) } /// Returns true if the query has no patterns. From 34651686ddca390302c908e0604699b01b374b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Thu, 23 Jan 2025 15:52:21 +0100 Subject: [PATCH 15/20] value is simplified --- lib/rdf-query/src/solution.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/rdf-query/src/solution.rs b/lib/rdf-query/src/solution.rs index a230d9f..16a25d5 100644 --- a/lib/rdf-query/src/solution.rs +++ b/lib/rdf-query/src/solution.rs @@ -1,26 +1,26 @@ extern crate alloc; -use alloc::{boxed::Box, collections::BTreeMap}; -use rdf_model::Term; +use alloc::collections::BTreeMap; +use rdf_model::HeapTerm; use crate::variable::Variable; -type Value = Box; - +// TODO: the use of HeapTerm might be reconsidered. The idea is that the term +// stored in the solution must be owned by it, and not a reference. pub struct Solution { - bindings: BTreeMap, + bindings: BTreeMap, } impl Solution { - pub fn new(bindings: BTreeMap) -> Self { + pub fn new(bindings: BTreeMap) -> Self { Self { bindings } } - pub fn binding(&self, var: &Variable) -> Option<&Value> { + pub fn binding(&self, var: &Variable) -> Option<&HeapTerm> { self.bindings.get(var) } - pub fn each_binding(&self) -> impl Iterator { + pub fn each_binding(&self) -> impl Iterator { self.bindings.iter() } @@ -28,7 +28,7 @@ impl Solution { self.bindings.keys() } - pub fn each_value(&self) -> impl Iterator { + pub fn each_value(&self) -> impl Iterator { self.bindings.values() } } From 7f871e98b4dae9eb8d22573fdbdac9af0f749787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Thu, 23 Jan 2025 16:19:25 +0100 Subject: [PATCH 16/20] small simplification of the Solution creation --- lib/rdf-query/src/pattern.rs | 8 ++++---- lib/rdf-query/src/solution.rs | 7 ++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/rdf-query/src/pattern.rs b/lib/rdf-query/src/pattern.rs index 95ad621..372e76f 100644 --- a/lib/rdf-query/src/pattern.rs +++ b/lib/rdf-query/src/pattern.rs @@ -1,7 +1,7 @@ extern crate alloc; use alloc::{boxed::Box, collections::BTreeMap}; -use rdf_model::{HeapTerm, Statement, Term}; +use rdf_model::{Statement, Term}; use crate::{ matcher::Matcher, solution::Solution, traits::queryable::Queryable, variable::Variable, @@ -47,7 +47,7 @@ impl Pattern { .map(Variable::name) .map(Variable::unbound) { - bindings.insert(subject, HeapTerm::from(statement.subject())); + bindings.insert(subject, statement.subject()); } if let Some(predicate) = self @@ -56,7 +56,7 @@ impl Pattern { .map(Variable::name) .map(Variable::unbound) { - bindings.insert(predicate, HeapTerm::from(statement.predicate())); + bindings.insert(predicate, statement.predicate()); } if let Some(object) = self @@ -65,7 +65,7 @@ impl Pattern { .map(Variable::name) .map(Variable::unbound) { - bindings.insert(object, HeapTerm::from(statement.object())); + bindings.insert(object, statement.object()); } Solution::new(bindings) diff --git a/lib/rdf-query/src/solution.rs b/lib/rdf-query/src/solution.rs index 16a25d5..eb34442 100644 --- a/lib/rdf-query/src/solution.rs +++ b/lib/rdf-query/src/solution.rs @@ -12,7 +12,12 @@ pub struct Solution { } impl Solution { - pub fn new(bindings: BTreeMap) -> Self { + pub fn new(bindings: BTreeMap>) -> Self { + let bindings = bindings + .into_iter() + .map(|(var, term)| (var, term.into())) + .collect(); + Self { bindings } } From 9b6c9da153f61ed158031f14c4b381edf4d495d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Thu, 23 Jan 2025 16:29:48 +0100 Subject: [PATCH 17/20] remove some TODOs --- lib/rdf-query/src/solution.rs | 2 -- lib/rdf-query/src/traits/queryable.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/lib/rdf-query/src/solution.rs b/lib/rdf-query/src/solution.rs index eb34442..7d0d0eb 100644 --- a/lib/rdf-query/src/solution.rs +++ b/lib/rdf-query/src/solution.rs @@ -5,8 +5,6 @@ use rdf_model::HeapTerm; use crate::variable::Variable; -// TODO: the use of HeapTerm might be reconsidered. The idea is that the term -// stored in the solution must be owned by it, and not a reference. pub struct Solution { bindings: BTreeMap, } diff --git a/lib/rdf-query/src/traits/queryable.rs b/lib/rdf-query/src/traits/queryable.rs index 85c9cd0..3e96013 100644 --- a/lib/rdf-query/src/traits/queryable.rs +++ b/lib/rdf-query/src/traits/queryable.rs @@ -8,7 +8,6 @@ use crate::{pattern::Pattern, query::Query, solutions::Solutions}; pub trait Queryable: Enumerable { // fn query(&self, query: GenericQuery) -> GenericSolutions; - // TODO: is this naming correct? fn query_execute(&self, query: Query) -> Solutions where Self: Sized, @@ -16,7 +15,6 @@ pub trait Queryable: Enumerable { query.execute(self) } - // TODO: revisit this return type fn query_pattern(&self, pattern: &Pattern) -> Self where Self: Sized, From 4ddfeed6d2827c5d89eaa3c71ba64faf459e740b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Fri, 24 Jan 2025 10:29:53 +0100 Subject: [PATCH 18/20] simple implementation of the query method --- lib/rdf-query/src/graph_pattern.rs | 18 ++++++++++++++++++ lib/rdf-query/src/lib.rs | 1 + lib/rdf-query/src/traits/queryable.rs | 13 +++++++++++-- 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 lib/rdf-query/src/graph_pattern.rs diff --git a/lib/rdf-query/src/graph_pattern.rs b/lib/rdf-query/src/graph_pattern.rs new file mode 100644 index 0000000..acd644f --- /dev/null +++ b/lib/rdf-query/src/graph_pattern.rs @@ -0,0 +1,18 @@ +use crate::{pattern::Pattern, query::Query}; + +pub enum GraphPattern { + BasicGraphPattern(Query), + TriplePattern(Pattern), +} + +impl From for GraphPattern { + fn from(query: Query) -> Self { + Self::BasicGraphPattern(query) + } +} + +impl From for GraphPattern { + fn from(pattern: Pattern) -> Self { + Self::TriplePattern(pattern) + } +} diff --git a/lib/rdf-query/src/lib.rs b/lib/rdf-query/src/lib.rs index 67ebf3b..2d8890d 100644 --- a/lib/rdf-query/src/lib.rs +++ b/lib/rdf-query/src/lib.rs @@ -7,6 +7,7 @@ #![no_std] #![deny(unsafe_code)] +pub mod graph_pattern; pub mod matcher; pub mod pattern; pub mod query; diff --git a/lib/rdf-query/src/traits/queryable.rs b/lib/rdf-query/src/traits/queryable.rs index 3e96013..03eb159 100644 --- a/lib/rdf-query/src/traits/queryable.rs +++ b/lib/rdf-query/src/traits/queryable.rs @@ -1,12 +1,21 @@ // This is free and unencumbered software released into the public domain. extern crate alloc; +use alloc::vec; use rdf_model::Enumerable; -use crate::{pattern::Pattern, query::Query, solutions::Solutions}; +use crate::{graph_pattern::GraphPattern, pattern::Pattern, query::Query, solutions::Solutions}; pub trait Queryable: Enumerable { - // fn query(&self, query: GenericQuery) -> GenericSolutions; + fn query(&self, pattern: impl Into) -> Solutions + where + Self: Sized, + { + match pattern.into() { + GraphPattern::BasicGraphPattern(query) => self.query_execute(query), + GraphPattern::TriplePattern(pattern) => self.query_execute(Query::new(vec![pattern])), + } + } fn query_execute(&self, query: Query) -> Solutions where From 20aed7e2a08c07df967d860a161ffed6e9c57273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81ngel=20Iglesias=20Pr=C3=A9stamo?= Date: Fri, 24 Jan 2025 10:44:26 +0100 Subject: [PATCH 19/20] minor improvements of the pattern struct --- lib/rdf-query/src/pattern.rs | 53 +++++++++++++++--------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/lib/rdf-query/src/pattern.rs b/lib/rdf-query/src/pattern.rs index 372e76f..de44559 100644 --- a/lib/rdf-query/src/pattern.rs +++ b/lib/rdf-query/src/pattern.rs @@ -1,6 +1,6 @@ extern crate alloc; -use alloc::{boxed::Box, collections::BTreeMap}; +use alloc::{boxed::Box, collections::BTreeMap, vec}; use rdf_model::{Statement, Term}; use crate::{ @@ -39,54 +39,45 @@ impl Pattern { /// Returns a query solution constructed by binding any variables in this /// pattern with the corresponding terms in the given statement. pub fn solution(&self, statement: Box) -> Solution { - let mut bindings = BTreeMap::new(); + let mut ans = BTreeMap::new(); - if let Some(subject) = self - .subject - .as_variable() - .map(Variable::name) - .map(Variable::unbound) - { - bindings.insert(subject, statement.subject()); - } - - if let Some(predicate) = self - .predicate - .as_variable() - .map(Variable::name) - .map(Variable::unbound) - { - bindings.insert(predicate, statement.predicate()); - } + let bindings = vec![ + (self.subject.as_variable(), statement.subject()), + (self.predicate.as_variable(), statement.predicate()), + (self.object.as_variable(), statement.object()), + ]; - if let Some(object) = self - .object - .as_variable() - .map(Variable::name) - .map(Variable::unbound) - { - bindings.insert(object, statement.object()); + for (variable, term) in bindings { + if let Some(variable) = variable.map(Variable::name).map(Variable::unbound) { + ans.insert(variable, term); + } } - Solution::new(bindings) + Solution::new(ans) } } impl PartialEq> for Pattern { fn eq(&self, statement: &Box) -> bool { - self.subject == statement.subject() - && self.predicate == statement.predicate() - && self.object == statement.object() + let (s, p, o) = ( + statement.subject(), + statement.predicate(), + statement.object(), + ); + self.subject == s && self.predicate == p && self.object == o } } impl core::fmt::Debug for Pattern { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_struct("Pattern") - .field("graph_name", &self.graph_name) .field("subject", &self.subject) .field("predicate", &self.predicate) .field("object", &self.object) + .field( + "graph_name", + &self.graph_name.as_ref().map(|gn| gn.as_str()), + ) .finish() } } From abe38b3b4cc954d18af0e0fb72a323ef9f8475d7 Mon Sep 17 00:00:00 2001 From: angel-ip Date: Tue, 18 Feb 2025 10:32:56 +0100 Subject: [PATCH 20/20] minor fix so that the project can be built --- lib/rdf-borsh/src/borsh_reader.rs | 10 +++++++++- lib/rdf-reader/src/providers/oxrdf.rs | 9 ++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/rdf-borsh/src/borsh_reader.rs b/lib/rdf-borsh/src/borsh_reader.rs index 6186d74..f700c8d 100644 --- a/lib/rdf-borsh/src/borsh_reader.rs +++ b/lib/rdf-borsh/src/borsh_reader.rs @@ -69,7 +69,15 @@ impl Reader for BorshReader { } impl Source for BorshReader {} -impl Enumerable for BorshReader {} + +impl Enumerable for BorshReader { + fn grep(&self, pattern: &impl PartialEq>) -> Self + where + Self: Sized, + { + todo!() + } +} impl MaybeDurable for BorshReader {} impl MaybeIndexed for BorshReader {} impl MaybeMutable for BorshReader {} diff --git a/lib/rdf-reader/src/providers/oxrdf.rs b/lib/rdf-reader/src/providers/oxrdf.rs index 91f3bf6..4bcb20c 100644 --- a/lib/rdf-reader/src/providers/oxrdf.rs +++ b/lib/rdf-reader/src/providers/oxrdf.rs @@ -45,7 +45,14 @@ impl Reader for OxrdfReader { impl Source for OxrdfReader {} -impl Enumerable for OxrdfReader {} +impl Enumerable for OxrdfReader { + fn grep(&self, pattern: &impl PartialEq>) -> Self + where + Self: Sized, + { + todo!() + } +} impl Countable for OxrdfReader { fn count(&self) -> usize {