diff --git a/publish/src/app.rs b/publish/src/app.rs index 42a4af2..da2187d 100644 --- a/publish/src/app.rs +++ b/publish/src/app.rs @@ -21,7 +21,7 @@ use ldp::{RdfSource, RdfSourceUpdateResponse, ResourceRequestBuilder, Serializat use oxigraph::io::RdfFormat; use oxigraph::model::vocab::{rdf, rdfs}; use oxigraph::model::{BaseDirection, Dataset, NamedNode, Quad, Term, TermRef}; -use tracing::{debug, error}; +use tracing::{debug, error, info}; #[derive(Debug, Clone)] pub(crate) enum Message { @@ -187,12 +187,12 @@ impl Publisher { document.add_text(Schema::iri_field(), rdf_source.origin()); for quad in rdf_source.dataset() { - if let Some(field_name) = - self.ontology.field_name(&quad.predicate.into_owned()) + if let Some(field) = + self.ontology.field_for_property(&quad.predicate.into_owned()) && let TermRef::Literal(literal) = quad.object { let field = Schema::schema() - .get_field(&field_name) + .get_field(&field.key) .expect("Field not found in schema"); document.add_text(field, literal.value().to_lowercase()); } diff --git a/publish/src/rdf/ontologies/ontology.ttl b/publish/src/rdf/ontologies/ontology.ttl index 9cfcc40..d71a3b6 100644 --- a/publish/src/rdf/ontologies/ontology.ttl +++ b/publish/src/rdf/ontologies/ontology.ttl @@ -24,32 +24,8 @@ # Annotation properties ################################################################# -### http://www.w3.org/2000/01/rdf-schema#comment -rdfs:comment :indexedByField "comment" . - - -### http://www.w3.org/2000/01/rdf-schema#label -rdfs:label :indexedByField "label" . - - -### https://graphofliberty.org/2026/04/ont/catalogId -:catalogId rdf:type owl:AnnotationProperty ; - rdfs:comment "An integer associated with the class for fast lookup in a database."@en ; - rdfs:label "catalog id" ; - rdfs:domain owl:Class . - - -### https://graphofliberty.org/2026/04/ont/indexedByField -:indexedByField rdf:type owl:AnnotationProperty ; - rdfs:comment "The property is associated with the given field in a full-text search database."@en ; - rdfs:label "indexed by field"@en ; - rdfs:domain owl:DatatypeProperty . - - -### https://graphofliberty.org/2026/04/ont/readOnly -:readOnly rdf:type owl:AnnotationProperty ; - rdfs:comment "Indicates that the property or class is read only (server managed) and should not be made editable in user-facing applications."@en ; - rdfs:label "read only"@en . +### http://www.w3.org/2000/01/rdf-schema#value +rdfs:value rdf:type owl:AnnotationProperty . ################################################################# @@ -123,6 +99,14 @@ rdam:P30154 rdf:type owl:ObjectProperty . rdam:uniformResourceLocator.en rdf:type owl:ObjectProperty . +### https://graphofliberty.org/2026/04/ont/indexedByField +:indexedByField rdf:type owl:ObjectProperty ; + rdfs:domain owl:DatatypeProperty ; + rdfs:range :IndexField ; + rdfs:comment "The property is associated with the given field in a full-text search database."@en ; + rdfs:label "indexed by field"@en . + + ### https://graphofliberty.org/2026/04/ont/template :template rdf:type owl:ObjectProperty ; rdfs:comment "A default set of triples used during the creation of new entities of the associated class."@en ; @@ -135,24 +119,20 @@ rdam:uniformResourceLocator.en rdf:type owl:ObjectProperty . ### http://fedora.info/definitions/v4/repository#created fedora:created rdf:type owl:DatatypeProperty ; - rdfs:subPropertyOf owl:topDataProperty ; - :readOnly "true"^^xsd:boolean . + rdfs:subPropertyOf owl:topDataProperty . ### http://fedora.info/definitions/v4/repository#createdBy fedora:createdBy rdf:type owl:DatatypeProperty ; - rdfs:subPropertyOf owl:topDataProperty ; - :readOnly "true"^^xsd:boolean . + rdfs:subPropertyOf owl:topDataProperty . ### http://fedora.info/definitions/v4/repository#lastModified -fedora:lastModified rdf:type owl:DatatypeProperty ; - :readOnly "true"^^xsd:boolean . +fedora:lastModified rdf:type owl:DatatypeProperty . ### http://fedora.info/definitions/v4/repository#lastModifiedBy -fedora:lastModifiedBy rdf:type owl:DatatypeProperty ; - :readOnly "true"^^xsd:boolean . +fedora:lastModifiedBy rdf:type owl:DatatypeProperty . ### http://rdaregistry.info/Elements/a/P50291 @@ -173,8 +153,20 @@ rdaa:surname.en rdf:type owl:DatatypeProperty . ### http://www.w3.org/ns/ldp#contains ldp:contains rdf:type owl:DatatypeProperty ; - rdfs:subPropertyOf owl:topDataProperty ; - :readOnly "true"^^xsd:boolean . + rdfs:subPropertyOf owl:topDataProperty . + + +### https://graphofliberty.org/2026/04/ont/catalogId +:catalogId rdf:type owl:DatatypeProperty ; + rdfs:domain owl:Class ; + rdfs:comment "An integer associated with the class for fast lookup in a database."@en ; + rdfs:label "catalog id" . + + +### https://graphofliberty.org/2026/04/ont/readOnly +:readOnly rdf:type owl:DatatypeProperty ; + rdfs:comment "Indicates that the property or class is read only (server managed) and should not be made editable in user-facing applications."@en ; + rdfs:label "read only"@en . ################################################################# @@ -182,13 +174,11 @@ ldp:contains rdf:type owl:DatatypeProperty ; ################################################################# ### http://fedora.info/definitions/v4/repository#Container -fedora:Container rdf:type owl:Class ; - :readOnly "true"^^xsd:boolean . +fedora:Container rdf:type owl:Class . ### http://fedora.info/definitions/v4/repository#Resource -fedora:Resource rdf:type owl:Class ; - :readOnly "true"^^xsd:boolean . +fedora:Resource rdf:type owl:Class . ### http://rdaregistry.info/Elements/c/C10001 @@ -220,54 +210,50 @@ rdac:C10013 rdf:type owl:Class ; ### http://www.w3.org/1999/02/22-rdf-syntax-ns#Property -rdf:Property rdf:type owl:Class ; - :catalogId "0"^^xsd:nonNegativeInteger . +rdf:Property rdf:type owl:Class . ### http://www.w3.org/2000/01/rdf-schema#Class -rdfs:Class rdf:type owl:Class ; - :catalogId "1"^^xsd:nonNegativeInteger . +rdfs:Class rdf:type owl:Class . + + +### http://www.w3.org/2002/07/owl#Class +owl:Class rdf:type owl:Class . + + +### http://www.w3.org/2002/07/owl#DatatypeProperty +owl:DatatypeProperty rdf:type owl:Class . ### http://www.w3.org/ns/ldp#BasicContainer -ldp:BasicContainer rdf:type owl:Class ; - :readOnly "true"^^xsd:boolean . +ldp:BasicContainer rdf:type owl:Class . ### http://www.w3.org/ns/ldp#Container -ldp:Container rdf:type owl:Class ; - :readOnly "true"^^xsd:boolean . +ldp:Container rdf:type owl:Class . ### http://www.w3.org/ns/ldp#RDFSource -ldp:RDFSource rdf:type owl:Class ; - :readOnly "true"^^xsd:boolean . +ldp:RDFSource rdf:type owl:Class . ### http://www.w3.org/ns/ldp#Resource -ldp:Resource rdf:type owl:Class ; - :readOnly "true"^^xsd:boolean . +ldp:Resource rdf:type owl:Class . ### https://graphofliberty.org/2026/04/ont/AudioBook :AudioBook rdf:type owl:Class ; - rdfs:subClassOf :Entity ; - rdfs:label "Audio Book"@en ; - :catalogId "2"^^xsd:nonNegativeInteger . + rdfs:subClassOf :Entity . ### https://graphofliberty.org/2026/04/ont/Book :Book rdf:type owl:Class ; - rdfs:subClassOf :Entity ; - rdfs:label "Book"@en ; - :catalogId "3"^^xsd:nonNegativeInteger . + rdfs:subClassOf :Entity . ### https://graphofliberty.org/2026/04/ont/Document :Document rdf:type owl:Class ; - rdfs:subClassOf :Entity ; - rdfs:label "Document"@en ; - :catalogId "4"^^xsd:nonNegativeInteger . + rdfs:subClassOf :Entity . ### https://graphofliberty.org/2026/04/ont/Entity @@ -275,52 +261,76 @@ ldp:Resource rdf:type owl:Class ; rdfs:label "Graph of Liberty Entity"@en . +### https://graphofliberty.org/2026/04/ont/IndexField +:IndexField rdf:type owl:Class ; + rdfs:comment "Describes a single field present within a larger document that is passed to a full-text search database."@en ; + rdfs:label "Index Field"@en . + + ### https://graphofliberty.org/2026/04/ont/Meme :Meme rdf:type owl:Class ; - rdfs:subClassOf :Entity ; - rdfs:label "Meme"@en ; - :catalogId "5"^^xsd:nonNegativeInteger . + rdfs:subClassOf :Entity . ### https://graphofliberty.org/2026/04/ont/Movie :Movie rdf:type owl:Class ; - rdfs:subClassOf :Entity ; - rdfs:label "Movie"@en ; - :catalogId "6"^^xsd:nonNegativeInteger . + rdfs:subClassOf :Entity . ### https://graphofliberty.org/2026/04/ont/Music :Music rdf:type owl:Class ; - rdfs:subClassOf :Entity ; - rdfs:label "Music"@en ; - :catalogId "7"^^xsd:nonNegativeInteger . + rdfs:subClassOf :Entity . ### https://graphofliberty.org/2026/04/ont/Person :Person rdf:type owl:Class ; - rdfs:subClassOf :Entity ; - rdfs:label "Person"@en ; - :catalogId "8"^^xsd:nonNegativeInteger . + rdfs:subClassOf :Entity . ### https://graphofliberty.org/2026/04/ont/Podcast :Podcast rdf:type owl:Class ; - rdfs:subClassOf :Entity ; - rdfs:label "Podcast"@en ; - :catalogId "9"^^xsd:nonNegativeInteger . + rdfs:subClassOf :Entity . ### https://graphofliberty.org/2026/04/ont/TVShow :TVShow rdf:type owl:Class ; - rdfs:subClassOf :Entity ; - rdfs:label "TV Show"@en ; - :catalogId "10"^^xsd:nonNegativeInteger . + rdfs:subClassOf :Entity . ################################################################# # Individuals ################################################################# +### http://fedora.info/definitions/v4/repository#Container +fedora:Container rdf:type owl:NamedIndividual ; + :readOnly "true"^^xsd:boolean . + + +### http://fedora.info/definitions/v4/repository#Resource +fedora:Resource rdf:type owl:NamedIndividual ; + :readOnly "true"^^xsd:boolean . + + +### http://fedora.info/definitions/v4/repository#created +fedora:created rdf:type owl:NamedIndividual ; + :readOnly "true"^^xsd:boolean . + + +### http://fedora.info/definitions/v4/repository#createdBy +fedora:createdBy rdf:type owl:NamedIndividual ; + :readOnly "true"^^xsd:boolean . + + +### http://fedora.info/definitions/v4/repository#lastModified +fedora:lastModified rdf:type owl:NamedIndividual ; + :readOnly "true"^^xsd:boolean . + + +### http://fedora.info/definitions/v4/repository#lastModifiedBy +fedora:lastModifiedBy rdf:type owl:NamedIndividual ; + :readOnly "true"^^xsd:boolean . + + ### http://rdaregistry.info/Elements/a/P50094 rdaa:P50094 rdf:type owl:NamedIndividual ; owl:sameAs rdaa:identifierForPerson.en . @@ -330,14 +340,16 @@ rdaa:P50094 rdf:type owl:NamedIndividual ; ### http://rdaregistry.info/Elements/a/P50291 rdaa:P50291 rdf:type owl:NamedIndividual ; - owl:sameAs rdaa:surname.en . + owl:sameAs rdaa:surname.en ; + :indexedByField :surname . ### http://rdaregistry.info/Elements/a/surname.en ### http://rdaregistry.info/Elements/a/P50292 rdaa:P50292 rdf:type owl:NamedIndividual ; - owl:sameAs rdaa:givenName.en . + owl:sameAs rdaa:givenName.en ; + :indexedByField :givenName . ### http://rdaregistry.info/Elements/a/givenName.en @@ -371,6 +383,100 @@ rdam:P30154 rdf:type owl:NamedIndividual ; rdam:uniformResourceLocator.en rdf:type owl:NamedIndividual . +### http://www.w3.org/1999/02/22-rdf-syntax-ns#Property +rdf:Property rdf:type owl:NamedIndividual ; + :catalogId "0"^^xsd:nonNegativeInteger . + + +### http://www.w3.org/2000/01/rdf-schema#Class +rdfs:Class rdf:type owl:NamedIndividual ; + :catalogId "1"^^xsd:nonNegativeInteger . + + +### http://www.w3.org/ns/ldp#BasicContainer +ldp:BasicContainer rdf:type owl:NamedIndividual ; + :readOnly "true"^^xsd:boolean . + + +### http://www.w3.org/ns/ldp#Container +ldp:Container rdf:type owl:NamedIndividual ; + :readOnly "true"^^xsd:boolean . + + +### http://www.w3.org/ns/ldp#RDFSource +ldp:RDFSource rdf:type owl:NamedIndividual ; + :readOnly "true"^^xsd:boolean . + + +### http://www.w3.org/ns/ldp#Resource +ldp:Resource rdf:type owl:NamedIndividual ; + :readOnly "true"^^xsd:boolean . + + +### http://www.w3.org/ns/ldp#contains +ldp:contains rdf:type owl:NamedIndividual ; + :readOnly "true"^^xsd:boolean . + + +### https://graphofliberty.org/2026/04/ont/AudioBook +:AudioBook rdf:type owl:NamedIndividual ; + :catalogId "2"^^xsd:nonNegativeInteger . + + +### https://graphofliberty.org/2026/04/ont/Book +:Book rdf:type owl:NamedIndividual ; + :catalogId "3"^^xsd:nonNegativeInteger . + + +### https://graphofliberty.org/2026/04/ont/Document +:Document rdf:type owl:NamedIndividual ; + :catalogId "4"^^xsd:nonNegativeInteger . + + +### https://graphofliberty.org/2026/04/ont/Meme +:Meme rdf:type owl:NamedIndividual ; + :catalogId "5"^^xsd:nonNegativeInteger . + + +### https://graphofliberty.org/2026/04/ont/Movie +:Movie rdf:type owl:NamedIndividual ; + :catalogId "6"^^xsd:nonNegativeInteger . + + +### https://graphofliberty.org/2026/04/ont/Music +:Music rdf:type owl:NamedIndividual ; + :catalogId "7"^^xsd:nonNegativeInteger . + + +### https://graphofliberty.org/2026/04/ont/Person +:Person rdf:type owl:NamedIndividual ; + :catalogId "8"^^xsd:nonNegativeInteger . + + +### https://graphofliberty.org/2026/04/ont/Podcast +:Podcast rdf:type owl:NamedIndividual ; + :catalogId "9"^^xsd:nonNegativeInteger . + + +### https://graphofliberty.org/2026/04/ont/TVShow +:TVShow rdf:type owl:NamedIndividual ; + :catalogId "10"^^xsd:nonNegativeInteger . + + +### https://graphofliberty.org/2026/04/ont/givenName +:givenName rdf:type owl:NamedIndividual , + :IndexField ; + rdfs:label "Given Name"@en ; + rdfs:value "given name" . + + +### https://graphofliberty.org/2026/04/ont/surname +:surname rdf:type owl:NamedIndividual , + :IndexField ; + rdfs:label "Surname"@en ; + rdfs:value "surname" . + + ################################################################# # Annotations ################################################################# @@ -378,12 +484,10 @@ rdam:uniformResourceLocator.en rdf:type owl:NamedIndividual . rdaa:P50094 rdfs:label "has identifier for person"@en . -rdaa:P50291 rdfs:label "has surname"@en ; - :indexedByField "surname" . +rdaa:P50291 rdfs:label "has surname"@en . -rdaa:P50292 rdfs:label "has given name"@en ; - :indexedByField "given name" . +rdaa:P50292 rdfs:label "has given name"@en . rdac:C10007 rdfs:label "Manifestation"@en . @@ -392,4 +496,31 @@ rdac:C10007 rdfs:label "Manifestation"@en . rdam:P30154 rdfs:label "has uniform resource locator"@en . +:AudioBook rdfs:label "Audio Book"@en . + + +:Book rdfs:label "Book"@en . + + +:Document rdfs:label "Document"@en . + + +:Meme rdfs:label "Meme"@en . + + +:Movie rdfs:label "Movie"@en . + + +:Music rdfs:label "Music"@en . + + +:Person rdfs:label "Person"@en . + + +:Podcast rdfs:label "Podcast"@en . + + +:TVShow rdfs:label "TV Show"@en . + + ### Generated by the OWL API (version 4.5.29.2024-05-13T12:11:03Z) https://github.com/owlcs/owlapi diff --git a/publish/src/rdf/ontology.rs b/publish/src/rdf/ontology.rs index 32a0e38..58eb587 100644 --- a/publish/src/rdf/ontology.rs +++ b/publish/src/rdf/ontology.rs @@ -8,6 +8,8 @@ use oxigraph::model::{ }; use oxigraph::sparql::{QueryResults, SparqlEvaluator}; use std::collections::HashMap; +use iced::widget::sensor::Key; +use tracing::debug; const PREFIXES: &[(&str, &str)] = &[ ("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"), @@ -192,6 +194,42 @@ SELECT DISTINCT ?class ?subject ?label ?comment ?read_only { results } + fn fields(dataset: &Dataset) -> HashMap { + let query = SparqlEvaluator::new() + .parse_query( + r#"PREFIX rdfs: +PREFIX gl: + +SELECT DISTINCT ?subject ?key ?label { + ?subject a gl:IndexField ; + rdfs:value ?key ; + rdfs:label ?label . + FILTER(langMATCHES(LANG(?label), "en") || !hasLANG(?label)) +}"#, + ) + .expect("Unable to parse field query"); + + let mut results = HashMap::new(); + if let QueryResults::Solutions(solutions) = + query.on_queryable_dataset(dataset).execute().unwrap() + { + for solution in solutions.filter_map(Result::ok) { + let subject = solution.get("subject").and_then(term_to_named_node); + let key = solution.get("key").and_then(term_to_string); + let label = solution.get("label").and_then(term_to_string); + + if let Some(subject) = subject && let Some(key) = key { + let field = IndexField { + key: key.to_owned(), + label: label.map(String::from), + }; + results.insert(subject.to_owned(), field); + } + } + } + results + } + pub fn build(&mut self) -> error::Result { let prefixes = PREFIXES .iter() @@ -211,12 +249,14 @@ SELECT DISTINCT ?class ?subject ?label ?comment ?read_only { let iri_info = Self::iri_information(&mut dataset); // Full-text search index field names - let mut field_map = HashMap::new(); + let fields = Self::fields(&dataset); + + let mut indexed_by = HashMap::new(); for quad in dataset.quads_for_pattern(None, Some(gl::INDEXED_BY_FIELD), None, None) { if let NamedOrBlankNodeRef::NamedNode(subject) = quad.subject - && let TermRef::Literal(literal) = quad.object + && let TermRef::NamedNode(field) = quad.object { - field_map.insert(subject.into_owned(), literal.value().to_string()); + indexed_by.insert(subject.into_owned(), field.into_owned()); } } @@ -237,7 +277,8 @@ SELECT DISTINCT ?class ?subject ?label ?comment ?read_only { dataset, prefixes, iri_info, - field_map, + fields, + indexed_by, catalog_ids, }) } @@ -251,11 +292,18 @@ pub struct IriInformation { pub read_only: bool, } +#[derive(Clone, Debug)] +pub struct IndexField { + pub key: String, + pub label: Option, +} + pub struct Ontology { dataset: Dataset, prefixes: HashMap, iri_info: HashMap, - field_map: HashMap, + fields: HashMap, + indexed_by: HashMap, catalog_ids: HashMap, } @@ -324,8 +372,9 @@ impl Ontology { .map(|base| NamedNode::new_unchecked(format!("{base}{name}"))) } - pub fn field_name(&self, property: &NamedNode) -> Option<&String> { - self.field_map.get(property) + pub fn field_for_property(&self, property: &NamedNode) -> Option<&IndexField> { + self.indexed_by.get(property) + .and_then(|node| self.fields.get(node)) } pub fn catalog_id(&self, class: &NamedNode) -> Option { @@ -435,4 +484,4 @@ SELECT ?class {{ None } } -} +} \ No newline at end of file diff --git a/publish/src/rdf/vocab.rs b/publish/src/rdf/vocab.rs index e4efd91..45ab243 100644 --- a/publish/src/rdf/vocab.rs +++ b/publish/src/rdf/vocab.rs @@ -10,6 +10,9 @@ pub mod gl { pub const ENTITY: NamedNodeRef = NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/Entity"); + + pub const INDEX_FIELD: NamedNodeRef = + NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/IndexField"); } pub mod owl {