From e59616d2db7b3fde54a6a83ce0927cbead43ab45 Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Tue, 27 Feb 2024 01:11:12 +0100 Subject: [PATCH] fix(cognitarium): consider blank node query as special variables --- .../okp4-cognitarium/src/querier/plan.rs | 33 +++-- .../src/querier/plan_builder.rs | 139 +++++++++++------- 2 files changed, 106 insertions(+), 66 deletions(-) diff --git a/contracts/okp4-cognitarium/src/querier/plan.rs b/contracts/okp4-cognitarium/src/querier/plan.rs index 4882770b..e2c73831 100644 --- a/contracts/okp4-cognitarium/src/querier/plan.rs +++ b/contracts/okp4-cognitarium/src/querier/plan.rs @@ -10,17 +10,30 @@ pub struct QueryPlan { /// Contains all the query variables, their index in this array are internally used as /// identifiers. - pub variables: Vec, + pub variables: Vec, +} + +#[derive(Eq, PartialEq, Debug, Clone)] +pub enum PlanVariable { + Basic(String), + BlankNode(String), } 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)| { - if it == var_name { - return Some(index); - } - None - }) + self.variables + .iter() + .enumerate() + .find_map(|(index, it)| match it { + PlanVariable::Basic(name) => { + if name == var_name { + return Some(index); + } + None + } + PlanVariable::BlankNode(_) => None, + }) } } @@ -44,7 +57,7 @@ pub enum QueryNode { /// Join two nodes by applying the cartesian product of the nodes variables. /// - /// This should be used when the nodes doesn't have variables in common, and can be seen as a + /// This should be used when the nodes don't have variables in common, and can be seen as a /// full join of disjoint datasets. CartesianProductJoin { left: Box, right: Box }, @@ -101,11 +114,13 @@ impl QueryNode { pub enum PatternValue { Constant(V), Variable(usize), + /// Special variable that is expected to resolve as a blank node. + BlankVariable(usize), } impl PatternValue { pub fn lookup_bound_variable(&self, callback: &mut impl FnMut(usize)) { - if let PatternValue::Variable(v) = self { + if let PatternValue::Variable(v) | PatternValue::BlankVariable(v) = self { callback(*v); } } diff --git a/contracts/okp4-cognitarium/src/querier/plan_builder.rs b/contracts/okp4-cognitarium/src/querier/plan_builder.rs index 9b094aed..bee1057b 100644 --- a/contracts/okp4-cognitarium/src/querier/plan_builder.rs +++ b/contracts/okp4-cognitarium/src/querier/plan_builder.rs @@ -1,10 +1,9 @@ use crate::msg::{ - SimpleWhereCondition, TriplePattern, VarOrNode, VarOrNodeOrLiteral, WhereClause, WhereCondition, + Node, SimpleWhereCondition, TriplePattern, VarOrNode, VarOrNodeOrLiteral, WhereClause, + WhereCondition, }; -use crate::querier::mapper::{ - literal_as_object, node_as_object, node_as_predicate, node_as_subject, -}; -use crate::querier::plan::{PatternValue, QueryNode, QueryPlan}; +use crate::querier::mapper::{iri_as_node, literal_as_object, node_as_predicate}; +use crate::querier::plan::{PatternValue, PlanVariable, QueryNode, QueryPlan}; use crate::state::{HasCachedNamespaces, Namespace, NamespaceResolver, Object, Predicate, Subject}; use cosmwasm_std::{StdError, StdResult, Storage}; use std::collections::HashMap; @@ -13,7 +12,7 @@ pub struct PlanBuilder<'a> { storage: &'a dyn Storage, ns_resolver: NamespaceResolver, prefixes: &'a HashMap, - variables: Vec, + variables: Vec, limit: Option, skip: Option, } @@ -143,19 +142,19 @@ impl<'a> PlanBuilder<'a> { fn build_subject_pattern(&mut self, value: VarOrNode) -> StdResult> { Ok(match value { - VarOrNode::Variable(v) => PatternValue::Variable(self.resolve_variable(v)), - VarOrNode::Node(n) => PatternValue::Constant(node_as_subject( - &mut self.ns_resolver, - self.storage, - self.prefixes, - n, - )?), + VarOrNode::Variable(v) => PatternValue::Variable(self.resolve_basic_variable(v)), + VarOrNode::Node(Node::BlankNode(b)) => { + PatternValue::BlankVariable(self.resolve_blank_variable(b)) + } + VarOrNode::Node(Node::NamedNode(iri)) => PatternValue::Constant(Subject::Named( + iri_as_node(&mut self.ns_resolver, self.storage, self.prefixes, iri)?, + )), }) } fn build_predicate_pattern(&mut self, value: VarOrNode) -> StdResult> { Ok(match value { - VarOrNode::Variable(v) => PatternValue::Variable(self.resolve_variable(v)), + VarOrNode::Variable(v) => PatternValue::Variable(self.resolve_basic_variable(v)), VarOrNode::Node(n) => PatternValue::Constant(node_as_predicate( &mut self.ns_resolver, self.storage, @@ -170,13 +169,20 @@ impl<'a> PlanBuilder<'a> { value: VarOrNodeOrLiteral, ) -> StdResult> { Ok(match value { - VarOrNodeOrLiteral::Variable(v) => PatternValue::Variable(self.resolve_variable(v)), - VarOrNodeOrLiteral::Node(n) => PatternValue::Constant(node_as_object( - &mut self.ns_resolver, - self.storage, - self.prefixes, - n, - )?), + VarOrNodeOrLiteral::Variable(v) => { + PatternValue::Variable(self.resolve_basic_variable(v)) + } + VarOrNodeOrLiteral::Node(Node::BlankNode(b)) => { + PatternValue::BlankVariable(self.resolve_blank_variable(b)) + } + VarOrNodeOrLiteral::Node(Node::NamedNode(iri)) => { + PatternValue::Constant(Object::Named(iri_as_node( + &mut self.ns_resolver, + self.storage, + self.prefixes, + iri, + )?)) + } VarOrNodeOrLiteral::Literal(l) => PatternValue::Constant(literal_as_object( &mut self.ns_resolver, self.storage, @@ -186,12 +192,27 @@ impl<'a> PlanBuilder<'a> { }) } - fn resolve_variable(&mut self, v: String) -> usize { - if let Some(index) = self.variables.iter().position(|name| name == &v) { + fn resolve_basic_variable(&mut self, v: String) -> usize { + if let Some(index) = self.variables.iter().position(|var| match var { + PlanVariable::Basic(name) => name == &v, + PlanVariable::BlankNode(_) => false, + }) { + return index; + } + + self.variables.push(PlanVariable::Basic(v)); + self.variables.len() - 1 + } + + fn resolve_blank_variable(&mut self, v: String) -> usize { + if let Some(index) = self.variables.iter().position(|var| match var { + PlanVariable::BlankNode(name) => name == &v, + PlanVariable::Basic(_) => false, + }) { return index; } - self.variables.push(v); + self.variables.push(PlanVariable::BlankNode(v)); self.variables.len() - 1 } } @@ -275,7 +296,7 @@ mod test { let builder = PlanBuilder::new(&deps.storage, prefixes, None); assert_eq!(builder.skip, None); assert_eq!(builder.limit, None); - assert_eq!(builder.variables, Vec::::new()); + assert_eq!(builder.variables, Vec::::new()); assert_eq!(builder.prefixes, &case.1); } @@ -307,19 +328,19 @@ mod test { ), ( TriplePattern { - subject: VarOrNode::Node(Node::BlankNode("_".to_string())), + subject: VarOrNode::Node(Node::BlankNode("1".to_string())), predicate: VarOrNode::Node(Node::NamedNode(IRI::Full( "http://okp4.space/hasTitle".to_string(), ))), - object: VarOrNodeOrLiteral::Node(Node::BlankNode("_".to_string())), + object: VarOrNodeOrLiteral::Node(Node::BlankNode("2".to_string())), }, Ok(QueryNode::TriplePattern { - subject: PatternValue::Constant(Subject::Blank("_".to_string())), + subject: PatternValue::BlankVariable(0usize), predicate: PatternValue::Constant(state::Node { namespace: 0u128, value: "hasTitle".to_string(), }), - object: PatternValue::Constant(Object::Blank("_".to_string())), + object: PatternValue::BlankVariable(1usize), }), ), ( @@ -337,7 +358,7 @@ mod test { namespace: 0u128, value: "123456789".to_string(), })), - predicate: PatternValue::Variable(1usize), + predicate: PatternValue::Variable(0usize), object: PatternValue::Constant(Object::Named(state::Node { namespace: 0u128, value: "1234567892".to_string(), @@ -351,8 +372,8 @@ mod test { object: VarOrNodeOrLiteral::Literal(Literal::Simple("simple".to_string())), }, Ok(QueryNode::TriplePattern { - subject: PatternValue::Variable(1usize), - predicate: PatternValue::Variable(0usize), + subject: PatternValue::Variable(0usize), + predicate: PatternValue::Variable(1usize), object: PatternValue::Constant(Object::Literal(state::Literal::Simple { value: "simple".to_string(), })), @@ -406,7 +427,7 @@ mod test { object: VarOrNodeOrLiteral::Variable("o".to_string()), }, Ok(QueryNode::Noop { - bound_variables: vec![1usize, 2usize], + bound_variables: vec![0usize, 1usize], }), ), ( @@ -418,7 +439,7 @@ mod test { object: VarOrNodeOrLiteral::Variable("o".to_string()), }, Ok(QueryNode::Noop { - bound_variables: vec![0usize, 2usize], + bound_variables: vec![0usize, 1usize], }), ), ( @@ -447,10 +468,10 @@ mod test { }, ) .unwrap(); - let prefixes = &PrefixMap::default().into_inner(); - let mut builder = PlanBuilder::new(&deps.storage, prefixes, None); - for case in cases { + let prefixes = &PrefixMap::default().into_inner(); + let mut builder = PlanBuilder::new(&deps.storage, prefixes, None); + assert_eq!(builder.build_triple_pattern(&case.0), case.1); } } @@ -490,7 +511,10 @@ mod test { entrypoint: QueryNode::Noop { bound_variables: vec![0usize, 1usize], }, - variables: vec!["predicate".to_string(), "object".to_string()], + variables: vec![ + PlanVariable::Basic("predicate".to_string()), + PlanVariable::Basic("object".to_string()), + ], }), ), ( @@ -508,9 +532,9 @@ mod test { object: PatternValue::Variable(2usize), }, variables: vec![ - "subject".to_string(), - "predicate".to_string(), - "object".to_string(), + PlanVariable::Basic("subject".to_string()), + PlanVariable::Basic("predicate".to_string()), + PlanVariable::Basic("object".to_string()), ], }), ), @@ -532,9 +556,9 @@ mod test { }), }, variables: vec![ - "subject".to_string(), - "predicate".to_string(), - "object".to_string(), + PlanVariable::Basic("subject".to_string()), + PlanVariable::Basic("predicate".to_string()), + PlanVariable::Basic("object".to_string()), ], }), ), @@ -556,9 +580,9 @@ mod test { }), }, variables: vec![ - "subject".to_string(), - "predicate".to_string(), - "object".to_string(), + PlanVariable::Basic("subject".to_string()), + PlanVariable::Basic("predicate".to_string()), + PlanVariable::Basic("object".to_string()), ], }), ), @@ -583,9 +607,9 @@ mod test { }), }, variables: vec![ - "subject".to_string(), - "predicate".to_string(), - "object".to_string(), + PlanVariable::Basic("subject".to_string()), + PlanVariable::Basic("predicate".to_string()), + PlanVariable::Basic("object".to_string()), ], }), ), @@ -626,16 +650,17 @@ mod test { right: Box::new(QueryNode::TriplePattern { subject: PatternValue::Variable(0usize), predicate: PatternValue::Variable(4usize), - object: PatternValue::Constant(Object::Blank("blank".to_string())), + object: PatternValue::BlankVariable(6usize), }), }, variables: vec![ - "var1".to_string(), - "var2".to_string(), - "var3".to_string(), - "var4".to_string(), - "var5".to_string(), - "var6".to_string(), + PlanVariable::Basic("var1".to_string()), + PlanVariable::Basic("var2".to_string()), + PlanVariable::Basic("var3".to_string()), + PlanVariable::Basic("var4".to_string()), + PlanVariable::Basic("var5".to_string()), + PlanVariable::Basic("var6".to_string()), + PlanVariable::BlankNode("blank".to_string()), ], }), ),