use anyhow::Result;
use crate::reasoner::Reasoner;
use crate::common::make_triple;
use log::{debug, info};
use oxigraph::store::{Store, StorageError};
use oxigraph::io::DatasetFormat;
use oxigraph::{
io::{GraphFormat, GraphParser},
model::*,
};
use std::fs;
use std::collections::HashMap;
use std::io::Cursor;
use std::string::String;
use std::time::Instant;
#[allow(non_upper_case_globals)]
const qfmt: &str = "PREFIX brick: <https://brickschema.org/schema/Brick#>
PREFIX tag: <https://brickschema.org/schema/BrickTag#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX qudt: <http://qudt.org/schema/qudt/>
";
pub struct TripleUpdate {
pub updates: HashMap<Triple, i64>
}
impl TripleUpdate {
pub fn new() -> Self {
TripleUpdate { updates: HashMap::new() }
}
pub fn add_triple(&mut self, triple: Triple) {
let counter = self.updates.entry(triple).or_insert(0);
*counter += 1;
}
pub fn remove_triple(&mut self, triple: Triple) {
let counter = self.updates.entry(triple).or_insert(0);
*counter -= 1;
}
pub fn merge(&mut self, other: Self) {
for (triple, count) in other.updates.into_iter() {
let counter = self.updates.entry(triple).or_insert(0);
*counter += count; }
}
}
pub struct Manager {
reasoner: Reasoner,
triple_store: Store,
}
impl Manager {
pub fn new() -> Self {
Manager {
reasoner: Reasoner::new(),
triple_store: Store::open("graph.db").unwrap(),
}
}
pub fn new_in_memory() -> Self {
Manager {
reasoner: Reasoner::new(),
triple_store: Store::new().unwrap(),
}
}
pub fn size(&self) -> usize {
self.triple_store.len().unwrap()
}
pub fn store(&self) -> Store {
self.triple_store.clone()
}
pub fn dump_string(&self) -> String {
let mut buffer = Vec::new();
self.triple_store.dump_dataset(&mut buffer, DatasetFormat::NQuads).unwrap();
String::from_utf8(buffer).unwrap()
}
pub fn load_triples(&mut self, triples: Vec<(String, String, String)>) -> Result<()> {
let load_triples: Vec<Triple> = triples
.into_iter()
.filter_map(|(s_, p_, o_)| {
let s: Term = {
if let Ok(named) = NamedNode::new(&s_) {
Term::NamedNode(named)
} else if let Ok(bnode) = BlankNode::new(&s_) {
Term::BlankNode(bnode)
} else {
return None;
}
};
let p: Term = if let Ok(named) = NamedNode::new(p_) {
Term::NamedNode(named)
} else {
return None;
};
let o: Term = {
if let Ok(named) = NamedNode::new(&o_) {
Term::NamedNode(named)
} else if let Ok(bnode) = BlankNode::new(&o_) {
Term::BlankNode(bnode)
} else {
Term::Literal(Literal::new_simple_literal(&o_))
}
};
if let Ok(t) = make_triple(s,p,o) {
Some(t)
} else {
None
}
})
.collect();
self.reasoner.load_triples(load_triples);
self.refresh();
Ok(())
}
pub fn load_file(&mut self, filename: &str) -> Result<()> {
self.reasoner.load_triples(parse_file(filename)?);
self.refresh();
Ok(())
}
fn refresh(&mut self) {
let refresh_start = Instant::now();
self.reasoner.reason();
self.triple_store
.transaction(|mut txn| {
for t in self.reasoner.view_output().iter() {
txn.insert(t.clone().in_graph(GraphNameRef::DefaultGraph).as_ref())?;
}
Ok(()) as std::result::Result<(), StorageError>
})
.unwrap();
info!("now have {} triples", self.triple_store.len().unwrap());
info!(
"refresh completed in {:.02}sec",
refresh_start.elapsed().as_secs_f64()
);
}
pub fn add_triples(&mut self, triples: Vec<Triple>) {
self.reasoner.load_triples(triples);
self.refresh();
}
pub fn process_updates(&mut self, updates: TripleUpdate) {
let has_deletions = updates.updates.iter().any(|(_, count)| *count < 0);
if has_deletions {
self.reasoner.clear();
}
let mut triples = self.reasoner.get_input();
println!("deletions? {} tripels {}", has_deletions, triples.len());
for (triple, count) in updates.updates.iter() {
if *count <= 0 {
println!("removing triple ({})", triples.len());
triples.retain(|v| *v != *triple);
println!("after ({})", triples.len());
} else {
triples.push(triple.clone());
}
}
for t in &triples {
println!("adding {:?}", t);
}
if has_deletions {
self.reasoner = Reasoner::new();
self.triple_store.clear().unwrap();
}
self.reasoner.load_triples(triples);
self.refresh();
}
}
pub fn parse_file(filename: &str) -> Result<Vec<Triple>> {
let gfmt: GraphFormat = if filename.ends_with(".ttl") {
GraphFormat::Turtle
} else if filename.ends_with(".n3") || filename.ends_with(".ntriples") {
GraphFormat::NTriples
} else {
GraphFormat::RdfXml
};
let data = fs::read_to_string(filename)?;
debug!("format: {:?} for {}", gfmt, filename);
let parser = GraphParser::from_format(gfmt);
let triples = parser.read_triples(Cursor::new(data))?.collect::<Result<Vec<_>,_>>().map_err(|e| e.into());
triples
}