.
This commit is contained in:
@@ -5,4 +5,5 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
oxigraph.workspace = true
|
||||
thiserror.workspace = true
|
||||
tracing.workspace = true
|
||||
@@ -0,0 +1,21 @@
|
||||
use thiserror::Error;
|
||||
|
||||
pub type Result<R> = std::result::Result<R, Error>;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
IriParse(#[from] oxigraph::model::IriParseError),
|
||||
|
||||
#[error(transparent)]
|
||||
Storage(#[from] oxigraph::store::StorageError),
|
||||
|
||||
#[error(transparent)]
|
||||
SparqlSyntax(#[from] oxigraph::sparql::SparqlSyntaxError),
|
||||
|
||||
#[error(transparent)]
|
||||
QueryEvaluation(#[from] oxigraph::sparql::QueryEvaluationError),
|
||||
|
||||
#[error(transparent)]
|
||||
UpdateEvaluation(#[from] oxigraph::sparql::UpdateEvaluationError)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
use oxigraph::model::{Dataset, GraphNameRef, NamedNodeRef, Quad, QuadRef};
|
||||
use oxigraph::sparql::{QueryResults, SparqlEvaluator};
|
||||
use oxigraph::store::{Store, Transaction};
|
||||
use tracing::{debug_span, field};
|
||||
|
||||
const RDF_SCHEMA_PREFIX: &str = "http://www.w3.org/2000/01/rdf-schema#";
|
||||
const GL_PREFIX: &str = "https://graphofliberty.org/";
|
||||
|
||||
const INFERENCE_GRAPH: GraphNameRef = GraphNameRef::NamedNode(NamedNodeRef::new_unchecked("https://graphofliberty.org/inference"));
|
||||
const INPUT_GRAPH: GraphNameRef = GraphNameRef::NamedNode(NamedNodeRef::new_unchecked("https://graphofliberty.org/input"));
|
||||
|
||||
pub struct InferenceEngine {
|
||||
ontology: Store,
|
||||
}
|
||||
|
||||
/// `prp-spo1`
|
||||
fn sub_property_of(transaction: Transaction) -> crate::Result<Dataset> {
|
||||
let span = debug_span!("rdfs:subPropertyOf", inferences = field::Empty).entered();
|
||||
|
||||
let mut query = SparqlEvaluator::new()
|
||||
.with_prefix("rdfs", RDF_SCHEMA_PREFIX)?
|
||||
.with_prefix("gl", GL_PREFIX)?
|
||||
.parse_query(r#"CONSTRUCT {
|
||||
?x ?p2 ?y .
|
||||
} WHERE {
|
||||
?p1 rdfs:subPropertyOf+ ?p2 .
|
||||
GRAPH gl:input { ?x ?p1 ?y . }
|
||||
}"#)?;
|
||||
|
||||
query.dataset_mut().set_default_graph_as_union();
|
||||
|
||||
let inferences = if let QueryResults::Graph(result) = query.on_transaction(&transaction).execute()? {
|
||||
result.filter_map(Result::ok)
|
||||
.map(|triple| Quad::new(triple.subject, triple.predicate, triple.object, INFERENCE_GRAPH))
|
||||
.collect()
|
||||
} else {
|
||||
Dataset::new()
|
||||
};
|
||||
|
||||
span.record("inferences", inferences.len());
|
||||
Ok(inferences)
|
||||
}
|
||||
|
||||
impl InferenceEngine {
|
||||
pub fn new(store: Store) -> Self {
|
||||
Self {
|
||||
ontology: store,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&self, dataset: &Dataset) -> crate::Result<Dataset> {
|
||||
let _span = debug_span!("Inference").entered();
|
||||
|
||||
let mut transaction = self.ontology.start_transaction()?;
|
||||
let input = dataset.iter().map(|triple| QuadRef::new(triple.subject, triple.predicate, triple.object, INPUT_GRAPH));
|
||||
transaction.extend(input);
|
||||
sub_property_of(transaction)
|
||||
}
|
||||
}
|
||||
+7
-2
@@ -1,4 +1,9 @@
|
||||
mod curie;
|
||||
pub mod vocab;
|
||||
//mod materialize;
|
||||
mod error;
|
||||
|
||||
pub use curie::{PREFIXES, CurieHelper};
|
||||
pub mod vocab;
|
||||
pub mod inference;
|
||||
|
||||
pub use curie::{PREFIXES, CurieHelper};
|
||||
pub use error::{Error, Result};
|
||||
@@ -0,0 +1,114 @@
|
||||
use crate::vocab;
|
||||
use oxigraph::model::{Dataset, GraphName, NamedNodeRef, NamedOrBlankNode, NamedOrBlankNodeRef, Term};
|
||||
use oxigraph::sparql::SparqlEvaluator;
|
||||
use oxigraph::store::Store;
|
||||
use tracing::{debug, debug_span};
|
||||
|
||||
|
||||
const RDF_SCHEMA: NamedNodeRef = NamedNodeRef::new_unchecked("http://www.w3.org/2000/01/rdf-schema#");
|
||||
|
||||
pub fn same_as(store: &mut Store) -> crate::Result<()> {
|
||||
let _span = debug_span!("Materialize owl:sameAs").entered();
|
||||
|
||||
let additional_quads = store
|
||||
.quads_for_pattern(None, Some(vocab::owl::SAME_AS), None, None)
|
||||
.filter_map(Result::ok)
|
||||
.fold(Dataset::new(), |mut new_dataset, alias| {
|
||||
if let NamedOrBlankNode::NamedNode(x) = alias.subject
|
||||
&& let Term::NamedNode(y) = alias.object
|
||||
{
|
||||
for mut quad in store.quads_for_pattern(
|
||||
Some(NamedOrBlankNodeRef::NamedNode(x.as_ref())),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).filter_map(Result::ok) {
|
||||
quad.subject = NamedOrBlankNode::NamedNode(y.clone());
|
||||
quad.graph_name = GraphName::NamedNode(INFERENCE_GRAPH.into_owned());
|
||||
new_dataset.insert(quad.as_ref());
|
||||
}
|
||||
|
||||
for mut quad in store.quads_for_pattern(
|
||||
Some(NamedOrBlankNodeRef::NamedNode(y.as_ref())),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).filter_map(Result::ok) {
|
||||
quad.subject = NamedOrBlankNode::NamedNode(x.clone());
|
||||
quad.graph_name = GraphName::NamedNode(INFERENCE_GRAPH.into_owned());
|
||||
new_dataset.insert(quad.as_ref());
|
||||
}
|
||||
}
|
||||
new_dataset
|
||||
});
|
||||
|
||||
let old_size = store.len()?;
|
||||
store.extend(&additional_quads)?;
|
||||
let new_size = store.len()?;
|
||||
|
||||
if new_size > old_size {
|
||||
let difference = new_size - old_size;
|
||||
debug!(?old_size, ?new_size, ?difference, "same_as");
|
||||
same_as(store)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn super_properties(store: &mut Store) -> crate::Result<()> {
|
||||
let _span = debug_span!("Materialize rdfs:subPropertyOf").entered();
|
||||
|
||||
let update = SparqlEvaluator::new()
|
||||
.with_prefix("rdfs", RDF_SCHEMA.as_str())?
|
||||
.with_prefix("inference", INFERENCE_GRAPH.as_str())?
|
||||
.parse_update(r#"INSERT {
|
||||
GRAPH inference: {
|
||||
?property rdfs:subPropertyOf ?parent .
|
||||
?subject ?parent ?object .
|
||||
}
|
||||
} WHERE {
|
||||
GRAPH ?g1 { ?property rdfs:subPropertyOf+ ?parent }
|
||||
OPTIONAL { GRAPH ?g2 { ?subject ?property ?object } }
|
||||
}"#)?;
|
||||
|
||||
let old_size = store.len()?;
|
||||
update.on_store(&store).execute()?;
|
||||
let new_size = store.len()?;
|
||||
|
||||
if new_size > old_size {
|
||||
let difference = new_size - old_size;
|
||||
debug!(?old_size, ?new_size, ?difference, "super_properties");
|
||||
super_properties(store)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn super_classes(store: &mut Store) -> crate::Result<()> {
|
||||
let _span = debug_span!("Materialize rdfs:subClassOf").entered();
|
||||
|
||||
let update = SparqlEvaluator::new()
|
||||
.with_prefix("rdfs", RDF_SCHEMA.as_str())?
|
||||
.with_prefix("inference", INFERENCE_GRAPH.as_str())?
|
||||
.parse_update(r#"INSERT {
|
||||
GRAPH inference: {
|
||||
?class rdfs:subClassOf ?parent .
|
||||
?item a ?parent .
|
||||
}
|
||||
} WHERE {
|
||||
GRAPH ?g1 { ?class rdfs:subClassOf+ ?parent }
|
||||
OPTIONAL { GRAPH ?g2 { ?item a ?class } }
|
||||
}"#)?;
|
||||
|
||||
let old_size = store.len()?;
|
||||
update.on_store(&store).execute()?;
|
||||
let new_size = store.len()?;
|
||||
|
||||
if new_size > old_size {
|
||||
let difference = new_size - old_size;
|
||||
debug!(?old_size, ?new_size, ?difference, "super_classes");
|
||||
super_classes(store)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user