use crate::graph::core::Graph;
use crate::utils::error::{Error, Result};
use oxigraph::sparql::SparqlEvaluator;
pub struct GraphUpdate<'a> {
graph: &'a Graph,
}
impl<'a> GraphUpdate<'a> {
pub fn new(graph: &'a Graph) -> Self {
Self { graph }
}
pub fn execute(&self, update: &str) -> Result<()> {
let evaluator = SparqlEvaluator::new();
let prepared = evaluator
.parse_update(update)
.map_err(|e| Error::with_source("SPARQL Update parse error", Box::new(e)))?;
prepared
.on_store(self.graph.inner())
.execute()
.map_err(|e| Error::with_source("SPARQL Update execution error", Box::new(e)))?;
self.graph.bump_epoch();
Ok(())
}
pub fn insert(&self, data: &str) -> Result<()> {
let update = format!("INSERT DATA {{ {} }}", data);
self.execute(&update)
}
pub fn delete(&self, data: &str) -> Result<()> {
let update = format!("DELETE DATA {{ {} }}", data);
self.execute(&update)
}
pub fn delete_where(&self, pattern: &str) -> Result<()> {
let update = format!("DELETE WHERE {{ {} }}", pattern);
self.execute(&update)
}
pub fn update(
&self, delete_pattern: &str, insert_pattern: &str, where_pattern: &str,
) -> Result<()> {
let update = format!(
"DELETE {{ {} }} INSERT {{ {} }} WHERE {{ {} }}",
delete_pattern, insert_pattern, where_pattern
);
self.execute(&update)
}
pub fn clear(&self) -> Result<()> {
self.execute("CLEAR DEFAULT")
}
pub fn clear_graph(&self, graph_iri: &str) -> Result<()> {
let update = format!("CLEAR GRAPH <{}>", graph_iri);
self.execute(&update)
}
pub fn clear_all(&self) -> Result<()> {
self.execute("CLEAR ALL")
}
pub fn load(&self, url: &str) -> Result<()> {
let update = format!("LOAD <{}>", url);
self.execute(&update)
}
pub fn load_into(&self, url: &str, graph_iri: &str) -> Result<()> {
let update = format!("LOAD <{}> INTO GRAPH <{}>", url, graph_iri);
self.execute(&update)
}
pub fn copy(&self, source: &str, destination: &str) -> Result<()> {
let update = format!("COPY <{}> TO <{}>", source, destination);
self.execute(&update)
}
pub fn move_graph(&self, source: &str, destination: &str) -> Result<()> {
let update = format!("MOVE <{}> TO <{}>", source, destination);
self.execute(&update)
}
pub fn add(&self, source: &str, destination: &str) -> Result<()> {
let update = format!("ADD <{}> TO <{}>", source, destination);
self.execute(&update)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::graph::core::Graph;
#[test]
fn test_update_insert() {
let graph = Graph::new().unwrap();
let update = GraphUpdate::new(&graph);
update
.insert("<http://example.org/alice> <http://example.org/name> \"Alice\"")
.unwrap();
assert!(!graph.is_empty());
let results = graph
.query_cached(
"SELECT ?name WHERE { <http://example.org/alice> <http://example.org/name> ?name }",
)
.unwrap();
match results {
crate::graph::types::CachedResult::Solutions(rows) => {
assert!(!rows.is_empty());
assert_eq!(rows[0].get("name"), Some(&"\"Alice\"".to_string()));
}
_ => panic!("Expected solutions"),
}
}
#[test]
fn test_update_delete() {
let graph = Graph::new().unwrap();
graph
.insert_turtle(
r#"
@prefix ex: <http://example.org/> .
ex:alice ex:name "Alice" .
"#,
)
.unwrap();
let update = GraphUpdate::new(&graph);
let initial_len = graph.len();
update
.delete("<http://example.org/alice> <http://example.org/name> \"Alice\"")
.unwrap();
assert!(graph.len() < initial_len);
}
#[test]
fn test_update_delete_where() {
let graph = Graph::new().unwrap();
graph
.insert_turtle(
r#"
@prefix ex: <http://example.org/> .
ex:alice ex:name "Alice" .
ex:bob ex:name "Bob" .
"#,
)
.unwrap();
let update = GraphUpdate::new(&graph);
let initial_len = graph.len();
update
.delete_where("?s <http://example.org/name> \"Alice\"")
.unwrap();
assert!(graph.len() < initial_len);
let results = graph
.query_cached("ASK { <http://example.org/alice> <http://example.org/name> \"Alice\" }")
.unwrap();
match results {
crate::graph::types::CachedResult::Boolean(false) => {}
_ => panic!("Expected false (triple should be deleted)"),
}
}
#[test]
fn test_update_update_operation() {
let graph = Graph::new().unwrap();
graph
.insert_turtle(
r#"
@prefix ex: <http://example.org/> .
ex:alice ex:age "30" .
"#,
)
.unwrap();
let update = GraphUpdate::new(&graph);
update
.update(
"?s <http://example.org/age> ?o",
"?s <http://example.org/age> \"31\"",
"?s <http://example.org/age> ?o",
)
.unwrap();
let results = graph
.query_cached(
"SELECT ?age WHERE { <http://example.org/alice> <http://example.org/age> ?age }",
)
.unwrap();
match results {
crate::graph::types::CachedResult::Solutions(rows) => {
assert!(!rows.is_empty());
assert_eq!(rows[0].get("age"), Some(&"\"31\"".to_string()));
}
_ => panic!("Expected solutions"),
}
}
#[test]
fn test_update_clear() {
let graph = Graph::new().unwrap();
graph
.insert_turtle(
r#"
@prefix ex: <http://example.org/> .
ex:alice a ex:Person .
"#,
)
.unwrap();
let update = GraphUpdate::new(&graph);
assert!(!graph.is_empty());
update.clear().unwrap();
assert!(graph.is_empty());
}
#[test]
fn test_update_clear_graph() {
let graph = Graph::new().unwrap();
graph
.insert_turtle_in(
r#"
@prefix ex: <http://example.org/> .
ex:alice a ex:Person .
"#,
"http://example.org/graph1",
)
.unwrap();
let update = GraphUpdate::new(&graph);
let initial_len = graph.len();
update.clear_graph("http://example.org/graph1").unwrap();
assert!(graph.len() < initial_len);
}
#[test]
fn test_update_clear_all() {
let graph = Graph::new().unwrap();
graph
.insert_turtle(
r#"
@prefix ex: <http://example.org/> .
ex:alice a ex:Person .
"#,
)
.unwrap();
let update = GraphUpdate::new(&graph);
assert!(!graph.is_empty());
update.clear_all().unwrap();
assert!(graph.is_empty());
}
#[test]
fn test_update_execute_invalid_syntax() {
let graph = Graph::new().unwrap();
let update = GraphUpdate::new(&graph);
let result = update.execute("INVALID UPDATE SYNTAX");
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("SPARQL Update"));
}
#[test]
fn test_update_copy() {
let graph = Graph::new().unwrap();
graph
.insert_turtle_in(
r#"
@prefix ex: <http://example.org/> .
ex:alice a ex:Person .
"#,
"http://example.org/graph1",
)
.unwrap();
let update = GraphUpdate::new(&graph);
update
.copy("http://example.org/graph1", "http://example.org/graph2")
.unwrap();
let results1 = graph
.query_cached("ASK { GRAPH <http://example.org/graph1> { ?s ?p ?o } }")
.unwrap();
let results2 = graph
.query_cached("ASK { GRAPH <http://example.org/graph2> { ?s ?p ?o } }")
.unwrap();
match (results1, results2) {
(
crate::graph::types::CachedResult::Boolean(true),
crate::graph::types::CachedResult::Boolean(true),
) => {}
_ => panic!("Both graphs should have data after copy"),
}
}
}