From da76633c3892b4defee99e3ab4c1de0b1618f24f Mon Sep 17 00:00:00 2001 From: Arnaud Mimart <33665250+amimart@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:41:09 +0200 Subject: [PATCH] feat(cognitarium)!: rework WhereClause api design adopt a more specific naming for the type of clauses and allow to combine multiple ones, simplifying the addition of new ones --- contracts/axone-cognitarium/src/contract.rs | 411 ++++++++++-------- contracts/axone-cognitarium/src/msg.rs | 38 +- .../axone-cognitarium/src/querier/mod.rs | 1 + .../axone-cognitarium/src/querier/plan.rs | 13 + .../src/querier/plan_builder.rs | 70 ++- 5 files changed, 279 insertions(+), 254 deletions(-) diff --git a/contracts/axone-cognitarium/src/contract.rs b/contracts/axone-cognitarium/src/contract.rs index 1793f660..fdb1a43a 100644 --- a/contracts/axone-cognitarium/src/contract.rs +++ b/contracts/axone-cognitarium/src/contract.rs @@ -53,10 +53,8 @@ pub fn execute( pub mod execute { use super::*; - use crate::msg::{ - DataFormat, Prefix, SimpleWhereCondition, TripleDeleteTemplate, WhereClause, WhereCondition, - }; - use crate::querier::{PlanBuilder, QueryEngine, ResolvedVariables}; + use crate::msg::{DataFormat, Prefix, TripleDeleteTemplate, WhereClause}; + use crate::querier::{PlanBuilder, QueryEngine, QueryPlan, ResolvedVariables}; use crate::rdf::PrefixMap; use crate::state::{HasCachedNamespaces, Triple}; use crate::storer::StoreEngine; @@ -95,21 +93,18 @@ pub mod execute { info: MessageInfo, prefixes: Vec, delete: Vec, - r#where: WhereClause, + r#where: Option, ) -> Result { verify_owner(&deps, &info)?; let delete = if delete.is_empty() { - Left( - r#where + Left(match r#where { + Some(WhereClause::Bgp { ref patterns }) => patterns .iter() - .map(|c| match c { - WhereCondition::Simple(SimpleWhereCondition::TriplePattern(t)) => { - (t.subject.clone(), t.predicate.clone(), t.object.clone()) - } - }) + .map(|p| (p.subject.clone(), p.predicate.clone(), p.object.clone())) .collect(), - ) + _ => Err(StdError::generic_err("Missing triple templates to delete"))?, + }) } else { Right( delete @@ -121,7 +116,10 @@ pub mod execute { 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 plan = match r#where { + Some(ref w) => plan_builder.build_plan(w)?, + None => QueryPlan::empty_plan(), + }; let query_engine = QueryEngine::new(deps.storage); let delete_templates = query_engine.make_triple_templates( @@ -131,7 +129,7 @@ pub mod execute { plan_builder.cached_namespaces(), )?; - let triples = if r#where.is_empty() { + let triples = if r#where.is_none() { let empty_vars = ResolvedVariables::with_capacity(0); delete_templates .into_iter() @@ -176,8 +174,8 @@ pub mod query { use super::*; use crate::msg::{ ConstructQuery, ConstructResponse, DescribeQuery, DescribeResponse, Node, SelectQuery, - SelectResponse, SimpleWhereCondition, StoreResponse, TripleConstructTemplate, - TriplePattern, VarOrNamedNode, VarOrNode, VarOrNodeOrLiteral, WhereCondition, + SelectResponse, StoreResponse, TripleConstructTemplate, TriplePattern, VarOrNamedNode, + VarOrNode, VarOrNodeOrLiteral, WhereClause, }; use crate::querier::{PlanBuilder, QueryEngine}; use crate::rdf::PrefixMap; @@ -227,10 +225,17 @@ pub mod query { object: VarOrNodeOrLiteral::Variable(format!("{var}{o}")), }; - let mut r#where = query.r#where; - r#where.push(WhereCondition::Simple(SimpleWhereCondition::TriplePattern( - select.clone(), - ))); + let r#where = match query.r#where { + Some(c) => WhereClause::LateralJoin { + left: Box::new(c), + right: Box::new(WhereClause::Bgp { + patterns: vec![select.clone()], + }), + }, + None => WhereClause::Bgp { + patterns: vec![select.clone()], + }, + }; (vec![select], r#where) } @@ -243,9 +248,9 @@ pub mod query { ( vec![select.clone()], - vec![WhereCondition::Simple(SimpleWhereCondition::TriplePattern( - select, - ))], + WhereClause::Bgp { + patterns: vec![select.clone()], + }, ) } }; @@ -279,18 +284,17 @@ pub mod query { } = query; 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() + match &r#where { + WhereClause::Bgp { patterns } => patterns + .iter() + .map(|p| TripleConstructTemplate { + subject: p.subject.clone(), + predicate: p.predicate.clone(), + object: p.object.clone(), + }) + .collect(), + _ => Err(StdError::generic_err("missing triples to construct"))?, + } } else { construct }; @@ -431,14 +435,14 @@ mod tests { use crate::error::StoreError; use crate::msg::ExecuteMsg::{DeleteData, InsertData}; use crate::msg::Node::{BlankNode, NamedNode}; - use crate::msg::SimpleWhereCondition::TriplePattern; use crate::msg::IRI::{Full, Prefixed}; use crate::msg::{ ConstructQuery, ConstructResponse, DescribeQuery, DescribeResponse, Head, Literal, Prefix, Results, SelectItem, SelectQuery, SelectResponse, StoreLimitsInput, StoreLimitsInputBuilder, StoreResponse, Value, VarOrNamedNode, VarOrNamedNodeOrLiteral, - VarOrNode, VarOrNodeOrLiteral, WhereCondition, + VarOrNode, VarOrNodeOrLiteral, }; + use crate::msg::{TriplePattern, WhereClause}; use crate::state::{ namespaces, triples, Namespace, Node, Object, StoreLimits, StoreStat, Subject, Triple, }; @@ -531,7 +535,7 @@ mod tests { DeleteData { prefixes: vec![], delete: vec![], - r#where: vec![], + r#where: None, }, ]; @@ -910,18 +914,21 @@ mod tests { "https://ontology.axone.space/thesaurus/topic/Test".to_string(), )), }], - r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Full( - "https://ontology.axone.space/dataverse/dataspace/metadata/unknown" - .to_string(), - ))), - predicate: VarOrNamedNode::NamedNode(Full( - "https://ontology.axone.space/core/hasTopic".to_string(), - )), - object: VarOrNodeOrLiteral::Node(NamedNode(Full( - "https://ontology.axone.space/thesaurus/topic/Test".to_string(), - ))), - }))], + r#where: WhereClause::Bgp { + patterns: vec![TriplePattern { + subject: VarOrNode::Node(NamedNode(Full( + "https://ontology.axone.space/dataverse/dataspace/metadata/unknown" + .to_string(), + ))), + predicate: VarOrNamedNode::NamedNode(Full( + "https://ontology.axone.space/core/hasTopic".to_string(), + )), + object: VarOrNodeOrLiteral::Node(NamedNode(Full( + "https://ontology.axone.space/thesaurus/topic/Test".to_string(), + ))), + }], + } + .into(), }, 0, 0, @@ -939,15 +946,18 @@ mod tests { "https://ontology.axone.space/thesaurus/topic/Test".to_string(), )), }], - r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - predicate: VarOrNamedNode::NamedNode(Full( - "https://ontology.axone.space/core/hasTopic".to_string(), - )), - object: VarOrNodeOrLiteral::Node(NamedNode(Full( - "https://ontology.axone.space/thesaurus/topic/Test".to_string(), - ))), - }))], + r#where: WhereClause::Bgp { + patterns: vec![TriplePattern { + subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), + predicate: VarOrNamedNode::NamedNode(Full( + "https://ontology.axone.space/core/hasTopic".to_string(), + )), + object: VarOrNodeOrLiteral::Node(NamedNode(Full( + "https://ontology.axone.space/thesaurus/topic/Test".to_string(), + ))), + }], + } + .into(), }, 1, 0, @@ -972,13 +982,18 @@ mod tests { "thesaurus:Test".to_string(), )), }], - r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), - predicate: VarOrNamedNode::NamedNode(Prefixed("core:hasTopic".to_string())), - object: VarOrNodeOrLiteral::Node(NamedNode(Prefixed( - "thesaurus:Test".to_string(), - ))), - }))], + r#where: WhereClause::Bgp { + patterns: vec![TriplePattern { + subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), + predicate: VarOrNamedNode::NamedNode(Prefixed( + "core:hasTopic".to_string(), + )), + object: VarOrNodeOrLiteral::Node(NamedNode(Prefixed( + "thesaurus:Test".to_string(), + ))), + }], + } + .into(), }, 1, 0, @@ -1001,11 +1016,16 @@ mod tests { 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: VarOrNamedNode::NamedNode(Prefixed("core:hasTopic".to_string())), - object: VarOrNodeOrLiteral::Variable("o".to_string()), - }))], + r#where: WhereClause::Bgp { + patterns: vec![TriplePattern { + subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), + predicate: VarOrNamedNode::NamedNode(Prefixed( + "core:hasTopic".to_string(), + )), + object: VarOrNodeOrLiteral::Variable("o".to_string()), + }], + } + .into(), }, 1, 0, @@ -1019,11 +1039,14 @@ mod tests { 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: VarOrNamedNode::Variable("p".to_string()), - object: VarOrNodeOrLiteral::Variable("o".to_string()), - }))], + r#where: WhereClause::Bgp { + patterns: vec![TriplePattern { + subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), + predicate: VarOrNamedNode::Variable("p".to_string()), + object: VarOrNodeOrLiteral::Variable("o".to_string()), + }], + } + .into(), }, 11, 2, @@ -1033,11 +1056,14 @@ mod tests { 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()), - }))], + r#where: WhereClause::Bgp { + patterns: vec![TriplePattern { + subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), + predicate: VarOrNamedNode::Variable("p".to_string()), + object: VarOrNodeOrLiteral::Variable("o".to_string()), + }], + } + .into(), }, 11, 2, @@ -1047,11 +1073,14 @@ mod tests { 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()), - }))], + r#where: WhereClause::Bgp { + patterns: vec![TriplePattern { + subject: VarOrNode::Variable("s".to_string()), + predicate: VarOrNamedNode::Variable("p".to_string()), + object: VarOrNodeOrLiteral::Variable("0".to_string()), + }], + } + .into(), }, 40, 17, @@ -1076,7 +1105,7 @@ mod tests { "thesaurus:Test".to_string(), )), }], - r#where: vec![], + r#where: None, }, 1, 0, @@ -1160,15 +1189,18 @@ mod tests { "https://ontology.axone.space/thesaurus/topic/Test".to_string(), )), }], - r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { - subject: VarOrNode::Node(NamedNode(Prefixed("foo:bar".to_string()))), - predicate: VarOrNamedNode::NamedNode(Full( - "https://ontology.axone.space/core/hasTopic".to_string(), - )), - object: VarOrNodeOrLiteral::Node(NamedNode(Full( - "https://ontology.axone.space/thesaurus/topic/Test".to_string(), - ))), - }))], + r#where: WhereClause::Bgp { + patterns: vec![TriplePattern { + subject: VarOrNode::Node(NamedNode(Prefixed("foo:bar".to_string()))), + predicate: VarOrNamedNode::NamedNode(Full( + "https://ontology.axone.space/core/hasTopic".to_string(), + )), + object: VarOrNodeOrLiteral::Node(NamedNode(Full( + "https://ontology.axone.space/thesaurus/topic/Test".to_string(), + ))), + }], + } + .into(), }, expected: StdError::generic_err("Prefix not found: foo").into(), }, @@ -1182,13 +1214,16 @@ mod tests { 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.axone.space/thesaurus/topic/Test".to_string(), - ))), - predicate: VarOrNamedNode::Variable("p".to_string()), - object: VarOrNodeOrLiteral::Variable("o".to_string()), - }))], + r#where: WhereClause::Bgp { + patterns: vec![TriplePattern { + subject: VarOrNode::Node(NamedNode(Full( + "https://ontology.axone.space/thesaurus/topic/Test".to_string(), + ))), + predicate: VarOrNamedNode::Variable("p".to_string()), + object: VarOrNodeOrLiteral::Variable("o".to_string()), + }], + } + .into(), }, expected: StdError::generic_err("Selected variable not found in query").into(), }, @@ -1299,15 +1334,14 @@ mod tests { SelectItem::Variable("a".to_string()), SelectItem::Variable("b".to_string()), ], - r#where: vec![WhereCondition::Simple(TriplePattern( - msg::TriplePattern { + r#where: WhereClause::Bgp{patterns:vec![TriplePattern { subject: VarOrNode::Variable("a".to_string()), predicate: VarOrNamedNode::NamedNode(Full( "https://ontology.axone.space/core/hasDescription".to_string(), )), object: VarOrNodeOrLiteral::Variable("b".to_string()), }, - ))], + ]}, limit: None, }, SelectResponse { @@ -1390,15 +1424,14 @@ mod tests { select: vec![ SelectItem::Variable("a".to_string()), ], - r#where: vec![WhereCondition::Simple(TriplePattern( - msg::TriplePattern { + r#where: WhereClause::Bgp{patterns:vec![TriplePattern { subject: VarOrNode::Variable("a".to_string()), predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasDescription".to_string(), )), object: VarOrNodeOrLiteral::Literal(Literal::LanguageTaggedString { value: "A test Dataset.".to_string(), language: "en".to_string() }), }, - ))], + ]}, limit: None, }, SelectResponse { @@ -1425,13 +1458,12 @@ mod tests { select: vec![ SelectItem::Variable("a".to_string()), ], - r#where: vec![WhereCondition::Simple(TriplePattern( - msg::TriplePattern { + r#where: WhereClause::Bgp{patterns:vec![TriplePattern { subject: VarOrNode::Node(NamedNode(Full("https://ontology.axone.space/dataverse/dataset/metadata/d1615703-4ee1-4e2f-997e-15aecf1eea4e".to_string()))), predicate: VarOrNamedNode::Variable("a".to_string()), object: VarOrNodeOrLiteral::Literal(Literal::LanguageTaggedString { value: "A test Dataset.".to_string(), language: "en".to_string() }), }, - ))], + ]}, limit: None, }, SelectResponse { @@ -1492,25 +1524,22 @@ mod tests { SelectQuery { prefixes: vec![Prefix { prefix: "core".to_string(), namespace: "https://ontology.axone.space/core/".to_string() }], select: vec![SelectItem::Variable("a".to_string()), SelectItem::Variable("b".to_string())], - r#where: vec![ - WhereCondition::Simple(TriplePattern( - msg::TriplePattern { + r#where: WhereClause::Bgp{patterns:vec![ + TriplePattern { subject: VarOrNode::Variable("a".to_string()), predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasTemporalCoverage".to_string(), )), object: VarOrNodeOrLiteral::Node(BlankNode("a".to_string())), }, - )), - WhereCondition::Simple(TriplePattern( - msg::TriplePattern { + TriplePattern { subject: VarOrNode::Node(BlankNode("a".to_string())), predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasStartDate".to_string(), )), object: VarOrNodeOrLiteral::Variable("b".to_string()), }, - ))], + ]}, limit: None, }, SelectResponse { @@ -1541,25 +1570,22 @@ mod tests { SelectQuery { prefixes: vec![Prefix { prefix: "core".to_string(), namespace: "https://ontology.axone.space/core/".to_string() }], select: vec![SelectItem::Variable("a".to_string()), SelectItem::Variable("b".to_string())], - r#where: vec![ - WhereCondition::Simple(TriplePattern( - msg::TriplePattern { + r#where: WhereClause::Bgp{patterns:vec![ + TriplePattern { subject: VarOrNode::Variable("a".to_string()), predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasTemporalCoverage".to_string(), )), object: VarOrNodeOrLiteral::Variable("blank".to_string()), }, - )), - WhereCondition::Simple(TriplePattern( - msg::TriplePattern { + TriplePattern { subject: VarOrNode::Variable("blank".to_string()), predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasStartDate".to_string(), )), object: VarOrNodeOrLiteral::Variable("b".to_string()), - }, - ))], + } + ]}, limit: None, }, SelectResponse { @@ -1590,25 +1616,22 @@ mod tests { SelectQuery { prefixes: vec![Prefix { prefix: "core".to_string(), namespace: "https://ontology.axone.space/core/".to_string() }], select: vec![SelectItem::Variable("a".to_string()), SelectItem::Variable("b".to_string())], - r#where: vec![ - WhereCondition::Simple(TriplePattern( - msg::TriplePattern { + r#where: WhereClause::Bgp{patterns:vec![ + TriplePattern { subject: VarOrNode::Variable("a".to_string()), predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasTemporalCoverage".to_string(), )), object: VarOrNodeOrLiteral::Node(BlankNode("blank1".to_string())), }, - )), - WhereCondition::Simple(TriplePattern( - msg::TriplePattern { + TriplePattern { subject: VarOrNode::Node(BlankNode("blank2".to_string())), predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasInformation".to_string(), )), object: VarOrNodeOrLiteral::Variable("b".to_string()), }, - ))], + ]}, limit: None, }, SelectResponse { @@ -1639,16 +1662,15 @@ mod tests { SelectQuery { prefixes: vec![Prefix { prefix: "core".to_string(), namespace: "https://ontology.axone.space/core/".to_string() }], select: vec![SelectItem::Variable("a".to_string()), SelectItem::Variable("b".to_string())], - r#where: vec![ - WhereCondition::Simple(TriplePattern( - msg::TriplePattern { + r#where: WhereClause::Bgp{patterns:vec![ + TriplePattern { subject: VarOrNode::Variable("a".to_string()), predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasTemporalCoverage".to_string(), )), object: VarOrNodeOrLiteral::Variable("b".to_string()), }, - ))], + ]}, limit: None, }, SelectResponse { @@ -1716,7 +1738,7 @@ mod tests { SelectItem::Variable("a".to_string()), SelectItem::Variable("b".to_string()), ], - r#where: vec![], + r#where: WhereClause::Bgp { patterns: vec![] }, limit: None, }, Err(StdError::generic_err( @@ -1727,7 +1749,7 @@ mod tests { SelectQuery { prefixes: vec![], select: vec![], - r#where: vec![], + r#where: WhereClause::Bgp { patterns: vec![] }, limit: Some(8000), }, Err(StdError::generic_err("Maximum query limit exceeded")), @@ -1739,16 +1761,18 @@ mod tests { namespace: "https://ontology.axone.space/core/".to_string(), }], select: vec![SelectItem::Variable("a".to_string())], - r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { - subject: VarOrNode::Variable("a".to_string()), - predicate: VarOrNamedNode::NamedNode(Prefixed( - "invalid:hasDescription".to_string(), - )), - object: VarOrNodeOrLiteral::Literal(Literal::LanguageTaggedString { - value: "A test Dataset.".to_string(), - language: "en".to_string(), - }), - }))], + r#where: WhereClause::Bgp { + patterns: vec![TriplePattern { + subject: VarOrNode::Variable("a".to_string()), + predicate: VarOrNamedNode::NamedNode(Prefixed( + "invalid:hasDescription".to_string(), + )), + object: VarOrNodeOrLiteral::Literal(Literal::LanguageTaggedString { + value: "A test Dataset.".to_string(), + language: "en".to_string(), + }), + }], + }, limit: None, }, Err(StdError::generic_err("Prefix not found: invalid")), @@ -1757,16 +1781,18 @@ mod tests { SelectQuery { prefixes: vec![], select: vec![SelectItem::Variable("u".to_string())], - r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { - subject: VarOrNode::Variable("a".to_string()), - predicate: VarOrNamedNode::NamedNode(Full( - "https://ontology.axone.space/core/hasDescription".to_string(), - )), - object: VarOrNodeOrLiteral::Literal(Literal::LanguageTaggedString { - value: "A test Dataset.".to_string(), - language: "en".to_string(), - }), - }))], + r#where: WhereClause::Bgp { + patterns: vec![TriplePattern { + subject: VarOrNode::Variable("a".to_string()), + predicate: VarOrNamedNode::NamedNode(Full( + "https://ontology.axone.space/core/hasDescription".to_string(), + )), + object: VarOrNodeOrLiteral::Literal(Literal::LanguageTaggedString { + value: "A test Dataset.".to_string(), + language: "en".to_string(), + }), + }], + }, limit: None, }, Err(StdError::generic_err( @@ -1816,7 +1842,7 @@ mod tests { query: DescribeQuery { prefixes: vec![], resource: VarOrNamedNode::NamedNode(Full("https://ontology.axone.space/dataverse/dataspace/metadata/dcf48417-01c5-4b43-9bc7-49e54c028473".to_string())), - r#where: vec![], + r#where: None, }, format: Some(DataFormat::Turtle), }, @@ -1839,7 +1865,7 @@ mod tests { query: DescribeQuery { prefixes: vec![], resource: VarOrNamedNode::NamedNode(Full("https://ontology.axone.space/dataverse/dataspace/metadata/dcf48417-01c5-4b43-9bc7-49e54c028473".to_string())), - r#where: vec![], + r#where: None, }, format: Some(DataFormat::RDFXml), }, @@ -1868,15 +1894,15 @@ mod tests { query: DescribeQuery { prefixes: vec![], resource: VarOrNamedNode::NamedNode(Full("https://ontology.axone.space/dataverse/dataspace/metadata/dcf48417-01c5-4b43-9bc7-49e54c028473".to_string())), - r#where: vec![WhereCondition::Simple(TriplePattern( - msg::TriplePattern { + r#where: WhereClause::Bgp { patterns: vec![ + TriplePattern { subject: VarOrNode::Variable("a".to_string()), predicate: VarOrNamedNode::NamedNode(Full( "https://ontology.axone.space/core/hasDescription".to_string(), )), object: VarOrNodeOrLiteral::Variable("b".to_string()), }, - ))], + ]}.into(), }, format: Some(DataFormat::NTriples), }, @@ -1903,7 +1929,7 @@ mod tests { query: DescribeQuery { prefixes: vec![], resource: VarOrNamedNode::NamedNode(Full("https://ontology.axone.space/dataverse/dataspace/metadata/dcf48417-01c5-4b43-9bc7-49e54c028473".to_string())), - r#where: vec![], + r#where: None, }, format: Some(DataFormat::NQuads), }, @@ -1977,7 +2003,7 @@ mod tests { }, ], resource: VarOrNamedNode::NamedNode(Prefixed("metadata:dcf48417-01c5-4b43-9bc7-49e54c028473".to_string())), - r#where: vec![], + r#where: None, }, format: Some(DataFormat::Turtle), }, @@ -2042,15 +2068,15 @@ mod tests { query: DescribeQuery { prefixes: vec![Prefix { prefix: "core".to_string(), namespace: "https://ontology.axone.space/core/".to_string() }], resource: VarOrNamedNode::Variable("a".to_string()), - r#where: vec![WhereCondition::Simple(TriplePattern( - msg::TriplePattern { + r#where: WhereClause::Bgp {patterns: vec![ + TriplePattern { subject: VarOrNode::Variable("a".to_string()), predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasDescription".to_string(), )), object: VarOrNodeOrLiteral::Literal(Literal::LanguageTaggedString { value: "A test Dataset.".to_string(), language: "en".to_string() }), }, - ))], + ]}.into(), }, format: Some(DataFormat::Turtle), }, @@ -2100,22 +2126,22 @@ mod tests { } #[test] - fn variable_mutiple_resources_describe() { + fn variable_multiple_resources_describe() { let cases = vec![ ( QueryMsg::Describe { query: DescribeQuery { prefixes: vec![Prefix { prefix: "core".to_string(), namespace: "https://ontology.axone.space/core/".to_string() }], resource: VarOrNamedNode::Variable("a".to_string()), - r#where: vec![WhereCondition::Simple(TriplePattern( - msg::TriplePattern { + r#where: WhereClause::Bgp {patterns: vec![ + TriplePattern { subject: VarOrNode::Variable("a".to_string()), predicate: VarOrNamedNode::NamedNode(Prefixed( "core:hasPublisher".to_string(), )), object: VarOrNodeOrLiteral::Literal(Literal::Simple("AXONE".to_string())), }, - ))], + ]}.into(), }, format: Some(DataFormat::Turtle), }, @@ -2175,16 +2201,15 @@ mod tests { Prefix { prefix: "metadata-dataset".to_string(), namespace: "https://ontology.axone.space/dataverse/dataset/metadata/".to_string() }, ], resource: VarOrNamedNode::Variable("x".to_string()), - r#where: vec![WhereCondition::Simple(TriplePattern( - msg::TriplePattern { + r#where: WhereClause::Bgp {patterns: vec![ + 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("x".to_string()), }, - )), - ], + ]}.into(), }, format: Some(DataFormat::Turtle), }, @@ -2246,13 +2271,13 @@ mod tests { query: ConstructQuery { prefixes: vec![], construct: vec![], - r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { + r#where: WhereClause::Bgp{patterns:vec![TriplePattern { subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), predicate: VarOrNamedNode::NamedNode(Full( "https://ontology.axone.space/core/hasTag".to_string(), )), object: VarOrNodeOrLiteral::Variable("o".to_string()), - }))], + }]}, }, format: None, }, @@ -2282,13 +2307,13 @@ mod tests { object: VarOrNodeOrLiteral::Variable("o".to_string()), } ], - r#where: vec![WhereCondition::Simple(TriplePattern(msg::TriplePattern { + r#where: WhereClause::Bgp{patterns:vec![TriplePattern { subject: VarOrNode::Node(NamedNode(Full(id.to_string()))), predicate: VarOrNamedNode::NamedNode(Full( "https://ontology.axone.space/core/hasTag".to_string(), )), object: VarOrNodeOrLiteral::Variable("o".to_string()), - }))], + }]}, }, format: Some(DataFormat::NTriples), }, @@ -2335,32 +2360,32 @@ mod tests { object: VarOrNodeOrLiteral::Variable("info_o".to_string()), } ], - r#where: vec![ - WhereCondition::Simple(TriplePattern(msg::TriplePattern { + r#where: WhereClause::Bgp {patterns:vec![ + 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 { + }, + 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 { + }, + 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 { + }, + 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), }, diff --git a/contracts/axone-cognitarium/src/msg.rs b/contracts/axone-cognitarium/src/msg.rs index 9885d6f5..6bd049e1 100644 --- a/contracts/axone-cognitarium/src/msg.rs +++ b/contracts/axone-cognitarium/src/msg.rs @@ -68,11 +68,12 @@ 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. + /// If nothing is provided and the `where` clause is a single Bgp, the patterns are used for + /// deletion. delete: Vec, /// Defines the patterns that data (RDF triples) should match in order for it to be - /// considered for deletion. - r#where: WhereClause, + /// considered for deletion, if any. + r#where: Option, }, } @@ -424,7 +425,7 @@ pub struct DescribeQuery { pub resource: VarOrNamedNode, /// The WHERE clause. /// This clause is used to specify the resource identifier to describe using variable bindings. - pub r#where: WhereClause, + pub r#where: Option, } /// # ConstructQuery @@ -435,7 +436,8 @@ 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. + /// If nothing is provided and the `where` clause is a single Bgp, the patterns are used for + /// construction. pub construct: Vec, /// The WHERE clause. /// This clause is used to specify the triples to construct using variable bindings. @@ -463,25 +465,15 @@ pub enum SelectItem { /// # WhereClause /// Represents a WHERE clause in a [SelectQuery], i.e. a set of conditions to filter the results. -pub type WhereClause = Vec; - -/// # WhereCondition -/// Represents a condition in a [WhereClause]. -#[cw_serde] -pub enum WhereCondition { - /// # Simple - /// Represents a simple condition. - Simple(SimpleWhereCondition), -} - -/// # SimpleWhereCondition -/// Represents a simple condition in a [WhereCondition]. #[cw_serde] -pub enum SimpleWhereCondition { - /// # TriplePattern - /// Represents a triple pattern, i.e. a condition on a triple based on its subject, predicate and - /// object. - TriplePattern(TriplePattern), +pub enum WhereClause { + /// # Bgp + /// Represents a basic graph pattern expressed as a set of triple patterns. + Bgp { patterns: Vec }, + + /// # LateralJoin + /// Evaluates right for all result row of left + LateralJoin { left: Box, right: Box }, } /// # TripleDeleteTemplate diff --git a/contracts/axone-cognitarium/src/querier/mod.rs b/contracts/axone-cognitarium/src/querier/mod.rs index fe87d846..b9ecd6ff 100644 --- a/contracts/axone-cognitarium/src/querier/mod.rs +++ b/contracts/axone-cognitarium/src/querier/mod.rs @@ -5,5 +5,6 @@ mod plan_builder; mod variable; pub use engine::*; +pub use plan::*; pub use plan_builder::*; pub use variable::ResolvedVariables; diff --git a/contracts/axone-cognitarium/src/querier/plan.rs b/contracts/axone-cognitarium/src/querier/plan.rs index 00bb2f15..6feafd07 100644 --- a/contracts/axone-cognitarium/src/querier/plan.rs +++ b/contracts/axone-cognitarium/src/querier/plan.rs @@ -20,6 +20,13 @@ pub enum PlanVariable { } impl QueryPlan { + pub fn empty_plan() -> Self { + Self { + entrypoint: QueryNode::noop(), + variables: Vec::new(), + } + } + /// 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)| { @@ -74,6 +81,12 @@ pub enum QueryNode { } impl QueryNode { + pub fn noop() -> Self { + QueryNode::Noop { + bound_variables: Vec::new(), + } + } + pub fn bound_variables(&self) -> BTreeSet { let mut vars = BTreeSet::new(); self.lookup_bound_variables(&mut |v| { diff --git a/contracts/axone-cognitarium/src/querier/plan_builder.rs b/contracts/axone-cognitarium/src/querier/plan_builder.rs index 6849f82a..c8febaf1 100644 --- a/contracts/axone-cognitarium/src/querier/plan_builder.rs +++ b/contracts/axone-cognitarium/src/querier/plan_builder.rs @@ -1,11 +1,8 @@ -use crate::msg::{ - Node, SimpleWhereCondition, TriplePattern, VarOrNamedNode, VarOrNode, VarOrNodeOrLiteral, - WhereClause, WhereCondition, -}; +use crate::msg::{Node, TriplePattern, VarOrNamedNode, VarOrNode, VarOrNodeOrLiteral, WhereClause}; 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::{StdResult, Storage}; +use cosmwasm_std::{StdError, StdResult, Storage}; use std::collections::HashMap; pub struct PlanBuilder<'a> { @@ -45,15 +42,7 @@ impl<'a> PlanBuilder<'a> { } pub fn build_plan(&mut self, where_clause: &WhereClause) -> StdResult { - let bgp: Vec = where_clause - .iter() - .map(|cond| { - let WhereCondition::Simple(SimpleWhereCondition::TriplePattern(pattern)) = cond; - self.build_triple_pattern(pattern) - }) - .collect::>>()?; - - let mut node = Self::build_from_bgp(bgp); + let mut node = self.build_node(where_clause)?; if let Some(skip) = self.skip { node = QueryNode::Skip { @@ -73,28 +62,40 @@ impl<'a> PlanBuilder<'a> { }) } - fn build_from_bgp(bgp: Vec) -> QueryNode { - bgp.into_iter() - .reduce(|left: QueryNode, right: QueryNode| -> QueryNode { - if left + fn build_node(&mut self, where_clause: &WhereClause) -> StdResult { + match where_clause { + WhereClause::Bgp { patterns } => self.build_from_bgp(patterns.iter()), + WhereClause::LateralJoin { .. } => Err(StdError::generic_err("not implemented")), + } + } + + fn build_from_bgp<'b>( + &mut self, + bgp: impl Iterator, + ) -> StdResult { + bgp.map(|pattern| self.build_triple_pattern(pattern)) + .reduce(|acc, item| { + let acc = acc?; + let item = item?; + + if acc .bound_variables() - .intersection(&right.bound_variables()) + .intersection(&item.bound_variables()) .next() .is_some() { - return QueryNode::ForLoopJoin { - left: Box::new(left), - right: Box::new(right), - }; + Ok(QueryNode::ForLoopJoin { + left: Box::new(acc), + right: Box::new(item), + }) + } else { + Ok(QueryNode::CartesianProductJoin { + left: Box::new(acc), + right: Box::new(item), + }) } - QueryNode::CartesianProductJoin { - left: Box::new(left), - right: Box::new(right), - } - }) - .unwrap_or(QueryNode::Noop { - bound_variables: vec![], }) + .unwrap_or(Ok(QueryNode::noop())) } fn build_triple_pattern(&mut self, pattern: &TriplePattern) -> StdResult { @@ -716,14 +717,7 @@ mod test { } assert_eq!( - builder.build_plan( - &case - .2 - .into_iter() - .map(SimpleWhereCondition::TriplePattern) - .map(WhereCondition::Simple) - .collect() - ), + builder.build_plan(&WhereClause::Bgp { patterns: case.2 }), case.3 ) }