.
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CurieHelper {
|
||||
prefixes: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
impl CurieHelper {
|
||||
pub fn new(prefixes: BTreeMap<String, String>) -> Self {
|
||||
Self {
|
||||
prefixes,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn abbreviate(&self, iri: &str) -> Option<String> {
|
||||
for (name, base) in &self.prefixes {
|
||||
if let Some(local_name) = iri.strip_prefix(base) {
|
||||
return Some(format!("{name}:{local_name}"));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn expand(&self, abbreviated_iri: &str) -> Option<String> {
|
||||
let (prefix, name) = abbreviated_iri.split_once(':')?;
|
||||
self.prefixes
|
||||
.get(prefix)
|
||||
.map(|base| format!("{base}{name}"))
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,5 @@ pub(crate) mod term_helper;
|
||||
pub mod vocab;
|
||||
pub(crate) mod materialize;
|
||||
pub(crate) mod conversion;
|
||||
pub(crate) mod language;
|
||||
pub(crate) mod language;
|
||||
pub(crate) mod curie;
|
||||
+53
-43
@@ -1,18 +1,20 @@
|
||||
use crate::error;
|
||||
use crate::rdf::vocab::gl;
|
||||
use oxigraph::model::vocab::{rdf, rdfs, xsd};
|
||||
use oxigraph::model::{Dataset, Graph, LiteralRef, NamedNode, NamedNodeRef, NamedOrBlankNode, NamedOrBlankNodeRef, Term, TermRef, Triple, TripleRef};
|
||||
use oxigraph::sparql::{QueryResults, SparqlEvaluator};
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use oxigraph::model::{Dataset, Graph, GraphName, GraphNameRef, LiteralRef, NamedNode, NamedNodeRef, NamedOrBlankNode, NamedOrBlankNodeRef, Term, TermRef, Triple, TripleRef};
|
||||
use oxigraph::sparql::{PreparedSparqlQuery, QueryResults, SparqlEvaluator};
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
use std::fmt::Display;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::LazyLock;
|
||||
use oxigraph::store::Store;
|
||||
use tracing::{debug_span, info};
|
||||
use tracing::debug_span;
|
||||
use crate::rdf::{conversion, materialize};
|
||||
use crate::rdf::conversion::{quad_into_term, term_into_named_node, term_into_string, term_to_named_node};
|
||||
use crate::rdf::language::LanguageCondition;
|
||||
|
||||
const ONTOLOGY_PREFIX: &str = "https://graphofliberty.org/2026/04/ont/";
|
||||
const ONTOLOGY_GRAPH_NAME: GraphNameRef = GraphNameRef::NamedNode(NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont"));
|
||||
|
||||
static PREFIXES: LazyLock<BTreeMap<String, String>> = LazyLock::new(|| {
|
||||
BTreeMap::from_iter([
|
||||
("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"),
|
||||
@@ -44,12 +46,13 @@ static PREFIXES: LazyLock<BTreeMap<String, String>> = LazyLock::new(|| {
|
||||
("rdax", "http://rdaregistry.info/Elements/x/"),
|
||||
("schema", "https://schema.org/"),
|
||||
("quill", "http://fedora.quill.lan/rest/"),
|
||||
("gl", "https://graphofliberty.org/2026/04/ont/"),
|
||||
("gl", "ONTOLOGY_PREFIX"),
|
||||
].map(|(k, v)| (k.to_string(), v.to_string())))
|
||||
});
|
||||
|
||||
pub struct OntologyBuilder {
|
||||
path: Option<PathBuf>,
|
||||
materialize_inferences: bool,
|
||||
}
|
||||
|
||||
impl OntologyBuilder {
|
||||
@@ -59,9 +62,18 @@ impl OntologyBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn materialize_inferences(mut self) -> Self {
|
||||
self.materialize_inferences = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> error::Result<Ontology> {
|
||||
let mut store = if let Some(path) = self.path {
|
||||
Store::open_read_only(path)
|
||||
if self.materialize_inferences {
|
||||
Store::open(path)
|
||||
} else {
|
||||
Store::open_read_only(path)
|
||||
}
|
||||
} else {
|
||||
Store::new()
|
||||
}?;
|
||||
@@ -71,10 +83,12 @@ impl OntologyBuilder {
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect::<HashMap<String, String>>();
|
||||
|
||||
/*materialize::same_as(&mut store)?;
|
||||
materialize::super_properties(&mut store)?;
|
||||
materialize::super_classes(&mut store)?;
|
||||
debug_span!("Optimize Ontology").in_scope(|| store.optimize())?;*/
|
||||
if self.materialize_inferences {
|
||||
materialize::same_as(&mut store)?;
|
||||
materialize::super_properties(&mut store)?;
|
||||
materialize::super_classes(&mut store)?;
|
||||
debug_span!("Optimize Ontology").in_scope(|| store.optimize())?;
|
||||
}
|
||||
|
||||
let mut indexed_by = HashMap::new();
|
||||
for quad in store.quads_for_pattern(None, Some(gl::INDEXED_BY_FIELD), None, None)
|
||||
@@ -92,6 +106,7 @@ impl OntologyBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct IndexEntry {
|
||||
pub catalog_id: u64,
|
||||
pub fields: HashMap<String, String>,
|
||||
@@ -142,14 +157,22 @@ impl Ontology {
|
||||
pub fn builder() -> OntologyBuilder {
|
||||
OntologyBuilder {
|
||||
path: None,
|
||||
materialize_inferences: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prefixes(&self) -> &BTreeMap<String, String> {
|
||||
pub fn prefixes() -> &'static BTreeMap<String, String> {
|
||||
&*PREFIXES
|
||||
}
|
||||
|
||||
pub fn index(&self, language: &LanguageCondition) -> error::Result<HashMap<NamedNode, IndexEntry>> {
|
||||
pub fn to_dataset(&self) -> Dataset {
|
||||
self.store
|
||||
.quads_for_pattern(None, None, None, Some(ONTOLOGY_GRAPH_NAME))
|
||||
.filter_map(Result::ok)
|
||||
.collect::<Dataset>()
|
||||
}
|
||||
|
||||
pub fn index_query(language: &LanguageCondition) -> PreparedSparqlQuery {
|
||||
let language_filter = language.to_filter_expression("fieldValue");
|
||||
let query = format!(r#"SELECT ?individual ?catalogId ?fieldName ?fieldValue
|
||||
WHERE {{
|
||||
@@ -165,12 +188,20 @@ WHERE {{
|
||||
{language_filter}
|
||||
}}"#);
|
||||
let mut sparql = SparqlEvaluator::new()
|
||||
.with_prefix("gl", "https://graphofliberty.org/2026/04/ont/")?
|
||||
.parse_query(&query)?;
|
||||
.with_prefix("gl", ONTOLOGY_PREFIX).unwrap()
|
||||
.parse_query(&query)
|
||||
.unwrap();
|
||||
sparql.dataset_mut().set_default_graph_as_union();
|
||||
sparql
|
||||
}
|
||||
|
||||
pub fn execute_query(&self, query: PreparedSparqlQuery) -> error::Result<QueryResults<'_>> {
|
||||
Ok(query.on_store(&self.store).execute()?)
|
||||
}
|
||||
|
||||
pub fn transform_index_results(query_results: QueryResults) -> HashMap<NamedNode, IndexEntry> {
|
||||
let mut results = HashMap::new();
|
||||
if let QueryResults::Solutions(solutions) = sparql.on_store(&self.store).execute()? {
|
||||
if let QueryResults::Solutions(solutions) = query_results {
|
||||
for solution in solutions.filter_map(Result::ok) {
|
||||
let individual = solution.get("individual").and_then(conversion::term_to_named_node);
|
||||
let catalog_id = solution.get("catalogId").and_then(conversion::term_to_u64);
|
||||
@@ -187,8 +218,7 @@ WHERE {{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
results
|
||||
}
|
||||
|
||||
pub fn catalog_id(&self, class: &NamedNode) -> Option<u64> {
|
||||
@@ -211,7 +241,7 @@ WHERE {{
|
||||
}}"#);
|
||||
let mut sparql = SparqlEvaluator::new()
|
||||
.with_prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#")?
|
||||
.with_prefix("gl", "https://graphofliberty.org/2026/04/ont/")?
|
||||
.with_prefix("gl", ONTOLOGY_PREFIX)?
|
||||
.parse_query(&query)?;
|
||||
sparql.dataset_mut().set_default_graph_as_union();
|
||||
|
||||
@@ -232,32 +262,12 @@ WHERE {{
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub fn abbreviate(&self, node: NamedNodeRef<'_>) -> String {
|
||||
for (prefix_name, prefix_iri) in &self.prefixes {
|
||||
if let Some(local_name) = node.as_str().strip_prefix(prefix_iri) {
|
||||
return if local_name.is_empty() {
|
||||
format!("{prefix_name}:")
|
||||
} else {
|
||||
format!("{prefix_name}:{local_name}")
|
||||
};
|
||||
}
|
||||
}
|
||||
node.as_str().to_string()
|
||||
}
|
||||
|
||||
pub fn expand(&self, prefixed_iri: &str) -> Option<NamedNode> {
|
||||
let (prefix, name) = prefixed_iri.split_once(':')?;
|
||||
self.prefixes
|
||||
.get(prefix)
|
||||
.map(|base| NamedNode::new_unchecked(format!("{base}{name}")))
|
||||
}
|
||||
|
||||
pub fn info(&self, iri: &NamedNode, language: &LanguageCondition) -> LabeledIri {
|
||||
let label = self.store.quads_for_pattern(Some(iri.as_ref().into()), Some(rdfs::LABEL), None, None)
|
||||
.filter_map(Result::ok)
|
||||
.map(quad_into_term)
|
||||
.map(conversion::quad_into_term)
|
||||
.filter(|term| language.primary_matches_term(term))
|
||||
.filter_map(term_into_string)
|
||||
.filter_map(conversion::term_into_string)
|
||||
.next()
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -273,9 +283,9 @@ WHERE {{
|
||||
.filter_map(|quad| {
|
||||
let label = self.store.quads_for_pattern(Some(quad.subject.as_ref().into()), Some(rdfs::LABEL), None, None)
|
||||
.filter_map(Result::ok)
|
||||
.map(quad_into_term)
|
||||
.map(conversion::quad_into_term)
|
||||
.filter(|term| language.primary_matches_term(term))
|
||||
.filter_map(term_into_string)
|
||||
.filter_map(conversion::term_into_string)
|
||||
.next();
|
||||
if let Some(label) = label && let NamedOrBlankNode::NamedNode(subject) = quad.subject {
|
||||
Some(LabeledIri {
|
||||
|
||||
Reference in New Issue
Block a user