From 25dd1ba832debe52679a8f3a1071b3f7ebec3f738f117fd9bc324aaae920d89d Mon Sep 17 00:00:00 2001 From: Alex Wied <543423+centromere@users.noreply.github.com> Date: Tue, 7 Jul 2026 22:06:39 -0400 Subject: [PATCH] . --- Cargo.lock | 88 +++---- ontology/graphofliberty.ttl | 19 +- publish/src/app.rs | 45 ++-- publish/src/rdf/conversion.rs | 20 +- publish/src/rdf/ontologies/ontology.ttl | 302 ------------------------ publish/src/rdf/ontology.rs | 51 ++-- publish/src/rdf/vocab.rs | 6 +- 7 files changed, 118 insertions(+), 413 deletions(-) delete mode 100644 publish/src/rdf/ontologies/ontology.ttl diff --git a/Cargo.lock b/Cargo.lock index 34eacd9..f361dc9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -343,7 +343,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "shlex 1.3.0", "syn", ] @@ -515,9 +515,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.65" +version = "1.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" dependencies = [ "find-msvc-tools", "jobserver", @@ -690,7 +690,7 @@ dependencies = [ "linebender_resource_handle", "log", "rangemap", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "self_cell", "skrifa 0.40.0", "smol_str 0.3.6", @@ -722,18 +722,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -741,18 +741,18 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crunchy" @@ -768,7 +768,7 @@ dependencies = [ "cosmic-text", "etagere", "lru", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "wgpu", ] @@ -1631,7 +1631,7 @@ dependencies = [ [[package]] name = "iced" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#bb22add15fc5528b5eb319408d5e60992fdc8a90" +source = "git+https://github.com/iced-rs/iced.git?branch=master#7c6ce8789c4671db81a3920d06e87ab6d8edef1d" dependencies = [ "iced_core", "iced_debug", @@ -1646,7 +1646,7 @@ dependencies = [ [[package]] name = "iced_core" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#bb22add15fc5528b5eb319408d5e60992fdc8a90" +source = "git+https://github.com/iced-rs/iced.git?branch=master#7c6ce8789c4671db81a3920d06e87ab6d8edef1d" dependencies = [ "bitflags 2.13.0", "bytes", @@ -1655,7 +1655,7 @@ dependencies = [ "log", "num-traits", "raw-window-handle", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "smol_str 0.2.2", "thiserror 2.0.18", "web-time", @@ -1664,7 +1664,7 @@ dependencies = [ [[package]] name = "iced_debug" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#bb22add15fc5528b5eb319408d5e60992fdc8a90" +source = "git+https://github.com/iced-rs/iced.git?branch=master#7c6ce8789c4671db81a3920d06e87ab6d8edef1d" dependencies = [ "iced_core", "iced_futures", @@ -1674,12 +1674,12 @@ dependencies = [ [[package]] name = "iced_futures" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#bb22add15fc5528b5eb319408d5e60992fdc8a90" +source = "git+https://github.com/iced-rs/iced.git?branch=master#7c6ce8789c4671db81a3920d06e87ab6d8edef1d" dependencies = [ "futures", "iced_core", "log", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "tokio", "wasm-bindgen-futures", "wasmtimer", @@ -1688,7 +1688,7 @@ dependencies = [ [[package]] name = "iced_graphics" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#bb22add15fc5528b5eb319408d5e60992fdc8a90" +source = "git+https://github.com/iced-rs/iced.git?branch=master#7c6ce8789c4671db81a3920d06e87ab6d8edef1d" dependencies = [ "bitflags 2.13.0", "bytemuck", @@ -1698,7 +1698,7 @@ dependencies = [ "iced_futures", "log", "raw-window-handle", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "thiserror 2.0.18", "unicode-segmentation", ] @@ -1706,7 +1706,7 @@ dependencies = [ [[package]] name = "iced_program" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#bb22add15fc5528b5eb319408d5e60992fdc8a90" +source = "git+https://github.com/iced-rs/iced.git?branch=master#7c6ce8789c4671db81a3920d06e87ab6d8edef1d" dependencies = [ "iced_graphics", "iced_runtime", @@ -1715,7 +1715,7 @@ dependencies = [ [[package]] name = "iced_renderer" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#bb22add15fc5528b5eb319408d5e60992fdc8a90" +source = "git+https://github.com/iced-rs/iced.git?branch=master#7c6ce8789c4671db81a3920d06e87ab6d8edef1d" dependencies = [ "iced_graphics", "iced_tiny_skia", @@ -1727,7 +1727,7 @@ dependencies = [ [[package]] name = "iced_runtime" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#bb22add15fc5528b5eb319408d5e60992fdc8a90" +source = "git+https://github.com/iced-rs/iced.git?branch=master#7c6ce8789c4671db81a3920d06e87ab6d8edef1d" dependencies = [ "bytes", "iced_core", @@ -1738,7 +1738,7 @@ dependencies = [ [[package]] name = "iced_tiny_skia" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#bb22add15fc5528b5eb319408d5e60992fdc8a90" +source = "git+https://github.com/iced-rs/iced.git?branch=master#7c6ce8789c4671db81a3920d06e87ab6d8edef1d" dependencies = [ "bytemuck", "cosmic-text", @@ -1746,7 +1746,7 @@ dependencies = [ "iced_graphics", "kurbo", "log", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "softbuffer", "tiny-skia", ] @@ -1754,7 +1754,7 @@ dependencies = [ [[package]] name = "iced_wgpu" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#bb22add15fc5528b5eb319408d5e60992fdc8a90" +source = "git+https://github.com/iced-rs/iced.git?branch=master#7c6ce8789c4671db81a3920d06e87ab6d8edef1d" dependencies = [ "bitflags 2.13.0", "bytemuck", @@ -1763,7 +1763,7 @@ dependencies = [ "iced_debug", "iced_graphics", "log", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "thiserror 2.0.18", "wgpu", ] @@ -1771,13 +1771,13 @@ dependencies = [ [[package]] name = "iced_widget" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#bb22add15fc5528b5eb319408d5e60992fdc8a90" +source = "git+https://github.com/iced-rs/iced.git?branch=master#7c6ce8789c4671db81a3920d06e87ab6d8edef1d" dependencies = [ "iced_renderer", "iced_runtime", "log", "num-traits", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "thiserror 2.0.18", "unicode-segmentation", ] @@ -1785,7 +1785,7 @@ dependencies = [ [[package]] name = "iced_winit" version = "0.15.0-dev" -source = "git+https://github.com/iced-rs/iced.git?branch=master#bb22add15fc5528b5eb319408d5e60992fdc8a90" +source = "git+https://github.com/iced-rs/iced.git?branch=master#7c6ce8789c4671db81a3920d06e87ab6d8edef1d" dependencies = [ "arboard", "iced_debug", @@ -1794,7 +1794,7 @@ dependencies = [ "log", "mundy", "objc2 0.5.2", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "thiserror 2.0.18", "tracing", "wasm-bindgen-futures", @@ -2026,11 +2026,11 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "libc", ] @@ -2902,7 +2902,7 @@ dependencies = [ "oxrocksdb-sys", "oxsdatatypes", "rand", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "siphasher", "sparesults", "spareval", @@ -3553,9 +3553,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] name = "rustc_version" @@ -3943,7 +3943,7 @@ dependencies = [ "oxsdatatypes", "rand", "regex", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "sha1", "sha2", "sparesults", @@ -4106,7 +4106,7 @@ dependencies = [ "rayon", "regex", "rust-stemmers", - "rustc-hash 2.1.2", + "rustc-hash 2.1.3", "serde", "serde_json", "sketches-ddsketch", @@ -5734,18 +5734,18 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.52" +version = "0.8.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +checksum = "75726053136156d419e285b9b7eddaaea9e3fea6ce32eed44a89901f0bd98de1" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.52" +version = "0.8.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +checksum = "4714fd92cf900833d49538023a9b3915155210801d1c1169eba513b2addefd71" dependencies = [ "proc-macro2", "quote", diff --git a/ontology/graphofliberty.ttl b/ontology/graphofliberty.ttl index 564df4a..fd63a32 100644 --- a/ontology/graphofliberty.ttl +++ b/ontology/graphofliberty.ttl @@ -135,7 +135,8 @@ ldp:contains rdf:type owl:DatatypeProperty ; ### https://graphofliberty.org/2026/04/ont/catalogId :catalogId rdf:type owl:DatatypeProperty ; - rdfs:domain :Entity ; + rdfs:domain :SearchableClass ; + rdfs:range xsd:nonNegativeInteger ; rdfs:comment "An integer associated with the class for fast lookup in a database."@en ; rdfs:label "catalog id" . @@ -143,6 +144,9 @@ ldp:contains rdf:type owl:DatatypeProperty ; ### https://graphofliberty.org/2026/04/ont/fieldLabel :fieldLabel rdf:type owl:DatatypeProperty ; rdfs:domain :IndexDocumentField ; + rdfs:range rdf:dirLangString , + rdf:langString , + xsd:string ; rdfs:comment "The label of a field, which shall be displayed to the user."@en ; rdfs:label "field label"@en . @@ -150,6 +154,7 @@ ldp:contains rdf:type owl:DatatypeProperty ; ### https://graphofliberty.org/2026/04/ont/fieldName :fieldName rdf:type owl:DatatypeProperty ; rdfs:domain :IndexDocumentField ; + rdfs:range xsd:string ; rdfs:comment "The name of the field, as defined in the full-text search document schema."@en ; rdfs:label "field name"@en . @@ -166,7 +171,7 @@ ldp:contains rdf:type owl:DatatypeProperty ; ### https://graphofliberty.org/2026/04/ont/Entity :Entity rdf:type owl:Class ; - rdfs:comment "An Entity is a first-class citizen of the Graph of Liberty catalog. It is the class of all cultural artifacts which are to be preserved and indexed."@en ; + rdfs:comment "An Entity is a first-class citizen of the Graph of Liberty catalog. It is the class of all cultural artifacts which are to be preserved."@en ; rdfs:label "Graph of Liberty Entity"@en . @@ -176,6 +181,12 @@ ldp:contains rdf:type owl:DatatypeProperty ; rdfs:label "Index Document Field"@en . +### https://graphofliberty.org/2026/04/ont/SearchableClass +:SearchableClass rdf:type owl:Class ; + rdfs:comment "The class of classes which are to be indexed in the full-text search database."@en ; + rdfs:label "Searchable Class"@en . + + ################################################################# # Individuals ################################################################# @@ -217,7 +228,7 @@ fedora:lastModifiedBy rdf:type owl:NamedIndividual ; ### http://www.w3.org/1999/02/22-rdf-syntax-ns#Property rdf:Property rdf:type owl:NamedIndividual , - :Entity ; + :SearchableClass ; :associatedProperty rdfs:comment , rdfs:label , skos:definition ; @@ -226,7 +237,7 @@ rdf:Property rdf:type owl:NamedIndividual , ### http://www.w3.org/2000/01/rdf-schema#Class rdfs:Class rdf:type owl:NamedIndividual , - :Entity ; + :SearchableClass ; :associatedProperty rdfs:comment , rdfs:label , skos:definition ; diff --git a/publish/src/app.rs b/publish/src/app.rs index 54691a0..8f4f544 100644 --- a/publish/src/app.rs +++ b/publish/src/app.rs @@ -20,6 +20,7 @@ use oxigraph::model::vocab::{rdf, rdfs}; use oxigraph::model::{BaseDirection, Dataset, NamedNode, NamedNodeRef, Quad, Term}; use tracing::{debug, debug_span, error, trace}; use crate::rdf::language; +use crate::rdf::vocab::rda; use crate::widget::iri_input::iri_input; #[derive(Clone, Debug)] @@ -314,7 +315,7 @@ impl Publisher { if let Some(search_state) = &mut self.search_state { search_state.action = action; } else { - if let Some(entity) = self.ontology.labeled_entities(&*language::ENGLISH_OR_UNTAGGED).next() { + if let Some(entity) = self.ontology.searchable_classes(&*language::ENGLISH_OR_UNTAGGED).next() { let (id, window_task) = window::open(Settings::default()); self.search_state = Some(SearchState { window_id: id, @@ -450,7 +451,7 @@ impl Publisher { Message::SaveGraph(overwrite) => { let client = self.http_client.clone(); let options = SerializationOptions::from_format(RdfFormat::Turtle) - ;//.with_filter(self.ontology.exclude_read_only()); + .with_filter(self.ontology.exclude_read_only()); let request = self .document .to_update(options) @@ -540,21 +541,9 @@ impl Publisher { container(space()).width(BUTTON_WIDTH) }; - let property_label = self - .ontology - .info(&triple.predicate, &*language::ENGLISH_OR_UNTAGGED) - .label; - - let property: Element = if state.read_only { - text(property_label).into() - } else { - button(text(property_label)) - .on_press(Message::OpenQueryWindow( - SearchResultClickAction::Predicate(key), - )) - .style(button::text) - .into() - }; + let predicate_input = iri_input(self.ontology.prefixes(), "Predicate", triple.predicate.as_str()) + .on_input(move |value| Message::ValueUpdated(key, value)) + .on_control_click(Message::OpenQueryWindow(SearchResultClickAction::Predicate(key))); let term = TermHelper::new(&triple.object); @@ -630,7 +619,7 @@ impl Publisher { let row = row![ button_area, - property, + predicate_input, value_label, object_input, datatype_selector, @@ -646,15 +635,12 @@ impl Publisher { fn view_new_entity_buttons( &self, - entities: impl Iterator, + entities: impl IntoIterator, ) -> Element<'_, Message> { - let buttons = entities.map(|entity| { - let label = self - .ontology - .info(&entity, &*language::ENGLISH_OR_UNTAGGED) - .label; + let buttons = entities.into_iter().map(|entity| { + let label = format!("{} ({})", entity.label, self.ontology.abbreviate(entity.iri.as_ref())); button(text(label)) - .on_press(Message::NewDocument(entity)) + .on_press(Message::NewDocument(entity.iri)) .into() }); @@ -671,7 +657,7 @@ impl Publisher { text_input("Query", &search_state.query).on_input(Message::QueryUpdated); let mut entities = self.ontology - .labeled_entities(&*language::ENGLISH_OR_UNTAGGED) + .searchable_classes(&*language::ENGLISH_OR_UNTAGGED) .collect::>(); entities.sort_by(|a, b| Ord::cmp(&a.label, &b.label)); @@ -743,11 +729,12 @@ impl Publisher { .collect(); let body: Element = if self.show_new_document_buttons { + let subclasses = self.ontology.subclasses_of(&rda::ENTITY.into_owned()); + let labeled_subclasses = subclasses.iter().map(|iri| self.ontology.info(iri, &*language::ENGLISH_OR_UNTAGGED)); column![ //self.view_new_entity_buttons(self.ontology.subclass_of(gl::ENTITY)), - //self.view_new_entity_buttons(self.ontology.subclass_of(rda::ENTITY)), - ] - .into() + self.view_new_entity_buttons(labeled_subclasses), + ].into() } else { column(rows).into() }; diff --git a/publish/src/rdf/conversion.rs b/publish/src/rdf/conversion.rs index fcfcdea..94058c8 100644 --- a/publish/src/rdf/conversion.rs +++ b/publish/src/rdf/conversion.rs @@ -13,6 +13,14 @@ pub fn term_to_named_node(term: &Term) -> Option<&NamedNode> { } } +pub fn term_into_named_node(term: Term) -> Option { + if let Term::NamedNode(node) = term { + Some(node) + } else { + None + } +} + pub fn term_into_string(term: Term) -> Option { term_as_str(&term).map(String::from) } @@ -30,18 +38,6 @@ pub fn term_as_str(term: &Term) -> Option<&str> { } } -/*pub fn term_to_tagged_string(term: &Term) -> Option<(String, Option>)> { - term_as_str(&term).and_then(|value| { - if let Term::Literal(literal) = term { - literal.language() - .map(|language| { - let tag = LanguageTag::parse(language.to_string()).ok(); - (value.to_string(), tag) - }) - } else { None } - }) -}*/ - pub fn term_to_boolean(term: &Term) -> Option { if let Term::Literal(literal) = term { if literal.datatype() == xsd::BOOLEAN { diff --git a/publish/src/rdf/ontologies/ontology.ttl b/publish/src/rdf/ontologies/ontology.ttl deleted file mode 100644 index 14f2ced..0000000 --- a/publish/src/rdf/ontologies/ontology.ttl +++ /dev/null @@ -1,302 +0,0 @@ -@prefix : . -@prefix ldp: . -@prefix owl: . -@prefix rdf: . -@prefix xml: . -@prefix xsd: . -@prefix rdaa: . -@prefix rdac: . -@prefix rdae: . -@prefix rdai: . -@prefix rdam: . -@prefix rdan: . -@prefix rdap: . -@prefix rdat: . -@prefix rdaw: . -@prefix rdax: . -@prefix rdfs: . -@prefix skos: . -@prefix fedora: . -@base . - - rdf:type owl:Ontology . - -################################################################# -# Annotation properties -################################################################# - -### http://www.w3.org/2000/01/rdf-schema#value -rdfs:value rdf:type owl:AnnotationProperty . - - -################################################################# -# Datatypes -################################################################# - -### http://www.w3.org/1999/02/22-rdf-syntax-ns#dirLangString -rdf:dirLangString rdf:type rdfs:Datatype ; - rdfs:label "Directional language-tagged string"@en . - - -### http://www.w3.org/1999/02/22-rdf-syntax-ns#langString -rdf:langString rdf:type rdfs:Datatype ; - rdfs:label "Language-tagged string"@en . - - -### http://www.w3.org/2001/XMLSchema#boolean -xsd:boolean rdf:type rdfs:Datatype ; - rdfs:label "Boolean"@en . - - -### http://www.w3.org/2001/XMLSchema#dateTime -xsd:dateTime rdf:type rdfs:Datatype ; - rdfs:comment "Date and time with or without timezone"@en . - - -### http://www.w3.org/2001/XMLSchema#decimal -xsd:decimal rdf:type rdfs:Datatype ; - rdfs:comment "Arbitrary-precision decimal numbers"@en ; - rdfs:label "Decimal"@en . - - -### http://www.w3.org/2001/XMLSchema#integer -xsd:integer rdf:type rdfs:Datatype ; - rdfs:comment "Arbitrary-size integer numbers"@en ; - rdfs:label "Integer"@en . - - -### http://www.w3.org/2001/XMLSchema#nonNegativeInteger -xsd:nonNegativeInteger rdf:type rdfs:Datatype ; - rdfs:label "Non-negative Integer"@en . - - -### http://www.w3.org/2001/XMLSchema#string -xsd:string rdf:type rdfs:Datatype ; - rdfs:label "String"@en . - - -### http://www.w3.org/2001/XMLSchema#unsignedShort -xsd:unsignedShort rdf:type rdfs:Datatype ; - rdfs:label "Unsigned short"@en . - - -################################################################# -# Object Properties -################################################################# - -### http://fedora.info/definitions/v4/repository#hasParent -fedora:hasParent rdf:type owl:ObjectProperty . - - -### https://graphofliberty.org/2026/04/ont/associatedProperty -:associatedProperty rdf:type owl:ObjectProperty ; - rdfs:label "associated property"@en . - - -### https://graphofliberty.org/2026/04/ont/indexedByField -:indexedByField rdf:type owl:ObjectProperty ; - rdfs:range :IndexDocumentField ; - 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 ; - rdfs:label "has template"@en . - - -################################################################# -# Data properties -################################################################# - -### http://fedora.info/definitions/v4/repository#created -fedora:created rdf:type owl:DatatypeProperty ; - rdfs:subPropertyOf owl:topDataProperty . - - -### http://fedora.info/definitions/v4/repository#createdBy -fedora:createdBy rdf:type owl:DatatypeProperty ; - rdfs:subPropertyOf owl:topDataProperty . - - -### http://fedora.info/definitions/v4/repository#lastModified -fedora:lastModified rdf:type owl:DatatypeProperty . - - -### http://fedora.info/definitions/v4/repository#lastModifiedBy -fedora:lastModifiedBy rdf:type owl:DatatypeProperty . - - -### http://www.w3.org/ns/ldp#contains -ldp:contains rdf:type owl:DatatypeProperty ; - rdfs:subPropertyOf owl:topDataProperty . - - -### https://graphofliberty.org/2026/04/ont/catalogId -:catalogId rdf:type owl:DatatypeProperty ; - 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/fieldLabel -:fieldLabel rdf:type owl:DatatypeProperty ; - rdfs:domain :IndexDocumentField ; - rdfs:comment "The label of a field, which shall be displayed to the user."@en ; - rdfs:label "field label"@en . - - -### https://graphofliberty.org/2026/04/ont/fieldName -:fieldName rdf:type owl:DatatypeProperty ; - rdfs:domain :IndexDocumentField ; - rdfs:comment "The name of the field, as defined in the full-text search document schema."@en ; - rdfs:label "field name"@en . - - -### 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 . - - -################################################################# -# Classes -################################################################# - -### https://graphofliberty.org/2026/04/ont/Entity -:Entity rdf:type owl:Class ; - rdfs:label "Graph of Liberty Entity"@en . - - -### https://graphofliberty.org/2026/04/ont/IndexDocumentField -:IndexDocumentField rdf:type owl:Class ; - rdfs:comment "Describes a single field present within a document that is indexed in a full-text search database."@en ; - rdfs:label "Index Document Field"@en . - - -################################################################# -# 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#hasParent -fedora:hasParent 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/x/P00028 -rdax:P00028 rdf:type owl:NamedIndividual ; - :associatedProperty rdfs:label , - skos:definition ; - :catalogId "2"^^xsd:nonNegativeInteger ; - rdfs:label "has related entity of RDA entity"@en . - - -### http://www.w3.org/1999/02/22-rdf-syntax-ns#Property -rdf:Property rdf:type owl:NamedIndividual ; - :associatedProperty rdfs:comment , - rdfs:label ; - :catalogId "0"^^xsd:nonNegativeInteger . - - -### http://www.w3.org/2000/01/rdf-schema#Class -rdfs:Class rdf:type owl:NamedIndividual ; - :associatedProperty rdfs:comment , - rdfs:label ; - :catalogId "1"^^xsd:nonNegativeInteger . - - -### http://www.w3.org/2000/01/rdf-schema#comment -rdfs:comment rdf:type owl:NamedIndividual ; - :indexedByField :Comment . - - -### http://www.w3.org/2000/01/rdf-schema#label -rdfs:label rdf:type owl:NamedIndividual ; - :indexedByField :Label . - - -### http://www.w3.org/2004/02/skos/core#definition -skos:definition rdf:type owl:NamedIndividual ; - :indexedByField :Definition . - - -### 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/Comment -:Comment rdf:type owl:NamedIndividual , - :IndexDocumentField ; - :fieldLabel "Comment"@en ; - :fieldName "comment" ; - rdfs:label "Comment Field"@en . - - -### https://graphofliberty.org/2026/04/ont/Definition -:Definition rdf:type owl:NamedIndividual , - :IndexDocumentField ; - :fieldLabel "Definition"@en ; - :fieldName "definition" ; - rdfs:label "Definition Field"@en . - - -### https://graphofliberty.org/2026/04/ont/Label -:Label rdf:type owl:NamedIndividual , - :IndexDocumentField ; - :fieldLabel "Label"@en ; - :fieldName "label" ; - rdfs:label "Label Field"@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 27f642a..fae5b01 100644 --- a/publish/src/rdf/ontology.rs +++ b/publish/src/rdf/ontology.rs @@ -1,15 +1,16 @@ use crate::error; use crate::rdf::vocab::gl; use oxigraph::model::vocab::{rdf, rdfs, xsd}; -use oxigraph::model::{LiteralRef, NamedNode, NamedNodeRef, NamedOrBlankNode, NamedOrBlankNodeRef, Term, TermRef, Triple, TripleRef}; +use oxigraph::model::{Dataset, LiteralRef, NamedNode, NamedNodeRef, NamedOrBlankNode, NamedOrBlankNodeRef, Term, TermRef, Triple, TripleRef}; use oxigraph::sparql::{QueryResults, SparqlEvaluator}; -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::fmt::Display; use std::path::{Path, PathBuf}; use std::sync::LazyLock; use oxigraph::store::Store; -use tracing::debug_span; +use tracing::{debug_span, info}; 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; static PREFIXES: LazyLock> = LazyLock::new(|| { @@ -114,14 +115,6 @@ impl Display for LabeledIri { } } -#[derive(Clone, Debug)] -pub struct Entity { - pub label: String, - pub comment: Option, - pub catalog_id: u64, - pub properties: HashSet, -} - #[derive(Clone, Debug)] pub struct IndexField { pub name: String, @@ -148,13 +141,13 @@ impl Ontology { let language_filter = language.to_filter_expression("fieldValue"); let query = format!(r#"SELECT ?individual ?catalogId ?fieldName ?fieldValue WHERE {{ - ?entity a gl:Entity ; + ?class a gl:SearchableClass ; gl:catalogId ?catalogId ; gl:associatedProperty ?property . ?property gl:indexedByField/gl:fieldName ?fieldName . - ?individual a ?entity ; + ?individual a ?class ; ?property ?fieldValue . {language_filter} @@ -250,9 +243,9 @@ WHERE {{ 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) + .map(quad_into_term) .filter(|term| language.primary_matches_term(term)) - .filter_map(conversion::term_into_string) + .filter_map(term_into_string) .next() .unwrap_or_default(); @@ -262,15 +255,15 @@ WHERE {{ } } - pub fn labeled_entities(&self, language: &LanguageCondition) -> impl Iterator { - self.store.quads_for_pattern(None, Some(rdf::TYPE), Some(gl::ENTITY.into()), None) + pub fn searchable_classes(&self, language: &LanguageCondition) -> impl Iterator { + self.store.quads_for_pattern(None, Some(rdf::TYPE), Some(gl::SEARCHABLE.into()), None) .filter_map(Result::ok) .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(conversion::quad_into_term) + .map(quad_into_term) .filter(|term| language.primary_matches_term(term)) - .filter_map(conversion::term_into_string) + .filter_map(term_into_string) .next(); if let Some(label) = label && let NamedOrBlankNode::NamedNode(subject) = quad.subject { Some(LabeledIri { @@ -281,6 +274,15 @@ WHERE {{ }) } + pub fn subclasses_of(&self, class: &NamedNode) -> BTreeSet { + 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) -> impl Iterator { self.store .quads_for_pattern( @@ -311,6 +313,17 @@ WHERE {{ .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 dataset = self.store + .quads_for_pattern(None, Some(gl::READ_ONLY), Some(true_term), None) + .filter_map(Result::ok) + .collect::(); + move |triple| { + dataset.quads_for_subject(triple.subject).count() == 0 + } + } + pub fn template_triples<'a>( &'a self, class: NamedNodeRef<'a>, diff --git a/publish/src/rdf/vocab.rs b/publish/src/rdf/vocab.rs index 81e5d34..c0fc2e8 100644 --- a/publish/src/rdf/vocab.rs +++ b/publish/src/rdf/vocab.rs @@ -10,6 +10,9 @@ pub mod gl { pub const CATALOG_ID: NamedNodeRef = NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/catalogId"); + pub const SEARCHABLE: NamedNodeRef = + NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/Searchable"); + pub const ENTITY: NamedNodeRef = NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/Entity"); @@ -18,9 +21,6 @@ pub mod gl { pub const READ_ONLY: NamedNodeRef = NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/readOnly"); - - /*pub const INDEX_FIELD: NamedNodeRef = - NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/IndexField");*/ } pub mod owl {