.
This commit is contained in:
+39
-14
@@ -1,5 +1,5 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use oxigraph::model::{Graph, Literal, NamedNode, NamedNodeRef, NamedOrBlankNode, Term, Triple};
|
use oxigraph::model::{Graph, Literal, LiteralRef, NamedNode, NamedNodeRef, NamedOrBlankNode, Term, TermRef, Triple};
|
||||||
use oxilangtag::LanguageTag;
|
use oxilangtag::LanguageTag;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use gl_search::Schema;
|
use gl_search::Schema;
|
||||||
@@ -125,28 +125,53 @@ impl Class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn template(&self, subject: NamedNode) -> Graph {
|
pub fn template(&self, subject: NamedNode) -> Graph {
|
||||||
let empty_english_string_term = Term::Literal(Literal::new_language_tagged_literal_unchecked("", "en"));
|
let subject = subject.as_ref();
|
||||||
let empty_string_term = Term::Literal(Literal::new_simple_literal(""));
|
let empty_english_string_literal = LiteralRef::new_language_tagged_literal_unchecked("", "en");
|
||||||
let empty_node = NamedNode::new_unchecked("");
|
let empty_string_literal = LiteralRef::new_simple_literal("");
|
||||||
let hash_node = NamedNode::new_unchecked("#");
|
let empty_node = NamedNodeRef::new_unchecked("");
|
||||||
|
let hash_node = NamedNodeRef::new_unchecked("#");
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Class::RdfProperty => Graph::new(),
|
Class::RdfProperty => Graph::new(),
|
||||||
Class::RdfsClass => Graph::new(),
|
Class::RdfsClass => Graph::new(),
|
||||||
Class::SkosConcept => Graph::new(),
|
Class::SkosConcept => Graph::new(),
|
||||||
Class::Work => Graph::from_iter([
|
Class::Work => Graph::from_iter([
|
||||||
Triple::new(NamedOrBlankNode::NamedNode(subject), vocab::rdawd::TITLE_OF_WORK, empty_english_string_term)
|
Triple::new(subject, vocab::rdawd::TITLE_OF_WORK, empty_english_string_literal),
|
||||||
|
Triple::new(subject, vocab::rdawo::IDENTIFIER_FOR_WORK, empty_node),
|
||||||
|
Triple::new(subject, vocab::rdawo::EXPRESSION_OF_WORK, empty_node),
|
||||||
]),
|
]),
|
||||||
Class::Person => Graph::from_iter([
|
Class::Person => Graph::from_iter([
|
||||||
Triple::new(NamedOrBlankNode::NamedNode(subject.clone()), vocab::rdaad::GIVEN_NAME, empty_english_string_term.clone()),
|
Triple::new(subject, vocab::rdaad::GIVEN_NAME, empty_english_string_literal),
|
||||||
Triple::new(NamedOrBlankNode::NamedNode(subject.clone()), vocab::rdaad::SURNAME, empty_english_string_term),
|
Triple::new(subject, vocab::rdaad::SURNAME, empty_english_string_literal),
|
||||||
Triple::new(NamedOrBlankNode::NamedNode(subject.clone()), vocab::rdaao::IDENTIFIER_FOR_PERSON, Term::NamedNode(hash_node.clone())),
|
Triple::new(subject, vocab::rdaao::IDENTIFIER_FOR_PERSON, hash_node),
|
||||||
Triple::new(NamedOrBlankNode::NamedNode(hash_node.clone()), vocab::rdano::SCHEME_OF_NOMEN, Term::NamedNode(empty_node)),
|
Triple::new(hash_node, vocab::rdano::SCHEME_OF_NOMEN, empty_node),
|
||||||
Triple::new(NamedOrBlankNode::NamedNode(subject.clone()), vocab::rdand::NOMEN_STRING, empty_string_term),
|
Triple::new(hash_node, vocab::rdand::NOMEN_STRING, empty_string_literal),
|
||||||
|
]),
|
||||||
|
Class::CorporateBody => Graph::from_iter([
|
||||||
|
Triple::new(subject, vocab::rdaao::IDENTIFIER_FOR_CORPORATE_BODY, hash_node),
|
||||||
|
Triple::new(subject, vocab::rdaad::NAME_OF_CORPORATE_BODY, empty_english_string_literal),
|
||||||
|
Triple::new(hash_node, vocab::rdano::SCHEME_OF_NOMEN, empty_node),
|
||||||
|
Triple::new(hash_node, vocab::rdand::NOMEN_STRING, empty_string_literal),
|
||||||
|
]),
|
||||||
|
Class::Expression => Graph::from_iter([
|
||||||
|
Triple::new(subject, vocab::rdaeo::WORK_EXPRESSED, empty_node),
|
||||||
|
Triple::new(subject, vocab::rdaeo::LANGUAGE_OF_EXPRESSION, vocab::lclang::ENGLISH),
|
||||||
|
Triple::new(subject, vocab::rdaeo::TITLE_OF_EXPRESSION, empty_english_string_literal),
|
||||||
|
Triple::new(subject, vocab::rdaeo::CONTENT_TYPE, empty_node),
|
||||||
|
Triple::new(subject, vocab::rdaeo::MANIFESTATION_OF_EXPRESSION, empty_node),
|
||||||
|
]),
|
||||||
|
Class::Manifestation => Graph::from_iter([
|
||||||
|
Triple::new(subject, vocab::rdamd::TITLE_OF_MANIFESTATION, empty_english_string_literal),
|
||||||
|
Triple::new(subject, vocab::rdamd::UNIFORM_RESOURCE_LOCATOR, empty_string_literal),
|
||||||
|
Triple::new(subject, vocab::rdamo::CARRIER_TYPE, vocab::rdact::ONLINE_RESOURCE),
|
||||||
|
Triple::new(subject, vocab::rdamo::MEDIA_TYPE, empty_node),
|
||||||
|
Triple::new(subject, vocab::rdamo::FILE_TYPE, empty_node),
|
||||||
|
Triple::new(subject, vocab::rdamo::CATEGORY_OF_MANIFESTATION, empty_node),
|
||||||
|
Triple::new(subject, vocab::rdamo::EXPRESSION_MANIFESTED, empty_node),
|
||||||
|
Triple::new(subject, vocab::rdamo::IDENTIFIER_FOR_MANIFESTATION, hash_node),
|
||||||
|
Triple::new(hash_node, vocab::rdano::SCHEME_OF_NOMEN, empty_node),
|
||||||
|
Triple::new(hash_node, vocab::rdand::NOMEN_STRING, empty_string_literal),
|
||||||
]),
|
]),
|
||||||
Class::CorporateBody => Graph::new(),
|
|
||||||
Class::Expression => Graph::new(),
|
|
||||||
Class::Manifestation => Graph::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,11 +106,11 @@ PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|||||||
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
|
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
|
||||||
PREFIX gl: <https://graphofliberty.org/2026/04/ont/>
|
PREFIX gl: <https://graphofliberty.org/2026/04/ont/>
|
||||||
|
|
||||||
SELECT ?subject ?class WHERE {{
|
SELECT ?subject ?class WHERE {
|
||||||
VALUES ?class {{ rdf:Property rdfs:Class }}
|
VALUES ?class { rdf:Property rdfs:Class }
|
||||||
?subject a ?class ;
|
?subject a ?class ;
|
||||||
gl:readOnly "true"^^xsd:boolean .
|
gl:readOnly "true"^^xsd:boolean .
|
||||||
}}"#.to_owned();
|
}"#.to_owned();
|
||||||
|
|
||||||
let request = OntologyQueryRequest {
|
let request = OntologyQueryRequest {
|
||||||
sparql_query: Some(sparql_query),
|
sparql_query: Some(sparql_query),
|
||||||
|
|||||||
+74
-3
@@ -14,11 +14,11 @@ pub mod glo {
|
|||||||
NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/nextUrl");
|
NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/nextUrl");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod skos {
|
pub mod lclang {
|
||||||
use oxigraph::model::NamedNodeRef;
|
use oxigraph::model::NamedNodeRef;
|
||||||
|
|
||||||
pub const CONCEPT: NamedNodeRef =
|
pub const ENGLISH: NamedNodeRef =
|
||||||
NamedNodeRef::new_unchecked("http://www.w3.org/2004/02/skos/core#Concept");
|
NamedNodeRef::new_unchecked("http://id.loc.gov/vocabulary/languages/eng");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod owl {
|
pub mod owl {
|
||||||
@@ -47,6 +47,13 @@ pub mod rdac {
|
|||||||
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/c/C10007");
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/c/C10007");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod rdact {
|
||||||
|
use oxigraph::model::NamedNodeRef;
|
||||||
|
|
||||||
|
pub const ONLINE_RESOURCE: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/termList/RDACarrierType/1018");
|
||||||
|
}
|
||||||
|
|
||||||
pub mod rdaad {
|
pub mod rdaad {
|
||||||
use oxigraph::model::NamedNodeRef;
|
use oxigraph::model::NamedNodeRef;
|
||||||
|
|
||||||
@@ -65,6 +72,9 @@ pub mod rdaao {
|
|||||||
|
|
||||||
pub const IDENTIFIER_FOR_PERSON: NamedNodeRef =
|
pub const IDENTIFIER_FOR_PERSON: NamedNodeRef =
|
||||||
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/a/object/P50094");
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/a/object/P50094");
|
||||||
|
|
||||||
|
pub const IDENTIFIER_FOR_CORPORATE_BODY: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/a/object/P50006");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod rdawd {
|
pub mod rdawd {
|
||||||
@@ -74,6 +84,16 @@ pub mod rdawd {
|
|||||||
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/w/datatype/P10088");
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/w/datatype/P10088");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod rdawo {
|
||||||
|
use oxigraph::model::NamedNodeRef;
|
||||||
|
|
||||||
|
pub const IDENTIFIER_FOR_WORK: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/w/object/P10002");
|
||||||
|
|
||||||
|
pub const EXPRESSION_OF_WORK: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/w/object/P10078");
|
||||||
|
}
|
||||||
|
|
||||||
pub mod rdaed {
|
pub mod rdaed {
|
||||||
use oxigraph::model::NamedNodeRef;
|
use oxigraph::model::NamedNodeRef;
|
||||||
|
|
||||||
@@ -81,11 +101,55 @@ pub mod rdaed {
|
|||||||
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/e/datatype/P20312");
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/e/datatype/P20312");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod rdaeo {
|
||||||
|
use oxigraph::model::NamedNodeRef;
|
||||||
|
|
||||||
|
pub const WORK_EXPRESSED: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/e/object/P20231");
|
||||||
|
|
||||||
|
pub const LANGUAGE_OF_EXPRESSION: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/e/object/P20006");
|
||||||
|
|
||||||
|
pub const TITLE_OF_EXPRESSION: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/e/datatype/P20312");
|
||||||
|
|
||||||
|
pub const CONTENT_TYPE: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/e/object/P20001");
|
||||||
|
|
||||||
|
pub const MANIFESTATION_OF_EXPRESSION: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/e/object/P20059");
|
||||||
|
}
|
||||||
|
|
||||||
pub mod rdamd {
|
pub mod rdamd {
|
||||||
use oxigraph::model::NamedNodeRef;
|
use oxigraph::model::NamedNodeRef;
|
||||||
|
|
||||||
pub const TITLE_OF_MANIFESTATION: NamedNodeRef =
|
pub const TITLE_OF_MANIFESTATION: NamedNodeRef =
|
||||||
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/m/datatype/P30335");
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/m/datatype/P30335");
|
||||||
|
|
||||||
|
pub const UNIFORM_RESOURCE_LOCATOR: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/m/datatype/P30154");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod rdamo {
|
||||||
|
use oxigraph::model::NamedNodeRef;
|
||||||
|
|
||||||
|
pub const CARRIER_TYPE: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/m/object/P30001");
|
||||||
|
|
||||||
|
pub const MEDIA_TYPE: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/m/object/P30002");
|
||||||
|
|
||||||
|
pub const IDENTIFIER_FOR_MANIFESTATION: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/m/object/P30004");
|
||||||
|
|
||||||
|
pub const FILE_TYPE: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/m/object/P30018");
|
||||||
|
|
||||||
|
pub const CATEGORY_OF_MANIFESTATION: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/m/object/P30335");
|
||||||
|
|
||||||
|
pub const EXPRESSION_MANIFESTED: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/m/object/P30139");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod rdand {
|
pub mod rdand {
|
||||||
@@ -101,3 +165,10 @@ pub mod rdano {
|
|||||||
pub const SCHEME_OF_NOMEN: NamedNodeRef =
|
pub const SCHEME_OF_NOMEN: NamedNodeRef =
|
||||||
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/n/object/P80069");
|
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/n/object/P80069");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod skos {
|
||||||
|
use oxigraph::model::NamedNodeRef;
|
||||||
|
|
||||||
|
pub const CONCEPT: NamedNodeRef =
|
||||||
|
NamedNodeRef::new_unchecked("http://www.w3.org/2004/02/skos/core#Concept");
|
||||||
|
}
|
||||||
@@ -1,396 +0,0 @@
|
|||||||
use crate::error;
|
|
||||||
use crate::rdf::conversion;
|
|
||||||
use gl_graph::language::LanguageCondition;
|
|
||||||
use gl_graph::vocab::gl;
|
|
||||||
use iced::futures::TryFutureExt;
|
|
||||||
use oxigraph::model::vocab::{rdf, rdfs, xsd};
|
|
||||||
use oxigraph::model::{
|
|
||||||
Dataset, Graph, GraphNameRef, LiteralRef, NamedNode, NamedNodeRef, NamedOrBlankNode,
|
|
||||||
NamedOrBlankNodeRef, TermRef, Triple, TripleRef,
|
|
||||||
};
|
|
||||||
use oxigraph::sparql::{QueryResults, SparqlEvaluator};
|
|
||||||
use oxigraph::store::Store;
|
|
||||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
|
||||||
use std::fmt::Display;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
use std::sync::LazyLock;
|
|
||||||
use tracing::{debug_span, field};
|
|
||||||
|
|
||||||
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#"),
|
|
||||||
("rdfs", "http://www.w3.org/2000/01/rdf-schema#"),
|
|
||||||
("owl", "http://www.w3.org/2002/07/owl#"),
|
|
||||||
("xsd", "http://www.w3.org/2001/XMLSchema#"),
|
|
||||||
("ldp", "http://www.w3.org/ns/ldp#"),
|
|
||||||
("dc", "http://purl.org/dc/elements/1.1/"),
|
|
||||||
("posix", "http://www.w3.org/ns/posix/stat#"),
|
|
||||||
(
|
|
||||||
"ebucore",
|
|
||||||
"http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#",
|
|
||||||
),
|
|
||||||
("prov", "http://www.w3.org/ns/prov#"),
|
|
||||||
("locid", "http://id.loc.gov/vocabulary/identifiers/"),
|
|
||||||
("loclang", "http://id.loc.gov/vocabulary/languages/"),
|
|
||||||
("premis", "http://www.loc.gov/premis/rdf/v1#"),
|
|
||||||
("premis3", "http://www.loc.gov/premis/rdf/v3/"),
|
|
||||||
("dcterms", "http://purl.org/dc/terms/"),
|
|
||||||
("fedora", "http://fedora.info/definitions/v4/repository#"),
|
|
||||||
("rdaa", "http://rdaregistry.info/Elements/a/"),
|
|
||||||
("rdac", "http://rdaregistry.info/Elements/c/"),
|
|
||||||
("rdae", "http://rdaregistry.info/Elements/e/"),
|
|
||||||
("rdai", "http://rdaregistry.info/Elements/i/"),
|
|
||||||
("rdam", "http://rdaregistry.info/Elements/m/"),
|
|
||||||
("rdan", "http://rdaregistry.info/Elements/n/"),
|
|
||||||
("rdap", "http://rdaregistry.info/Elements/p/"),
|
|
||||||
("rdaf", "http://rdaregistry.info/Elements/rof/"),
|
|
||||||
("rdat", "http://rdaregistry.info/Elements/t/"),
|
|
||||||
("rdau", "http://rdaregistry.info/Elements/u/"),
|
|
||||||
("rdaw", "http://rdaregistry.info/Elements/w/"),
|
|
||||||
("rdax", "http://rdaregistry.info/Elements/x/"),
|
|
||||||
("rdaco", "http://rdaregistry.info/termList/RDAContentType/"),
|
|
||||||
("rdact", "http://rdaregistry.info/termList/RDACarrierType/"),
|
|
||||||
("rdamt", "http://rdaregistry.info/termList/RDAMediaType/"),
|
|
||||||
("rdaft", "http://rdaregistry.info/termList/fileType/"),
|
|
||||||
("schema", "https://schema.org/"),
|
|
||||||
("gl", "http://fedora.quill.lan/rest/"),
|
|
||||||
("glo", ONTOLOGY_PREFIX),
|
|
||||||
]
|
|
||||||
.map(|(k, v)| (k.to_string(), v.to_string())),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
pub struct OntologyBuilder {
|
|
||||||
path: Option<PathBuf>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OntologyBuilder {
|
|
||||||
pub fn with_path(mut self, path: impl AsRef<Path>) -> Self {
|
|
||||||
let path = path.as_ref().to_owned();
|
|
||||||
self.path = Some(path);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build(self) -> error::Result<Ontology> {
|
|
||||||
let store = if let Some(path) = self.path {
|
|
||||||
Store::open(path)
|
|
||||||
} else {
|
|
||||||
Store::new()
|
|
||||||
}?;
|
|
||||||
|
|
||||||
let prefixes = PREFIXES
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
|
||||||
.collect::<HashMap<String, String>>();
|
|
||||||
|
|
||||||
/*let mut indexed_by = HashMap::new();
|
|
||||||
for quad in store
|
|
||||||
.quads_for_pattern(None, Some(gl::INDEXED_BY_FIELD), None, None)
|
|
||||||
.filter_map(Result::ok)
|
|
||||||
{
|
|
||||||
if let NamedOrBlankNode::NamedNode(subject) = quad.subject
|
|
||||||
&& let Term::NamedNode(field) = quad.object
|
|
||||||
{
|
|
||||||
indexed_by.insert(subject, field);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
Ok(Ontology { store, prefixes })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct IndexEntry {
|
|
||||||
pub category_id: u64,
|
|
||||||
pub fields: HashMap<String, String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq)]
|
|
||||||
pub struct LabeledIri {
|
|
||||||
pub iri: NamedNode,
|
|
||||||
pub label: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for LabeledIri {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.iri == other.iri
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for LabeledIri {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
||||||
self.label.partial_cmp(&other.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for LabeledIri {
|
|
||||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
||||||
self.label.cmp(&other.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for LabeledIri {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "{}", self.label)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct IndexField {
|
|
||||||
pub name: String,
|
|
||||||
pub label: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Ontology {
|
|
||||||
store: Store,
|
|
||||||
prefixes: HashMap<String, String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ontology {
|
|
||||||
pub fn builder() -> OntologyBuilder {
|
|
||||||
OntologyBuilder { path: None }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prefixes() -> &'static BTreeMap<String, String> {
|
|
||||||
&*PREFIXES
|
|
||||||
}
|
|
||||||
|
|
||||||
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 store(&self) -> Store {
|
|
||||||
self.store.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn query_for_indexable_triples(
|
|
||||||
&self,
|
|
||||||
language: &LanguageCondition,
|
|
||||||
source: Option<Dataset>,
|
|
||||||
) -> impl Future<Output = error::Result<HashMap<NamedNode, IndexEntry>>> + 'static {
|
|
||||||
let language_filter = language.to_filter_expression("fieldValue");
|
|
||||||
let query = format!(
|
|
||||||
r#"SELECT ?individual ?categoryId ?fieldName ?fieldValue
|
|
||||||
WHERE {{
|
|
||||||
?class a gl:SearchableClass ;
|
|
||||||
gl:categoryId ?categoryId ;
|
|
||||||
gl:associatedProperty ?property .
|
|
||||||
|
|
||||||
?property gl:indexedByField/gl:fieldName ?fieldName .
|
|
||||||
|
|
||||||
?individual a ?class ;
|
|
||||||
?property ?fieldValue .
|
|
||||||
|
|
||||||
{language_filter}
|
|
||||||
}}"#
|
|
||||||
);
|
|
||||||
let mut sparql = SparqlEvaluator::new()
|
|
||||||
.with_prefix("gl", ONTOLOGY_PREFIX)
|
|
||||||
.unwrap()
|
|
||||||
.parse_query(&query)
|
|
||||||
.expect("Unable to parse query");
|
|
||||||
sparql.dataset_mut().set_default_graph_as_union();
|
|
||||||
|
|
||||||
let store = self.store.clone();
|
|
||||||
tokio::task::spawn_blocking(move || {
|
|
||||||
let span = debug_span!("Indexable Triples Query", solutions = field::Empty).entered();
|
|
||||||
let query_results = if let Some(source) = &source {
|
|
||||||
sparql.on_queryable_dataset(source).execute()
|
|
||||||
} else {
|
|
||||||
sparql.on_store(&store).execute()
|
|
||||||
}
|
|
||||||
.expect("Unable to execute indexing query");
|
|
||||||
|
|
||||||
let mut results = HashMap::new();
|
|
||||||
if let QueryResults::Solutions(solutions) = query_results {
|
|
||||||
let mut counter = 0usize;
|
|
||||||
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("categoryId").and_then(conversion::term_to_u64);
|
|
||||||
let field_name = solution.get("fieldName").and_then(conversion::term_as_str);
|
|
||||||
let field_value = solution.get("fieldValue").and_then(conversion::term_as_str);
|
|
||||||
if let (
|
|
||||||
Some(individual),
|
|
||||||
Some(catalog_id),
|
|
||||||
Some(field_name),
|
|
||||||
Some(field_value),
|
|
||||||
) = (individual, catalog_id, field_name, field_value)
|
|
||||||
{
|
|
||||||
results
|
|
||||||
.entry(individual.to_owned())
|
|
||||||
.and_modify(|entry: &mut IndexEntry| {
|
|
||||||
entry
|
|
||||||
.fields
|
|
||||||
.insert(field_name.to_owned(), field_value.to_owned());
|
|
||||||
})
|
|
||||||
.or_insert(IndexEntry {
|
|
||||||
category_id: catalog_id,
|
|
||||||
fields: HashMap::from_iter([(
|
|
||||||
field_name.to_owned(),
|
|
||||||
field_value.to_owned(),
|
|
||||||
)]),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
counter += 1;
|
|
||||||
}
|
|
||||||
span.record("solutions", counter);
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
results
|
|
||||||
})
|
|
||||||
.map_err(error::Error::from)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn category_id(&self, class: &NamedNode) -> Option<u64> {
|
|
||||||
None
|
|
||||||
/*self.store
|
|
||||||
.quads_for_pattern(Some(class.into()), Some(gl::CATEGORY_ID), None, None)
|
|
||||||
.filter_map(Result::ok)
|
|
||||||
.map(conversion::quad_into_term)
|
|
||||||
.filter_map(conversion::term_into_u64)
|
|
||||||
.next()*/
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fields_for_class(
|
|
||||||
&self,
|
|
||||||
class: &NamedNode,
|
|
||||||
language: &LanguageCondition,
|
|
||||||
) -> error::Result<Vec<IndexField>> {
|
|
||||||
let language_filter = language.to_filter_expression("label");
|
|
||||||
let query = format!(
|
|
||||||
r#"SELECT DISTINCT ?name ?label {{
|
|
||||||
{class} gl:associatedProperty/gl:indexedByField ?field .
|
|
||||||
|
|
||||||
?field gl:fieldName ?name ;
|
|
||||||
gl:fieldLabel ?label .
|
|
||||||
|
|
||||||
{language_filter}
|
|
||||||
}}"#
|
|
||||||
);
|
|
||||||
let mut sparql = SparqlEvaluator::new()
|
|
||||||
.with_prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#")?
|
|
||||||
.with_prefix("gl", ONTOLOGY_PREFIX)?
|
|
||||||
.parse_query(&query)?;
|
|
||||||
sparql.dataset_mut().set_default_graph_as_union();
|
|
||||||
|
|
||||||
let mut results = Vec::new();
|
|
||||||
if let QueryResults::Solutions(solutions) = sparql.on_store(&self.store).execute()? {
|
|
||||||
for solution in solutions.filter_map(Result::ok) {
|
|
||||||
let name = solution.get("name").and_then(conversion::term_as_str);
|
|
||||||
let label = solution.get("label").and_then(conversion::term_as_str);
|
|
||||||
|
|
||||||
if let Some(name) = name
|
|
||||||
&& let Some(label) = label
|
|
||||||
{
|
|
||||||
results.push(IndexField {
|
|
||||||
name: name.to_string(),
|
|
||||||
label: label.to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(results)
|
|
||||||
}
|
|
||||||
|
|
||||||
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(conversion::quad_into_term)
|
|
||||||
.filter(|term| language.primary_matches_term(term))
|
|
||||||
.filter_map(conversion::term_into_string)
|
|
||||||
.next()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
LabeledIri {
|
|
||||||
iri: iri.clone(),
|
|
||||||
label,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subclasses_of(&self, class: &NamedNode) -> BTreeSet<NamedNode> {
|
|
||||||
self.store
|
|
||||||
.quads_for_pattern(None, Some(rdfs::SUB_CLASS_OF), Some(class.into()), None)
|
|
||||||
.filter_map(Result::ok)
|
|
||||||
.filter_map(|quad| {
|
|
||||||
if let NamedOrBlankNode::NamedNode(subject) = quad.subject {
|
|
||||||
Some(subject)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn datatypes(&self) -> BTreeSet<NamedNode> {
|
|
||||||
self.store
|
|
||||||
.quads_for_pattern(
|
|
||||||
None,
|
|
||||||
Some(rdf::TYPE),
|
|
||||||
Some(TermRef::NamedNode(rdfs::DATATYPE)),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.filter_map(Result::ok)
|
|
||||||
.filter_map(|quad| match quad.subject {
|
|
||||||
NamedOrBlankNode::NamedNode(subject) => Some(subject),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_read_only<'a>(&self, triple: impl Into<TripleRef<'a>>) -> bool {
|
|
||||||
let triple = triple.into();
|
|
||||||
let true_term = TermRef::Literal(LiteralRef::new_typed_literal("true", xsd::BOOLEAN));
|
|
||||||
|
|
||||||
let subject = match (triple.predicate, triple.object) {
|
|
||||||
(rdf::TYPE, TermRef::NamedNode(class)) => class,
|
|
||||||
(predicate, _) => predicate,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.store
|
|
||||||
.quads_for_pattern(
|
|
||||||
Some(NamedOrBlankNodeRef::NamedNode(subject)),
|
|
||||||
Some(gl::READ_ONLY),
|
|
||||||
Some(true_term),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.filter_map(Result::ok)
|
|
||||||
.count()
|
|
||||||
>= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exclude_read_only(&self) -> impl Fn(TripleRef<'_>) -> bool + 'static {
|
|
||||||
let true_term = TermRef::Literal(LiteralRef::new_typed_literal("true", xsd::BOOLEAN));
|
|
||||||
let read_only_graph = self
|
|
||||||
.store
|
|
||||||
.quads_for_pattern(None, Some(gl::READ_ONLY), Some(true_term), None)
|
|
||||||
.filter_map(Result::ok)
|
|
||||||
.map(Triple::from)
|
|
||||||
.collect::<Graph>();
|
|
||||||
move |triple| {
|
|
||||||
if triple.predicate == rdf::TYPE
|
|
||||||
&& let TermRef::NamedNode(class) = triple.object
|
|
||||||
{
|
|
||||||
read_only_graph.triples_for_subject(class).count() == 0
|
|
||||||
} else {
|
|
||||||
read_only_graph
|
|
||||||
.triples_for_subject(triple.predicate)
|
|
||||||
.count()
|
|
||||||
== 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user