Skip to content

Commit

Permalink
Merge pull request #304 from okp4/feat/delete-implementation
Browse files Browse the repository at this point in the history
🤯 Delete implementation
  • Loading branch information
ccamel authored Sep 6, 2023
2 parents fd92286 + 74c9461 commit 2b9d953
Show file tree
Hide file tree
Showing 14 changed files with 1,517 additions and 473 deletions.
444 changes: 390 additions & 54 deletions contracts/okp4-cognitarium/src/contract.rs

Large diffs are not rendered by default.

113 changes: 107 additions & 6 deletions contracts/okp4-cognitarium/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::collections::BTreeMap;

/// Instantiate message
#[cw_serde]
#[derive(Default)]
pub struct InstantiateMsg {
/// Limitations regarding store usage.
#[serde(default)]
Expand Down Expand Up @@ -66,11 +67,12 @@ pub enum ExecuteMsg {
DeleteData {
/// The prefixes used in the operation.
prefixes: Vec<Prefix>,
/// The items to delete.
/// Specifies the specific triple patterns to delete.
/// If nothing is provided, the patterns from the `where` clause are used for deletion.
delete: Vec<TriplePattern>,
/// The WHERE clause to apply.
/// If not provided, all the RDF triples are considered.
r#where: Option<WhereClause>,
/// Defines the patterns that data (RDF triples) should match in order for it to be
/// considered for deletion.
r#where: WhereClause,
},
}

Expand Down Expand Up @@ -430,7 +432,7 @@ pub struct ConstructQuery {
}

/// # Prefix
/// Represents a prefix in a [SelectQuery]. A prefix is a shortcut for a namespace used in the query.
/// Represents a prefix, i.e. a shortcut for a namespace used in a query.
#[cw_serde]
pub struct Prefix {
/// The prefix.
Expand Down Expand Up @@ -483,6 +485,27 @@ pub struct TriplePattern {
pub object: VarOrNodeOrLiteral,
}

impl TriplePattern {
/// Returns the variables used in the triple pattern.
pub fn variables(&self) -> Vec<String> {
let mut variables: Vec<String> = vec![];

if let VarOrNode::Variable(var) = &self.subject {
variables.push(var.clone());
}

if let VarOrNode::Variable(var) = &self.predicate {
variables.push(var.clone());
}

if let VarOrNodeOrLiteral::Variable(var) = &self.object {
variables.push(var.clone());
}

variables
}
}

/// # VarOrNode
/// Represents either a variable or a node.
#[cw_serde]
Expand Down Expand Up @@ -562,7 +585,12 @@ pub enum Node {

#[cfg(test)]
mod tests {
use crate::msg::{InstantiateMsg, StoreLimitsInput};
use crate::msg::Literal::Simple;
use crate::msg::Node::{BlankNode, NamedNode};
use crate::msg::IRI::{Full, Prefixed};
use crate::msg::{
InstantiateMsg, StoreLimitsInput, TriplePattern, VarOrNode, VarOrNodeOrLiteral,
};
use cosmwasm_std::Uint128;
use schemars::_serde_json;

Expand Down Expand Up @@ -597,4 +625,77 @@ 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: VarOrNode::Variable(p.clone()),
object: VarOrNodeOrLiteral::Variable(o.clone()),
},
vec![s.clone(), p.clone(), o.clone()],
),
(
TriplePattern {
subject: VarOrNode::Node(NamedNode(Full(node.clone()))),
predicate: VarOrNode::Variable(p.clone()),
object: VarOrNodeOrLiteral::Variable(o.clone()),
},
vec![p.clone(), o.clone()],
),
(
TriplePattern {
subject: VarOrNode::Node(NamedNode(Prefixed(prefixed.clone()))),
predicate: VarOrNode::Variable(p.clone()),
object: VarOrNodeOrLiteral::Variable(o.clone()),
},
vec![p.clone(), o.clone()],
),
(
TriplePattern {
subject: VarOrNode::Node(BlankNode(node.clone())),
predicate: VarOrNode::Variable(p.clone()),
object: VarOrNodeOrLiteral::Variable(o.clone()),
},
vec![p.clone(), o.clone()],
),
(
TriplePattern {
subject: VarOrNode::Variable(s.clone()),
predicate: VarOrNode::Node(NamedNode(Full(node.clone()))),
object: VarOrNodeOrLiteral::Variable(o.clone()),
},
vec![s.clone(), o],
),
(
TriplePattern {
subject: VarOrNode::Variable(s.clone()),
predicate: VarOrNode::Variable(p.clone()),
object: VarOrNodeOrLiteral::Literal(Simple(literal.clone())),
},
vec![s, p],
),
(
TriplePattern {
subject: VarOrNode::Node(BlankNode(node)),
predicate: VarOrNode::Node(NamedNode(Prefixed(prefixed))),
object: VarOrNodeOrLiteral::Literal(Simple(literal)),
},
vec![],
),
];

for (triple_pattern, expected) in cases {
assert_eq!(triple_pattern.variables(), expected);
}
}
}
8 changes: 4 additions & 4 deletions contracts/okp4-cognitarium/src/querier/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl<'a> QueryEngine<'a> {
SelectItem::Variable(v) => v,
})
.map(|name| -> StdResult<(String, usize)> {
match plan.get_var_index(name.as_str()) {
match plan.get_var_index(name) {
Some(index) => Ok((name.clone(), index)),
None => Err(StdError::generic_err(
"Selected variable not found in query",
Expand Down Expand Up @@ -443,7 +443,7 @@ mod test {
use crate::rdf::TripleReader;
use crate::state;
use crate::state::{Literal, Store, StoreStat, NAMESPACE_KEY_INCREMENT, STORE};
use crate::storer::TripleStorer;
use crate::storer::StoreEngine;
use cosmwasm_std::testing::mock_dependencies;
use cosmwasm_std::{Addr, Uint128};
use std::env;
Expand All @@ -455,7 +455,7 @@ mod test {
let mut bytes: Vec<u8> = Vec::new();

File::open(
Path::new(env::var("CARGO_MANIFEST_DIR").unwrap().as_str())
Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap())
.join("testdata")
.join(file),
)
Expand All @@ -481,7 +481,7 @@ mod test {
let data = read_test_data("sample.rdf.xml");
let buf = BufReader::new(data.as_slice());
let mut reader = TripleReader::new(&DataFormat::RDFXml, buf);
let mut storer = TripleStorer::new(storage).unwrap();
let mut storer = StoreEngine::new(storage).unwrap();
let count = storer.store_all(&mut reader).unwrap();

assert_eq!(count, Uint128::new(40u128));
Expand Down
2 changes: 1 addition & 1 deletion contracts/okp4-cognitarium/src/querier/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub struct QueryPlan {
impl QueryPlan {
pub fn get_var_index(&self, var_name: &str) -> Option<usize> {
self.variables.iter().enumerate().find_map(|(index, it)| {
if it.as_str() == var_name {
if it == var_name {
return Some(index);
}
None
Expand Down
97 changes: 35 additions & 62 deletions contracts/okp4-cognitarium/src/querier/plan_builder.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
use crate::msg::{
Literal, Node, Prefix, SimpleWhereCondition, TriplePattern, VarOrNode, VarOrNodeOrLiteral,
WhereClause, WhereCondition, IRI,
Literal, Node, SimpleWhereCondition, TriplePattern, VarOrNode, VarOrNodeOrLiteral, WhereClause,
WhereCondition, IRI,
};
use crate::querier::plan::{PatternValue, QueryNode, QueryPlan};
use crate::rdf::expand_uri;
use crate::state::{namespaces, Object, Predicate, Subject};
use crate::{rdf, state};
use cosmwasm_std::{StdError, StdResult, Storage};
use std::collections::HashMap;

pub struct PlanBuilder<'a> {
storage: &'a dyn Storage,
prefixes: HashMap<String, String>,
prefixes: &'a HashMap<String, String>,
variables: Vec<String>,
limit: Option<usize>,
skip: Option<usize>,
}

impl<'a> PlanBuilder<'a> {
pub fn new(storage: &'a dyn Storage, prefixes: &[Prefix]) -> Self {
pub fn new(storage: &'a dyn Storage, prefixes: &'a HashMap<String, String>) -> Self {
Self {
storage,
prefixes: Self::make_prefixes(prefixes),
prefixes,
variables: Vec::new(),
skip: None,
limit: None,
Expand Down Expand Up @@ -156,34 +157,10 @@ impl<'a> PlanBuilder<'a> {

fn build_named_node(&mut self, value: IRI) -> StdResult<state::Node> {
match value {
IRI::Prefixed(prefixed) => prefixed
.rfind(':')
.map_or_else(
|| {
Err(StdError::generic_err(
"Malformed prefixed IRI: no prefix delimiter found",
))
},
Ok,
)
.and_then(|index| {
self.prefixes
.get(&prefixed.as_str()[..index])
.map(|resolved_prefix| {
[resolved_prefix, &prefixed.as_str()[index + 1..]].join("")
})
.map_or_else(
|| {
Err(StdError::generic_err(
"Malformed prefixed IRI: prefix not found",
))
},
Ok,
)
}),
IRI::Prefixed(prefixed) => expand_uri(&prefixed, self.prefixes),
IRI::Full(full) => Ok(full),
}
.and_then(|iri| rdf::explode_iri(iri.as_str()))
.and_then(|iri| rdf::explode_iri(&iri))
.and_then(|(ns_key, v)| {
namespaces()
.load(self.storage, ns_key)
Expand All @@ -202,18 +179,13 @@ impl<'a> PlanBuilder<'a> {
self.variables.push(v);
self.variables.len() - 1
}

fn make_prefixes(as_list: &[Prefix]) -> HashMap<String, String> {
as_list.iter().fold(HashMap::new(), |mut map, prefix| {
map.insert(prefix.prefix.clone(), prefix.namespace.clone());
map
})
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::msg::Prefix;
use crate::rdf::PrefixMap;
use crate::state::Namespace;
use cosmwasm_std::testing::mock_dependencies;

Expand Down Expand Up @@ -273,14 +245,16 @@ mod test {
let deps = mock_dependencies();

for case in cases {
let builder = PlanBuilder::new(&deps.storage, &case.0);
let prefixes = &PrefixMap::from(case.0).into_inner();
let builder = PlanBuilder::new(&deps.storage, prefixes);
assert_eq!(builder.skip, None);
assert_eq!(builder.limit, None);
assert_eq!(builder.variables, Vec::<String>::new());
assert_eq!(builder.prefixes, case.1);
assert_eq!(builder.prefixes, &case.1);
}

let mut builder = PlanBuilder::new(&deps.storage, &[]);
let prefixes = &PrefixMap::default().into_inner();
let mut builder = PlanBuilder::new(&deps.storage, prefixes);
builder = builder.with_skip(20usize).with_limit(50usize);
assert_eq!(builder.skip, Some(20usize));
assert_eq!(builder.limit, Some(50usize));
Expand Down Expand Up @@ -315,15 +289,11 @@ mod test {
),
(
IRI::Prefixed("resource".to_string()),
Err(StdError::generic_err(
"Malformed prefixed IRI: no prefix delimiter found",
)),
Err(StdError::generic_err("Malformed CURIE: resource")),
),
(
IRI::Prefixed("okp5:resource".to_string()),
Err(StdError::generic_err(
"Malformed prefixed IRI: prefix not found",
)),
Err(StdError::generic_err("Prefix not found: okp5")),
),
];

Expand Down Expand Up @@ -351,19 +321,19 @@ mod test {
)
.unwrap();

let mut builder = PlanBuilder::new(
&deps.storage,
&[
Prefix {
prefix: "rdf".to_string(),
namespace: "http://www.w3.org/1999/02/22-rdf-syntax-ns#".to_string(),
},
Prefix {
prefix: "okp4".to_string(),
namespace: "http://okp4.space/".to_string(),
},
],
);
let prefixes = &<PrefixMap>::from(vec![
Prefix {
prefix: "rdf".to_string(),
namespace: "http://www.w3.org/1999/02/22-rdf-syntax-ns#".to_string(),
},
Prefix {
prefix: "okp4".to_string(),
namespace: "http://okp4.space/".to_string(),
},
])
.into_inner();
let mut builder = PlanBuilder::new(&deps.storage, prefixes);

for case in cases {
assert_eq!(builder.build_named_node(case.0), case.1);
}
Expand Down Expand Up @@ -526,7 +496,9 @@ mod test {
},
)
.unwrap();
let mut builder = PlanBuilder::new(&deps.storage, &[]);
let prefixes = &PrefixMap::default().into_inner();
let mut builder = PlanBuilder::new(&deps.storage, prefixes);

for case in cases {
assert_eq!(builder.build_triple_pattern(&case.0), case.1);
}
Expand Down Expand Up @@ -729,7 +701,8 @@ mod test {
.unwrap();

for case in cases {
let mut builder = PlanBuilder::new(&deps.storage, &[]);
let prefixes = &PrefixMap::default().into_inner();
let mut builder = PlanBuilder::new(&deps.storage, prefixes);
if let Some(skip) = case.0 {
builder = builder.with_skip(skip);
}
Expand Down
Loading

0 comments on commit 2b9d953

Please sign in to comment.