From 2e60a1203095fa7749e317b4651437f3d3fe5ff6 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:11:15 +0100 Subject: [PATCH 01/22] feat(cognitarium): introduce blank node id counter state --- contracts/okp4-cognitarium/src/contract.rs | 7 ++++++- contracts/okp4-cognitarium/src/state/blank_nodes.rs | 4 ++++ contracts/okp4-cognitarium/src/state/mod.rs | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 contracts/okp4-cognitarium/src/state/blank_nodes.rs diff --git a/contracts/okp4-cognitarium/src/contract.rs b/contracts/okp4-cognitarium/src/contract.rs index c0df6740..af214d45 100644 --- a/contracts/okp4-cognitarium/src/contract.rs +++ b/contracts/okp4-cognitarium/src/contract.rs @@ -7,7 +7,7 @@ use cw2::set_contract_version; use crate::error::ContractError; use crate::msg::{DataFormat, ExecuteMsg, InstantiateMsg, QueryMsg}; -use crate::state::{Store, NAMESPACE_KEY_INCREMENT, STORE}; +use crate::state::{Store, BLANK_NODE_IDENTIFIER_COUNTER, NAMESPACE_KEY_INCREMENT, STORE}; // version info for migration info const CONTRACT_NAME: &str = concat!("crates.io:", env!("CARGO_PKG_NAME")); @@ -24,6 +24,7 @@ pub fn instantiate( STORE.save(deps.storage, &Store::new(info.sender, msg.limits.into()))?; NAMESPACE_KEY_INCREMENT.save(deps.storage, &0u128)?; + BLANK_NODE_IDENTIFIER_COUNTER.save(deps.storage, &0u128)?; Ok(Response::default()) } @@ -443,6 +444,10 @@ mod tests { ); assert_eq!(NAMESPACE_KEY_INCREMENT.load(&deps.storage).unwrap(), 0u128); + assert_eq!( + BLANK_NODE_IDENTIFIER_COUNTER.load(&deps.storage).unwrap(), + 0u128 + ); } #[test] diff --git a/contracts/okp4-cognitarium/src/state/blank_nodes.rs b/contracts/okp4-cognitarium/src/state/blank_nodes.rs new file mode 100644 index 00000000..3149784e --- /dev/null +++ b/contracts/okp4-cognitarium/src/state/blank_nodes.rs @@ -0,0 +1,4 @@ +use cw_storage_plus::Item; + +/// A counter serving as blank node unique identifier generator. +pub const BLANK_NODE_IDENTIFIER_COUNTER: Item<'_, u128> = Item::new("blank_node_key"); diff --git a/contracts/okp4-cognitarium/src/state/mod.rs b/contracts/okp4-cognitarium/src/state/mod.rs index e2f548f8..e09022e4 100644 --- a/contracts/okp4-cognitarium/src/state/mod.rs +++ b/contracts/okp4-cognitarium/src/state/mod.rs @@ -1,7 +1,9 @@ +mod blank_nodes; mod namespaces; mod store; mod triples; +pub use blank_nodes::*; pub use namespaces::*; pub use store::*; pub use triples::*; From 96286f77f00a2c6f388e0030e87a9219dcecc7d9 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Tue, 27 Feb 2024 22:10:22 +0100 Subject: [PATCH 02/22] feat(rdf): allow to get incremented raw id from issuer --- packages/okp4-rdf/src/normalize.rs | 52 ++++++++++++++++++------------ 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/packages/okp4-rdf/src/normalize.rs b/packages/okp4-rdf/src/normalize.rs index cede5069..37c0dc91 100644 --- a/packages/okp4-rdf/src/normalize.rs +++ b/packages/okp4-rdf/src/normalize.rs @@ -38,7 +38,10 @@ impl<'a> Normalizer<'a> { blank_node_to_quads: HashMap::new(), hash_to_blank_nodes: BTreeMap::new(), blank_node_to_hash: HashMap::new(), - canonical_issuer: IdentifierIssuer::new(Self::CANONICAL_BLANK_NODES_IDENTIFIER_PREFIX), + canonical_issuer: IdentifierIssuer::new( + Self::CANONICAL_BLANK_NODES_IDENTIFIER_PREFIX, + 0u128, + ), } } @@ -69,7 +72,7 @@ impl<'a> Normalizer<'a> { self.hash_to_blank_nodes = BTreeMap::new(); self.blank_node_to_hash = HashMap::new(); self.canonical_issuer = - IdentifierIssuer::new(Self::CANONICAL_BLANK_NODES_IDENTIFIER_PREFIX); + IdentifierIssuer::new(Self::CANONICAL_BLANK_NODES_IDENTIFIER_PREFIX, 0u128); } fn track_blank_nodes(&mut self, dataset: &[Quad<'a>]) { @@ -124,7 +127,7 @@ impl<'a> Normalizer<'a> { for (hash, node) in unique_nodes { self.hash_to_blank_nodes.remove(&hash); - self.canonical_issuer.get_or_issue(&node); + self.canonical_issuer.get_or_issue(node); } Ok(()) @@ -146,8 +149,8 @@ impl<'a> Normalizer<'a> { } let mut scoped_issuer = - IdentifierIssuer::new(Self::TEMPORARY_BLANK_NODES_IDENTIFIER_PREFIX); - scoped_issuer.get_or_issue(node); + IdentifierIssuer::new(Self::TEMPORARY_BLANK_NODES_IDENTIFIER_PREFIX, 0u128); + scoped_issuer.get_or_issue(node.clone()); let (n_degree_hash, issuer) = self.compute_n_degree_hash(&mut scoped_issuer, node)?; @@ -157,7 +160,7 @@ impl<'a> Normalizer<'a> { hash_path_list.sort_by(|left, right| left.0.cmp(&right.0)); for (_, issuer) in hash_path_list { for node in issuer.issue_log { - self.canonical_issuer.get_or_issue(&node); + self.canonical_issuer.get_or_issue(node); } } } @@ -212,7 +215,7 @@ impl<'a> Normalizer<'a> { let mut hasher = sha2::Sha256::new(); let mut chosen_issuer = - IdentifierIssuer::new(Self::TEMPORARY_BLANK_NODES_IDENTIFIER_PREFIX); + IdentifierIssuer::new(Self::TEMPORARY_BLANK_NODES_IDENTIFIER_PREFIX, 0u128); let mut chosen_path = String::new(); for (hash, related) in hashes { @@ -230,7 +233,7 @@ impl<'a> Normalizer<'a> { if !issuer.issued(&related) { recursion_list.push(related.clone()); } - path.push_str(&issuer.get_or_issue(&related)); + path.push_str(&issuer.get_str_or_issue(related)); } } @@ -242,7 +245,7 @@ impl<'a> Normalizer<'a> { for related in recursion_list { let (result, mut issuer) = self.compute_n_degree_hash(&mut issuer, &related)?; path.push_str("_:"); - path.push_str(&issuer.get_or_issue(&related)); + path.push_str(&issuer.get_str_or_issue(related)); path.push('<'); path.push_str(&result); path.push('>'); @@ -327,38 +330,47 @@ impl<'a> Default for Normalizer<'a> { /// Canonical blank node identifier issuer, specified by: https://www.w3.org/TR/rdf-canon/#issue-identifier. #[derive(Clone, Eq, PartialEq, Debug)] -struct IdentifierIssuer { +pub struct IdentifierIssuer { prefix: String, - counter: u64, - issued: HashMap, + pub counter: u128, + issued: HashMap, issue_log: Vec, } impl IdentifierIssuer { - pub fn new(prefix: &str) -> Self { + pub fn new(prefix: &str, counter_offset: u128) -> Self { Self { prefix: prefix.to_string(), - counter: 0, + counter: counter_offset, issued: HashMap::new(), issue_log: Vec::new(), } } - pub fn get_or_issue(&mut self, identifier: &str) -> String { - match self.issued.entry(identifier.to_string()) { + pub fn get_or_issue(&mut self, identifier: String) -> (u128, String) { + match self.issued.entry(identifier.clone()) { Entry::Occupied(e) => e.get().clone(), Entry::Vacant(e) => { - let generated_id = format!("{}{}", self.prefix, self.counter); + let n = self.counter; + let str = format!("{}{}", self.prefix, n); self.counter += 1; - self.issue_log.push(identifier.to_string()); - e.insert(generated_id).clone() + self.issue_log.push(identifier); + e.insert((n, str)).clone() } } } + pub fn get_n_or_issue(&mut self, identifier: String) -> u128 { + self.get_or_issue(identifier).0 + } + + pub fn get_str_or_issue(&mut self, identifier: String) -> String { + self.get_or_issue(identifier).1 + } + pub fn get(&self, identifier: &str) -> Option<&str> { - self.issued.get(identifier).map(String::as_str) + self.issued.get(identifier).map(|(_, str)| str.as_str()) } pub fn issued(&self, identifier: &str) -> bool { From 1a492e7463c0e34288a2cbefded5488f3ca380f3 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Tue, 27 Feb 2024 22:11:13 +0100 Subject: [PATCH 03/22] feat(cognitarium): update triple state model with u128 as bnode --- contracts/okp4-cognitarium/src/querier/plan.rs | 10 ++++------ contracts/okp4-cognitarium/src/state/triples.rs | 14 ++++++-------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/contracts/okp4-cognitarium/src/querier/plan.rs b/contracts/okp4-cognitarium/src/querier/plan.rs index 41579240..de6719b3 100644 --- a/contracts/okp4-cognitarium/src/querier/plan.rs +++ b/contracts/okp4-cognitarium/src/querier/plan.rs @@ -155,16 +155,14 @@ mod tests { child: Box::new(QueryNode::ForLoopJoin { left: Box::new(QueryNode::CartesianProductJoin { left: Box::new(QueryNode::TriplePattern { - subject: PatternValue::Constant(Subject::Blank( - "_".to_string(), - )), - predicate: PatternValue::Variable(4usize), + subject: PatternValue::BlankVariable(4usize), + predicate: PatternValue::Variable(5usize), object: PatternValue::Variable(0usize), }), right: Box::new(QueryNode::TriplePattern { subject: PatternValue::Variable(3usize), predicate: PatternValue::Variable(1usize), - object: PatternValue::Constant(Object::Blank("_".to_string())), + object: PatternValue::BlankVariable(4usize), }), }), right: Box::new(QueryNode::TriplePattern { @@ -175,7 +173,7 @@ mod tests { }), }), }, - BTreeSet::from([0usize, 1usize, 2usize, 3usize, 4usize]), + BTreeSet::from([0usize, 1usize, 2usize, 3usize, 4usize, 5usize]), ), ]; diff --git a/contracts/okp4-cognitarium/src/state/triples.rs b/contracts/okp4-cognitarium/src/state/triples.rs index feabd2dc..f9f395e6 100644 --- a/contracts/okp4-cognitarium/src/state/triples.rs +++ b/contracts/okp4-cognitarium/src/state/triples.rs @@ -77,7 +77,7 @@ impl Subject { key } Subject::Blank(n) => { - let val = n.as_bytes(); + let val = n.to_be_bytes(); let mut key: Vec = Vec::with_capacity(val.len() + 1); key.push(b'b'); key.extend(val); @@ -108,7 +108,7 @@ impl Object { .update(n.value.as_bytes()); } Object::Blank(n) => { - hasher.update(&[b'b']).update(n.as_bytes()); + hasher.update(&[b'b']).update(n.to_be_bytes().as_slice()); } Object::Literal(l) => { hasher.update(&[b'l']); @@ -131,7 +131,8 @@ impl Object { } } -pub type BlankNode = String; +pub const BLANK_NODE_SIZE: usize = 16usize; +pub type BlankNode = u128; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub struct Node { @@ -191,10 +192,7 @@ mod test { value: "val".to_string(), }), ), - ( - Object::Blank("val1".to_string()), - Object::Blank("val2".to_string()), - ), + (Object::Blank(0u128), Object::Blank(1u128)), ( Object::Literal(Literal::Simple { value: "val1".to_string(), @@ -272,7 +270,7 @@ mod test { }), ), ( - Object::Blank("val".to_string()), + Object::Blank(0u128), Object::Literal(Literal::Simple { value: "val".to_string(), }), From 93acb9392032f10c273d5b02433251ec774be86d Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Tue, 27 Feb 2024 22:11:40 +0100 Subject: [PATCH 04/22] feat(cognitarium): generate bnode identifiers on query --- contracts/okp4-cognitarium/src/contract.rs | 15 +- .../okp4-cognitarium/src/querier/engine.rs | 147 ++++++++++-------- .../okp4-cognitarium/src/querier/mapper.rs | 26 +--- .../okp4-cognitarium/src/querier/variable.rs | 64 +++----- .../okp4-cognitarium/src/storer/engine.rs | 57 +++++-- 5 files changed, 155 insertions(+), 154 deletions(-) diff --git a/contracts/okp4-cognitarium/src/contract.rs b/contracts/okp4-cognitarium/src/contract.rs index af214d45..9a427169 100644 --- a/contracts/okp4-cognitarium/src/contract.rs +++ b/contracts/okp4-cognitarium/src/contract.rs @@ -319,6 +319,7 @@ pub mod util { }; use crate::querier::SelectResults; use crate::state::{Namespace, NamespaceResolver}; + use okp4_rdf::normalize::IdentifierIssuer; use std::collections::{BTreeMap, HashSet}; pub fn as_select_variables(patterns: &[TriplePattern]) -> Vec { @@ -347,6 +348,7 @@ pub mod util { ns_cache: Vec, ) -> StdResult { let mut ns_resolver: NamespaceResolver = ns_cache.into(); + let mut id_issuer = IdentifierIssuer::new("b", 0u128); let mut bindings: Vec> = vec![]; for solution in res.solutions { @@ -356,11 +358,14 @@ pub mod util { .map(|(name, var)| -> StdResult<(String, Value)> { Ok(( name, - var.as_value(&mut |ns_key| { - let res = ns_resolver.resolve_from_key(deps.storage, ns_key); - res.and_then(NamespaceResolver::none_as_error_middleware) - .map(|ns| ns.value) - })?, + var.as_value( + &mut |ns_key| { + let res = ns_resolver.resolve_from_key(deps.storage, ns_key); + res.and_then(NamespaceResolver::none_as_error_middleware) + .map(|ns| ns.value) + }, + &mut id_issuer, + )?, )) }) .collect::>>()?; diff --git a/contracts/okp4-cognitarium/src/querier/engine.rs b/contracts/okp4-cognitarium/src/querier/engine.rs index dc4617da..dd9e611a 100644 --- a/contracts/okp4-cognitarium/src/querier/engine.rs +++ b/contracts/okp4-cognitarium/src/querier/engine.rs @@ -1,7 +1,5 @@ -use crate::msg::{SelectItem, TriplePattern, VarOrNode, VarOrNodeOrLiteral}; -use crate::querier::mapper::{ - literal_as_object, node_as_object, node_as_predicate, node_as_subject, -}; +use crate::msg::{Node, SelectItem, TriplePattern, VarOrNode, VarOrNodeOrLiteral}; +use crate::querier::mapper::{iri_as_node, literal_as_object, node_as_predicate}; use crate::querier::plan::{PatternValue, QueryNode, QueryPlan}; use crate::querier::variable::{ResolvedVariable, ResolvedVariables}; use crate::rdf::Atom; @@ -9,6 +7,7 @@ use crate::state::{triples, Namespace, NamespaceResolver, Object, Predicate, Sub use crate::{rdf, state}; use cosmwasm_std::{Order, StdError, StdResult, Storage}; use either::{Either, Left, Right}; +use okp4_rdf::normalize::IdentifierIssuer; use std::collections::{BTreeMap, HashMap, VecDeque}; use std::iter; use std::rc::Rc; @@ -568,19 +567,19 @@ impl TripleTemplate { pattern: &TriplePattern, ) -> StdResult { Ok(TripleTemplate { - subject: Self::build_subject_pattern( + subject: Self::build_subject_template( ns_resolver, storage, prefixes, pattern.subject.clone(), )?, - predicate: Self::build_predicate_pattern( + predicate: Self::build_predicate_template( ns_resolver, storage, prefixes, pattern.predicate.clone(), )?, - object: Self::build_object_pattern( + object: Self::build_object_template( ns_resolver, storage, prefixes, @@ -645,7 +644,7 @@ impl TripleTemplate { } } - fn build_subject_pattern( + fn build_subject_template( ns_resolver: &mut NamespaceResolver, storage: &dyn Storage, prefixes: &HashMap, @@ -653,11 +652,17 @@ impl TripleTemplate { ) -> StdResult> { Ok(match value { VarOrNode::Variable(v) => Right(v), - VarOrNode::Node(n) => Left(node_as_subject(ns_resolver, storage, prefixes, n)?), + VarOrNode::Node(Node::BlankNode(b)) => Right(b), + VarOrNode::Node(Node::NamedNode(iri)) => Left(Subject::Named(iri_as_node( + ns_resolver, + storage, + prefixes, + iri, + )?)), }) } - fn build_predicate_pattern( + fn build_predicate_template( ns_resolver: &mut NamespaceResolver, storage: &dyn Storage, prefixes: &HashMap, @@ -669,7 +674,7 @@ impl TripleTemplate { }) } - fn build_object_pattern( + fn build_object_template( ns_resolver: &mut NamespaceResolver, storage: &dyn Storage, prefixes: &HashMap, @@ -677,7 +682,13 @@ impl TripleTemplate { ) -> StdResult> { Ok(match value { VarOrNodeOrLiteral::Variable(v) => Right(v), - VarOrNodeOrLiteral::Node(n) => Left(node_as_object(ns_resolver, storage, prefixes, n)?), + VarOrNodeOrLiteral::Node(Node::BlankNode(b)) => Right(b), + VarOrNodeOrLiteral::Node(Node::NamedNode(iri)) => Left(Object::Named(iri_as_node( + ns_resolver, + storage, + prefixes, + iri, + )?)), VarOrNodeOrLiteral::Literal(l) => { Left(literal_as_object(ns_resolver, storage, prefixes, l)?) } @@ -687,6 +698,7 @@ impl TripleTemplate { pub struct ResolvedAtomIterator<'a> { ns_resolver: &'a mut NamespaceResolver, + id_issuer: IdentifierIssuer, storage: &'a dyn Storage, iter: SolutionsIterator<'a>, templates: Vec, @@ -703,6 +715,7 @@ impl<'a> ResolvedAtomIterator<'a> { ) -> StdResult { Ok(Self { ns_resolver, + id_issuer: IdentifierIssuer::new("b", 0u128), storage, iter: solutions, templates: patterns @@ -733,11 +746,9 @@ impl<'a> Iterator for ResolvedAtomIterator<'a> { self.buffer.push_back(Err(err)); } Ok(vars) => { - for res in self - .templates - .iter() - .map(|template| template.resolve(self.ns_resolver, self.storage, &vars)) - { + for res in self.templates.iter().map(|template| { + template.resolve(self.ns_resolver, &mut self.id_issuer, self.storage, &vars) + }) { match res { Ok(Some(atom)) => self.buffer.push_back(Ok(atom)), Err(err) => self.buffer.push_back(Err(err)), @@ -781,6 +792,7 @@ impl AtomTemplate { pub fn resolve( &self, ns_resolver: &mut NamespaceResolver, + id_issuer: &mut IdentifierIssuer, storage: &dyn Storage, vars: &BTreeMap, ) -> StdResult> { @@ -790,7 +802,7 @@ impl AtomTemplate { .map(|ns| ns.value) }; - let subject = match self.resolve_atom_subject(resolve_ns_fn, vars)? { + let subject = match self.resolve_atom_subject(resolve_ns_fn, id_issuer, vars)? { Some(s) => s, None => return Ok(None), }; @@ -800,7 +812,7 @@ impl AtomTemplate { None => return Ok(None), }; - let value = match self.resolve_atom_value(resolve_ns_fn, vars)? { + let value = match self.resolve_atom_value(resolve_ns_fn, id_issuer, vars)? { Some(v) => v, None => return Ok(None), }; @@ -815,6 +827,7 @@ impl AtomTemplate { fn resolve_atom_subject( &self, resolve_ns_fn: &mut F, + id_issuer: &mut IdentifierIssuer, vars: &BTreeMap, ) -> StdResult> where @@ -827,7 +840,9 @@ impl AtomTemplate { &mut |value| { Ok(match value { Subject::Named(n) => rdf::Subject::NamedNode(n.as_iri(resolve_ns_fn)?), - Subject::Blank(n) => rdf::Subject::BlankNode(n), + Subject::Blank(n) => { + rdf::Subject::BlankNode(id_issuer.get_str_or_issue(n.to_string())) + } }) }, "subject", @@ -854,6 +869,7 @@ impl AtomTemplate { fn resolve_atom_value( &self, resolve_ns_fn: &mut F, + id_issuer: &mut IdentifierIssuer, vars: &BTreeMap, ) -> StdResult> where @@ -866,7 +882,9 @@ impl AtomTemplate { &mut |value| { Ok(match value { Object::Named(n) => rdf::Value::NamedNode(n.as_iri(resolve_ns_fn)?), - Object::Blank(n) => rdf::Value::BlankNode(n), + Object::Blank(n) => { + rdf::Value::BlankNode(id_issuer.get_str_or_issue(n.to_string())) + } Object::Literal(l) => match l { state::Literal::Simple { value } => rdf::Value::LiteralSimple(value), state::Literal::I18NString { value, language } => { @@ -917,7 +935,9 @@ mod test { use crate::querier::plan::PlanVariable; use crate::state; use crate::state::Object::{Literal, Named}; - use crate::state::{Node, Store, StoreStat, NAMESPACE_KEY_INCREMENT, STORE}; + use crate::state::{ + Node, Store, StoreStat, BLANK_NODE_IDENTIFIER_COUNTER, NAMESPACE_KEY_INCREMENT, STORE, + }; use crate::storer::StoreEngine; use cosmwasm_std::testing::mock_dependencies; use cosmwasm_std::{Addr, Uint128}; @@ -954,6 +974,7 @@ mod test { ) .unwrap(); NAMESPACE_KEY_INCREMENT.save(storage, &0u128).unwrap(); + BLANK_NODE_IDENTIFIER_COUNTER.save(storage, &0u128).unwrap(); let data = read_test_data("sample.rdf.xml"); let buf = BufReader::new(data.as_slice()); let mut reader = TripleReader::new(&okp4_rdf::serde::DataFormat::RDFXml, buf); @@ -1295,33 +1316,30 @@ mod test { #[test] fn for_loop_join_iter() { struct TestCase { - left: Vec, - right: Vec, - expects: Vec<(String, String)>, + left: Vec, + right: Vec, + expects: Vec<(u128, u128)>, } let cases = vec![ TestCase { left: vec![], - right: vec!["1".to_string(), "2".to_string()], + right: vec![0u128, 1u128], expects: vec![], }, TestCase { - left: vec!["A".to_string()], - right: vec!["1".to_string(), "2".to_string()], - expects: vec![ - ("A".to_string(), "1".to_string()), - ("A".to_string(), "2".to_string()), - ], + left: vec![2u128], + right: vec![0u128, 1u128], + expects: vec![(2u128, 0u128), (2u128, 1u128)], }, TestCase { - left: vec!["A".to_string(), "B".to_string()], - right: vec!["1".to_string(), "2".to_string()], + left: vec![2u128, 3u128], + right: vec![0u128, 1u128], expects: vec![ - ("A".to_string(), "1".to_string()), - ("A".to_string(), "2".to_string()), - ("B".to_string(), "1".to_string()), - ("B".to_string(), "2".to_string()), + (2u128, 0u128), + (2u128, 1u128), + (3u128, 0u128), + (3u128, 1u128), ], }, ]; @@ -1330,13 +1348,13 @@ mod test { let result = ForLoopJoinIterator::new( Box::new(case.left.iter().map(|v| { let mut vars = ResolvedVariables::with_capacity(3); - vars.merge_index(1, ResolvedVariable::Subject(Subject::Blank(v.clone()))); + vars.merge_index(1, ResolvedVariable::Subject(Subject::Blank(*v))); Ok(vars) })), Rc::new(|input| { Box::new(case.right.iter().map(move |v| { let mut vars = input.clone(); - vars.merge_index(2, ResolvedVariable::Subject(Subject::Blank(v.clone()))); + vars.merge_index(2, ResolvedVariable::Subject(Subject::Blank(*v))); Ok(vars) })) }), @@ -1349,8 +1367,8 @@ mod test { .iter() .map(|(v1, v2)| { let mut vars = ResolvedVariables::with_capacity(3); - vars.merge_index(1, ResolvedVariable::Subject(Subject::Blank(v1.clone()))); - vars.merge_index(2, ResolvedVariable::Subject(Subject::Blank(v2.clone()))); + vars.merge_index(1, ResolvedVariable::Subject(Subject::Blank(*v1))); + vars.merge_index(2, ResolvedVariable::Subject(Subject::Blank(*v2))); vars }) .collect(); @@ -1362,38 +1380,35 @@ mod test { #[test] fn cartesian_join_iter() { struct TestCase { - left: Vec, - right: Vec, - expects: Vec>, + left: Vec, + right: Vec, + expects: Vec>, } let cases = vec![ TestCase { left: vec![], - right: vec!["1".to_string(), "2".to_string()], + right: vec![0u128, 1u128], expects: vec![], }, TestCase { - left: vec!["1".to_string(), "2".to_string()], + left: vec![0u128, 1u128], right: vec![], expects: vec![], }, TestCase { - left: vec!["A".to_string()], - right: vec!["1".to_string(), "2".to_string()], - expects: vec![ - vec!["1".to_string(), "A".to_string()], - vec!["2".to_string(), "A".to_string()], - ], + left: vec![2u128], + right: vec![0u128, 1u128], + expects: vec![vec![0u128, 2u128], vec![1u128, 2u128]], }, TestCase { - left: vec!["A".to_string(), "B".to_string()], - right: vec!["1".to_string(), "2".to_string()], + left: vec![2u128, 3u128], + right: vec![0u128, 1u128], expects: vec![ - vec!["1".to_string(), "A".to_string()], - vec!["2".to_string(), "A".to_string()], - vec!["1".to_string(), "B".to_string()], - vec!["2".to_string(), "B".to_string()], + vec![0u128, 2u128], + vec![1u128, 2u128], + vec![0u128, 3u128], + vec![1u128, 3u128], ], }, ]; @@ -1404,13 +1419,13 @@ mod test { .iter() .map(|v| { let mut vars = ResolvedVariables::with_capacity(2); - vars.merge_index(0, ResolvedVariable::Subject(Subject::Blank(v.clone()))); + vars.merge_index(0, ResolvedVariable::Subject(Subject::Blank(*v))); vars }) .collect(), Box::new(case.left.iter().map(|v| { let mut vars = ResolvedVariables::with_capacity(2); - vars.merge_index(1, ResolvedVariable::Subject(Subject::Blank(v.clone()))); + vars.merge_index(1, ResolvedVariable::Subject(Subject::Blank(*v))); Ok(vars) })), VecDeque::new(), @@ -1424,10 +1439,10 @@ mod test { .map(|v| { let mut vars = ResolvedVariables::with_capacity(2); if let Some(val) = v.get(0) { - vars.merge_index(0, ResolvedVariable::Subject(Subject::Blank(val.clone()))); + vars.merge_index(0, ResolvedVariable::Subject(Subject::Blank(*val))); } if let Some(val) = v.get(1) { - vars.merge_index(1, ResolvedVariable::Subject(Subject::Blank(val.clone()))); + vars.merge_index(1, ResolvedVariable::Subject(Subject::Blank(*val))); } vars }) @@ -1439,12 +1454,12 @@ mod test { #[test] fn triple_pattern_iter_compute_io() { - let t_subject = Subject::Blank("s".to_string()); + let t_subject = Subject::Blank(0u128); let t_predicate = state::Node { namespace: 0u128, value: "whatever".to_string(), }; - let t_object = Object::Blank("o".to_string()); + let t_object = Object::Blank(1u128); let mut variables = ResolvedVariables::with_capacity(6); variables.merge_index(1, ResolvedVariable::Subject(t_subject.clone())); @@ -1523,7 +1538,7 @@ mod test { predicate: PatternValue::Variable(4), object: PatternValue::Variable(5), expects: Some(( - (Some(Subject::Blank("o".to_string())), None, None), + (Some(Subject::Blank(1u128)), None, None), (false, false), (None, Some(4), Some(5)), )), diff --git a/contracts/okp4-cognitarium/src/querier/mapper.rs b/contracts/okp4-cognitarium/src/querier/mapper.rs index c7785b52..6daf4098 100644 --- a/contracts/okp4-cognitarium/src/querier/mapper.rs +++ b/contracts/okp4-cognitarium/src/querier/mapper.rs @@ -1,22 +1,10 @@ use crate::msg::{Literal, Node, IRI}; use crate::state; -use crate::state::{NamespaceResolver, Object, Predicate, Subject}; +use crate::state::{NamespaceResolver, Object, Predicate}; use cosmwasm_std::{StdError, StdResult, Storage}; use okp4_rdf::uri::{expand_uri, explode_iri}; use std::collections::HashMap; -pub fn node_as_subject( - ns_resolver: &mut NamespaceResolver, - storage: &dyn Storage, - prefixes: &HashMap, - node: Node, -) -> StdResult { - Ok(match node { - Node::NamedNode(iri) => Subject::Named(iri_as_node(ns_resolver, storage, prefixes, iri)?), - Node::BlankNode(blank) => Subject::Blank(blank), - }) -} - pub fn node_as_predicate( ns_resolver: &mut NamespaceResolver, storage: &dyn Storage, @@ -31,18 +19,6 @@ pub fn node_as_predicate( } } -pub fn node_as_object( - ns_resolver: &mut NamespaceResolver, - storage: &dyn Storage, - prefixes: &HashMap, - node: Node, -) -> StdResult { - Ok(match node { - Node::NamedNode(iri) => Object::Named(iri_as_node(ns_resolver, storage, prefixes, iri)?), - Node::BlankNode(blank) => Object::Blank(blank), - }) -} - pub fn literal_as_object( ns_resolver: &mut NamespaceResolver, storage: &dyn Storage, diff --git a/contracts/okp4-cognitarium/src/querier/variable.rs b/contracts/okp4-cognitarium/src/querier/variable.rs index d9a37f47..1c04aa41 100644 --- a/contracts/okp4-cognitarium/src/querier/variable.rs +++ b/contracts/okp4-cognitarium/src/querier/variable.rs @@ -1,6 +1,7 @@ use crate::msg::{Value, IRI}; use crate::state::{Literal, Object, Predicate, Subject}; use cosmwasm_std::StdResult; +use okp4_rdf::normalize::IdentifierIssuer; #[derive(Eq, PartialEq, Debug, Clone)] pub enum ResolvedVariable { @@ -48,7 +49,7 @@ impl ResolvedVariable { }) } - pub fn as_value(&self, ns_fn: &mut F) -> StdResult + pub fn as_value(&self, ns_fn: &mut F, id_issuer: &mut IdentifierIssuer) -> StdResult where F: FnMut(u128) -> StdResult, { @@ -58,7 +59,7 @@ impl ResolvedVariable { value: IRI::Full(iri), })?, Subject::Blank(blank) => Value::BlankNode { - value: blank.to_string(), + value: id_issuer.get_str_or_issue(blank.to_string()), }, }, ResolvedVariable::Predicate(predicate) => { @@ -71,7 +72,7 @@ impl ResolvedVariable { value: IRI::Full(named.as_iri(ns_fn)?), }, Object::Blank(blank) => Value::BlankNode { - value: blank.to_string(), + value: id_issuer.get_str_or_issue(blank.to_string()), }, Object::Literal(literal) => match literal { Literal::Simple { value } => Value::Literal { @@ -155,9 +156,9 @@ mod tests { fn conversions() { let cases: Vec<(Option, Option, Option)> = vec![ ( - Some(Subject::Blank("_".to_string())), + Some(Subject::Blank(0u128)), None, - Some(Object::Blank("_".to_string())), + Some(Object::Blank(0u128)), ), ( Some(Subject::Named(Node { @@ -226,9 +227,9 @@ mod tests { }), ), ( - ResolvedVariable::Subject(Subject::Blank("_".to_string())), + ResolvedVariable::Subject(Subject::Blank(0u128)), Ok(Value::BlankNode { - value: "_".to_string(), + value: "b0".to_string(), }), ), ( @@ -250,9 +251,9 @@ mod tests { }), ), ( - ResolvedVariable::Object(Object::Blank("_".to_string())), + ResolvedVariable::Object(Object::Blank(0u128)), Ok(Value::BlankNode { - value: "_".to_string(), + value: "b0".to_string(), }), ), ( @@ -292,61 +293,38 @@ mod tests { ), ]; + let mut id_issuer = IdentifierIssuer::new("b", 0u128); for (var, expected) in cases { - assert_eq!(var.as_value(&mut ns), expected) + assert_eq!(var.as_value(&mut ns, &mut id_issuer), expected) } } #[test] fn merged_variables() { let mut vars1 = ResolvedVariables::with_capacity(3); - vars1.merge_index( - 0, - ResolvedVariable::Object(Object::Blank("foo".to_string())), - ); - vars1.merge_index( - 2, - ResolvedVariable::Object(Object::Blank("bar".to_string())), - ); + vars1.merge_index(0, ResolvedVariable::Object(Object::Blank(0u128))); + vars1.merge_index(2, ResolvedVariable::Object(Object::Blank(1u128))); let mut vars2 = ResolvedVariables::with_capacity(3); - vars2.merge_index( - 1, - ResolvedVariable::Object(Object::Blank("pop".to_string())), - ); - vars2.merge_index( - 2, - ResolvedVariable::Object(Object::Blank("bar".to_string())), - ); + vars2.merge_index(1, ResolvedVariable::Object(Object::Blank(2u128))); + vars2.merge_index(2, ResolvedVariable::Object(Object::Blank(1u128))); assert_eq!( vars2.get(1), - &Some(ResolvedVariable::Object(Object::Blank("pop".to_string()))) + &Some(ResolvedVariable::Object(Object::Blank(2u128))) ); assert_eq!(vars1.get(1), &None); let mut expected_result = ResolvedVariables::with_capacity(3); - expected_result.merge_index( - 0, - ResolvedVariable::Object(Object::Blank("foo".to_string())), - ); - expected_result.merge_index( - 1, - ResolvedVariable::Object(Object::Blank("pop".to_string())), - ); - expected_result.merge_index( - 2, - ResolvedVariable::Object(Object::Blank("bar".to_string())), - ); + expected_result.merge_index(0, ResolvedVariable::Object(Object::Blank(0u128))); + expected_result.merge_index(1, ResolvedVariable::Object(Object::Blank(2u128))); + expected_result.merge_index(2, ResolvedVariable::Object(Object::Blank(1u128))); let result = vars1.merge_with(&vars2); assert_eq!(result, Some(expected_result)); let mut vars3 = ResolvedVariables::with_capacity(3); - vars3.merge_index( - 1, - ResolvedVariable::Object(Object::Blank("pop".to_string())), - ); + vars3.merge_index(1, ResolvedVariable::Object(Object::Blank(2u128))); vars3.merge_index( 2, ResolvedVariable::Predicate(Node { diff --git a/contracts/okp4-cognitarium/src/storer/engine.rs b/contracts/okp4-cognitarium/src/storer/engine.rs index c8f480e9..71564f95 100644 --- a/contracts/okp4-cognitarium/src/storer/engine.rs +++ b/contracts/okp4-cognitarium/src/storer/engine.rs @@ -1,9 +1,11 @@ use crate::error::StoreError; use crate::state::{ - triples, Literal, NamespaceBatchService, Node, Object, Store, Subject, Triple, STORE, + triples, Literal, NamespaceBatchService, Node, Object, Store, Subject, Triple, + BLANK_NODE_IDENTIFIER_COUNTER, BLANK_NODE_SIZE, STORE, }; use crate::ContractError; use cosmwasm_std::{StdError, StdResult, Storage, Uint128}; +use okp4_rdf::normalize::IdentifierIssuer; use okp4_rdf::serde::TripleReader; use okp4_rdf::uri::explode_iri; use rio_api::model; @@ -15,6 +17,7 @@ pub struct StoreEngine<'a> { storage: &'a mut dyn Storage, store: Store, ns_batch_svc: NamespaceBatchService, + blank_node_id_issuer: IdentifierIssuer, initial_triple_count: Uint128, initial_byte_size: Uint128, } @@ -22,11 +25,13 @@ pub struct StoreEngine<'a> { impl<'a> StoreEngine<'a> { pub fn new(storage: &'a mut dyn Storage) -> StdResult { let store = STORE.load(storage)?; + let blank_node_id_counter = BLANK_NODE_IDENTIFIER_COUNTER.load(storage)?; let ns_batch_svc = NamespaceBatchService::new(storage)?; Ok(Self { storage, store: store.clone(), ns_batch_svc, + blank_node_id_issuer: IdentifierIssuer::new("", blank_node_id_counter), initial_triple_count: store.stat.triple_count, initial_byte_size: store.stat.byte_size, }) @@ -53,11 +58,15 @@ impl<'a> StoreEngine<'a> { ))?; } - let triple = Self::rio_to_triple(t, &mut |ns_str| { - self.ns_batch_svc - .resolve_or_allocate(self.storage, ns_str) - .map(|ns| ns.key) - })?; + let triple = Self::rio_to_triple( + t, + &mut |ns_str| { + self.ns_batch_svc + .resolve_or_allocate(self.storage, ns_str) + .map(|ns| ns.key) + }, + &mut self.blank_node_id_issuer, + )?; let t_size = Uint128::from(self.triple_size(&triple).map_err(ContractError::Std)? as u128); if t_size > self.store.limits.max_triple_byte_size { Err(StoreError::TripleByteSize( @@ -159,6 +168,8 @@ impl<'a> StoreEngine<'a> { self.store.stat.namespace_count -= Uint128::new(ns_diff.neg() as u128); } + BLANK_NODE_IDENTIFIER_COUNTER.save(self.storage, &self.blank_node_id_issuer.counter)?; + STORE.save(self.storage, &self.store)?; let count_diff = self @@ -173,24 +184,34 @@ impl<'a> StoreEngine<'a> { Ok(count_diff) } - fn rio_to_triple(triple: model::Triple<'_>, ns_fn: &mut F) -> StdResult + fn rio_to_triple( + triple: model::Triple<'_>, + ns_fn: &mut F, + id_issuer: &mut IdentifierIssuer, + ) -> StdResult where F: FnMut(String) -> StdResult, { Ok(Triple { - subject: Self::rio_to_subject(triple.subject, ns_fn)?, + subject: Self::rio_to_subject(triple.subject, ns_fn, id_issuer)?, predicate: Self::rio_to_node(triple.predicate, ns_fn)?, - object: Self::rio_to_object(triple.object, ns_fn)?, + object: Self::rio_to_object(triple.object, ns_fn, id_issuer)?, }) } - fn rio_to_subject(subject: model::Subject<'_>, ns_fn: &mut F) -> StdResult + fn rio_to_subject( + subject: model::Subject<'_>, + ns_fn: &mut F, + id_issuer: &mut IdentifierIssuer, + ) -> StdResult where F: FnMut(String) -> StdResult, { match subject { model::Subject::NamedNode(node) => Self::rio_to_node(node, ns_fn).map(Subject::Named), - model::Subject::BlankNode(node) => Ok(Subject::Blank(node.id.to_string())), + model::Subject::BlankNode(node) => Ok(Subject::Blank( + id_issuer.get_n_or_issue(node.id.to_string()), + )), model::Subject::Triple(_) => Err(StdError::generic_err("RDF star syntax unsupported")), } } @@ -206,12 +227,18 @@ impl<'a> StoreEngine<'a> { }) } - fn rio_to_object(object: Term<'_>, ns_fn: &mut F) -> StdResult + fn rio_to_object( + object: Term<'_>, + ns_fn: &mut F, + id_issuer: &mut IdentifierIssuer, + ) -> StdResult where F: FnMut(String) -> StdResult, { match object { - Term::BlankNode(node) => Ok(Object::Blank(node.id.to_string())), + Term::BlankNode(node) => { + Ok(Object::Blank(id_issuer.get_n_or_issue(node.id.to_string()))) + } Term::NamedNode(node) => Self::rio_to_node(node, ns_fn).map(Object::Named), Term::Literal(literal) => Self::rio_to_literal(literal, ns_fn).map(Object::Literal), Term::Triple(_) => Err(StdError::generic_err("RDF star syntax unsupported")), @@ -248,7 +275,7 @@ impl<'a> StoreEngine<'a> { fn subject_size(&mut self, subject: &Subject) -> StdResult { match subject { Subject::Named(n) => self.node_size(n), - Subject::Blank(n) => Ok(n.len()), + Subject::Blank(_) => Ok(BLANK_NODE_SIZE), } } @@ -266,7 +293,7 @@ impl<'a> StoreEngine<'a> { fn object_size(&mut self, object: &Object) -> StdResult { Ok(match object { - Object::Blank(n) => n.len(), + Object::Blank(_) => BLANK_NODE_SIZE, Object::Named(n) => self.node_size(n)?, Object::Literal(l) => match l { Literal::Simple { value } => value.len(), From 7dfb48ebf8c2ae1fa69616f9de957d3576889fb7 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Tue, 27 Feb 2024 22:55:16 +0100 Subject: [PATCH 05/22] feat(cognitarium)!: rework delete input to remove template bnodes --- contracts/okp4-cognitarium/src/contract.rs | 58 +++----- contracts/okp4-cognitarium/src/msg.rs | 134 +++++++++++++++--- .../okp4-cognitarium/src/querier/engine.rs | 64 ++++----- 3 files changed, 160 insertions(+), 96 deletions(-) diff --git a/contracts/okp4-cognitarium/src/contract.rs b/contracts/okp4-cognitarium/src/contract.rs index 9a427169..7eb124d9 100644 --- a/contracts/okp4-cognitarium/src/contract.rs +++ b/contracts/okp4-cognitarium/src/contract.rs @@ -50,7 +50,9 @@ pub fn execute( pub mod execute { use super::*; - use crate::msg::{DataFormat, Prefix, TriplePattern, WhereClause}; + use crate::msg::{ + DataFormat, HasVariables, Prefix, TripleDeleteTemplate, TriplePattern, WhereClause, + }; use crate::querier::{PlanBuilder, QueryEngine}; use crate::rdf::PrefixMap; use crate::state::HasCachedNamespaces; @@ -88,28 +90,31 @@ pub mod execute { deps: DepsMut<'_>, info: MessageInfo, prefixes: Vec, - delete: Vec, + delete: Vec, r#where: WhereClause, ) -> Result { verify_owner(&deps, &info)?; - let patterns: Vec = if delete.is_empty() { - util::as_triple_patterns(&r#where)? - } else { - delete - }; + if delete.is_empty() { + return Ok(Response::new() + .add_attribute("action", "delete") + .add_attribute("triple_count", 0)); + } let prefix_map = ::from(prefixes).into_inner(); let mut plan_builder = PlanBuilder::new(deps.storage, &prefix_map, None); let plan = plan_builder.build_plan(&r#where)?; let triples = QueryEngine::new(deps.storage) - .select(plan, util::as_select_variables(&patterns))? + .select(plan, delete.as_select_item())? .solutions .resolve_triples( deps.storage, &prefix_map, - patterns, + delete + .into_iter() + .map(|t| (t.subject, t.predicate, t.object)) + .collect(), plan_builder.cached_namespaces(), )?; @@ -141,9 +146,9 @@ pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> StdResult { pub mod query { use super::*; use crate::msg::{ - ConstructQuery, ConstructResponse, DescribeQuery, DescribeResponse, Node, SelectQuery, - SelectResponse, SimpleWhereCondition, StoreResponse, TriplePattern, VarOrNamedNode, - VarOrNode, VarOrNodeOrLiteral, WhereCondition, + ConstructQuery, ConstructResponse, DescribeQuery, DescribeResponse, HasVariables, Node, + SelectQuery, SelectResponse, SimpleWhereCondition, StoreResponse, TriplePattern, + VarOrNamedNode, VarOrNode, VarOrNodeOrLiteral, WhereCondition, }; use crate::querier::{PlanBuilder, QueryEngine}; use crate::rdf::PrefixMap; @@ -277,7 +282,7 @@ pub mod query { let plan = plan_builder.build_plan(&r#where)?; let atoms = QueryEngine::new(deps.storage) - .select(plan, util::as_select_variables(&patterns)) + .select(plan, patterns.as_select_item()) .and_then(|res| { res.solutions.resolve_atoms( deps.storage, @@ -313,34 +318,11 @@ pub mod query { pub mod util { use super::*; - use crate::msg::{ - Head, Results, SelectItem, SelectResponse, SimpleWhereCondition, TriplePattern, Value, - WhereCondition, - }; + use crate::msg::{Head, Results, SelectResponse, Value}; use crate::querier::SelectResults; use crate::state::{Namespace, NamespaceResolver}; use okp4_rdf::normalize::IdentifierIssuer; - use std::collections::{BTreeMap, HashSet}; - - pub fn as_select_variables(patterns: &[TriplePattern]) -> Vec { - let variables = patterns - .iter() - .flat_map(TriplePattern::variables) - .collect::>() - .into_iter() - .map(SelectItem::Variable) - .collect(); - variables - } - - pub fn as_triple_patterns(r#where: &[WhereCondition]) -> StdResult> { - r#where - .iter() - .map(|c| match c { - WhereCondition::Simple(SimpleWhereCondition::TriplePattern(tp)) => Ok(tp.clone()), - }) - .collect::>() - } + use std::collections::BTreeMap; pub fn map_select_solutions( deps: Deps<'_>, diff --git a/contracts/okp4-cognitarium/src/msg.rs b/contracts/okp4-cognitarium/src/msg.rs index 5fc2c077..570e3173 100644 --- a/contracts/okp4-cognitarium/src/msg.rs +++ b/contracts/okp4-cognitarium/src/msg.rs @@ -1,7 +1,7 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Binary, Uint128}; use derive_builder::Builder; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashSet}; /// Instantiate message #[cw_serde] @@ -67,9 +67,8 @@ pub enum ExecuteMsg { DeleteData { /// The prefixes used in the operation. prefixes: Vec, - /// Specifies the specific triple patterns to delete. - /// If nothing is provided, the patterns from the `where` clause are used for deletion. - delete: Vec, + /// Specifies the specific triple templates to delete. + delete: Vec, /// Defines the patterns that data (RDF triples) should match in order for it to be /// considered for deletion. r#where: WhereClause, @@ -484,6 +483,44 @@ pub enum SimpleWhereCondition { TriplePattern(TriplePattern), } +pub trait HasVariables { + /// Returns the set of variables used in a triple pattern or template. + fn variables(&self) -> Vec; + + /// Returns the set of variables used in a triple pattern or template as [SelectItem]. + fn as_select_item(&self) -> Vec { + self.variables().iter().map(SelectItem::Variable).collect() + } +} + +impl HasVariables for Vec { + fn variables(&self) -> Vec { + self.iter() + .flat_map(|t| t.variables()) + .collect::>() + .into_iter() + .collect() + } +} + +/// # TripleDeleteTemplate +/// Represents a triple template to be deleted. +#[cw_serde] +pub struct TripleDeleteTemplate { + /// The subject of the triple pattern. + pub subject: VarOrNamedNode, + /// The predicate of the triple pattern. + pub predicate: VarOrNamedNode, + /// The object of the triple pattern. + pub object: VarOrNamedNodeOrLiteral, +} + +impl HasVariables for TripleDeleteTemplate { + fn variables(&self) -> Vec { + var_util::merge_variables(&[&self.subject, &self.predicate, &self.object]) + } +} + /// # TriplePattern /// Represents a triple pattern in a [SimpleWhereCondition]. #[cw_serde] @@ -496,24 +533,28 @@ pub struct TriplePattern { pub object: VarOrNodeOrLiteral, } -impl TriplePattern { - /// Returns the variables used in the triple pattern. - pub fn variables(&self) -> Vec { - let mut variables: Vec = vec![]; - - if let VarOrNode::Variable(var) = &self.subject { - variables.push(var.clone()); - } +impl HasVariables for TriplePattern { + fn variables(&self) -> Vec { + var_util::merge_variables(&[&self.subject, &self.predicate, &self.object]) + } +} - if let VarOrNode::Variable(var) = &self.predicate { - variables.push(var.clone()); - } +trait MaybeVariable { + fn variable(&self) -> Option<&str>; +} - if let VarOrNodeOrLiteral::Variable(var) = &self.object { - variables.push(var.clone()); - } +mod var_util { + use crate::msg::MaybeVariable; + use std::collections::HashSet; - variables + pub fn merge_variables(maybe_vars: &[impl MaybeVariable]) -> Vec { + maybe_vars + .iter() + .map(MaybeVariable::variable) + .filter_map(|maybe_var| maybe_var) + .collect::>() + .into_iter() + .collect() } } @@ -529,6 +570,15 @@ pub enum VarOrNode { Node(Node), } +impl MaybeVariable for VarOrNode { + fn variable(&self) -> Option<&str> { + match self { + Self::Variable(v) => Some(v.as_str()), + _ => None, + } + } +} + /// # VarOrNamedNode { /// Represents either a variable or a named node (IRI). #[cw_serde] @@ -541,6 +591,15 @@ pub enum VarOrNamedNode { NamedNode(IRI), } +impl MaybeVariable for VarOrNamedNode { + fn variable(&self) -> Option<&str> { + match self { + Self::Variable(v) => Some(v.as_str()), + _ => None, + } + } +} + /// # VarOrNodeOrLiteral /// Represents either a variable, a node or a literal. #[cw_serde] @@ -557,6 +616,40 @@ pub enum VarOrNodeOrLiteral { Literal(Literal), } +impl MaybeVariable for VarOrNodeOrLiteral { + fn variable(&self) -> Option<&str> { + match self { + Self::Variable(v) => Some(v.as_str()), + _ => None, + } + } +} + +/// # VarOrNamedNodeOrLiteral +/// Represents either a variable, a named node or a literal. +#[cw_serde] +pub enum VarOrNamedNodeOrLiteral { + /// # Variable + /// A variable. + Variable(String), + /// # NamedNode + /// An RDF [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri). + NamedNode(IRI), + /// # Literal + /// An RDF [literal](https://www.w3.org/TR/rdf11-concepts/#dfn-literal), i.e. a simple literal, + /// a language-tagged string or a typed value. + Literal(Literal), +} + +impl MaybeVariable for VarOrNamedNodeOrLiteral { + fn variable(&self) -> Option<&str> { + match self { + Self::Variable(v) => Some(v.as_str()), + _ => None, + } + } +} + /// # Literal /// An RDF [literal](https://www.w3.org/TR/rdf11-concepts/#dfn-literal). #[cw_serde] @@ -600,7 +693,8 @@ mod tests { use crate::msg::Node::{BlankNode, NamedNode}; use crate::msg::IRI::{Full, Prefixed}; use crate::msg::{ - InstantiateMsg, StoreLimitsInput, TriplePattern, VarOrNode, VarOrNodeOrLiteral, + HasVariables, InstantiateMsg, StoreLimitsInput, TriplePattern, VarOrNode, + VarOrNodeOrLiteral, }; use cosmwasm_std::Uint128; use schemars::_serde_json; diff --git a/contracts/okp4-cognitarium/src/querier/engine.rs b/contracts/okp4-cognitarium/src/querier/engine.rs index dd9e611a..b7f1c1c5 100644 --- a/contracts/okp4-cognitarium/src/querier/engine.rs +++ b/contracts/okp4-cognitarium/src/querier/engine.rs @@ -1,4 +1,7 @@ -use crate::msg::{Node, SelectItem, TriplePattern, VarOrNode, VarOrNodeOrLiteral}; +use crate::msg::{ + Node, SelectItem, TriplePattern, VarOrNamedNode, VarOrNamedNodeOrLiteral, VarOrNode, + VarOrNodeOrLiteral, +}; use crate::querier::mapper::{iri_as_node, literal_as_object, node_as_predicate}; use crate::querier::plan::{PatternValue, QueryNode, QueryPlan}; use crate::querier::variable::{ResolvedVariable, ResolvedVariables}; @@ -440,13 +443,13 @@ impl<'a> SolutionsIterator<'a> { self, storage: &dyn Storage, prefixes: &HashMap, - patterns: Vec, + templates: Vec<(VarOrNamedNode, VarOrNamedNode, VarOrNamedNodeOrLiteral)>, ns_cache: Vec, ) -> StdResult> { let mut ns_resolver = ns_cache.into(); let triples_iter = - ResolvedTripleIterator::try_new(&mut ns_resolver, storage, self, prefixes, patterns)?; + ResolvedTripleIterator::try_new(&mut ns_resolver, storage, self, prefixes, templates)?; triples_iter.collect() } @@ -504,13 +507,13 @@ impl<'a> ResolvedTripleIterator<'a> { storage: &dyn Storage, solutions: SolutionsIterator<'a>, prefixes: &HashMap, - patterns: Vec, + templates: Vec<(VarOrNamedNode, VarOrNamedNode, VarOrNamedNodeOrLiteral)>, ) -> StdResult { Ok(Self { iter: solutions, - templates: patterns - .iter() - .map(|p| TripleTemplate::try_new(ns_resolver, storage, prefixes, p)) + templates: templates + .into_iter() + .map(|t| TripleTemplate::try_new(ns_resolver, storage, prefixes, t)) .collect::>>()?, buffer: VecDeque::new(), }) @@ -564,27 +567,12 @@ impl TripleTemplate { ns_resolver: &mut NamespaceResolver, storage: &dyn Storage, prefixes: &HashMap, - pattern: &TriplePattern, + (s_tpl, p_tpl, o_tpl): (VarOrNamedNode, VarOrNamedNode, VarOrNamedNodeOrLiteral), ) -> StdResult { Ok(TripleTemplate { - subject: Self::build_subject_template( - ns_resolver, - storage, - prefixes, - pattern.subject.clone(), - )?, - predicate: Self::build_predicate_template( - ns_resolver, - storage, - prefixes, - pattern.predicate.clone(), - )?, - object: Self::build_object_template( - ns_resolver, - storage, - prefixes, - pattern.object.clone(), - )?, + subject: Self::build_subject_template(ns_resolver, storage, prefixes, s_tpl)?, + predicate: Self::build_predicate_template(ns_resolver, storage, prefixes, p_tpl)?, + object: Self::build_object_template(ns_resolver, storage, prefixes, o_tpl)?, }) } @@ -648,12 +636,11 @@ impl TripleTemplate { ns_resolver: &mut NamespaceResolver, storage: &dyn Storage, prefixes: &HashMap, - value: VarOrNode, + value: VarOrNamedNode, ) -> StdResult> { Ok(match value { - VarOrNode::Variable(v) => Right(v), - VarOrNode::Node(Node::BlankNode(b)) => Right(b), - VarOrNode::Node(Node::NamedNode(iri)) => Left(Subject::Named(iri_as_node( + VarOrNamedNode::Variable(v) => Right(v), + VarOrNamedNode::NamedNode(iri) => Left(Subject::Named(iri_as_node( ns_resolver, storage, prefixes, @@ -666,11 +653,13 @@ impl TripleTemplate { ns_resolver: &mut NamespaceResolver, storage: &dyn Storage, prefixes: &HashMap, - value: VarOrNode, + value: VarOrNamedNode, ) -> StdResult> { Ok(match value { - VarOrNode::Variable(v) => Right(v), - VarOrNode::Node(n) => Left(node_as_predicate(ns_resolver, storage, prefixes, n)?), + VarOrNamedNode::Variable(v) => Right(v), + VarOrNamedNode::NamedNode(iri) => { + Left(iri_as_node(ns_resolver, storage, prefixes, iri)?) + } }) } @@ -678,18 +667,17 @@ impl TripleTemplate { ns_resolver: &mut NamespaceResolver, storage: &dyn Storage, prefixes: &HashMap, - value: VarOrNodeOrLiteral, + value: VarOrNamedNodeOrLiteral, ) -> StdResult> { Ok(match value { - VarOrNodeOrLiteral::Variable(v) => Right(v), - VarOrNodeOrLiteral::Node(Node::BlankNode(b)) => Right(b), - VarOrNodeOrLiteral::Node(Node::NamedNode(iri)) => Left(Object::Named(iri_as_node( + VarOrNamedNodeOrLiteral::Variable(v) => Right(v), + VarOrNamedNodeOrLiteral::NamedNode(iri) => Left(Object::Named(iri_as_node( ns_resolver, storage, prefixes, iri, )?)), - VarOrNodeOrLiteral::Literal(l) => { + VarOrNamedNodeOrLiteral::Literal(l) => { Left(literal_as_object(ns_resolver, storage, prefixes, l)?) } }) From a194d1c8bc86219df5b75710fe7001707b51219c Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Tue, 27 Feb 2024 23:01:16 +0100 Subject: [PATCH 06/22] refactor(cognitarium)!: use var or named node as msg pattern predicate --- contracts/okp4-cognitarium/src/contract.rs | 4 +- contracts/okp4-cognitarium/src/msg.rs | 2 +- .../okp4-cognitarium/src/querier/engine.rs | 4 +- .../okp4-cognitarium/src/querier/mapper.rs | 20 +----- .../src/querier/plan_builder.rs | 71 ++++++++----------- 5 files changed, 39 insertions(+), 62 deletions(-) diff --git a/contracts/okp4-cognitarium/src/contract.rs b/contracts/okp4-cognitarium/src/contract.rs index 7eb124d9..5af05a51 100644 --- a/contracts/okp4-cognitarium/src/contract.rs +++ b/contracts/okp4-cognitarium/src/contract.rs @@ -196,7 +196,7 @@ pub mod query { VarOrNamedNode::Variable(var) => { let select = TriplePattern { subject: VarOrNode::Variable(var.clone()), - predicate: VarOrNode::Variable(format!("{var}{p}")), + predicate: VarOrNamedNode::Variable(format!("{var}{p}")), object: VarOrNodeOrLiteral::Variable(format!("{var}{o}")), }; @@ -210,7 +210,7 @@ pub mod query { VarOrNamedNode::NamedNode(iri) => { let select = TriplePattern { subject: VarOrNode::Node(Node::NamedNode(iri.clone())), - predicate: VarOrNode::Variable(p), + predicate: VarOrNamedNode::Variable(p), object: VarOrNodeOrLiteral::Variable(o), }; diff --git a/contracts/okp4-cognitarium/src/msg.rs b/contracts/okp4-cognitarium/src/msg.rs index 570e3173..52b6bd25 100644 --- a/contracts/okp4-cognitarium/src/msg.rs +++ b/contracts/okp4-cognitarium/src/msg.rs @@ -528,7 +528,7 @@ pub struct TriplePattern { /// The subject of the triple pattern. pub subject: VarOrNode, /// The predicate of the triple pattern. - pub predicate: VarOrNode, + pub predicate: VarOrNamedNode, /// The object of the triple pattern. pub object: VarOrNodeOrLiteral, } diff --git a/contracts/okp4-cognitarium/src/querier/engine.rs b/contracts/okp4-cognitarium/src/querier/engine.rs index b7f1c1c5..0414b8de 100644 --- a/contracts/okp4-cognitarium/src/querier/engine.rs +++ b/contracts/okp4-cognitarium/src/querier/engine.rs @@ -1,8 +1,8 @@ use crate::msg::{ - Node, SelectItem, TriplePattern, VarOrNamedNode, VarOrNamedNodeOrLiteral, VarOrNode, + SelectItem, TriplePattern, VarOrNamedNode, VarOrNamedNodeOrLiteral, VarOrNode, VarOrNodeOrLiteral, }; -use crate::querier::mapper::{iri_as_node, literal_as_object, node_as_predicate}; +use crate::querier::mapper::{iri_as_node, literal_as_object}; use crate::querier::plan::{PatternValue, QueryNode, QueryPlan}; use crate::querier::variable::{ResolvedVariable, ResolvedVariables}; use crate::rdf::Atom; diff --git a/contracts/okp4-cognitarium/src/querier/mapper.rs b/contracts/okp4-cognitarium/src/querier/mapper.rs index 6daf4098..df6c7796 100644 --- a/contracts/okp4-cognitarium/src/querier/mapper.rs +++ b/contracts/okp4-cognitarium/src/querier/mapper.rs @@ -1,24 +1,10 @@ -use crate::msg::{Literal, Node, IRI}; +use crate::msg::{Literal, IRI}; use crate::state; -use crate::state::{NamespaceResolver, Object, Predicate}; -use cosmwasm_std::{StdError, StdResult, Storage}; +use crate::state::{NamespaceResolver, Object}; +use cosmwasm_std::{StdResult, Storage}; use okp4_rdf::uri::{expand_uri, explode_iri}; use std::collections::HashMap; -pub fn node_as_predicate( - ns_resolver: &mut NamespaceResolver, - storage: &dyn Storage, - prefixes: &HashMap, - node: Node, -) -> StdResult { - match node { - Node::NamedNode(iri) => iri_as_node(ns_resolver, storage, prefixes, iri), - Node::BlankNode(_) => Err(StdError::generic_err( - "Predicate pattern must be a named node", - )), - } -} - pub fn literal_as_object( ns_resolver: &mut NamespaceResolver, storage: &dyn Storage, diff --git a/contracts/okp4-cognitarium/src/querier/plan_builder.rs b/contracts/okp4-cognitarium/src/querier/plan_builder.rs index 52da74a1..7b67a72b 100644 --- a/contracts/okp4-cognitarium/src/querier/plan_builder.rs +++ b/contracts/okp4-cognitarium/src/querier/plan_builder.rs @@ -1,8 +1,8 @@ use crate::msg::{ - Node, SimpleWhereCondition, TriplePattern, VarOrNode, VarOrNodeOrLiteral, WhereClause, - WhereCondition, + Node, SimpleWhereCondition, TriplePattern, VarOrNamedNode, VarOrNode, VarOrNodeOrLiteral, + WhereClause, WhereCondition, }; -use crate::querier::mapper::{iri_as_node, literal_as_object, node_as_predicate}; +use crate::querier::mapper::{iri_as_node, literal_as_object}; use crate::querier::plan::{PatternValue, PlanVariable, QueryNode, QueryPlan}; use crate::state::{HasCachedNamespaces, Namespace, NamespaceResolver, Object, Predicate, Subject}; use cosmwasm_std::{StdError, StdResult, Storage}; @@ -152,14 +152,17 @@ impl<'a> PlanBuilder<'a> { }) } - fn build_predicate_pattern(&mut self, value: VarOrNode) -> StdResult> { + fn build_predicate_pattern( + &mut self, + value: VarOrNamedNode, + ) -> StdResult> { Ok(match value { - VarOrNode::Variable(v) => PatternValue::Variable(self.resolve_basic_variable(v)), - VarOrNode::Node(n) => PatternValue::Constant(node_as_predicate( + VarOrNamedNode::Variable(v) => PatternValue::Variable(self.resolve_basic_variable(v)), + VarOrNamedNode::NamedNode(iri) => PatternValue::Constant(iri_as_node( &mut self.ns_resolver, self.storage, self.prefixes, - n, + iri, )?), }) } @@ -317,7 +320,7 @@ mod test { ( TriplePattern { subject: VarOrNode::Variable("s".to_string()), - predicate: VarOrNode::Variable("p".to_string()), + predicate: VarOrNamedNode::Variable("p".to_string()), object: VarOrNodeOrLiteral::Variable("o".to_string()), }, Ok(QueryNode::TriplePattern { @@ -329,9 +332,9 @@ mod test { ( TriplePattern { subject: VarOrNode::Node(Node::BlankNode("1".to_string())), - predicate: VarOrNode::Node(Node::NamedNode(IRI::Full( + predicate: VarOrNamedNode::NamedNode(IRI::Full( "http://okp4.space/hasTitle".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Node(Node::BlankNode("2".to_string())), }, Ok(QueryNode::TriplePattern { @@ -348,7 +351,7 @@ mod test { subject: VarOrNode::Node(Node::NamedNode(IRI::Full( "http://okp4.space/123456789".to_string(), ))), - predicate: VarOrNode::Variable("p".to_string()), + predicate: VarOrNamedNode::Variable("p".to_string()), object: VarOrNodeOrLiteral::Node(Node::NamedNode(IRI::Full( "http://okp4.space/1234567892".to_string(), ))), @@ -368,7 +371,7 @@ mod test { ( TriplePattern { subject: VarOrNode::Variable("p".to_string()), - predicate: VarOrNode::Variable("s".to_string()), + predicate: VarOrNamedNode::Variable("s".to_string()), object: VarOrNodeOrLiteral::Literal(Literal::Simple("simple".to_string())), }, Ok(QueryNode::TriplePattern { @@ -382,7 +385,7 @@ mod test { ( TriplePattern { subject: VarOrNode::Variable("s".to_string()), - predicate: VarOrNode::Variable("p".to_string()), + predicate: VarOrNamedNode::Variable("p".to_string()), object: VarOrNodeOrLiteral::Literal(Literal::LanguageTaggedString { value: "tagged".to_string(), language: "en".to_string(), @@ -400,7 +403,7 @@ mod test { ( TriplePattern { subject: VarOrNode::Variable("s".to_string()), - predicate: VarOrNode::Variable("p".to_string()), + predicate: VarOrNamedNode::Variable("p".to_string()), object: VarOrNodeOrLiteral::Literal(Literal::TypedValue { value: "typed".to_string(), datatype: IRI::Full("http://okp4.space/type".to_string()), @@ -423,7 +426,7 @@ mod test { subject: VarOrNode::Node(Node::NamedNode(IRI::Full( "notexisting#outch".to_string(), ))), - predicate: VarOrNode::Variable("p".to_string()), + predicate: VarOrNamedNode::Variable("p".to_string()), object: VarOrNodeOrLiteral::Variable("o".to_string()), }, Ok(QueryNode::Noop { @@ -433,9 +436,9 @@ mod test { ( TriplePattern { subject: VarOrNode::Variable("s".to_string()), - predicate: VarOrNode::Node(Node::NamedNode(IRI::Full( + predicate: VarOrNamedNode::NamedNode(IRI::Full( "notexisting#outch".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Variable("o".to_string()), }, Ok(QueryNode::Noop { @@ -445,7 +448,7 @@ mod test { ( TriplePattern { subject: VarOrNode::Variable("s".to_string()), - predicate: VarOrNode::Variable("p".to_string()), + predicate: VarOrNamedNode::Variable("p".to_string()), object: VarOrNodeOrLiteral::Node(Node::NamedNode(IRI::Full( "notexisting#outch".to_string(), ))), @@ -485,18 +488,6 @@ mod test { vec![], Err(StdError::generic_err("Empty basic graph pattern")), ), - ( - None, - None, - vec![TriplePattern { - subject: VarOrNode::Variable("subject".to_string()), - predicate: VarOrNode::Node(Node::BlankNode("_".to_string())), - object: VarOrNodeOrLiteral::Variable("object".to_string()), - }], - Err(StdError::generic_err( - "Predicate pattern must be a named node", - )), - ), ( None, None, @@ -504,7 +495,7 @@ mod test { subject: VarOrNode::Node(Node::NamedNode(IRI::Full( "notexisting#outch".to_string(), ))), - predicate: VarOrNode::Variable("predicate".to_string()), + predicate: VarOrNamedNode::Variable("predicate".to_string()), object: VarOrNodeOrLiteral::Variable("object".to_string()), }], Ok(QueryPlan { @@ -522,7 +513,7 @@ mod test { None, vec![TriplePattern { subject: VarOrNode::Variable("subject".to_string()), - predicate: VarOrNode::Variable("predicate".to_string()), + predicate: VarOrNamedNode::Variable("predicate".to_string()), object: VarOrNodeOrLiteral::Variable("object".to_string()), }], Ok(QueryPlan { @@ -543,7 +534,7 @@ mod test { None, vec![TriplePattern { subject: VarOrNode::Variable("subject".to_string()), - predicate: VarOrNode::Variable("predicate".to_string()), + predicate: VarOrNamedNode::Variable("predicate".to_string()), object: VarOrNodeOrLiteral::Variable("object".to_string()), }], Ok(QueryPlan { @@ -567,7 +558,7 @@ mod test { Some(20usize), vec![TriplePattern { subject: VarOrNode::Variable("subject".to_string()), - predicate: VarOrNode::Variable("predicate".to_string()), + predicate: VarOrNamedNode::Variable("predicate".to_string()), object: VarOrNodeOrLiteral::Variable("object".to_string()), }], Ok(QueryPlan { @@ -591,7 +582,7 @@ mod test { Some(50usize), vec![TriplePattern { subject: VarOrNode::Variable("subject".to_string()), - predicate: VarOrNode::Variable("predicate".to_string()), + predicate: VarOrNamedNode::Variable("predicate".to_string()), object: VarOrNodeOrLiteral::Variable("object".to_string()), }], Ok(QueryPlan { @@ -619,17 +610,17 @@ mod test { vec![ TriplePattern { subject: VarOrNode::Variable("var1".to_string()), - predicate: VarOrNode::Variable("var2".to_string()), + predicate: VarOrNamedNode::Variable("var2".to_string()), object: VarOrNodeOrLiteral::Variable("var3".to_string()), }, TriplePattern { subject: VarOrNode::Variable("var4".to_string()), - predicate: VarOrNode::Variable("var5".to_string()), + predicate: VarOrNamedNode::Variable("var5".to_string()), object: VarOrNodeOrLiteral::Variable("var6".to_string()), }, TriplePattern { subject: VarOrNode::Variable("var1".to_string()), - predicate: VarOrNode::Variable("var5".to_string()), + predicate: VarOrNamedNode::Variable("var5".to_string()), object: VarOrNodeOrLiteral::Node(Node::BlankNode("blank".to_string())), }, ], @@ -670,12 +661,12 @@ mod test { vec![ TriplePattern { subject: VarOrNode::Node(Node::BlankNode("1".to_string())), - predicate: VarOrNode::Variable("1".to_string()), + predicate: VarOrNamedNode::Variable("1".to_string()), object: VarOrNodeOrLiteral::Node(Node::BlankNode("2".to_string())), }, TriplePattern { subject: VarOrNode::Node(Node::BlankNode("1".to_string())), - predicate: VarOrNode::Variable("1".to_string()), + predicate: VarOrNamedNode::Variable("1".to_string()), object: VarOrNodeOrLiteral::Variable("2".to_string()), }, ], From f15d8228c8a92efcb8532152e15eaffabe71fe23 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 28 Feb 2024 00:10:41 +0100 Subject: [PATCH 07/22] feat(cognitarium)!: rework construct clause based on template --- contracts/okp4-cognitarium/src/contract.rs | 327 +++++++++--------- contracts/okp4-cognitarium/src/msg.rs | 63 +++- .../okp4-cognitarium/src/querier/engine.rs | 27 +- contracts/okp4-cognitarium/src/rdf/mapper.rs | 15 +- 4 files changed, 229 insertions(+), 203 deletions(-) diff --git a/contracts/okp4-cognitarium/src/contract.rs b/contracts/okp4-cognitarium/src/contract.rs index 5af05a51..8da4b633 100644 --- a/contracts/okp4-cognitarium/src/contract.rs +++ b/contracts/okp4-cognitarium/src/contract.rs @@ -50,13 +50,12 @@ pub fn execute( pub mod execute { use super::*; - use crate::msg::{ - DataFormat, HasVariables, Prefix, TripleDeleteTemplate, TriplePattern, WhereClause, - }; + use crate::msg::{DataFormat, HasVariables, Prefix, TripleDeleteTemplate, WhereClause}; use crate::querier::{PlanBuilder, QueryEngine}; use crate::rdf::PrefixMap; use crate::state::HasCachedNamespaces; use crate::storer::StoreEngine; + use cosmwasm_std::Uint128; use okp4_rdf::serde::TripleReader; use std::io::BufReader; @@ -98,7 +97,7 @@ pub mod execute { if delete.is_empty() { return Ok(Response::new() .add_attribute("action", "delete") - .add_attribute("triple_count", 0)); + .add_attribute("triple_count", Uint128::from(0u128))); } let prefix_map = ::from(prefixes).into_inner(); @@ -228,12 +227,15 @@ pub mod query { let plan = plan_builder.build_plan(&r#where)?; let atoms = QueryEngine::new(deps.storage) - .select(plan, util::as_select_variables(&select)) + .select(plan, select.as_select_item()) .and_then(|res| { res.solutions.resolve_atoms( deps.storage, &prefix_map, - select, + select + .into_iter() + .map(|p| (p.subject, p.predicate, p.object)) + .collect(), plan_builder.cached_namespaces(), ) })?; @@ -271,23 +273,27 @@ pub mod query { prefixes, r#where, } = query; - let patterns: Vec = if construct.is_empty() { - util::as_triple_patterns(&r#where)? - } else { - construct - }; + if construct.is_empty() { + return Ok(ConstructResponse { + format, + data: Binary::from(vec![]), + }); + } let prefix_map = ::from(prefixes).into_inner(); let mut plan_builder = PlanBuilder::new(deps.storage, &prefix_map, None); let plan = plan_builder.build_plan(&r#where)?; let atoms = QueryEngine::new(deps.storage) - .select(plan, patterns.as_select_item()) + .select(plan, construct.as_select_item()) .and_then(|res| { res.solutions.resolve_atoms( deps.storage, &prefix_map, - patterns, + construct + .into_iter() + .map(|t| (t.subject, t.predicate, t.object)) + .collect(), plan_builder.cached_namespaces(), ) })?; @@ -372,8 +378,8 @@ mod tests { use crate::msg::{ ConstructQuery, ConstructResponse, DescribeQuery, DescribeResponse, Head, Literal, Prefix, Results, SelectItem, SelectQuery, SelectResponse, StoreLimitsInput, - StoreLimitsInputBuilder, StoreResponse, Value, VarOrNamedNode, VarOrNode, - VarOrNodeOrLiteral, WhereCondition, + StoreLimitsInputBuilder, StoreResponse, Value, VarOrNamedNode, VarOrNamedNodeOrLiteral, + VarOrNode, VarOrNodeOrLiteral, WhereCondition, }; use crate::state::{ namespaces, triples, Namespace, Node, Object, StoreLimits, StoreStat, Subject, Triple, @@ -756,26 +762,26 @@ mod tests { ( DeleteData { prefixes: vec![], - delete: vec![msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Full( + delete: vec![msg::TripleDeleteTemplate { + subject: VarOrNamedNode::NamedNode(Full( "https://ontology.okp4.space/dataverse/dataspace/metadata/unknown" .to_string(), - ))), - predicate: VarOrNode::Node(NamedNode(Full( + )), + predicate: VarOrNamedNode::NamedNode(Full( "https://ontology.okp4.space/core/hasTopic".to_string(), - ))), - object: VarOrNodeOrLiteral::Node(NamedNode(Full( + )), + object: VarOrNamedNodeOrLiteral::NamedNode(Full( "https://ontology.okp4.space/thesaurus/topic/Test".to_string(), - ))), + )), }], r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { subject: VarOrNode::Node(NamedNode(Full( "https://ontology.okp4.space/dataverse/dataspace/metadata/unknown" .to_string(), ))), - predicate: VarOrNode::Node(NamedNode(Full( + predicate: VarOrNamedNode::NamedNode(Full( "https://ontology.okp4.space/core/hasTopic".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Node(NamedNode(Full( "https://ontology.okp4.space/thesaurus/topic/Test".to_string(), ))), @@ -788,20 +794,20 @@ mod tests { ( DeleteData { prefixes: vec![], - delete: vec![msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - predicate: VarOrNode::Node(NamedNode(Full( + delete: vec![msg::TripleDeleteTemplate { + subject: VarOrNamedNode::NamedNode(Full(id.to_string())), + predicate: VarOrNamedNode::NamedNode(Full( "https://ontology.okp4.space/core/hasTopic".to_string(), - ))), - object: VarOrNodeOrLiteral::Node(NamedNode(Full( + )), + object: VarOrNamedNodeOrLiteral::NamedNode(Full( "https://ontology.okp4.space/thesaurus/topic/Test".to_string(), - ))), + )), }], r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - predicate: VarOrNode::Node(NamedNode(Full( + predicate: VarOrNamedNode::NamedNode(Full( "https://ontology.okp4.space/core/hasTopic".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Node(NamedNode(Full( "https://ontology.okp4.space/thesaurus/topic/Test".to_string(), ))), @@ -823,20 +829,16 @@ mod tests { namespace: "https://ontology.okp4.space/thesaurus/topic/".to_string(), }, ], - delete: vec![msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - predicate: VarOrNode::Node(NamedNode(Prefixed( - "core:hasTopic".to_string(), - ))), - object: VarOrNodeOrLiteral::Node(NamedNode(Prefixed( + delete: vec![msg::TripleDeleteTemplate { + subject: VarOrNamedNode::NamedNode(Full(id.to_string())), + predicate: VarOrNamedNode::NamedNode(Prefixed("core:hasTopic".to_string())), + object: VarOrNamedNodeOrLiteral::NamedNode(Prefixed( "thesaurus:Test".to_string(), - ))), + )), }], r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - predicate: VarOrNode::Node(NamedNode(Prefixed( - "core:hasTopic".to_string(), - ))), + predicate: VarOrNamedNode::NamedNode(Prefixed("core:hasTopic".to_string())), object: VarOrNodeOrLiteral::Node(NamedNode(Prefixed( "thesaurus:Test".to_string(), ))), @@ -858,18 +860,14 @@ mod tests { namespace: "https://ontology.okp4.space/thesaurus/topic/".to_string(), }, ], - delete: vec![msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - predicate: VarOrNode::Node(NamedNode(Prefixed( - "core:hasTopic".to_string(), - ))), - object: VarOrNodeOrLiteral::Variable("o".to_string()), + delete: vec![msg::TripleDeleteTemplate { + subject: VarOrNamedNode::NamedNode(Full(id.to_string())), + predicate: VarOrNamedNode::NamedNode(Prefixed("core:hasTopic".to_string())), + object: VarOrNamedNodeOrLiteral::Variable("o".to_string()), }], r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - predicate: VarOrNode::Node(NamedNode(Prefixed( - "core:hasTopic".to_string(), - ))), + predicate: VarOrNamedNode::NamedNode(Prefixed("core:hasTopic".to_string())), object: VarOrNodeOrLiteral::Variable("o".to_string()), }))], }, @@ -880,14 +878,14 @@ mod tests { ( DeleteData { prefixes: vec![], - delete: vec![msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - predicate: VarOrNode::Variable("p".to_string()), - object: VarOrNodeOrLiteral::Variable("o".to_string()), + delete: vec![msg::TripleDeleteTemplate { + subject: VarOrNamedNode::NamedNode(Full(id.to_string())), + predicate: VarOrNamedNode::Variable("p".to_string()), + object: VarOrNamedNodeOrLiteral::Variable("o".to_string()), }], r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - predicate: VarOrNode::Variable("p".to_string()), + predicate: VarOrNamedNode::Variable("p".to_string()), object: VarOrNodeOrLiteral::Variable("o".to_string()), }))], }, @@ -895,34 +893,34 @@ mod tests { 2, Uint128::from(5272u128), ), - ( - DeleteData { - prefixes: vec![], - delete: vec![], - r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - predicate: VarOrNode::Variable("p".to_string()), - object: VarOrNodeOrLiteral::Variable("o".to_string()), - }))], - }, - 11, - 2, - Uint128::from(5272u128), - ), - ( - DeleteData { - prefixes: vec![], - delete: vec![], - r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { - subject: VarOrNode::Variable("s".to_string()), - predicate: VarOrNode::Variable("p".to_string()), - object: VarOrNodeOrLiteral::Variable("0".to_string()), - }))], - }, - 40, - 17, - Uint128::from(0u128), - ), + // ( + // DeleteData { + // prefixes: vec![], + // delete: vec![], + // r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { + // subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), + // predicate: VarOrNamedNode::Variable("p".to_string()), + // object: VarOrNodeOrLiteral::Variable("o".to_string()), + // }))], + // }, + // 11, + // 2, + // Uint128::from(5272u128), + // ), + // ( + // DeleteData { + // prefixes: vec![], + // delete: vec![], + // r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { + // subject: VarOrNode::Variable("s".to_string()), + // predicate: VarOrNamedNode::Variable("p".to_string()), + // object: VarOrNodeOrLiteral::Variable("0".to_string()), + // }))], + // }, + // 40, + // 17, + // Uint128::from(0u128), + // ), ]; for case in cases { @@ -992,20 +990,20 @@ mod tests { TC { command: DeleteData { prefixes: vec![], - delete: vec![msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Prefixed("foo:bar".to_string()))), - predicate: VarOrNode::Node(NamedNode(Full( + delete: vec![msg::TripleDeleteTemplate { + subject: VarOrNamedNode::NamedNode(Prefixed("foo:bar".to_string())), + predicate: VarOrNamedNode::NamedNode(Full( "https://ontology.okp4.space/core/hasTopic".to_string(), - ))), - object: VarOrNodeOrLiteral::Node(NamedNode(Full( + )), + object: VarOrNamedNodeOrLiteral::NamedNode(Full( "https://ontology.okp4.space/thesaurus/topic/Test".to_string(), - ))), + )), }], r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { subject: VarOrNode::Node(NamedNode(Prefixed("foo:bar".to_string()))), - predicate: VarOrNode::Node(NamedNode(Full( + predicate: VarOrNamedNode::NamedNode(Full( "https://ontology.okp4.space/core/hasTopic".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Node(NamedNode(Full( "https://ontology.okp4.space/thesaurus/topic/Test".to_string(), ))), @@ -1016,18 +1014,18 @@ mod tests { TC { command: DeleteData { prefixes: vec![], - delete: vec![msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Full( + delete: vec![msg::TripleDeleteTemplate { + subject: VarOrNamedNode::NamedNode(Full( "https://ontology.okp4.space/thesaurus/topic/Test".to_string(), - ))), - predicate: VarOrNode::Variable("z".to_string()), - object: VarOrNodeOrLiteral::Variable("o".to_string()), + )), + predicate: VarOrNamedNode::Variable("z".to_string()), + object: VarOrNamedNodeOrLiteral::Variable("o".to_string()), }], r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { subject: VarOrNode::Node(NamedNode(Full( "https://ontology.okp4.space/thesaurus/topic/Test".to_string(), ))), - predicate: VarOrNode::Variable("p".to_string()), + predicate: VarOrNamedNode::Variable("p".to_string()), object: VarOrNodeOrLiteral::Variable("o".to_string()), }))], }, @@ -1036,16 +1034,16 @@ mod tests { TC { command: DeleteData { prefixes: vec![], - delete: vec![msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Full( + delete: vec![msg::TripleDeleteTemplate { + subject: VarOrNamedNode::NamedNode(Full( "https://ontology.okp4.space/thesaurus/topic/Test".to_string(), - ))), - predicate: VarOrNode::Node(NamedNode(Full( + )), + predicate: VarOrNamedNode::NamedNode(Full( "https://ontology.okp4.space/core/hasTopic".to_string(), - ))), - object: VarOrNodeOrLiteral::Node(NamedNode(Full( + )), + object: VarOrNamedNodeOrLiteral::NamedNode(Full( "https://ontology.okp4.space/thesaurus/topic/Test".to_string(), - ))), + )), }], r#where: vec![], }, @@ -1161,9 +1159,9 @@ mod tests { r#where: vec![WhereCondition::Simple(TriplePattern( msg::TriplePattern { subject: VarOrNode::Variable("a".to_string()), - predicate: VarOrNode::Node(NamedNode(Full( + predicate: VarOrNamedNode::NamedNode(Full( "https://ontology.okp4.space/core/hasDescription".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Variable("b".to_string()), }, ))], @@ -1252,9 +1250,9 @@ mod tests { r#where: vec![WhereCondition::Simple(TriplePattern( msg::TriplePattern { subject: VarOrNode::Variable("a".to_string()), - predicate: VarOrNode::Node(NamedNode(Prefixed( + predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasDescription".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Literal(Literal::LanguageTaggedString { value: "A test Dataset.".to_string(), language: "en".to_string() }), }, ))], @@ -1287,7 +1285,7 @@ mod tests { r#where: vec![WhereCondition::Simple(TriplePattern( msg::TriplePattern { subject: VarOrNode::Node(NamedNode(Full("https://ontology.okp4.space/dataverse/dataset/metadata/d1615703-4ee1-4e2f-997e-15aecf1eea4e".to_string()))), - predicate: VarOrNode::Variable("a".to_string()), + predicate: VarOrNamedNode::Variable("a".to_string()), object: VarOrNodeOrLiteral::Literal(Literal::LanguageTaggedString { value: "A test Dataset.".to_string(), language: "en".to_string() }), }, ))], @@ -1355,18 +1353,18 @@ mod tests { WhereCondition::Simple(TriplePattern( msg::TriplePattern { subject: VarOrNode::Variable("a".to_string()), - predicate: VarOrNode::Node(NamedNode(Prefixed( + predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasTemporalCoverage".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Node(BlankNode("a".to_string())), }, )), WhereCondition::Simple(TriplePattern( msg::TriplePattern { subject: VarOrNode::Node(BlankNode("a".to_string())), - predicate: VarOrNode::Node(NamedNode(Prefixed( + predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasStartDate".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Variable("b".to_string()), }, ))], @@ -1404,18 +1402,18 @@ mod tests { WhereCondition::Simple(TriplePattern( msg::TriplePattern { subject: VarOrNode::Variable("a".to_string()), - predicate: VarOrNode::Node(NamedNode(Prefixed( + predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasTemporalCoverage".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Variable("blank".to_string()), }, )), WhereCondition::Simple(TriplePattern( msg::TriplePattern { subject: VarOrNode::Variable("blank".to_string()), - predicate: VarOrNode::Node(NamedNode(Prefixed( + predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasStartDate".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Variable("b".to_string()), }, ))], @@ -1453,18 +1451,18 @@ mod tests { WhereCondition::Simple(TriplePattern( msg::TriplePattern { subject: VarOrNode::Variable("a".to_string()), - predicate: VarOrNode::Node(NamedNode(Prefixed( + predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasTemporalCoverage".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Node(BlankNode("blank1".to_string())), }, )), WhereCondition::Simple(TriplePattern( msg::TriplePattern { subject: VarOrNode::Node(BlankNode("blank2".to_string())), - predicate: VarOrNode::Node(NamedNode(Prefixed( + predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasInformation".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Variable("b".to_string()), }, ))], @@ -1502,9 +1500,9 @@ mod tests { WhereCondition::Simple(TriplePattern( msg::TriplePattern { subject: VarOrNode::Variable("a".to_string()), - predicate: VarOrNode::Node(NamedNode(Prefixed( + predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasTemporalCoverage".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Variable("b".to_string()), }, ))], @@ -1524,7 +1522,7 @@ mod tests { ( "b".to_string(), Value::BlankNode { - value: "riog00000001".to_string(), + value: "b0".to_string(), } ) ]) @@ -1600,9 +1598,9 @@ mod tests { select: vec![SelectItem::Variable("a".to_string())], r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { subject: VarOrNode::Variable("a".to_string()), - predicate: VarOrNode::Node(NamedNode(Prefixed( + predicate: VarOrNamedNode::NamedNode(Prefixed( "invalid:hasDescription".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Literal(Literal::LanguageTaggedString { value: "A test Dataset.".to_string(), language: "en".to_string(), @@ -1618,9 +1616,9 @@ mod tests { select: vec![SelectItem::Variable("u".to_string())], r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { subject: VarOrNode::Variable("a".to_string()), - predicate: VarOrNode::Node(NamedNode(Full( + predicate: VarOrNamedNode::NamedNode(Full( "https://ontology.okp4.space/core/hasDescription".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Literal(Literal::LanguageTaggedString { value: "A test Dataset.".to_string(), language: "en".to_string(), @@ -1730,9 +1728,9 @@ mod tests { r#where: vec![WhereCondition::Simple(TriplePattern( msg::TriplePattern { subject: VarOrNode::Variable("a".to_string()), - predicate: VarOrNode::Node(NamedNode(Full( + predicate: VarOrNamedNode::NamedNode(Full( "https://ontology.okp4.space/core/hasDescription".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Variable("b".to_string()), }, ))], @@ -1904,9 +1902,9 @@ mod tests { r#where: vec![WhereCondition::Simple(TriplePattern( msg::TriplePattern { subject: VarOrNode::Variable("a".to_string()), - predicate: VarOrNode::Node(NamedNode(Prefixed( + predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasDescription".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Literal(Literal::LanguageTaggedString { value: "A test Dataset.".to_string(), language: "en".to_string() }), }, ))], @@ -1969,9 +1967,9 @@ mod tests { r#where: vec![WhereCondition::Simple(TriplePattern( msg::TriplePattern { subject: VarOrNode::Variable("a".to_string()), - predicate: VarOrNode::Node(NamedNode(Prefixed( + predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasPublisher".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Literal(Literal::Simple("OKP4".to_string())), }, ))], @@ -2037,9 +2035,9 @@ mod tests { r#where: vec![WhereCondition::Simple(TriplePattern( msg::TriplePattern { subject: VarOrNode::Node(NamedNode(Prefixed("metadata-dataset:80b1f84e-86dc-4730-b54f-701ad9b1888a".to_string()))), - predicate: VarOrNode::Node(NamedNode(Prefixed( + predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasTemporalCoverage".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Variable("x".to_string()), }, )), @@ -2050,7 +2048,7 @@ mod tests { DescribeResponse { format: DataFormat::Turtle, data: Binary::from( - " , ;\n\t \"2022-01-01T00:00:00+00:00\"^^ .\n".to_string().as_bytes().to_vec()), + " , ;\n\t \"2022-01-01T00:00:00+00:00\"^^ .\n".to_string().as_bytes().to_vec()), } ), ]; @@ -2095,27 +2093,28 @@ mod tests { #[test] fn proper_construct() { let id = "https://ontology.okp4.space/dataverse/dataspace/metadata/dcf48417-01c5-4b43-9bc7-49e54c028473"; - let cases = vec![( - QueryMsg::Construct { - query: ConstructQuery { - prefixes: vec![], - construct: vec![], - r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - predicate: VarOrNode::Node(NamedNode(Full( - "https://ontology.okp4.space/core/hasTag".to_string(), - ))), - object: VarOrNodeOrLiteral::Variable("o".to_string()), - }))], - }, - format: None, - }, - ConstructResponse { - format: DataFormat::Turtle, - data: Binary::from( - " \"Test\" , \"OKP4\" .\n".to_string().as_bytes().to_vec()), - }, - ), + let cases = vec![ + // ( + // QueryMsg::Construct { + // query: ConstructQuery { + // prefixes: vec![], + // construct: vec![], + // r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { + // subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), + // predicate: VarOrNamedNode::NamedNode(Full( + // "https://ontology.okp4.space/core/hasTag".to_string(), + // )), + // object: VarOrNodeOrLiteral::Variable("o".to_string()), + // }))], + // }, + // format: None, + // }, + // ConstructResponse { + // format: DataFormat::Turtle, + // data: Binary::from( + // " \"Test\" , \"OKP4\" .\n".to_string().as_bytes().to_vec()), + // }, + // ), ( QueryMsg::Construct { query: ConstructQuery { @@ -2124,19 +2123,19 @@ mod tests { Prefix { prefix: "metadata-dataset".to_string(), namespace: "https://ontology.okp4.space/dataverse/dataset/metadata/".to_string() }, ], construct: vec![ - msg::TriplePattern { + msg::TripleConstructTemplate { subject: VarOrNode::Node(NamedNode(Prefixed("my-ns:instance-1".to_string()))), - predicate: VarOrNode::Node(NamedNode(Full( + predicate: VarOrNamedNode::NamedNode(Full( "https://my-ns/predicate/tag".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Variable("o".to_string()), } ], r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - predicate: VarOrNode::Node(NamedNode(Full( + predicate: VarOrNamedNode::NamedNode(Full( "https://ontology.okp4.space/core/hasTag".to_string(), - ))), + )), object: VarOrNodeOrLiteral::Variable("o".to_string()), }))], }, diff --git a/contracts/okp4-cognitarium/src/msg.rs b/contracts/okp4-cognitarium/src/msg.rs index 52b6bd25..c0b5a87c 100644 --- a/contracts/okp4-cognitarium/src/msg.rs +++ b/contracts/okp4-cognitarium/src/msg.rs @@ -434,8 +434,7 @@ pub struct ConstructQuery { /// The prefixes used in the query. pub prefixes: Vec, /// The triples to construct. - /// If nothing is provided, the patterns from the `where` clause are used for construction. - pub construct: Vec, + pub construct: Vec, /// The WHERE clause. /// This clause is used to specify the triples to construct using variable bindings. pub r#where: WhereClause, @@ -489,7 +488,10 @@ pub trait HasVariables { /// Returns the set of variables used in a triple pattern or template as [SelectItem]. fn as_select_item(&self) -> Vec { - self.variables().iter().map(SelectItem::Variable).collect() + self.variables() + .into_iter() + .map(SelectItem::Variable) + .collect() } } @@ -517,7 +519,33 @@ pub struct TripleDeleteTemplate { impl HasVariables for TripleDeleteTemplate { fn variables(&self) -> Vec { - var_util::merge_variables(&[&self.subject, &self.predicate, &self.object]) + var_util::merge_variables(&[ + self.subject.variable(), + self.predicate.variable(), + self.object.variable(), + ]) + } +} + +/// # TripleConstructTemplate +/// Represents a triple template to be forged for a construct query. +#[cw_serde] +pub struct TripleConstructTemplate { + /// The subject of the triple pattern. + pub subject: VarOrNode, + /// The predicate of the triple pattern. + pub predicate: VarOrNamedNode, + /// The object of the triple pattern. + pub object: VarOrNodeOrLiteral, +} + +impl HasVariables for TripleConstructTemplate { + fn variables(&self) -> Vec { + var_util::merge_variables(&[ + self.subject.variable(), + self.predicate.variable(), + self.object.variable(), + ]) } } @@ -535,7 +563,11 @@ pub struct TriplePattern { impl HasVariables for TriplePattern { fn variables(&self) -> Vec { - var_util::merge_variables(&[&self.subject, &self.predicate, &self.object]) + var_util::merge_variables(&[ + self.subject.variable(), + self.predicate.variable(), + self.object.variable(), + ]) } } @@ -544,16 +576,16 @@ trait MaybeVariable { } mod var_util { - use crate::msg::MaybeVariable; use std::collections::HashSet; - pub fn merge_variables(maybe_vars: &[impl MaybeVariable]) -> Vec { + pub fn merge_variables(maybe_vars: &[Option<&str>]) -> Vec { maybe_vars .iter() - .map(MaybeVariable::variable) + .copied() .filter_map(|maybe_var| maybe_var) .collect::>() .into_iter() + .map(str::to_string) .collect() } } @@ -689,6 +721,7 @@ pub enum Node { #[cfg(test)] mod tests { + use super::*; use crate::msg::Literal::Simple; use crate::msg::Node::{BlankNode, NamedNode}; use crate::msg::IRI::{Full, Prefixed}; @@ -744,7 +777,7 @@ mod tests { ( TriplePattern { subject: VarOrNode::Variable(s.clone()), - predicate: VarOrNode::Variable(p.clone()), + predicate: VarOrNamedNode::Variable(p.clone()), object: VarOrNodeOrLiteral::Variable(o.clone()), }, vec![s.clone(), p.clone(), o.clone()], @@ -752,7 +785,7 @@ mod tests { ( TriplePattern { subject: VarOrNode::Node(NamedNode(Full(node.clone()))), - predicate: VarOrNode::Variable(p.clone()), + predicate: VarOrNamedNode::Variable(p.clone()), object: VarOrNodeOrLiteral::Variable(o.clone()), }, vec![p.clone(), o.clone()], @@ -760,7 +793,7 @@ mod tests { ( TriplePattern { subject: VarOrNode::Node(NamedNode(Prefixed(prefixed.clone()))), - predicate: VarOrNode::Variable(p.clone()), + predicate: VarOrNamedNode::Variable(p.clone()), object: VarOrNodeOrLiteral::Variable(o.clone()), }, vec![p.clone(), o.clone()], @@ -768,7 +801,7 @@ mod tests { ( TriplePattern { subject: VarOrNode::Node(BlankNode(node.clone())), - predicate: VarOrNode::Variable(p.clone()), + predicate: VarOrNamedNode::Variable(p.clone()), object: VarOrNodeOrLiteral::Variable(o.clone()), }, vec![p.clone(), o.clone()], @@ -776,7 +809,7 @@ mod tests { ( TriplePattern { subject: VarOrNode::Variable(s.clone()), - predicate: VarOrNode::Node(NamedNode(Full(node.clone()))), + predicate: VarOrNamedNode::NamedNode(Full(node.clone())), object: VarOrNodeOrLiteral::Variable(o.clone()), }, vec![s.clone(), o], @@ -784,7 +817,7 @@ mod tests { ( TriplePattern { subject: VarOrNode::Variable(s.clone()), - predicate: VarOrNode::Variable(p.clone()), + predicate: VarOrNamedNode::Variable(p.clone()), object: VarOrNodeOrLiteral::Literal(Simple(literal.clone())), }, vec![s, p], @@ -792,7 +825,7 @@ mod tests { ( TriplePattern { subject: VarOrNode::Node(BlankNode(node)), - predicate: VarOrNode::Node(NamedNode(Prefixed(prefixed))), + predicate: VarOrNamedNode::NamedNode(Prefixed(prefixed)), object: VarOrNodeOrLiteral::Literal(Simple(literal)), }, vec![], diff --git a/contracts/okp4-cognitarium/src/querier/engine.rs b/contracts/okp4-cognitarium/src/querier/engine.rs index 0414b8de..03b95767 100644 --- a/contracts/okp4-cognitarium/src/querier/engine.rs +++ b/contracts/okp4-cognitarium/src/querier/engine.rs @@ -1,6 +1,5 @@ use crate::msg::{ - SelectItem, TriplePattern, VarOrNamedNode, VarOrNamedNodeOrLiteral, VarOrNode, - VarOrNodeOrLiteral, + SelectItem, VarOrNamedNode, VarOrNamedNodeOrLiteral, VarOrNode, VarOrNodeOrLiteral, }; use crate::querier::mapper::{iri_as_node, literal_as_object}; use crate::querier::plan::{PatternValue, QueryNode, QueryPlan}; @@ -457,13 +456,13 @@ impl<'a> SolutionsIterator<'a> { self, storage: &dyn Storage, prefixes: &HashMap, - patterns: Vec, + templates: Vec<(VarOrNode, VarOrNamedNode, VarOrNodeOrLiteral)>, ns_cache: Vec, ) -> StdResult> { let mut ns_resolver = ns_cache.into(); let atoms_iter = - ResolvedAtomIterator::try_new(&mut ns_resolver, storage, self, prefixes, patterns)?; + ResolvedAtomIterator::try_new(&mut ns_resolver, storage, self, prefixes, templates)?; atoms_iter.collect() } } @@ -699,16 +698,16 @@ impl<'a> ResolvedAtomIterator<'a> { storage: &'a dyn Storage, solutions: SolutionsIterator<'a>, prefixes: &HashMap, - patterns: Vec, + templates: Vec<(VarOrNode, VarOrNamedNode, VarOrNodeOrLiteral)>, ) -> StdResult { Ok(Self { ns_resolver, id_issuer: IdentifierIssuer::new("b", 0u128), storage, iter: solutions, - templates: patterns - .iter() - .map(|p| AtomTemplate::try_new(prefixes, p)) + templates: templates + .into_iter() + .map(|t| AtomTemplate::try_new(prefixes, t)) .collect::>>()?, buffer: VecDeque::new(), }) @@ -758,18 +757,18 @@ struct AtomTemplate { impl AtomTemplate { pub fn try_new( prefixes: &HashMap, - pattern: &TriplePattern, + (s_tpl, p_tpl, o_tpl): (VarOrNode, VarOrNamedNode, VarOrNodeOrLiteral), ) -> StdResult { Ok(Self { - subject: match &pattern.subject { + subject: match s_tpl { VarOrNode::Variable(key) => Right(key.clone()), VarOrNode::Node(n) => Left((n.clone(), prefixes).try_into()?), }, - property: match &pattern.predicate { - VarOrNode::Variable(key) => Right(key.clone()), - VarOrNode::Node(n) => Left((n.clone(), prefixes).try_into()?), + property: match p_tpl { + VarOrNamedNode::Variable(key) => Right(key), + VarOrNamedNode::NamedNode(iri) => Left((iri, prefixes).try_into()?), }, - value: match &pattern.object { + value: match o_tpl { VarOrNodeOrLiteral::Variable(key) => Right(key.clone()), VarOrNodeOrLiteral::Node(n) => Left((n.clone(), prefixes).try_into()?), VarOrNodeOrLiteral::Literal(l) => Left((l.clone(), prefixes).try_into()?), diff --git a/contracts/okp4-cognitarium/src/rdf/mapper.rs b/contracts/okp4-cognitarium/src/rdf/mapper.rs index 968bf1c2..95977462 100644 --- a/contracts/okp4-cognitarium/src/rdf/mapper.rs +++ b/contracts/okp4-cognitarium/src/rdf/mapper.rs @@ -20,20 +20,15 @@ impl TryFrom<(msg::Node, &HashMap)> for Subject { } } -impl TryFrom<(msg::Node, &HashMap)> for Property { +impl TryFrom<(msg::IRI, &HashMap)> for Property { type Error = StdError; fn try_from( - (node, prefixes): (msg::Node, &HashMap), + (iri, prefixes): (msg::IRI, &HashMap), ) -> Result { - match node { - msg::Node::NamedNode(msg::IRI::Full(uri)) => Ok(Property(uri)), - msg::Node::NamedNode(msg::IRI::Prefixed(curie)) => { - Ok(Property(expand_uri(&curie, prefixes)?)) - } - _ => Err(StdError::generic_err(format!( - "Unsupported predicate node: {node:?}. Expected URI" - ))), + match iri { + msg::IRI::Full(uri) => Ok(Property(uri)), + msg::IRI::Prefixed(curie) => Ok(Property(expand_uri(&curie, prefixes)?)), } } } From 54f6b63c89683e1b091fddc6c5686effb0fa7913 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 28 Feb 2024 00:16:26 +0100 Subject: [PATCH 08/22] test(cognitarium): add bnode insert counter test --- contracts/okp4-cognitarium/src/contract.rs | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/contracts/okp4-cognitarium/src/contract.rs b/contracts/okp4-cognitarium/src/contract.rs index 8da4b633..e28d412b 100644 --- a/contracts/okp4-cognitarium/src/contract.rs +++ b/contracts/okp4-cognitarium/src/contract.rs @@ -562,6 +562,39 @@ mod tests { } } + #[test] + fn proper_insert_blank_nodes() { + let mut deps = mock_dependencies(); + + let info = mock_info("owner", &[]); + instantiate( + deps.as_mut(), + mock_env(), + info.clone(), + InstantiateMsg::default(), + ) + .unwrap(); + + let insert_msg = InsertData { + format: None, + data: read_test_data("blank-nodes.ttl"), + }; + + let res = execute(deps.as_mut(), mock_env(), info.clone(), insert_msg.clone()); + assert!(res.is_ok()); + assert_eq!( + BLANK_NODE_IDENTIFIER_COUNTER.load(&deps.storage).unwrap(), + 2u128 + ); + + let res = execute(deps.as_mut(), mock_env(), info.clone(), insert_msg); + assert!(res.is_ok()); + assert_eq!( + BLANK_NODE_IDENTIFIER_COUNTER.load(&deps.storage).unwrap(), + 4u128 + ); + } + #[test] fn insert_existing_triples() { let mut deps = mock_dependencies(); From 2ea17d7a85b4dcbfb6d114f8cc91e4edfa074ae5 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 28 Feb 2024 01:02:53 +0100 Subject: [PATCH 09/22] test(cognitarium): update msg tests --- contracts/okp4-cognitarium/src/msg.rs | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/contracts/okp4-cognitarium/src/msg.rs b/contracts/okp4-cognitarium/src/msg.rs index c0b5a87c..2754a568 100644 --- a/contracts/okp4-cognitarium/src/msg.rs +++ b/contracts/okp4-cognitarium/src/msg.rs @@ -484,7 +484,7 @@ pub enum SimpleWhereCondition { pub trait HasVariables { /// Returns the set of variables used in a triple pattern or template. - fn variables(&self) -> Vec; + fn variables(&self) -> HashSet; /// Returns the set of variables used in a triple pattern or template as [SelectItem]. fn as_select_item(&self) -> Vec { @@ -496,12 +496,8 @@ pub trait HasVariables { } impl HasVariables for Vec { - fn variables(&self) -> Vec { - self.iter() - .flat_map(|t| t.variables()) - .collect::>() - .into_iter() - .collect() + fn variables(&self) -> HashSet { + self.iter().flat_map(|t| t.variables()).collect() } } @@ -518,7 +514,7 @@ pub struct TripleDeleteTemplate { } impl HasVariables for TripleDeleteTemplate { - fn variables(&self) -> Vec { + fn variables(&self) -> HashSet { var_util::merge_variables(&[ self.subject.variable(), self.predicate.variable(), @@ -540,7 +536,7 @@ pub struct TripleConstructTemplate { } impl HasVariables for TripleConstructTemplate { - fn variables(&self) -> Vec { + fn variables(&self) -> HashSet { var_util::merge_variables(&[ self.subject.variable(), self.predicate.variable(), @@ -562,7 +558,7 @@ pub struct TriplePattern { } impl HasVariables for TriplePattern { - fn variables(&self) -> Vec { + fn variables(&self) -> HashSet { var_util::merge_variables(&[ self.subject.variable(), self.predicate.variable(), @@ -578,13 +574,11 @@ trait MaybeVariable { mod var_util { use std::collections::HashSet; - pub fn merge_variables(maybe_vars: &[Option<&str>]) -> Vec { + pub fn merge_variables(maybe_vars: &[Option<&str>]) -> HashSet { maybe_vars .iter() .copied() .filter_map(|maybe_var| maybe_var) - .collect::>() - .into_iter() .map(str::to_string) .collect() } @@ -833,7 +827,10 @@ mod tests { ]; for (triple_pattern, expected) in cases { - assert_eq!(triple_pattern.variables(), expected); + assert_eq!( + triple_pattern.variables(), + HashSet::from_iter(expected.into_iter()) + ); } } } From f22eb817d4f4e047e69b41a68b085a440ca43ba7 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 28 Feb 2024 01:03:07 +0100 Subject: [PATCH 10/22] feat(cognitarium): issue new id for bnode provided in construct --- contracts/okp4-cognitarium/src/contract.rs | 144 +++++++++++++++++---- 1 file changed, 119 insertions(+), 25 deletions(-) diff --git a/contracts/okp4-cognitarium/src/contract.rs b/contracts/okp4-cognitarium/src/contract.rs index e28d412b..f0d84b1d 100644 --- a/contracts/okp4-cognitarium/src/contract.rs +++ b/contracts/okp4-cognitarium/src/contract.rs @@ -146,12 +146,13 @@ pub mod query { use super::*; use crate::msg::{ ConstructQuery, ConstructResponse, DescribeQuery, DescribeResponse, HasVariables, Node, - SelectQuery, SelectResponse, SimpleWhereCondition, StoreResponse, TriplePattern, - VarOrNamedNode, VarOrNode, VarOrNodeOrLiteral, WhereCondition, + SelectQuery, SelectResponse, SimpleWhereCondition, StoreResponse, TripleConstructTemplate, + TriplePattern, VarOrNamedNode, VarOrNode, VarOrNodeOrLiteral, WhereCondition, }; use crate::querier::{PlanBuilder, QueryEngine}; use crate::rdf::PrefixMap; use crate::state::HasCachedNamespaces; + use okp4_rdf::normalize::IdentifierIssuer; use okp4_rdf::serde::TripleWriter; pub fn store(deps: Deps<'_>) -> StdResult { @@ -280,6 +281,26 @@ pub mod query { }); } + let mut id_issuer = IdentifierIssuer::new("a", 0u128); + let construct: Vec<_> = construct + .into_iter() + .map(|t| TripleConstructTemplate { + subject: match t.subject { + VarOrNode::Node(Node::BlankNode(n)) => { + VarOrNode::Node(Node::BlankNode(id_issuer.get_str_or_issue(n.clone()))) + } + _ => t.subject, + }, + predicate: t.predicate, + object: match t.object { + VarOrNodeOrLiteral::Node(Node::BlankNode(n)) => VarOrNodeOrLiteral::Node( + Node::BlankNode(id_issuer.get_str_or_issue(n.clone())), + ), + _ => t.object, + }, + }) + .collect(); + let prefix_map = ::from(prefixes).into_inner(); let mut plan_builder = PlanBuilder::new(deps.storage, &prefix_map, None); let plan = plan_builder.build_plan(&r#where)?; @@ -587,6 +608,7 @@ mod tests { 2u128 ); + // we insert the same data again to check the creation of new blank nodes let res = execute(deps.as_mut(), mock_env(), info.clone(), insert_msg); assert!(res.is_ok()); assert_eq!( @@ -2128,6 +2150,10 @@ mod tests { let id = "https://ontology.okp4.space/dataverse/dataspace/metadata/dcf48417-01c5-4b43-9bc7-49e54c028473"; let cases = vec![ // ( + // InsertData { + // format: Some(DataFormat::RDFXml), + // data: read_test_data("sample.rdf.xml"), + // }, // QueryMsg::Construct { // query: ConstructQuery { // prefixes: vec![], @@ -2148,40 +2174,117 @@ mod tests { // " \"Test\" , \"OKP4\" .\n".to_string().as_bytes().to_vec()), // }, // ), + ( + InsertData { + format: Some(DataFormat::RDFXml), + data: read_test_data("sample.rdf.xml"), + }, + QueryMsg::Construct { + query: ConstructQuery { + prefixes: vec![ + Prefix { prefix: "my-ns".to_string(), namespace: "https://my-ns.org/".to_string() }, + Prefix { prefix: "metadata-dataset".to_string(), namespace: "https://ontology.okp4.space/dataverse/dataset/metadata/".to_string() }, + ], + construct: vec![ + msg::TripleConstructTemplate { + subject: VarOrNode::Node(NamedNode(Prefixed("my-ns:instance-1".to_string()))), + predicate: VarOrNamedNode::NamedNode(Full( + "https://my-ns/predicate/tag".to_string(), + )), + object: VarOrNodeOrLiteral::Variable("o".to_string()), + } + ], + r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { + subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), + predicate: VarOrNamedNode::NamedNode(Full( + "https://ontology.okp4.space/core/hasTag".to_string(), + )), + object: VarOrNodeOrLiteral::Variable("o".to_string()), + }))], + }, + format: Some(DataFormat::NTriples), + }, + ConstructResponse { + format: DataFormat::NTriples, + data: Binary::from( + " \"Test\" .\n \"OKP4\" .\n".to_string().as_bytes().to_vec()), + }, + ), ( + InsertData { + format: Some(DataFormat::Turtle), + data: read_test_data("blank-nodes.ttl"), + }, QueryMsg::Construct { query: ConstructQuery { prefixes: vec![ - Prefix { prefix: "my-ns".to_string(), namespace: "https://my-ns.org/".to_string() }, + Prefix { prefix: "core".to_string(), namespace: "https://ontology.okp4.space/core/".to_string() }, Prefix { prefix: "metadata-dataset".to_string(), namespace: "https://ontology.okp4.space/dataverse/dataset/metadata/".to_string() }, ], construct: vec![ msg::TripleConstructTemplate { - subject: VarOrNode::Node(NamedNode(Prefixed("my-ns:instance-1".to_string()))), + subject: VarOrNode::Node(BlankNode("my-metadata".to_string())), predicate: VarOrNamedNode::NamedNode(Full( - "https://my-ns/predicate/tag".to_string(), + "https://my-ns/predicate/tcov".to_string(), )), - object: VarOrNodeOrLiteral::Variable("o".to_string()), + object: VarOrNodeOrLiteral::Variable("tcov".to_string()), + }, + msg::TripleConstructTemplate { + subject: VarOrNode::Node(BlankNode("my-metadata".to_string())), + predicate: VarOrNamedNode::NamedNode(Full( + "https://my-ns/predicate/info".to_string(), + )), + object: VarOrNodeOrLiteral::Variable("info".to_string()), + }, + msg::TripleConstructTemplate { + subject: VarOrNode::Variable("tcov".to_string()), + predicate: VarOrNamedNode::Variable("tcov_p".to_string()), + object: VarOrNodeOrLiteral::Variable("tcov_o".to_string()), + }, + msg::TripleConstructTemplate { + subject: VarOrNode::Variable("info".to_string()), + predicate: VarOrNamedNode::Variable("info_p".to_string()), + object: VarOrNodeOrLiteral::Variable("info_o".to_string()), } ], - r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - predicate: VarOrNamedNode::NamedNode(Full( - "https://ontology.okp4.space/core/hasTag".to_string(), + r#where: vec![ + WhereCondition::Simple(TriplePattern(msg::TriplePattern { + subject: VarOrNode::Node(NamedNode(Prefixed("metadata-dataset:80b1f84e-86dc-4730-b54f-701ad9b1888a".to_string()))), + predicate: VarOrNamedNode::NamedNode(Prefixed( + "core:hasTemporalCoverage".to_string(), )), - object: VarOrNodeOrLiteral::Variable("o".to_string()), - }))], + object: VarOrNodeOrLiteral::Variable("tcov".to_string()), + })), + WhereCondition::Simple(TriplePattern(msg::TriplePattern { + subject: VarOrNode::Node(NamedNode(Prefixed("metadata-dataset:80b1f84e-86dc-4730-b54f-701ad9b1888a".to_string()))), + predicate: VarOrNamedNode::NamedNode(Prefixed( + "core:hasInformations".to_string(), + )), + object: VarOrNodeOrLiteral::Variable("info".to_string()), + })), + WhereCondition::Simple(TriplePattern(msg::TriplePattern { + subject: VarOrNode::Variable("tcov".to_string()), + predicate: VarOrNamedNode::Variable("tcov_p".to_string()), + object: VarOrNodeOrLiteral::Variable("tcov_o".to_string()), + })), + WhereCondition::Simple(TriplePattern(msg::TriplePattern { + subject: VarOrNode::Variable("info".to_string()), + predicate: VarOrNamedNode::Variable("info_p".to_string()), + object: VarOrNodeOrLiteral::Variable("info_o".to_string()), + })) + ], }, format: Some(DataFormat::NTriples), }, ConstructResponse { format: DataFormat::NTriples, data: Binary::from( - " \"Test\" .\n \"OKP4\" .\n".to_string().as_bytes().to_vec()), + " .\n .\n .\n \"this is a dataset\" .\n .\n .\n .\n \"this is a dataset\" .\n .\n .\n \"2022-01-01T00:00:00+00:00\"^^ .\n \"this is a dataset\" .\n".to_string().as_bytes().to_vec()), }, - )]; + ), + ]; - for (q, expected) in cases { + for (data, q, expected) in cases { let mut deps = mock_dependencies(); let info = mock_info("owner", &[]); @@ -2195,16 +2298,7 @@ mod tests { ) .unwrap(); - execute( - deps.as_mut(), - mock_env(), - info.clone(), - InsertData { - format: Some(DataFormat::RDFXml), - data: read_test_data("sample.rdf.xml"), - }, - ) - .unwrap(); + execute(deps.as_mut(), mock_env(), info.clone(), data).unwrap(); let res = query(deps.as_ref(), mock_env(), q); From 8514d5fb9df08709e18b69696a94a8cb8a4db91d Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 28 Feb 2024 01:16:40 +0100 Subject: [PATCH 11/22] style: little coup de polish --- contracts/okp4-cognitarium/src/contract.rs | 8 ++++---- contracts/okp4-cognitarium/src/msg.rs | 4 ++-- contracts/okp4-cognitarium/src/querier/engine.rs | 12 ++++++------ contracts/okp4-cognitarium/src/querier/variable.rs | 4 ++-- contracts/okp4-dataverse/src/registrar/registry.rs | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/contracts/okp4-cognitarium/src/contract.rs b/contracts/okp4-cognitarium/src/contract.rs index f0d84b1d..fd87c327 100644 --- a/contracts/okp4-cognitarium/src/contract.rs +++ b/contracts/okp4-cognitarium/src/contract.rs @@ -287,15 +287,15 @@ pub mod query { .map(|t| TripleConstructTemplate { subject: match t.subject { VarOrNode::Node(Node::BlankNode(n)) => { - VarOrNode::Node(Node::BlankNode(id_issuer.get_str_or_issue(n.clone()))) + VarOrNode::Node(Node::BlankNode(id_issuer.get_str_or_issue(n))) } _ => t.subject, }, predicate: t.predicate, object: match t.object { - VarOrNodeOrLiteral::Node(Node::BlankNode(n)) => VarOrNodeOrLiteral::Node( - Node::BlankNode(id_issuer.get_str_or_issue(n.clone())), - ), + VarOrNodeOrLiteral::Node(Node::BlankNode(n)) => { + VarOrNodeOrLiteral::Node(Node::BlankNode(id_issuer.get_str_or_issue(n))) + } _ => t.object, }, }) diff --git a/contracts/okp4-cognitarium/src/msg.rs b/contracts/okp4-cognitarium/src/msg.rs index 2754a568..3e8311b2 100644 --- a/contracts/okp4-cognitarium/src/msg.rs +++ b/contracts/okp4-cognitarium/src/msg.rs @@ -497,7 +497,7 @@ pub trait HasVariables { impl HasVariables for Vec { fn variables(&self) -> HashSet { - self.iter().flat_map(|t| t.variables()).collect() + self.iter().flat_map(HasVariables::variables).collect() } } @@ -578,7 +578,7 @@ mod var_util { maybe_vars .iter() .copied() - .filter_map(|maybe_var| maybe_var) + .flatten() .map(str::to_string) .collect() } diff --git a/contracts/okp4-cognitarium/src/querier/engine.rs b/contracts/okp4-cognitarium/src/querier/engine.rs index 03b95767..a3c09ddf 100644 --- a/contracts/okp4-cognitarium/src/querier/engine.rs +++ b/contracts/okp4-cognitarium/src/querier/engine.rs @@ -624,7 +624,7 @@ impl TripleTemplate { F: Fn(&ResolvedVariable) -> Option, { match term { - Left(p) => StdResult::Ok(Some(p.clone())), + Left(p) => Ok(Some(p.clone())), Right(key) => vars.get(key).map(from_var).ok_or_else(|| { StdError::generic_err(format!("Unbound {:?} variable: {:?}", term_name, key)) }), @@ -761,17 +761,17 @@ impl AtomTemplate { ) -> StdResult { Ok(Self { subject: match s_tpl { - VarOrNode::Variable(key) => Right(key.clone()), - VarOrNode::Node(n) => Left((n.clone(), prefixes).try_into()?), + VarOrNode::Variable(key) => Right(key), + VarOrNode::Node(n) => Left((n, prefixes).try_into()?), }, property: match p_tpl { VarOrNamedNode::Variable(key) => Right(key), VarOrNamedNode::NamedNode(iri) => Left((iri, prefixes).try_into()?), }, value: match o_tpl { - VarOrNodeOrLiteral::Variable(key) => Right(key.clone()), - VarOrNodeOrLiteral::Node(n) => Left((n.clone(), prefixes).try_into()?), - VarOrNodeOrLiteral::Literal(l) => Left((l.clone(), prefixes).try_into()?), + VarOrNodeOrLiteral::Variable(key) => Right(key), + VarOrNodeOrLiteral::Node(n) => Left((n, prefixes).try_into()?), + VarOrNodeOrLiteral::Literal(l) => Left((l, prefixes).try_into()?), }, }) } diff --git a/contracts/okp4-cognitarium/src/querier/variable.rs b/contracts/okp4-cognitarium/src/querier/variable.rs index 1c04aa41..538a159b 100644 --- a/contracts/okp4-cognitarium/src/querier/variable.rs +++ b/contracts/okp4-cognitarium/src/querier/variable.rs @@ -17,7 +17,7 @@ impl ResolvedVariable { ResolvedVariable::Predicate(p) => Subject::Named(p.clone()), ResolvedVariable::Object(o) => match o { Object::Named(node) => Subject::Named(node.clone()), - Object::Blank(node) => Subject::Blank(node.clone()), + Object::Blank(node) => Subject::Blank(*node), Object::Literal(_) => None?, }, }) @@ -42,7 +42,7 @@ impl ResolvedVariable { Some(match self { ResolvedVariable::Subject(s) => match s { Subject::Named(node) => Object::Named(node.clone()), - Subject::Blank(node) => Object::Blank(node.clone()), + Subject::Blank(node) => Object::Blank(*node), }, ResolvedVariable::Predicate(p) => Object::Named(p.clone()), ResolvedVariable::Object(o) => o.clone(), diff --git a/contracts/okp4-dataverse/src/registrar/registry.rs b/contracts/okp4-dataverse/src/registrar/registry.rs index 72260733..0045e004 100644 --- a/contracts/okp4-dataverse/src/registrar/registry.rs +++ b/contracts/okp4-dataverse/src/registrar/registry.rs @@ -4,8 +4,8 @@ use crate::state::DATAVERSE; use crate::ContractError; use cosmwasm_std::{DepsMut, StdResult, Storage, WasmMsg}; use okp4_cognitarium::msg::{ - DataFormat, Node, SelectItem, SelectQuery, SimpleWhereCondition, TriplePattern, VarOrNode, - VarOrNodeOrLiteral, WhereCondition, IRI, + DataFormat, Node, SelectItem, SelectQuery, SimpleWhereCondition, TriplePattern, VarOrNamedNode, + VarOrNode, VarOrNodeOrLiteral, WhereCondition, IRI, }; use okp4_cognitarium_client::CognitariumClient; @@ -42,7 +42,7 @@ impl ClaimRegistrar { subject: VarOrNode::Node(Node::NamedNode(IRI::Full( credential.id.to_string(), ))), - predicate: VarOrNode::Variable("p".to_string()), + predicate: VarOrNamedNode::Variable("p".to_string()), object: VarOrNodeOrLiteral::Variable("o".to_string()), }, ))], From ea2013b8380317d9d9e380e9cb730e2bf84faec3 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 28 Feb 2024 01:22:18 +0100 Subject: [PATCH 12/22] docs(cognitarium): update docs from schema --- contracts/okp4-cognitarium/src/msg.rs | 2 +- docs/okp4-cognitarium.md | 58 ++++++++++++++++++++------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/contracts/okp4-cognitarium/src/msg.rs b/contracts/okp4-cognitarium/src/msg.rs index 3e8311b2..e8d96f60 100644 --- a/contracts/okp4-cognitarium/src/msg.rs +++ b/contracts/okp4-cognitarium/src/msg.rs @@ -51,7 +51,7 @@ pub enum ExecuteMsg { /// "where": [ /// { "simple": { "triplePattern": { /// "subject": { "variable": "s" }, - /// "predicate": { "node": { "namedNode": {"prefixed": "foaf:givenName"} } }, + /// "predicate": { "namedNode": {"prefixed": "foaf:givenName"} }, /// "object": { "literal": { "simple": "Myrddin" } } /// } } }, /// { "simple": { "triplePattern": { diff --git a/docs/okp4-cognitarium.md b/docs/okp4-cognitarium.md index 9d65c31e..019224b5 100644 --- a/docs/okp4-cognitarium.md +++ b/docs/okp4-cognitarium.md @@ -411,16 +411,16 @@ Only the smart contract owner (i.e. the address who instantiated it) is authoriz Delete the data (RDF triples) from the store matching the patterns defined by the provided query. For non-existing triples it acts as no-op. -Example: `json { "prefixes": [ { "prefix": "foaf", "namespace": "http://xmlns.com/foaf/0.1/" } ], "delete": [ { "subject": { "variable": "s" }, "predicate": { "variable": "p" }, "object": { "variable": "o" } } ], "where": [ { "simple": { "triplePattern": { "subject": { "variable": "s" }, "predicate": { "node": { "namedNode": {"prefixed": "foaf:givenName"} } }, "object": { "literal": { "simple": "Myrddin" } } } } }, { "simple": { "triplePattern": { "subject": { "variable": "s" }, "predicate": { "variable": "p" }, "object": { "variable": "o" } } } } ] ` +Example: `json { "prefixes": [ { "prefix": "foaf", "namespace": "http://xmlns.com/foaf/0.1/" } ], "delete": [ { "subject": { "variable": "s" }, "predicate": { "variable": "p" }, "object": { "variable": "o" } } ], "where": [ { "simple": { "triplePattern": { "subject": { "variable": "s" }, "predicate": { "namedNode": {"prefixed": "foaf:givenName"} }, "object": { "literal": { "simple": "Myrddin" } } } } }, { "simple": { "triplePattern": { "subject": { "variable": "s" }, "predicate": { "variable": "p" }, "object": { "variable": "o" } } } } ] ` Only the smart contract owner (i.e. the address who instantiated it) is authorized to perform this action. -| parameter | description | -| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `delete_data` | _(Required.) _ **object**. | -| `delete_data.delete` | _(Required.) _ **Array<[TriplePattern](#triplepattern)>**. Specifies the specific triple patterns to delete. If nothing is provided, the patterns from the `where` clause are used for deletion. | -| `delete_data.prefixes` | _(Required.) _ **Array<[Prefix](#prefix)>**. The prefixes used in the operation. | -| `delete_data.where` | _(Required.) _ **Array<[WhereCondition](#wherecondition)>**. Defines the patterns that data (RDF triples) should match in order for it to be considered for deletion. | +| parameter | description | +| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `delete_data` | _(Required.) _ **object**. | +| `delete_data.delete` | _(Required.) _ **Array<[TripleDeleteTemplate](#tripledeletetemplate)>**. Specifies the specific triple templates to delete. | +| `delete_data.prefixes` | _(Required.) _ **Array<[Prefix](#prefix)>**. The prefixes used in the operation. | +| `delete_data.where` | _(Required.) _ **Array<[WhereCondition](#wherecondition)>**. Defines the patterns that data (RDF triples) should match in order for it to be considered for deletion. | ## QueryMsg @@ -536,11 +536,11 @@ An RDF [blank node](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node). Represents a CONSTRUCT query over the triple store, allowing to retrieve a set of triples serialized in a specific format. -| property | description | -| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `construct` | _(Required.) _ **Array<[TriplePattern](#triplepattern)>**. The triples to construct. If nothing is provided, the patterns from the `where` clause are used for construction. | -| `prefixes` | _(Required.) _ **Array<[Prefix](#prefix)>**. The prefixes used in the query. | -| `where` | _(Required.) _ **Array<[WhereCondition](#wherecondition)>**. The WHERE clause. This clause is used to specify the triples to construct using variable bindings. | +| property | description | +| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `construct` | _(Required.) _ **Array<[TripleConstructTemplate](#tripleconstructtemplate)>**. The triples to construct. | +| `prefixes` | _(Required.) _ **Array<[Prefix](#prefix)>**. The prefixes used in the query. | +| `where` | _(Required.) _ **Array<[WhereCondition](#wherecondition)>**. The WHERE clause. This clause is used to specify the triples to construct using variable bindings. | ### DataFormat @@ -747,6 +747,26 @@ Contains usage information about the triple store. | `namespace_count` | _(Required.) _ **[Uint128](#uint128)**. The total number of IRI namespace present in the store. | | `triple_count` | _(Required.) _ **[Uint128](#uint128)**. The total number of triple present in the store. | +### TripleConstructTemplate + +Represents a triple template to be forged for a construct query. + +| property | description | +| ----------- | ----------------------------------------------------------------------------------------------- | +| `object` | _(Required.) _ **[VarOrNodeOrLiteral](#varornodeorliteral)**. The object of the triple pattern. | +| `predicate` | _(Required.) _ **[VarOrNamedNode](#varornamednode)**. The predicate of the triple pattern. | +| `subject` | _(Required.) _ **[VarOrNode](#varornode)**. The subject of the triple pattern. | + +### TripleDeleteTemplate + +Represents a triple template to be deleted. + +| property | description | +| ----------- | --------------------------------------------------------------------------------------------------------- | +| `object` | _(Required.) _ **[VarOrNamedNodeOrLiteral](#varornamednodeorliteral)**. The object of the triple pattern. | +| `predicate` | _(Required.) _ **[VarOrNamedNode](#varornamednode)**. The predicate of the triple pattern. | +| `subject` | _(Required.) _ **[VarOrNamedNode](#varornamednode)**. The subject of the triple pattern. | + ### TriplePattern Represents a triple pattern in a [SimpleWhereCondition]. @@ -754,7 +774,7 @@ Represents a triple pattern in a [SimpleWhereCondition]. | property | description | | ----------- | ----------------------------------------------------------------------------------------------- | | `object` | _(Required.) _ **[VarOrNodeOrLiteral](#varornodeorliteral)**. The object of the triple pattern. | -| `predicate` | _(Required.) _ **[VarOrNode](#varornode)**. The predicate of the triple pattern. | +| `predicate` | _(Required.) _ **[VarOrNamedNode](#varornamednode)**. The predicate of the triple pattern. | | `subject` | _(Required.) _ **[VarOrNode](#varornode)**. The subject of the triple pattern. | ### Turtle @@ -809,6 +829,16 @@ Represents either a variable or a named node (IRI). | [Variable](#variable) | **object**. A variable. | | [NamedNode](#namednode) | **object**. An RDF [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri). | +### VarOrNamedNodeOrLiteral + +Represents either a variable, a named node or a literal. + +| variant | description | +| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Variable](#variable) | **object**. A variable. | +| [NamedNode](#namednode) | **object**. An RDF [IRI](https://www.w3.org/TR/rdf11-concepts/#dfn-iri). | +| [Literal](#literal) | **object**. An RDF [literal](https://www.w3.org/TR/rdf11-concepts/#dfn-literal), i.e. a simple literal, a language-tagged string or a typed value. | + ### VarOrNode Represents either a variable or a node. @@ -846,4 +876,4 @@ Represents a condition in a [WhereClause]. --- -_Rendered by [Fadroma](https://fadroma.tech) ([@fadroma/schema 1.1.0](https://www.npmjs.com/package/@fadroma/schema)) from `okp4-cognitarium.json` (`b3bea598ea8fda42`)_ +_Rendered by [Fadroma](https://fadroma.tech) ([@fadroma/schema 1.1.0](https://www.npmjs.com/package/@fadroma/schema)) from `okp4-cognitarium.json` (`cd4c70c82335248d`)_ From 8fecdea6dcb4bd367b1ce3eb84174d079be1ba7d Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 28 Feb 2024 01:27:55 +0100 Subject: [PATCH 13/22] test(dataverse): update tests according to cognitarium api --- contracts/okp4-dataverse/src/contract.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/okp4-dataverse/src/contract.rs b/contracts/okp4-dataverse/src/contract.rs index 84cfa8da..9504b3f8 100644 --- a/contracts/okp4-dataverse/src/contract.rs +++ b/contracts/okp4-dataverse/src/contract.rs @@ -131,8 +131,8 @@ mod tests { }; use okp4_cognitarium::msg::{ DataFormat, Head, Node, Results, SelectItem, SelectQuery, SelectResponse, - SimpleWhereCondition, TriplePattern, Value, VarOrNode, VarOrNodeOrLiteral, WhereCondition, - IRI, + SimpleWhereCondition, TriplePattern, Value, VarOrNamedNode, VarOrNode, VarOrNodeOrLiteral, + WhereCondition, IRI, }; use std::collections::BTreeMap; @@ -220,7 +220,7 @@ mod tests { subject: VarOrNode::Node(Node::NamedNode(IRI::Full( "http://example.edu/credentials/3732".to_string(), ))), - predicate: VarOrNode::Variable("p".to_string()), + predicate: VarOrNamedNode::Variable("p".to_string()), object: VarOrNodeOrLiteral::Variable("o".to_string()), }) )], From 61ff0782de781381157327f3b83a6b0d4c7fd715 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 28 Feb 2024 11:28:42 +0100 Subject: [PATCH 14/22] feat(cognitarium): reuse where clause in construct empty --- contracts/okp4-cognitarium/src/contract.rs | 283 +++++++++++---------- 1 file changed, 147 insertions(+), 136 deletions(-) diff --git a/contracts/okp4-cognitarium/src/contract.rs b/contracts/okp4-cognitarium/src/contract.rs index fd87c327..0e1776f3 100644 --- a/contracts/okp4-cognitarium/src/contract.rs +++ b/contracts/okp4-cognitarium/src/contract.rs @@ -274,12 +274,23 @@ pub mod query { prefixes, r#where, } = query; - if construct.is_empty() { - return Ok(ConstructResponse { - format, - data: Binary::from(vec![]), - }); - } + + let construct = if construct.is_empty() { + r#where + .iter() + .map(|t| match t { + WhereCondition::Simple(SimpleWhereCondition::TriplePattern(t)) => { + TripleConstructTemplate { + subject: t.subject.clone(), + predicate: t.predicate.clone(), + object: t.object.clone(), + } + } + }) + .collect() + } else { + construct + }; let mut id_issuer = IdentifierIssuer::new("a", 0u128); let construct: Vec<_> = construct @@ -2149,139 +2160,139 @@ mod tests { fn proper_construct() { let id = "https://ontology.okp4.space/dataverse/dataspace/metadata/dcf48417-01c5-4b43-9bc7-49e54c028473"; let cases = vec![ - // ( - // InsertData { - // format: Some(DataFormat::RDFXml), - // data: read_test_data("sample.rdf.xml"), - // }, - // QueryMsg::Construct { - // query: ConstructQuery { - // prefixes: vec![], - // construct: vec![], - // r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { - // subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - // predicate: VarOrNamedNode::NamedNode(Full( - // "https://ontology.okp4.space/core/hasTag".to_string(), - // )), - // object: VarOrNodeOrLiteral::Variable("o".to_string()), - // }))], - // }, - // format: None, - // }, - // ConstructResponse { - // format: DataFormat::Turtle, - // data: Binary::from( - // " \"Test\" , \"OKP4\" .\n".to_string().as_bytes().to_vec()), - // }, - // ), - ( - InsertData { - format: Some(DataFormat::RDFXml), - data: read_test_data("sample.rdf.xml"), - }, - QueryMsg::Construct { - query: ConstructQuery { - prefixes: vec![ - Prefix { prefix: "my-ns".to_string(), namespace: "https://my-ns.org/".to_string() }, - Prefix { prefix: "metadata-dataset".to_string(), namespace: "https://ontology.okp4.space/dataverse/dataset/metadata/".to_string() }, - ], - construct: vec![ - msg::TripleConstructTemplate { - subject: VarOrNode::Node(NamedNode(Prefixed("my-ns:instance-1".to_string()))), - predicate: VarOrNamedNode::NamedNode(Full( - "https://my-ns/predicate/tag".to_string(), - )), - object: VarOrNodeOrLiteral::Variable("o".to_string()), - } - ], - r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - predicate: VarOrNamedNode::NamedNode(Full( - "https://ontology.okp4.space/core/hasTag".to_string(), - )), - object: VarOrNodeOrLiteral::Variable("o".to_string()), - }))], - }, - format: Some(DataFormat::NTriples), + ( + InsertData { + format: Some(DataFormat::RDFXml), + data: read_test_data("sample.rdf.xml"), + }, + QueryMsg::Construct { + query: ConstructQuery { + prefixes: vec![], + construct: vec![], + r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { + subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), + predicate: VarOrNamedNode::NamedNode(Full( + "https://ontology.okp4.space/core/hasTag".to_string(), + )), + object: VarOrNodeOrLiteral::Variable("o".to_string()), + }))], + }, + format: None, + }, + ConstructResponse { + format: DataFormat::Turtle, + data: Binary::from( + " \"Test\" , \"OKP4\" .\n".to_string().as_bytes().to_vec()), + }, + ), + ( + InsertData { + format: Some(DataFormat::RDFXml), + data: read_test_data("sample.rdf.xml"), + }, + QueryMsg::Construct { + query: ConstructQuery { + prefixes: vec![ + Prefix { prefix: "my-ns".to_string(), namespace: "https://my-ns.org/".to_string() }, + Prefix { prefix: "metadata-dataset".to_string(), namespace: "https://ontology.okp4.space/dataverse/dataset/metadata/".to_string() }, + ], + construct: vec![ + msg::TripleConstructTemplate { + subject: VarOrNode::Node(NamedNode(Prefixed("my-ns:instance-1".to_string()))), + predicate: VarOrNamedNode::NamedNode(Full( + "https://my-ns/predicate/tag".to_string(), + )), + object: VarOrNodeOrLiteral::Variable("o".to_string()), + } + ], + r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { + subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), + predicate: VarOrNamedNode::NamedNode(Full( + "https://ontology.okp4.space/core/hasTag".to_string(), + )), + object: VarOrNodeOrLiteral::Variable("o".to_string()), + }))], + }, + format: Some(DataFormat::NTriples), + }, + ConstructResponse { + format: DataFormat::NTriples, + data: Binary::from( + " \"Test\" .\n \"OKP4\" .\n".to_string().as_bytes().to_vec()), + }, + ), + ( + InsertData { + format: Some(DataFormat::Turtle), + data: read_test_data("blank-nodes.ttl"), + }, + QueryMsg::Construct { + query: ConstructQuery { + prefixes: vec![ + Prefix { prefix: "core".to_string(), namespace: "https://ontology.okp4.space/core/".to_string() }, + Prefix { prefix: "metadata-dataset".to_string(), namespace: "https://ontology.okp4.space/dataverse/dataset/metadata/".to_string() }, + ], + construct: vec![ + msg::TripleConstructTemplate { + subject: VarOrNode::Node(BlankNode("my-metadata".to_string())), + predicate: VarOrNamedNode::NamedNode(Full( + "https://my-ns/predicate/tcov".to_string(), + )), + object: VarOrNodeOrLiteral::Variable("tcov".to_string()), }, - ConstructResponse { - format: DataFormat::NTriples, - data: Binary::from( - " \"Test\" .\n \"OKP4\" .\n".to_string().as_bytes().to_vec()), + msg::TripleConstructTemplate { + subject: VarOrNode::Node(BlankNode("my-metadata".to_string())), + predicate: VarOrNamedNode::NamedNode(Full( + "https://my-ns/predicate/info".to_string(), + )), + object: VarOrNodeOrLiteral::Variable("info".to_string()), }, - ), - ( - InsertData { - format: Some(DataFormat::Turtle), - data: read_test_data("blank-nodes.ttl"), + msg::TripleConstructTemplate { + subject: VarOrNode::Variable("tcov".to_string()), + predicate: VarOrNamedNode::Variable("tcov_p".to_string()), + object: VarOrNodeOrLiteral::Variable("tcov_o".to_string()), }, - QueryMsg::Construct { - query: ConstructQuery { - prefixes: vec![ - Prefix { prefix: "core".to_string(), namespace: "https://ontology.okp4.space/core/".to_string() }, - Prefix { prefix: "metadata-dataset".to_string(), namespace: "https://ontology.okp4.space/dataverse/dataset/metadata/".to_string() }, - ], - construct: vec![ - msg::TripleConstructTemplate { - subject: VarOrNode::Node(BlankNode("my-metadata".to_string())), - predicate: VarOrNamedNode::NamedNode(Full( - "https://my-ns/predicate/tcov".to_string(), - )), - object: VarOrNodeOrLiteral::Variable("tcov".to_string()), - }, - msg::TripleConstructTemplate { - subject: VarOrNode::Node(BlankNode("my-metadata".to_string())), - predicate: VarOrNamedNode::NamedNode(Full( - "https://my-ns/predicate/info".to_string(), - )), - object: VarOrNodeOrLiteral::Variable("info".to_string()), - }, - msg::TripleConstructTemplate { - subject: VarOrNode::Variable("tcov".to_string()), - predicate: VarOrNamedNode::Variable("tcov_p".to_string()), - object: VarOrNodeOrLiteral::Variable("tcov_o".to_string()), - }, - msg::TripleConstructTemplate { - subject: VarOrNode::Variable("info".to_string()), - predicate: VarOrNamedNode::Variable("info_p".to_string()), - object: VarOrNodeOrLiteral::Variable("info_o".to_string()), - } - ], - r#where: vec![ - WhereCondition::Simple(TriplePattern(msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Prefixed("metadata-dataset:80b1f84e-86dc-4730-b54f-701ad9b1888a".to_string()))), - predicate: VarOrNamedNode::NamedNode(Prefixed( - "core:hasTemporalCoverage".to_string(), - )), - object: VarOrNodeOrLiteral::Variable("tcov".to_string()), - })), - WhereCondition::Simple(TriplePattern(msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Prefixed("metadata-dataset:80b1f84e-86dc-4730-b54f-701ad9b1888a".to_string()))), - predicate: VarOrNamedNode::NamedNode(Prefixed( - "core:hasInformations".to_string(), - )), - object: VarOrNodeOrLiteral::Variable("info".to_string()), - })), - WhereCondition::Simple(TriplePattern(msg::TriplePattern { - subject: VarOrNode::Variable("tcov".to_string()), - predicate: VarOrNamedNode::Variable("tcov_p".to_string()), - object: VarOrNodeOrLiteral::Variable("tcov_o".to_string()), - })), - WhereCondition::Simple(TriplePattern(msg::TriplePattern { - subject: VarOrNode::Variable("info".to_string()), - predicate: VarOrNamedNode::Variable("info_p".to_string()), - object: VarOrNodeOrLiteral::Variable("info_o".to_string()), - })) - ], - }, - format: Some(DataFormat::NTriples), - }, - ConstructResponse { - format: DataFormat::NTriples, - data: Binary::from( - " .\n .\n .\n \"this is a dataset\" .\n .\n .\n .\n \"this is a dataset\" .\n .\n .\n \"2022-01-01T00:00:00+00:00\"^^ .\n \"this is a dataset\" .\n".to_string().as_bytes().to_vec()), - }, - ), + msg::TripleConstructTemplate { + subject: VarOrNode::Variable("info".to_string()), + predicate: VarOrNamedNode::Variable("info_p".to_string()), + object: VarOrNodeOrLiteral::Variable("info_o".to_string()), + } + ], + r#where: vec![ + WhereCondition::Simple(TriplePattern(msg::TriplePattern { + subject: VarOrNode::Node(NamedNode(Prefixed("metadata-dataset:80b1f84e-86dc-4730-b54f-701ad9b1888a".to_string()))), + predicate: VarOrNamedNode::NamedNode(Prefixed( + "core:hasTemporalCoverage".to_string(), + )), + object: VarOrNodeOrLiteral::Variable("tcov".to_string()), + })), + WhereCondition::Simple(TriplePattern(msg::TriplePattern { + subject: VarOrNode::Node(NamedNode(Prefixed("metadata-dataset:80b1f84e-86dc-4730-b54f-701ad9b1888a".to_string()))), + predicate: VarOrNamedNode::NamedNode(Prefixed( + "core:hasInformations".to_string(), + )), + object: VarOrNodeOrLiteral::Variable("info".to_string()), + })), + WhereCondition::Simple(TriplePattern(msg::TriplePattern { + subject: VarOrNode::Variable("tcov".to_string()), + predicate: VarOrNamedNode::Variable("tcov_p".to_string()), + object: VarOrNodeOrLiteral::Variable("tcov_o".to_string()), + })), + WhereCondition::Simple(TriplePattern(msg::TriplePattern { + subject: VarOrNode::Variable("info".to_string()), + predicate: VarOrNamedNode::Variable("info_p".to_string()), + object: VarOrNodeOrLiteral::Variable("info_o".to_string()), + })) + ], + }, + format: Some(DataFormat::NTriples), + }, + ConstructResponse { + format: DataFormat::NTriples, + data: Binary::from( + " .\n .\n .\n \"this is a dataset\" .\n .\n .\n .\n \"this is a dataset\" .\n .\n .\n \"2022-01-01T00:00:00+00:00\"^^ .\n \"this is a dataset\" .\n".to_string().as_bytes().to_vec()), + }, + ), ]; for (data, q, expected) in cases { From 72766ff6c29b3db0286e54f89282a1c3ca4d27e6 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 28 Feb 2024 16:01:24 +0100 Subject: [PATCH 15/22] perf(cognitarium): use resolved variables to construct atoms --- contracts/okp4-cognitarium/src/contract.rs | 50 ++++---- .../okp4-cognitarium/src/querier/engine.rs | 109 ++++++++++-------- 2 files changed, 87 insertions(+), 72 deletions(-) diff --git a/contracts/okp4-cognitarium/src/contract.rs b/contracts/okp4-cognitarium/src/contract.rs index 0e1776f3..05ba5fa9 100644 --- a/contracts/okp4-cognitarium/src/contract.rs +++ b/contracts/okp4-cognitarium/src/contract.rs @@ -145,12 +145,12 @@ pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> StdResult { pub mod query { use super::*; use crate::msg::{ - ConstructQuery, ConstructResponse, DescribeQuery, DescribeResponse, HasVariables, Node, - SelectQuery, SelectResponse, SimpleWhereCondition, StoreResponse, TripleConstructTemplate, + ConstructQuery, ConstructResponse, DescribeQuery, DescribeResponse, Node, SelectQuery, + SelectResponse, SimpleWhereCondition, StoreResponse, TripleConstructTemplate, TriplePattern, VarOrNamedNode, VarOrNode, VarOrNodeOrLiteral, WhereCondition, }; use crate::querier::{PlanBuilder, QueryEngine}; - use crate::rdf::PrefixMap; + use crate::rdf::{Atom, PrefixMap}; use crate::state::HasCachedNamespaces; use okp4_rdf::normalize::IdentifierIssuer; use okp4_rdf::serde::TripleWriter; @@ -228,18 +228,16 @@ pub mod query { let plan = plan_builder.build_plan(&r#where)?; let atoms = QueryEngine::new(deps.storage) - .select(plan, select.as_select_item()) - .and_then(|res| { - res.solutions.resolve_atoms( - deps.storage, - &prefix_map, - select - .into_iter() - .map(|p| (p.subject, p.predicate, p.object)) - .collect(), - plan_builder.cached_namespaces(), - ) - })?; + .construct_atoms( + plan, + &prefix_map, + select + .into_iter() + .map(|t| (t.subject, t.predicate, t.object)) + .collect(), + plan_builder.cached_namespaces(), + )? + .collect::>>()?; let out: Vec = Vec::default(); let mut writer = TripleWriter::new(&(&format).into(), out); @@ -317,18 +315,16 @@ pub mod query { let plan = plan_builder.build_plan(&r#where)?; let atoms = QueryEngine::new(deps.storage) - .select(plan, construct.as_select_item()) - .and_then(|res| { - res.solutions.resolve_atoms( - deps.storage, - &prefix_map, - construct - .into_iter() - .map(|t| (t.subject, t.predicate, t.object)) - .collect(), - plan_builder.cached_namespaces(), - ) - })?; + .construct_atoms( + plan, + &prefix_map, + construct + .into_iter() + .map(|t| (t.subject, t.predicate, t.object)) + .collect(), + plan_builder.cached_namespaces(), + )? + .collect::>>()?; let out: Vec = Vec::default(); let mut writer = TripleWriter::new(&(&format).into(), out); diff --git a/contracts/okp4-cognitarium/src/querier/engine.rs b/contracts/okp4-cognitarium/src/querier/engine.rs index a3c09ddf..e4bb0a36 100644 --- a/contracts/okp4-cognitarium/src/querier/engine.rs +++ b/contracts/okp4-cognitarium/src/querier/engine.rs @@ -54,6 +54,27 @@ impl<'a> QueryEngine<'a> { }) } + pub fn construct_atoms( + &'a self, + plan: QueryPlan, + prefixes: &HashMap, + templates: Vec<(VarOrNode, VarOrNamedNode, VarOrNodeOrLiteral)>, + ns_cache: Vec, + ) -> StdResult> { + let templates = templates + .into_iter() + .map(|t| AtomTemplate::try_new(&plan, prefixes, t)) + .collect::>>()?; + + ResolvedAtomIterator::try_new( + self.storage, + ns_cache.into(), + IdentifierIssuer::new("b", 0u128), + self.eval_plan(plan), + templates, + ) + } + pub fn eval_plan(&'a self, plan: QueryPlan) -> ResolvedVariablesIterator<'_> { return self.eval_node(plan.entrypoint)(ResolvedVariables::with_capacity( plan.variables.len(), @@ -451,20 +472,6 @@ impl<'a> SolutionsIterator<'a> { ResolvedTripleIterator::try_new(&mut ns_resolver, storage, self, prefixes, templates)?; triples_iter.collect() } - - pub fn resolve_atoms( - self, - storage: &dyn Storage, - prefixes: &HashMap, - templates: Vec<(VarOrNode, VarOrNamedNode, VarOrNodeOrLiteral)>, - ns_cache: Vec, - ) -> StdResult> { - let mut ns_resolver = ns_cache.into(); - - let atoms_iter = - ResolvedAtomIterator::try_new(&mut ns_resolver, storage, self, prefixes, templates)?; - atoms_iter.collect() - } } impl<'a> Iterator for SolutionsIterator<'a> { @@ -684,31 +691,28 @@ impl TripleTemplate { } pub struct ResolvedAtomIterator<'a> { - ns_resolver: &'a mut NamespaceResolver, - id_issuer: IdentifierIssuer, storage: &'a dyn Storage, - iter: SolutionsIterator<'a>, + ns_resolver: NamespaceResolver, + id_issuer: IdentifierIssuer, + upstream_iter: ResolvedVariablesIterator<'a>, templates: Vec, buffer: VecDeque>, } impl<'a> ResolvedAtomIterator<'a> { pub fn try_new( - ns_resolver: &'a mut NamespaceResolver, storage: &'a dyn Storage, - solutions: SolutionsIterator<'a>, - prefixes: &HashMap, - templates: Vec<(VarOrNode, VarOrNamedNode, VarOrNodeOrLiteral)>, + ns_resolver: NamespaceResolver, + id_issuer: IdentifierIssuer, + upstream_iter: ResolvedVariablesIterator<'a>, + templates: Vec, ) -> StdResult { Ok(Self { - ns_resolver, - id_issuer: IdentifierIssuer::new("b", 0u128), storage, - iter: solutions, - templates: templates - .into_iter() - .map(|t| AtomTemplate::try_new(prefixes, t)) - .collect::>>()?, + ns_resolver, + id_issuer, + upstream_iter, + templates, buffer: VecDeque::new(), }) } @@ -723,7 +727,7 @@ impl<'a> Iterator for ResolvedAtomIterator<'a> { return Some(val); } - let upstream_res = match self.iter.next() { + let upstream_res = match self.upstream_iter.next() { None => None?, Some(res) => res, }; @@ -734,7 +738,12 @@ impl<'a> Iterator for ResolvedAtomIterator<'a> { } Ok(vars) => { for res in self.templates.iter().map(|template| { - template.resolve(self.ns_resolver, &mut self.id_issuer, self.storage, &vars) + template.resolve( + self.storage, + &mut self.ns_resolver, + &mut self.id_issuer, + &vars, + ) }) { match res { Ok(Some(atom)) => self.buffer.push_back(Ok(atom)), @@ -748,28 +757,38 @@ impl<'a> Iterator for ResolvedAtomIterator<'a> { } } -struct AtomTemplate { - subject: Either, - property: Either, - value: Either, +pub struct AtomTemplate { + subject: Either, + property: Either, + value: Either, } impl AtomTemplate { pub fn try_new( + plan: &QueryPlan, prefixes: &HashMap, (s_tpl, p_tpl, o_tpl): (VarOrNode, VarOrNamedNode, VarOrNodeOrLiteral), ) -> StdResult { Ok(Self { subject: match s_tpl { - VarOrNode::Variable(key) => Right(key), + VarOrNode::Variable(key) => Right(plan.get_var_index(key.as_str()).ok_or( + StdError::generic_err("Selected variable not found in query"), + )?), VarOrNode::Node(n) => Left((n, prefixes).try_into()?), }, property: match p_tpl { - VarOrNamedNode::Variable(key) => Right(key), + VarOrNamedNode::Variable(key) => Right(plan.get_var_index(key.as_str()).ok_or( + StdError::generic_err("Selected variable not found in query"), + )?), VarOrNamedNode::NamedNode(iri) => Left((iri, prefixes).try_into()?), }, value: match o_tpl { - VarOrNodeOrLiteral::Variable(key) => Right(key), + VarOrNodeOrLiteral::Variable(key) => Right( + plan.get_var_index(key.as_str()) + .ok_or(StdError::generic_err( + "Selected variable not found in query", + ))?, + ), VarOrNodeOrLiteral::Node(n) => Left((n, prefixes).try_into()?), VarOrNodeOrLiteral::Literal(l) => Left((l, prefixes).try_into()?), }, @@ -778,10 +797,10 @@ impl AtomTemplate { pub fn resolve( &self, + storage: &dyn Storage, ns_resolver: &mut NamespaceResolver, id_issuer: &mut IdentifierIssuer, - storage: &dyn Storage, - vars: &BTreeMap, + vars: &ResolvedVariables, ) -> StdResult> { let resolve_ns_fn = &mut |ns_key| { let res = ns_resolver.resolve_from_key(storage, ns_key); @@ -815,7 +834,7 @@ impl AtomTemplate { &self, resolve_ns_fn: &mut F, id_issuer: &mut IdentifierIssuer, - vars: &BTreeMap, + vars: &ResolvedVariables, ) -> StdResult> where F: FnMut(u128) -> StdResult, @@ -839,7 +858,7 @@ impl AtomTemplate { fn resolve_atom_property( &self, resolve_ns_fn: &mut F, - vars: &BTreeMap, + vars: &ResolvedVariables, ) -> StdResult> where F: FnMut(u128) -> StdResult, @@ -857,7 +876,7 @@ impl AtomTemplate { &self, resolve_ns_fn: &mut F, id_issuer: &mut IdentifierIssuer, - vars: &BTreeMap, + vars: &ResolvedVariables, ) -> StdResult> where F: FnMut(u128) -> StdResult, @@ -888,9 +907,9 @@ impl AtomTemplate { } fn resolve_atom_term( - term: &Either, + term: &Either, from_var: F, - vars: &BTreeMap, + vars: &ResolvedVariables, mapping_fn: &mut M, term_name: &str, ) -> StdResult> @@ -902,7 +921,7 @@ impl AtomTemplate { match term { Left(v) => Ok(Some(v.clone())), Right(key) => { - let var = vars.get(key).ok_or_else(|| { + let var = vars.get(*key).as_ref().ok_or_else(|| { StdError::generic_err(format!("Unbound {:?} variable: {:?}", term_name, key)) })?; From 57ce5196e613d2c7925af5f102d65a766e6d4f44 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 28 Feb 2024 16:21:26 +0100 Subject: [PATCH 16/22] refactor(cognitarium): share duplicate code for queries --- contracts/okp4-cognitarium/src/contract.rs | 144 ++++++++++----------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/contracts/okp4-cognitarium/src/contract.rs b/contracts/okp4-cognitarium/src/contract.rs index 05ba5fa9..f832dc02 100644 --- a/contracts/okp4-cognitarium/src/contract.rs +++ b/contracts/okp4-cognitarium/src/contract.rs @@ -150,10 +150,9 @@ pub mod query { TriplePattern, VarOrNamedNode, VarOrNode, VarOrNodeOrLiteral, WhereCondition, }; use crate::querier::{PlanBuilder, QueryEngine}; - use crate::rdf::{Atom, PrefixMap}; + use crate::rdf::PrefixMap; use crate::state::HasCachedNamespaces; use okp4_rdf::normalize::IdentifierIssuer; - use okp4_rdf::serde::TripleWriter; pub fn store(deps: Deps<'_>) -> StdResult { STORE.load(deps.storage).map(Into::into) @@ -190,9 +189,7 @@ pub mod query { ) -> StdResult { let (p, o) = ("_2p".to_owned(), "_3o".to_owned()); - let store = STORE.load(deps.storage)?; - - let (select, r#where) = match &query.resource { + let (construct, r#where) = match &query.resource { VarOrNamedNode::Variable(var) => { let select = TriplePattern { subject: VarOrNode::Variable(var.clone()), @@ -222,39 +219,17 @@ pub mod query { ) } }; - let prefix_map = ::from(query.prefixes).into_inner(); - let mut plan_builder = PlanBuilder::new(deps.storage, &prefix_map, None) - .with_limit(store.limits.max_query_limit as usize); - let plan = plan_builder.build_plan(&r#where)?; - let atoms = QueryEngine::new(deps.storage) - .construct_atoms( - plan, - &prefix_map, - select - .into_iter() - .map(|t| (t.subject, t.predicate, t.object)) - .collect(), - plan_builder.cached_namespaces(), - )? - .collect::>>()?; - - let out: Vec = Vec::default(); - let mut writer = TripleWriter::new(&(&format).into(), out); - - for atom in &atoms { - let triple = atom.into(); - - writer.write(&triple).map_err(|e| { - StdError::serialize_err( - "triple", - format!("Error writing triple {}: {}", &triple, e), - ) - })?; - } - let out = writer - .finish() - .map_err(|e| StdError::serialize_err("triple", format!("Error writing triple: {e}")))?; + let out = util::construct_atoms( + deps.storage, + &format, + query.prefixes, + construct + .into_iter() + .map(|t| (t.subject, t.predicate, t.object)) + .collect(), + r#where, + )?; Ok(DescribeResponse { format, @@ -310,38 +285,16 @@ pub mod query { }) .collect(); - let prefix_map = ::from(prefixes).into_inner(); - let mut plan_builder = PlanBuilder::new(deps.storage, &prefix_map, None); - let plan = plan_builder.build_plan(&r#where)?; - - let atoms = QueryEngine::new(deps.storage) - .construct_atoms( - plan, - &prefix_map, - construct - .into_iter() - .map(|t| (t.subject, t.predicate, t.object)) - .collect(), - plan_builder.cached_namespaces(), - )? - .collect::>>()?; - - let out: Vec = Vec::default(); - let mut writer = TripleWriter::new(&(&format).into(), out); - - for atom in &atoms { - let triple = atom.into(); - writer.write(&triple).map_err(|e| { - StdError::serialize_err( - "triple", - format!("Error writing triple {}: {}", &triple, e), - ) - })?; - } - - let out = writer - .finish() - .map_err(|e| StdError::serialize_err("triple", format!("Error writing triple: {e}")))?; + let out = util::construct_atoms( + deps.storage, + &format, + prefixes, + construct + .into_iter() + .map(|t| (t.subject, t.predicate, t.object)) + .collect(), + r#where, + )?; Ok(ConstructResponse { format, @@ -352,10 +305,16 @@ pub mod query { pub mod util { use super::*; - use crate::msg::{Head, Results, SelectResponse, Value}; - use crate::querier::SelectResults; - use crate::state::{Namespace, NamespaceResolver}; + use crate::msg::{ + Head, Prefix, Results, SelectResponse, Value, VarOrNamedNode, VarOrNode, + VarOrNodeOrLiteral, WhereClause, + }; + use crate::querier::{PlanBuilder, QueryEngine, SelectResults}; + use crate::rdf::{Atom, PrefixMap}; + use crate::state::{HasCachedNamespaces, Namespace, NamespaceResolver}; + use cosmwasm_std::Storage; use okp4_rdf::normalize::IdentifierIssuer; + use okp4_rdf::serde::TripleWriter; use std::collections::BTreeMap; pub fn map_select_solutions( @@ -393,6 +352,47 @@ pub mod util { results: Results { bindings }, }) } + + pub fn construct_atoms( + storage: &dyn Storage, + format: &DataFormat, + prefixes: Vec, + construct: Vec<(VarOrNode, VarOrNamedNode, VarOrNodeOrLiteral)>, + r#where: WhereClause, + ) -> StdResult> { + let store = STORE.load(storage)?; + + let prefix_map = ::from(prefixes).into_inner(); + let mut plan_builder = PlanBuilder::new(storage, &prefix_map, None) + .with_limit(store.limits.max_query_limit as usize); + let plan = plan_builder.build_plan(&r#where)?; + + let atoms = QueryEngine::new(storage) + .construct_atoms( + plan, + &prefix_map, + construct, + plan_builder.cached_namespaces(), + )? + .collect::>>()?; + + let out: Vec = Vec::default(); + let mut writer = TripleWriter::new(&format.into(), out); + + for atom in &atoms { + let triple = atom.into(); + + writer.write(&triple).map_err(|e| { + StdError::serialize_err( + "triple", + format!("Error writing triple {}: {}", &triple, e), + ) + })?; + } + writer + .finish() + .map_err(|e| StdError::serialize_err("triple", format!("Error writing triple: {e}"))) + } } #[cfg(test)] From e389931dc5606383c5f914375105fd8999750d54 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 28 Feb 2024 17:15:13 +0100 Subject: [PATCH 17/22] perf(cognitarium): use resolved vars to contruct triples --- contracts/okp4-cognitarium/src/contract.rs | 24 +-- contracts/okp4-cognitarium/src/msg.rs | 2 + .../okp4-cognitarium/src/querier/engine.rs | 177 ++++++++++++------ .../okp4-cognitarium/src/querier/plan.rs | 22 +-- 4 files changed, 143 insertions(+), 82 deletions(-) diff --git a/contracts/okp4-cognitarium/src/contract.rs b/contracts/okp4-cognitarium/src/contract.rs index f832dc02..108efca7 100644 --- a/contracts/okp4-cognitarium/src/contract.rs +++ b/contracts/okp4-cognitarium/src/contract.rs @@ -50,12 +50,13 @@ pub fn execute( pub mod execute { use super::*; - use crate::msg::{DataFormat, HasVariables, Prefix, TripleDeleteTemplate, WhereClause}; + use crate::msg::{DataFormat, Prefix, TripleDeleteTemplate, WhereClause}; use crate::querier::{PlanBuilder, QueryEngine}; use crate::rdf::PrefixMap; - use crate::state::HasCachedNamespaces; + use crate::state::{HasCachedNamespaces, Triple}; use crate::storer::StoreEngine; use cosmwasm_std::Uint128; + use either::{Left, Right}; use okp4_rdf::serde::TripleReader; use std::io::BufReader; @@ -105,17 +106,18 @@ pub mod execute { let plan = plan_builder.build_plan(&r#where)?; let triples = QueryEngine::new(deps.storage) - .select(plan, delete.as_select_item())? - .solutions - .resolve_triples( - deps.storage, + .construct_triples( + plan, &prefix_map, - delete - .into_iter() - .map(|t| (t.subject, t.predicate, t.object)) - .collect(), + Right( + delete + .into_iter() + .map(|t| (t.subject, t.predicate, t.object)) + .collect(), + ), plan_builder.cached_namespaces(), - )?; + )? + .collect::>>()?; let mut store = StoreEngine::new(deps.storage)?; let count = store.delete_all(&triples)?; diff --git a/contracts/okp4-cognitarium/src/msg.rs b/contracts/okp4-cognitarium/src/msg.rs index e8d96f60..0f11d04c 100644 --- a/contracts/okp4-cognitarium/src/msg.rs +++ b/contracts/okp4-cognitarium/src/msg.rs @@ -68,6 +68,7 @@ pub enum ExecuteMsg { /// The prefixes used in the operation. prefixes: Vec, /// Specifies the specific triple templates to delete. + /// If nothing is provided, the patterns from the `where` clause are used for deletion. delete: Vec, /// Defines the patterns that data (RDF triples) should match in order for it to be /// considered for deletion. @@ -434,6 +435,7 @@ pub struct ConstructQuery { /// The prefixes used in the query. pub prefixes: Vec, /// The triples to construct. + /// If nothing is provided, the patterns from the `where` clause are used for construction. pub construct: Vec, /// The WHERE clause. /// This clause is used to specify the triples to construct using variable bindings. diff --git a/contracts/okp4-cognitarium/src/querier/engine.rs b/contracts/okp4-cognitarium/src/querier/engine.rs index e4bb0a36..9513fea9 100644 --- a/contracts/okp4-cognitarium/src/querier/engine.rs +++ b/contracts/okp4-cognitarium/src/querier/engine.rs @@ -1,5 +1,5 @@ use crate::msg::{ - SelectItem, VarOrNamedNode, VarOrNamedNodeOrLiteral, VarOrNode, VarOrNodeOrLiteral, + Node, SelectItem, VarOrNamedNode, VarOrNamedNodeOrLiteral, VarOrNode, VarOrNodeOrLiteral, }; use crate::querier::mapper::{iri_as_node, literal_as_object}; use crate::querier::plan::{PatternValue, QueryNode, QueryPlan}; @@ -75,6 +75,48 @@ impl<'a> QueryEngine<'a> { ) } + pub fn construct_triples( + &'a self, + plan: QueryPlan, + prefixes: &HashMap, + templates: Either< + Vec<(VarOrNode, VarOrNamedNode, VarOrNodeOrLiteral)>, + Vec<(VarOrNamedNode, VarOrNamedNode, VarOrNamedNodeOrLiteral)>, + >, + ns_cache: Vec, + ) -> StdResult> { + let mut ns_resolver: NamespaceResolver = ns_cache.into(); + + let templates = match templates { + Left(tpl) => tpl + .into_iter() + .map(|t| { + TripleTemplate::try_new( + self.storage, + &mut ns_resolver, + &plan, + prefixes, + Left(t), + ) + }) + .collect::>>(), + Right(tpl) => tpl + .into_iter() + .map(|t| { + TripleTemplate::try_new( + self.storage, + &mut ns_resolver, + &plan, + prefixes, + Right(t), + ) + }) + .collect::>>(), + }?; + + ResolvedTripleIterator::try_new(self.eval_plan(plan), templates) + } + pub fn eval_plan(&'a self, plan: QueryPlan) -> ResolvedVariablesIterator<'_> { return self.eval_node(plan.entrypoint)(ResolvedVariables::with_capacity( plan.variables.len(), @@ -458,20 +500,6 @@ impl<'a> SolutionsIterator<'a> { fn new(iter: ResolvedVariablesIterator<'a>, bindings: BTreeMap) -> Self { Self { iter, bindings } } - - pub fn resolve_triples( - self, - storage: &dyn Storage, - prefixes: &HashMap, - templates: Vec<(VarOrNamedNode, VarOrNamedNode, VarOrNamedNodeOrLiteral)>, - ns_cache: Vec, - ) -> StdResult> { - let mut ns_resolver = ns_cache.into(); - - let triples_iter = - ResolvedTripleIterator::try_new(&mut ns_resolver, storage, self, prefixes, templates)?; - triples_iter.collect() - } } impl<'a> Iterator for SolutionsIterator<'a> { @@ -502,25 +530,19 @@ impl<'a> Iterator for SolutionsIterator<'a> { } pub struct ResolvedTripleIterator<'a> { - iter: SolutionsIterator<'a>, + upstream_iter: ResolvedVariablesIterator<'a>, templates: Vec, buffer: VecDeque>, } impl<'a> ResolvedTripleIterator<'a> { pub fn try_new( - ns_resolver: &mut NamespaceResolver, - storage: &dyn Storage, - solutions: SolutionsIterator<'a>, - prefixes: &HashMap, - templates: Vec<(VarOrNamedNode, VarOrNamedNode, VarOrNamedNodeOrLiteral)>, + upstream_iter: ResolvedVariablesIterator<'a>, + templates: Vec, ) -> StdResult { Ok(Self { - iter: solutions, - templates: templates - .into_iter() - .map(|t| TripleTemplate::try_new(ns_resolver, storage, prefixes, t)) - .collect::>>()?, + upstream_iter, + templates, buffer: VecDeque::new(), }) } @@ -535,7 +557,7 @@ impl<'a> Iterator for ResolvedTripleIterator<'a> { return Some(val); } - let upstream_res = match self.iter.next() { + let upstream_res = match self.upstream_iter.next() { None => None?, Some(res) => res, }; @@ -562,27 +584,36 @@ impl<'a> Iterator for ResolvedTripleIterator<'a> { } } -struct TripleTemplate { - subject: Either, - predicate: Either, - object: Either, +pub struct TripleTemplate { + subject: Either, + predicate: Either, + object: Either, } impl TripleTemplate { fn try_new( - ns_resolver: &mut NamespaceResolver, storage: &dyn Storage, + ns_resolver: &mut NamespaceResolver, + plan: &QueryPlan, prefixes: &HashMap, - (s_tpl, p_tpl, o_tpl): (VarOrNamedNode, VarOrNamedNode, VarOrNamedNodeOrLiteral), + template: Either< + (VarOrNode, VarOrNamedNode, VarOrNodeOrLiteral), + (VarOrNamedNode, VarOrNamedNode, VarOrNamedNodeOrLiteral), + >, ) -> StdResult { + let (s_tpl, p_tpl, o_tpl) = match template { + Right((s, p, o)) => (Right(s), p, Right(o)), + Left((s, p, o)) => (Left(s), p, Left(o)), + }; + Ok(TripleTemplate { - subject: Self::build_subject_template(ns_resolver, storage, prefixes, s_tpl)?, - predicate: Self::build_predicate_template(ns_resolver, storage, prefixes, p_tpl)?, - object: Self::build_object_template(ns_resolver, storage, prefixes, o_tpl)?, + subject: Self::build_subject_template(storage, ns_resolver, plan, prefixes, s_tpl)?, + predicate: Self::build_predicate_template(storage, ns_resolver, plan, prefixes, p_tpl)?, + object: Self::build_object_template(storage, ns_resolver, plan, prefixes, o_tpl)?, }) } - pub fn resolve(&self, vars: &BTreeMap) -> StdResult> { + pub fn resolve(&self, vars: &ResolvedVariables) -> StdResult> { let subject = match Self::resolve_triple_term( &self.subject, ResolvedVariable::as_subject, @@ -621,9 +652,9 @@ impl TripleTemplate { } fn resolve_triple_term( - term: &Either, + term: &Either, from_var: F, - vars: &BTreeMap, + vars: &ResolvedVariables, term_name: &str, ) -> StdResult> where @@ -632,37 +663,53 @@ impl TripleTemplate { { match term { Left(p) => Ok(Some(p.clone())), - Right(key) => vars.get(key).map(from_var).ok_or_else(|| { + Right(key) => vars.get(*key).as_ref().map(from_var).ok_or_else(|| { StdError::generic_err(format!("Unbound {:?} variable: {:?}", term_name, key)) }), } } fn build_subject_template( - ns_resolver: &mut NamespaceResolver, storage: &dyn Storage, + ns_resolver: &mut NamespaceResolver, + plan: &QueryPlan, prefixes: &HashMap, - value: VarOrNamedNode, - ) -> StdResult> { + value: Either, + ) -> StdResult> { Ok(match value { - VarOrNamedNode::Variable(v) => Right(v), - VarOrNamedNode::NamedNode(iri) => Left(Subject::Named(iri_as_node( - ns_resolver, - storage, - prefixes, - iri, - )?)), + Left(VarOrNode::Variable(v)) | Right(VarOrNamedNode::Variable(v)) => { + Right(plan.get_var_index(v.as_str()).ok_or(StdError::generic_err( + "Selected variable not found in query", + ))?) + } + Left(VarOrNode::Node(Node::BlankNode(n))) => Right( + plan.get_bnode_index(n.as_str()) + .ok_or(StdError::generic_err( + "Selected blank node not found in query", + ))?, + ), + Left(VarOrNode::Node(Node::NamedNode(iri))) | Right(VarOrNamedNode::NamedNode(iri)) => { + Left(Subject::Named(iri_as_node( + ns_resolver, + storage, + prefixes, + iri, + )?)) + } }) } fn build_predicate_template( - ns_resolver: &mut NamespaceResolver, storage: &dyn Storage, + ns_resolver: &mut NamespaceResolver, + plan: &QueryPlan, prefixes: &HashMap, value: VarOrNamedNode, - ) -> StdResult> { + ) -> StdResult> { Ok(match value { - VarOrNamedNode::Variable(v) => Right(v), + VarOrNamedNode::Variable(v) => Right(plan.get_var_index(v.as_str()).ok_or( + StdError::generic_err("Selected variable not found in query"), + )?), VarOrNamedNode::NamedNode(iri) => { Left(iri_as_node(ns_resolver, storage, prefixes, iri)?) } @@ -670,20 +717,32 @@ impl TripleTemplate { } fn build_object_template( - ns_resolver: &mut NamespaceResolver, storage: &dyn Storage, + ns_resolver: &mut NamespaceResolver, + plan: &QueryPlan, prefixes: &HashMap, - value: VarOrNamedNodeOrLiteral, - ) -> StdResult> { + value: Either, + ) -> StdResult> { Ok(match value { - VarOrNamedNodeOrLiteral::Variable(v) => Right(v), - VarOrNamedNodeOrLiteral::NamedNode(iri) => Left(Object::Named(iri_as_node( + Left(VarOrNodeOrLiteral::Variable(v)) | Right(VarOrNamedNodeOrLiteral::Variable(v)) => { + Right(plan.get_var_index(v.as_str()).ok_or(StdError::generic_err( + "Selected variable not found in query", + ))?) + } + Left(VarOrNodeOrLiteral::Node(Node::BlankNode(n))) => Right( + plan.get_bnode_index(n.as_str()) + .ok_or(StdError::generic_err( + "Selected blank node not found in query", + ))?, + ), + Left(VarOrNodeOrLiteral::Node(Node::NamedNode(iri))) + | Right(VarOrNamedNodeOrLiteral::NamedNode(iri)) => Left(Object::Named(iri_as_node( ns_resolver, storage, prefixes, iri, )?)), - VarOrNamedNodeOrLiteral::Literal(l) => { + Left(VarOrNodeOrLiteral::Literal(l)) | Right(VarOrNamedNodeOrLiteral::Literal(l)) => { Left(literal_as_object(ns_resolver, storage, prefixes, l)?) } }) diff --git a/contracts/okp4-cognitarium/src/querier/plan.rs b/contracts/okp4-cognitarium/src/querier/plan.rs index de6719b3..00bb2f15 100644 --- a/contracts/okp4-cognitarium/src/querier/plan.rs +++ b/contracts/okp4-cognitarium/src/querier/plan.rs @@ -22,18 +22,16 @@ pub enum PlanVariable { impl QueryPlan { /// Resolve the index corresponding to the variable name, if not attached to a blank node. pub fn get_var_index(&self, var_name: &str) -> Option { - self.variables - .iter() - .enumerate() - .find_map(|(index, it)| match it { - PlanVariable::Basic(name) => { - if name == var_name { - return Some(index); - } - None - } - PlanVariable::BlankNode(_) => None, - }) + self.variables.iter().enumerate().find_map(|(index, it)| { + matches!(it, PlanVariable::Basic(name) if name == var_name).then_some(index) + }) + } + + /// Resolve the index corresponding to blank node name. + pub fn get_bnode_index(&self, bnode_name: &str) -> Option { + self.variables.iter().enumerate().find_map(|(index, it)| { + matches!(it, PlanVariable::BlankNode(name) if name == bnode_name).then_some(index) + }) } } From 2e763eee87c05ea569a80f3e872f2b0b89161242 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 28 Feb 2024 17:16:09 +0100 Subject: [PATCH 18/22] fix(cognitarium): allow to reuse where as delete select --- contracts/okp4-cognitarium/src/contract.rs | 97 ++++++++++++---------- 1 file changed, 51 insertions(+), 46 deletions(-) diff --git a/contracts/okp4-cognitarium/src/contract.rs b/contracts/okp4-cognitarium/src/contract.rs index 108efca7..7f7c7a9f 100644 --- a/contracts/okp4-cognitarium/src/contract.rs +++ b/contracts/okp4-cognitarium/src/contract.rs @@ -50,12 +50,13 @@ pub fn execute( pub mod execute { use super::*; - use crate::msg::{DataFormat, Prefix, TripleDeleteTemplate, WhereClause}; + use crate::msg::{ + DataFormat, Prefix, SimpleWhereCondition, TripleDeleteTemplate, WhereClause, WhereCondition, + }; use crate::querier::{PlanBuilder, QueryEngine}; use crate::rdf::PrefixMap; use crate::state::{HasCachedNamespaces, Triple}; use crate::storer::StoreEngine; - use cosmwasm_std::Uint128; use either::{Left, Right}; use okp4_rdf::serde::TripleReader; use std::io::BufReader; @@ -95,28 +96,32 @@ pub mod execute { ) -> Result { verify_owner(&deps, &info)?; - if delete.is_empty() { - return Ok(Response::new() - .add_attribute("action", "delete") - .add_attribute("triple_count", Uint128::from(0u128))); - } + let delete = if delete.is_empty() { + Left( + r#where + .iter() + .map(|c| match c { + WhereCondition::Simple(SimpleWhereCondition::TriplePattern(t)) => { + (t.subject.clone(), t.predicate.clone(), t.object.clone()) + } + }) + .collect(), + ) + } else { + Right( + delete + .into_iter() + .map(|t| (t.subject, t.predicate, t.object)) + .collect(), + ) + }; let prefix_map = ::from(prefixes).into_inner(); let mut plan_builder = PlanBuilder::new(deps.storage, &prefix_map, None); let plan = plan_builder.build_plan(&r#where)?; let triples = QueryEngine::new(deps.storage) - .construct_triples( - plan, - &prefix_map, - Right( - delete - .into_iter() - .map(|t| (t.subject, t.predicate, t.object)) - .collect(), - ), - plan_builder.cached_namespaces(), - )? + .construct_triples(plan, &prefix_map, delete, plan_builder.cached_namespaces())? .collect::>>()?; let mut store = StoreEngine::new(deps.storage)?; @@ -957,34 +962,34 @@ mod tests { 2, Uint128::from(5272u128), ), - // ( - // DeleteData { - // prefixes: vec![], - // delete: vec![], - // r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { - // subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - // predicate: VarOrNamedNode::Variable("p".to_string()), - // object: VarOrNodeOrLiteral::Variable("o".to_string()), - // }))], - // }, - // 11, - // 2, - // Uint128::from(5272u128), - // ), - // ( - // DeleteData { - // prefixes: vec![], - // delete: vec![], - // r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { - // subject: VarOrNode::Variable("s".to_string()), - // predicate: VarOrNamedNode::Variable("p".to_string()), - // object: VarOrNodeOrLiteral::Variable("0".to_string()), - // }))], - // }, - // 40, - // 17, - // Uint128::from(0u128), - // ), + ( + DeleteData { + prefixes: vec![], + delete: vec![], + r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { + subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), + predicate: VarOrNamedNode::Variable("p".to_string()), + object: VarOrNodeOrLiteral::Variable("o".to_string()), + }))], + }, + 11, + 2, + Uint128::from(5272u128), + ), + ( + DeleteData { + prefixes: vec![], + delete: vec![], + r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { + subject: VarOrNode::Variable("s".to_string()), + predicate: VarOrNamedNode::Variable("p".to_string()), + object: VarOrNodeOrLiteral::Variable("0".to_string()), + }))], + }, + 40, + 17, + Uint128::from(0u128), + ), ]; for case in cases { From b574e5903656cd81b8c9a73b26e0fc0fde624fbe Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 28 Feb 2024 17:17:41 +0100 Subject: [PATCH 19/22] docs(cognitarium): update generated doc --- docs/okp4-cognitarium.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/okp4-cognitarium.md b/docs/okp4-cognitarium.md index 019224b5..9ef84626 100644 --- a/docs/okp4-cognitarium.md +++ b/docs/okp4-cognitarium.md @@ -415,12 +415,12 @@ Example: `json { "prefixes": [ { "prefix": "foaf", "namespace": "http://xmlns.co Only the smart contract owner (i.e. the address who instantiated it) is authorized to perform this action. -| parameter | description | -| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `delete_data` | _(Required.) _ **object**. | -| `delete_data.delete` | _(Required.) _ **Array<[TripleDeleteTemplate](#tripledeletetemplate)>**. Specifies the specific triple templates to delete. | -| `delete_data.prefixes` | _(Required.) _ **Array<[Prefix](#prefix)>**. The prefixes used in the operation. | -| `delete_data.where` | _(Required.) _ **Array<[WhereCondition](#wherecondition)>**. Defines the patterns that data (RDF triples) should match in order for it to be considered for deletion. | +| parameter | description | +| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `delete_data` | _(Required.) _ **object**. | +| `delete_data.delete` | _(Required.) _ **Array<[TripleDeleteTemplate](#tripledeletetemplate)>**. Specifies the specific triple templates to delete. If nothing is provided, the patterns from the `where` clause are used for deletion. | +| `delete_data.prefixes` | _(Required.) _ **Array<[Prefix](#prefix)>**. The prefixes used in the operation. | +| `delete_data.where` | _(Required.) _ **Array<[WhereCondition](#wherecondition)>**. Defines the patterns that data (RDF triples) should match in order for it to be considered for deletion. | ## QueryMsg @@ -536,11 +536,11 @@ An RDF [blank node](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node). Represents a CONSTRUCT query over the triple store, allowing to retrieve a set of triples serialized in a specific format. -| property | description | -| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `construct` | _(Required.) _ **Array<[TripleConstructTemplate](#tripleconstructtemplate)>**. The triples to construct. | -| `prefixes` | _(Required.) _ **Array<[Prefix](#prefix)>**. The prefixes used in the query. | -| `where` | _(Required.) _ **Array<[WhereCondition](#wherecondition)>**. The WHERE clause. This clause is used to specify the triples to construct using variable bindings. | +| property | description | +| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `construct` | _(Required.) _ **Array<[TripleConstructTemplate](#tripleconstructtemplate)>**. The triples to construct. If nothing is provided, the patterns from the `where` clause are used for construction. | +| `prefixes` | _(Required.) _ **Array<[Prefix](#prefix)>**. The prefixes used in the query. | +| `where` | _(Required.) _ **Array<[WhereCondition](#wherecondition)>**. The WHERE clause. This clause is used to specify the triples to construct using variable bindings. | ### DataFormat @@ -876,4 +876,4 @@ Represents a condition in a [WhereClause]. --- -_Rendered by [Fadroma](https://fadroma.tech) ([@fadroma/schema 1.1.0](https://www.npmjs.com/package/@fadroma/schema)) from `okp4-cognitarium.json` (`cd4c70c82335248d`)_ +_Rendered by [Fadroma](https://fadroma.tech) ([@fadroma/schema 1.1.0](https://www.npmjs.com/package/@fadroma/schema)) from `okp4-cognitarium.json` (`7aa73b0502a6138b`)_ From e22d9b93a501f9feae7a2c0788f14f17a31641ba Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 28 Feb 2024 18:00:17 +0100 Subject: [PATCH 20/22] feat(cognitarium): allow delete without where clause --- contracts/okp4-cognitarium/src/contract.rs | 71 +++++++++++++------ .../okp4-cognitarium/src/querier/engine.rs | 26 +++---- contracts/okp4-cognitarium/src/querier/mod.rs | 1 + .../src/querier/plan_builder.rs | 50 ++++++------- 4 files changed, 85 insertions(+), 63 deletions(-) diff --git a/contracts/okp4-cognitarium/src/contract.rs b/contracts/okp4-cognitarium/src/contract.rs index 7f7c7a9f..89f98ba0 100644 --- a/contracts/okp4-cognitarium/src/contract.rs +++ b/contracts/okp4-cognitarium/src/contract.rs @@ -53,7 +53,7 @@ pub mod execute { use crate::msg::{ DataFormat, Prefix, SimpleWhereCondition, TripleDeleteTemplate, WhereClause, WhereCondition, }; - use crate::querier::{PlanBuilder, QueryEngine}; + use crate::querier::{PlanBuilder, QueryEngine, ResolvedVariables}; use crate::rdf::PrefixMap; use crate::state::{HasCachedNamespaces, Triple}; use crate::storer::StoreEngine; @@ -120,9 +120,29 @@ pub mod execute { let mut plan_builder = PlanBuilder::new(deps.storage, &prefix_map, None); let plan = plan_builder.build_plan(&r#where)?; - let triples = QueryEngine::new(deps.storage) - .construct_triples(plan, &prefix_map, delete, plan_builder.cached_namespaces())? - .collect::>>()?; + let query_engine = QueryEngine::new(deps.storage); + let delete_templates = query_engine.make_triple_templates( + &plan, + &prefix_map, + delete, + plan_builder.cached_namespaces(), + )?; + + let triples = if r#where.is_empty() { + let empty_vars = ResolvedVariables::with_capacity(0); + delete_templates + .into_iter() + .filter_map(|tpl| match tpl.resolve(&empty_vars) { + Ok(Some(v)) => Some(Ok(v)), + Ok(None) => None, + Err(e) => Some(Err(e)), + }) + .collect::>>()? + } else { + query_engine + .construct_triples(plan, delete_templates)? + .collect::>>()? + }; let mut store = StoreEngine::new(deps.storage)?; let count = store.delete_all(&triples)?; @@ -990,6 +1010,31 @@ mod tests { 17, Uint128::from(0u128), ), + ( + DeleteData { + prefixes: vec![ + Prefix { + prefix: "core".to_string(), + namespace: "https://ontology.okp4.space/core/".to_string(), + }, + Prefix { + prefix: "thesaurus".to_string(), + namespace: "https://ontology.okp4.space/thesaurus/topic/".to_string(), + }, + ], + delete: vec![msg::TripleDeleteTemplate { + subject: VarOrNamedNode::NamedNode(Full(id.to_string())), + predicate: VarOrNamedNode::NamedNode(Prefixed("core:hasTopic".to_string())), + object: VarOrNamedNodeOrLiteral::NamedNode(Prefixed( + "thesaurus:Test".to_string(), + )), + }], + r#where: vec![], + }, + 1, + 0, + Uint128::from(6921u128), + ), ]; for case in cases { @@ -1100,24 +1145,6 @@ mod tests { }, expected: StdError::generic_err("Selected variable not found in query").into(), }, - TC { - command: DeleteData { - prefixes: vec![], - delete: vec![msg::TripleDeleteTemplate { - subject: VarOrNamedNode::NamedNode(Full( - "https://ontology.okp4.space/thesaurus/topic/Test".to_string(), - )), - predicate: VarOrNamedNode::NamedNode(Full( - "https://ontology.okp4.space/core/hasTopic".to_string(), - )), - object: VarOrNamedNodeOrLiteral::NamedNode(Full( - "https://ontology.okp4.space/thesaurus/topic/Test".to_string(), - )), - }], - r#where: vec![], - }, - expected: StdError::generic_err("Empty basic graph pattern").into(), - }, ]; for case in cases { diff --git a/contracts/okp4-cognitarium/src/querier/engine.rs b/contracts/okp4-cognitarium/src/querier/engine.rs index 9513fea9..834a90cc 100644 --- a/contracts/okp4-cognitarium/src/querier/engine.rs +++ b/contracts/okp4-cognitarium/src/querier/engine.rs @@ -78,26 +78,28 @@ impl<'a> QueryEngine<'a> { pub fn construct_triples( &'a self, plan: QueryPlan, + templates: Vec, + ) -> StdResult> { + ResolvedTripleIterator::try_new(self.eval_plan(plan), templates) + } + + pub fn make_triple_templates( + &'a self, + plan: &QueryPlan, prefixes: &HashMap, templates: Either< Vec<(VarOrNode, VarOrNamedNode, VarOrNodeOrLiteral)>, Vec<(VarOrNamedNode, VarOrNamedNode, VarOrNamedNodeOrLiteral)>, >, ns_cache: Vec, - ) -> StdResult> { + ) -> StdResult> { let mut ns_resolver: NamespaceResolver = ns_cache.into(); - let templates = match templates { + match templates { Left(tpl) => tpl .into_iter() .map(|t| { - TripleTemplate::try_new( - self.storage, - &mut ns_resolver, - &plan, - prefixes, - Left(t), - ) + TripleTemplate::try_new(self.storage, &mut ns_resolver, plan, prefixes, Left(t)) }) .collect::>>(), Right(tpl) => tpl @@ -106,15 +108,13 @@ impl<'a> QueryEngine<'a> { TripleTemplate::try_new( self.storage, &mut ns_resolver, - &plan, + plan, prefixes, Right(t), ) }) .collect::>>(), - }?; - - ResolvedTripleIterator::try_new(self.eval_plan(plan), templates) + } } pub fn eval_plan(&'a self, plan: QueryPlan) -> ResolvedVariablesIterator<'_> { diff --git a/contracts/okp4-cognitarium/src/querier/mod.rs b/contracts/okp4-cognitarium/src/querier/mod.rs index 4f068b7a..fe87d846 100644 --- a/contracts/okp4-cognitarium/src/querier/mod.rs +++ b/contracts/okp4-cognitarium/src/querier/mod.rs @@ -6,3 +6,4 @@ mod variable; pub use engine::*; pub use plan_builder::*; +pub use variable::ResolvedVariables; diff --git a/contracts/okp4-cognitarium/src/querier/plan_builder.rs b/contracts/okp4-cognitarium/src/querier/plan_builder.rs index 7b67a72b..baee369f 100644 --- a/contracts/okp4-cognitarium/src/querier/plan_builder.rs +++ b/contracts/okp4-cognitarium/src/querier/plan_builder.rs @@ -53,32 +53,27 @@ impl<'a> PlanBuilder<'a> { }) .collect::>>()?; - Self::build_from_bgp(bgp) - .map(|mut node| { - if let Some(skip) = self.skip { - node = QueryNode::Skip { - child: Box::new(node), - first: skip, - } - } - node - }) - .map(|mut node| { - if let Some(limit) = self.limit { - node = QueryNode::Limit { - child: Box::new(node), - first: limit, - } - } - node - }) - .map(|node| QueryPlan { - entrypoint: node, - variables: self.variables.clone(), - }) + let mut node = Self::build_from_bgp(bgp); + + if let Some(skip) = self.skip { + node = QueryNode::Skip { + child: Box::new(node), + first: skip, + } + } + if let Some(limit) = self.limit { + node = QueryNode::Limit { + child: Box::new(node), + first: limit, + } + } + Ok(QueryPlan { + entrypoint: node, + variables: self.variables.clone(), + }) } - fn build_from_bgp(bgp: Vec) -> StdResult { + fn build_from_bgp(bgp: Vec) -> QueryNode { bgp.into_iter() .reduce(|left: QueryNode, right: QueryNode| -> QueryNode { if left @@ -97,10 +92,9 @@ impl<'a> PlanBuilder<'a> { right: Box::new(right), } }) - .map_or_else( - || Err(StdError::generic_err("Empty basic graph pattern")), - Ok, - ) + .unwrap_or(QueryNode::Noop { + bound_variables: vec![], + }) } fn build_triple_pattern(&mut self, pattern: &TriplePattern) -> StdResult { From 9a7085ad4eadb29ec3f4a95765b7b982bc70e4ef Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 28 Feb 2024 18:09:21 +0100 Subject: [PATCH 21/22] style(cognitarium): make it readable --- contracts/okp4-cognitarium/src/contract.rs | 2 +- .../okp4-cognitarium/src/querier/engine.rs | 37 +++++++++---------- .../src/querier/plan_builder.rs | 9 ++++- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/contracts/okp4-cognitarium/src/contract.rs b/contracts/okp4-cognitarium/src/contract.rs index 89f98ba0..c422cf3a 100644 --- a/contracts/okp4-cognitarium/src/contract.rs +++ b/contracts/okp4-cognitarium/src/contract.rs @@ -140,7 +140,7 @@ pub mod execute { .collect::>>()? } else { query_engine - .construct_triples(plan, delete_templates)? + .construct_triples(plan, delete_templates) .collect::>>()? }; diff --git a/contracts/okp4-cognitarium/src/querier/engine.rs b/contracts/okp4-cognitarium/src/querier/engine.rs index 834a90cc..c48d8d80 100644 --- a/contracts/okp4-cognitarium/src/querier/engine.rs +++ b/contracts/okp4-cognitarium/src/querier/engine.rs @@ -66,31 +66,28 @@ impl<'a> QueryEngine<'a> { .map(|t| AtomTemplate::try_new(&plan, prefixes, t)) .collect::>>()?; - ResolvedAtomIterator::try_new( + Ok(ResolvedAtomIterator::new( self.storage, ns_cache.into(), IdentifierIssuer::new("b", 0u128), self.eval_plan(plan), templates, - ) + )) } pub fn construct_triples( &'a self, plan: QueryPlan, templates: Vec, - ) -> StdResult> { - ResolvedTripleIterator::try_new(self.eval_plan(plan), templates) + ) -> ResolvedTripleIterator<'_> { + ResolvedTripleIterator::new(self.eval_plan(plan), templates) } pub fn make_triple_templates( &'a self, plan: &QueryPlan, prefixes: &HashMap, - templates: Either< - Vec<(VarOrNode, VarOrNamedNode, VarOrNodeOrLiteral)>, - Vec<(VarOrNamedNode, VarOrNamedNode, VarOrNamedNodeOrLiteral)>, - >, + templates: Either, Vec>, ns_cache: Vec, ) -> StdResult> { let mut ns_resolver: NamespaceResolver = ns_cache.into(); @@ -536,15 +533,15 @@ pub struct ResolvedTripleIterator<'a> { } impl<'a> ResolvedTripleIterator<'a> { - pub fn try_new( + pub fn new( upstream_iter: ResolvedVariablesIterator<'a>, templates: Vec, - ) -> StdResult { - Ok(Self { + ) -> Self { + Self { upstream_iter, templates, buffer: VecDeque::new(), - }) + } } } @@ -590,16 +587,16 @@ pub struct TripleTemplate { object: Either, } +pub type TripleTemplateWithBlankNode = (VarOrNode, VarOrNamedNode, VarOrNodeOrLiteral); +pub type TripleTemplateNoBlankNode = (VarOrNamedNode, VarOrNamedNode, VarOrNamedNodeOrLiteral); + impl TripleTemplate { fn try_new( storage: &dyn Storage, ns_resolver: &mut NamespaceResolver, plan: &QueryPlan, prefixes: &HashMap, - template: Either< - (VarOrNode, VarOrNamedNode, VarOrNodeOrLiteral), - (VarOrNamedNode, VarOrNamedNode, VarOrNamedNodeOrLiteral), - >, + template: Either, ) -> StdResult { let (s_tpl, p_tpl, o_tpl) = match template { Right((s, p, o)) => (Right(s), p, Right(o)), @@ -759,21 +756,21 @@ pub struct ResolvedAtomIterator<'a> { } impl<'a> ResolvedAtomIterator<'a> { - pub fn try_new( + pub fn new( storage: &'a dyn Storage, ns_resolver: NamespaceResolver, id_issuer: IdentifierIssuer, upstream_iter: ResolvedVariablesIterator<'a>, templates: Vec, - ) -> StdResult { - Ok(Self { + ) -> Self { + Self { storage, ns_resolver, id_issuer, upstream_iter, templates, buffer: VecDeque::new(), - }) + } } } diff --git a/contracts/okp4-cognitarium/src/querier/plan_builder.rs b/contracts/okp4-cognitarium/src/querier/plan_builder.rs index baee369f..0d36e466 100644 --- a/contracts/okp4-cognitarium/src/querier/plan_builder.rs +++ b/contracts/okp4-cognitarium/src/querier/plan_builder.rs @@ -5,7 +5,7 @@ use crate::msg::{ use crate::querier::mapper::{iri_as_node, literal_as_object}; use crate::querier::plan::{PatternValue, PlanVariable, QueryNode, QueryPlan}; use crate::state::{HasCachedNamespaces, Namespace, NamespaceResolver, Object, Predicate, Subject}; -use cosmwasm_std::{StdError, StdResult, Storage}; +use cosmwasm_std::{StdResult, Storage}; use std::collections::HashMap; pub struct PlanBuilder<'a> { @@ -480,7 +480,12 @@ mod test { None, None, vec![], - Err(StdError::generic_err("Empty basic graph pattern")), + Ok(QueryPlan { + entrypoint: QueryNode::Noop { + bound_variables: vec![], + }, + variables: vec![], + }), ), ( None, From b7a12720a17b052aa08ef0d4b53d561cc1c91b40 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 28 Feb 2024 18:35:27 +0100 Subject: [PATCH 22/22] style(cognitarium): remove dead code --- contracts/okp4-cognitarium/src/msg.rs | 189 +------------------------- 1 file changed, 2 insertions(+), 187 deletions(-) diff --git a/contracts/okp4-cognitarium/src/msg.rs b/contracts/okp4-cognitarium/src/msg.rs index 0f11d04c..5ad0158b 100644 --- a/contracts/okp4-cognitarium/src/msg.rs +++ b/contracts/okp4-cognitarium/src/msg.rs @@ -1,7 +1,7 @@ use cosmwasm_schema::{cw_serde, QueryResponses}; use cosmwasm_std::{Binary, Uint128}; use derive_builder::Builder; -use std::collections::{BTreeMap, HashSet}; +use std::collections::BTreeMap; /// Instantiate message #[cw_serde] @@ -484,25 +484,6 @@ pub enum SimpleWhereCondition { TriplePattern(TriplePattern), } -pub trait HasVariables { - /// Returns the set of variables used in a triple pattern or template. - fn variables(&self) -> HashSet; - - /// Returns the set of variables used in a triple pattern or template as [SelectItem]. - fn as_select_item(&self) -> Vec { - self.variables() - .into_iter() - .map(SelectItem::Variable) - .collect() - } -} - -impl HasVariables for Vec { - fn variables(&self) -> HashSet { - self.iter().flat_map(HasVariables::variables).collect() - } -} - /// # TripleDeleteTemplate /// Represents a triple template to be deleted. #[cw_serde] @@ -515,16 +496,6 @@ pub struct TripleDeleteTemplate { pub object: VarOrNamedNodeOrLiteral, } -impl HasVariables for TripleDeleteTemplate { - fn variables(&self) -> HashSet { - var_util::merge_variables(&[ - self.subject.variable(), - self.predicate.variable(), - self.object.variable(), - ]) - } -} - /// # TripleConstructTemplate /// Represents a triple template to be forged for a construct query. #[cw_serde] @@ -537,16 +508,6 @@ pub struct TripleConstructTemplate { pub object: VarOrNodeOrLiteral, } -impl HasVariables for TripleConstructTemplate { - fn variables(&self) -> HashSet { - var_util::merge_variables(&[ - self.subject.variable(), - self.predicate.variable(), - self.object.variable(), - ]) - } -} - /// # TriplePattern /// Represents a triple pattern in a [SimpleWhereCondition]. #[cw_serde] @@ -559,33 +520,6 @@ pub struct TriplePattern { pub object: VarOrNodeOrLiteral, } -impl HasVariables for TriplePattern { - fn variables(&self) -> HashSet { - var_util::merge_variables(&[ - self.subject.variable(), - self.predicate.variable(), - self.object.variable(), - ]) - } -} - -trait MaybeVariable { - fn variable(&self) -> Option<&str>; -} - -mod var_util { - use std::collections::HashSet; - - pub fn merge_variables(maybe_vars: &[Option<&str>]) -> HashSet { - maybe_vars - .iter() - .copied() - .flatten() - .map(str::to_string) - .collect() - } -} - /// # VarOrNode /// Represents either a variable or a node. #[cw_serde] @@ -598,15 +532,6 @@ pub enum VarOrNode { Node(Node), } -impl MaybeVariable for VarOrNode { - fn variable(&self) -> Option<&str> { - match self { - Self::Variable(v) => Some(v.as_str()), - _ => None, - } - } -} - /// # VarOrNamedNode { /// Represents either a variable or a named node (IRI). #[cw_serde] @@ -619,15 +544,6 @@ pub enum VarOrNamedNode { NamedNode(IRI), } -impl MaybeVariable for VarOrNamedNode { - fn variable(&self) -> Option<&str> { - match self { - Self::Variable(v) => Some(v.as_str()), - _ => None, - } - } -} - /// # VarOrNodeOrLiteral /// Represents either a variable, a node or a literal. #[cw_serde] @@ -644,15 +560,6 @@ pub enum VarOrNodeOrLiteral { Literal(Literal), } -impl MaybeVariable for VarOrNodeOrLiteral { - fn variable(&self) -> Option<&str> { - match self { - Self::Variable(v) => Some(v.as_str()), - _ => None, - } - } -} - /// # VarOrNamedNodeOrLiteral /// Represents either a variable, a named node or a literal. #[cw_serde] @@ -669,15 +576,6 @@ pub enum VarOrNamedNodeOrLiteral { Literal(Literal), } -impl MaybeVariable for VarOrNamedNodeOrLiteral { - fn variable(&self) -> Option<&str> { - match self { - Self::Variable(v) => Some(v.as_str()), - _ => None, - } - } -} - /// # Literal /// An RDF [literal](https://www.w3.org/TR/rdf11-concepts/#dfn-literal). #[cw_serde] @@ -717,14 +615,7 @@ pub enum Node { #[cfg(test)] mod tests { - use super::*; - use crate::msg::Literal::Simple; - use crate::msg::Node::{BlankNode, NamedNode}; - use crate::msg::IRI::{Full, Prefixed}; - use crate::msg::{ - HasVariables, InstantiateMsg, StoreLimitsInput, TriplePattern, VarOrNode, - VarOrNodeOrLiteral, - }; + use crate::msg::{InstantiateMsg, StoreLimitsInput}; use cosmwasm_std::Uint128; use schemars::_serde_json; @@ -759,80 +650,4 @@ mod tests { assert_eq!(msg.limits.max_insert_data_byte_size, Uint128::MAX); assert_eq!(msg.limits.max_insert_data_triple_count, Uint128::MAX); } - - #[test] - fn variables_from_triple_pattern() { - let (s, p, o) = ("s".to_string(), "p".to_string(), "o".to_string()); - let (node, prefixed, literal) = ( - "node".to_string(), - "a:node".to_string(), - "literal".to_string(), - ); - - let cases = vec![ - ( - TriplePattern { - subject: VarOrNode::Variable(s.clone()), - predicate: VarOrNamedNode::Variable(p.clone()), - object: VarOrNodeOrLiteral::Variable(o.clone()), - }, - vec![s.clone(), p.clone(), o.clone()], - ), - ( - TriplePattern { - subject: VarOrNode::Node(NamedNode(Full(node.clone()))), - predicate: VarOrNamedNode::Variable(p.clone()), - object: VarOrNodeOrLiteral::Variable(o.clone()), - }, - vec![p.clone(), o.clone()], - ), - ( - TriplePattern { - subject: VarOrNode::Node(NamedNode(Prefixed(prefixed.clone()))), - predicate: VarOrNamedNode::Variable(p.clone()), - object: VarOrNodeOrLiteral::Variable(o.clone()), - }, - vec![p.clone(), o.clone()], - ), - ( - TriplePattern { - subject: VarOrNode::Node(BlankNode(node.clone())), - predicate: VarOrNamedNode::Variable(p.clone()), - object: VarOrNodeOrLiteral::Variable(o.clone()), - }, - vec![p.clone(), o.clone()], - ), - ( - TriplePattern { - subject: VarOrNode::Variable(s.clone()), - predicate: VarOrNamedNode::NamedNode(Full(node.clone())), - object: VarOrNodeOrLiteral::Variable(o.clone()), - }, - vec![s.clone(), o], - ), - ( - TriplePattern { - subject: VarOrNode::Variable(s.clone()), - predicate: VarOrNamedNode::Variable(p.clone()), - object: VarOrNodeOrLiteral::Literal(Simple(literal.clone())), - }, - vec![s, p], - ), - ( - TriplePattern { - subject: VarOrNode::Node(BlankNode(node)), - predicate: VarOrNamedNode::NamedNode(Prefixed(prefixed)), - object: VarOrNodeOrLiteral::Literal(Simple(literal)), - }, - vec![], - ), - ]; - - for (triple_pattern, expected) in cases { - assert_eq!( - triple_pattern.variables(), - HashSet::from_iter(expected.into_iter()) - ); - } - } }