2026-06-30 19:25:15 -04:00
|
|
|
use oxigraph::model::{Dataset, GraphName, NamedNodeRef, NamedOrBlankNode, NamedOrBlankNodeRef, Term};
|
|
|
|
|
use oxigraph::sparql::SparqlEvaluator;
|
2026-06-23 21:55:52 -04:00
|
|
|
use oxigraph::store::Store;
|
2026-06-30 19:25:15 -04:00
|
|
|
use tracing::{debug, debug_span};
|
2026-06-23 21:55:52 -04:00
|
|
|
use crate::error;
|
|
|
|
|
use crate::rdf::vocab::owl;
|
|
|
|
|
|
2026-06-30 19:25:15 -04:00
|
|
|
const INFERENCE_GRAPH: NamedNodeRef = NamedNodeRef::new_unchecked("https://graphofliberty.org/inference");
|
|
|
|
|
const RDF_SCHEMA: NamedNodeRef = NamedNodeRef::new_unchecked("http://www.w3.org/2000/01/rdf-schema#");
|
|
|
|
|
|
2026-06-29 15:20:02 -04:00
|
|
|
pub fn same_as(store: &mut Store) -> error::Result<()> {
|
2026-06-30 19:25:15 -04:00
|
|
|
let span = debug_span!("Materialize owl:sameAs");
|
|
|
|
|
let _enter = span.enter();
|
|
|
|
|
|
2026-06-23 21:55:52 -04:00
|
|
|
let additional_quads = store
|
|
|
|
|
.quads_for_pattern(None, Some(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());
|
2026-06-30 19:25:15 -04:00
|
|
|
quad.graph_name = GraphName::NamedNode(INFERENCE_GRAPH.into_owned());
|
2026-06-23 21:55:52 -04:00
|
|
|
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());
|
2026-06-30 19:25:15 -04:00
|
|
|
quad.graph_name = GraphName::NamedNode(INFERENCE_GRAPH.into_owned());
|
2026-06-23 21:55:52 -04:00
|
|
|
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 {
|
2026-06-30 19:25:15 -04:00
|
|
|
let difference = new_size - old_size;
|
|
|
|
|
debug!(?old_size, ?new_size, ?difference, "same_as");
|
|
|
|
|
same_as(store)?;
|
2026-06-23 21:55:52 -04:00
|
|
|
}
|
2026-06-30 19:25:15 -04:00
|
|
|
|
|
|
|
|
Ok(())
|
2026-06-23 21:55:52 -04:00
|
|
|
}
|
|
|
|
|
|
2026-06-30 19:25:15 -04:00
|
|
|
pub fn super_properties(store: &mut Store) -> error::Result<()> {
|
|
|
|
|
let span = debug_span!("Materialize rdfs:subPropertyOf");
|
|
|
|
|
let _enter = span.enter();
|
2026-06-23 21:55:52 -04:00
|
|
|
|
2026-06-30 19:25:15 -04:00
|
|
|
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 .
|
2026-06-23 21:55:52 -04:00
|
|
|
}
|
2026-06-30 19:25:15 -04:00
|
|
|
} WHERE {
|
|
|
|
|
GRAPH ?g1 { ?property rdfs:subPropertyOf+ ?parent }
|
2026-06-30 19:44:27 -04:00
|
|
|
OPTIONAL { GRAPH ?g2 { ?subject ?property ?object } }
|
2026-06-30 19:25:15 -04:00
|
|
|
}"#)?;
|
2026-06-23 21:55:52 -04:00
|
|
|
|
2026-06-29 15:20:02 -04:00
|
|
|
let old_size = store.len()?;
|
2026-06-30 19:25:15 -04:00
|
|
|
update.on_store(&store).execute()?;
|
2026-06-29 15:20:02 -04:00
|
|
|
let new_size = store.len()?;
|
2026-06-23 21:55:52 -04:00
|
|
|
|
|
|
|
|
if new_size > old_size {
|
2026-06-30 19:25:15 -04:00
|
|
|
let difference = new_size - old_size;
|
|
|
|
|
debug!(?old_size, ?new_size, ?difference, "super_properties");
|
|
|
|
|
super_properties(store)?;
|
2026-06-23 21:55:52 -04:00
|
|
|
}
|
2026-06-30 19:25:15 -04:00
|
|
|
|
|
|
|
|
Ok(())
|
2026-06-23 21:55:52 -04:00
|
|
|
}
|
|
|
|
|
|
2026-06-30 19:25:15 -04:00
|
|
|
pub fn super_classes(store: &mut Store) -> error::Result<()> {
|
|
|
|
|
let span = debug_span!("Materialize rdfs:subClassOf");
|
|
|
|
|
let _enter = span.enter();
|
2026-06-23 21:55:52 -04:00
|
|
|
|
2026-06-30 19:25:15 -04:00
|
|
|
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 .
|
2026-06-23 21:55:52 -04:00
|
|
|
}
|
2026-06-30 19:25:15 -04:00
|
|
|
} WHERE {
|
2026-06-30 19:35:47 -04:00
|
|
|
GRAPH ?g1 { ?class rdfs:subClassOf+ ?parent }
|
|
|
|
|
OPTIONAL { GRAPH ?g2 { ?item a ?class } }
|
2026-06-30 19:25:15 -04:00
|
|
|
}"#)?;
|
2026-06-23 21:55:52 -04:00
|
|
|
|
2026-06-30 19:25:15 -04:00
|
|
|
let old_size = store.len()?;
|
|
|
|
|
update.on_store(&store).execute()?;
|
|
|
|
|
let new_size = store.len()?;
|
2026-06-23 21:55:52 -04:00
|
|
|
|
2026-06-30 19:25:15 -04:00
|
|
|
if new_size > old_size {
|
|
|
|
|
let difference = new_size - old_size;
|
|
|
|
|
debug!(?old_size, ?new_size, ?difference, "super_classes");
|
|
|
|
|
super_classes(store)?;
|
2026-06-23 21:55:52 -04:00
|
|
|
}
|
2026-06-30 19:25:15 -04:00
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|