From a5f62602f614b74dc6527256ffecb1b250cf28d05cc596e2f693fdd3381f0a96 Mon Sep 17 00:00:00 2001 From: Alex Wied <543423+centromere@users.noreply.github.com> Date: Tue, 9 Jun 2026 15:40:28 -0400 Subject: [PATCH] . --- publish/src/app.rs | 10 +- publish/src/rdf/mod.rs | 1 + publish/src/rdf/ontologies/ontology.ttl | 134 +++--- publish/src/rdf/ontologies/owl.ttl | 552 ++++++++++++++++++++++++ publish/src/rdf/ontology.rs | 125 ++++-- publish/src/rdf/vocab.rs | 34 ++ search/src/index.rs | 4 +- 7 files changed, 751 insertions(+), 109 deletions(-) create mode 100644 publish/src/rdf/ontologies/owl.ttl create mode 100644 publish/src/rdf/vocab.rs diff --git a/publish/src/app.rs b/publish/src/app.rs index 2729ee4..888bba5 100644 --- a/publish/src/app.rs +++ b/publish/src/app.rs @@ -1,5 +1,5 @@ -use crate::rdf::ontology; use crate::rdf::ontology::Ontology; +use crate::rdf::vocab::{gl, rda}; use crate::rdf::term_helper::{TermHelper, TermHelperMut}; use gl_search::{doc, Schema, SearchDocument, SearchIndex}; use http::StatusCode; @@ -657,7 +657,7 @@ impl Publisher { let search_input = text_input("Query", &search_state.query).on_input(Message::QueryUpdated); - let label_column = table::column("Label", |result: &NamedNode| { + let type_column = table::column("Type", |result: &NamedNode| { let label = self.ontology.label(result.as_ref()).unwrap_or("Unknown"); button(text(label)) @@ -670,7 +670,7 @@ impl Publisher { .style(button::text) }); let results_table = - scrollable(table([label_column, iri_column], &search_state.results)); + scrollable(table([type_column, iri_column], &search_state.results)); return column![search_input, results_table].into(); } @@ -693,8 +693,8 @@ impl Publisher { let body: Element = if self.show_new_document_buttons { column![ - self.view_new_entity_buttons(self.ontology.subclass_of(ontology::GL_ENTITY)), - self.view_new_entity_buttons(self.ontology.subclass_of(ontology::RDA_ENTITY)), + self.view_new_entity_buttons(self.ontology.subclass_of(gl::ENTITY)), + self.view_new_entity_buttons(self.ontology.subclass_of(rda::ENTITY)), ] .into() } else { diff --git a/publish/src/rdf/mod.rs b/publish/src/rdf/mod.rs index ffae413..57a04a9 100644 --- a/publish/src/rdf/mod.rs +++ b/publish/src/rdf/mod.rs @@ -1,2 +1,3 @@ pub(crate) mod ontology; pub(crate) mod term_helper; +pub mod vocab; \ No newline at end of file diff --git a/publish/src/rdf/ontologies/ontology.ttl b/publish/src/rdf/ontologies/ontology.ttl index 9a0b594..5e6fdee 100644 --- a/publish/src/rdf/ontologies/ontology.ttl +++ b/publish/src/rdf/ontologies/ontology.ttl @@ -24,6 +24,14 @@ # 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 ; @@ -38,6 +46,12 @@ 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 . + + ################################################################# # Datatypes ################################################################# @@ -74,6 +88,10 @@ xsd:integer rdf:type rdfs:Datatype ; rdfs:label "Integer"@en . +### http://www.w3.org/2001/XMLSchema#nonNegativeInteger +xsd:nonNegativeInteger rdfs:label "Non-negative Integer"@en . + + ### http://www.w3.org/2001/XMLSchema#string xsd:string rdf:type rdfs:Datatype ; rdfs:label "String"@en . @@ -116,20 +134,24 @@ rdam:uniformResourceLocator.en rdf:type owl:ObjectProperty . ### http://fedora.info/definitions/v4/repository#created fedora:created rdf:type owl:DatatypeProperty ; - rdfs:subPropertyOf owl:topDataProperty . + rdfs:subPropertyOf owl:topDataProperty ; + :readOnly "true"^^xsd:boolean . ### http://fedora.info/definitions/v4/repository#createdBy fedora:createdBy rdf:type owl:DatatypeProperty ; - rdfs:subPropertyOf owl:topDataProperty . + rdfs:subPropertyOf owl:topDataProperty ; + :readOnly "true"^^xsd:boolean . ### http://fedora.info/definitions/v4/repository#lastModified -fedora:lastModified rdf:type owl:DatatypeProperty . +fedora:lastModified rdf:type owl:DatatypeProperty ; + :readOnly "true"^^xsd:boolean . ### http://fedora.info/definitions/v4/repository#lastModifiedBy -fedora:lastModifiedBy rdf:type owl:DatatypeProperty . +fedora:lastModifiedBy rdf:type owl:DatatypeProperty ; + :readOnly "true"^^xsd:boolean . ### http://rdaregistry.info/Elements/a/P50291 @@ -148,18 +170,24 @@ rdaa:givenName.en rdf:type owl:DatatypeProperty . 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 . + + ################################################################# # Classes ################################################################# ### http://fedora.info/definitions/v4/repository#Container fedora:Container rdf:type owl:Class ; - rdfs:subClassOf :ReadOnly . + :readOnly "true"^^xsd:boolean . ### http://fedora.info/definitions/v4/repository#Resource fedora:Resource rdf:type owl:Class ; - rdfs:subClassOf :ReadOnly . + :readOnly "true"^^xsd:boolean . ### http://rdaregistry.info/Elements/c/C10001 @@ -190,45 +218,55 @@ rdac:C10013 rdf:type owl:Class ; rdfs:label "RDA Entity"@en . +### http://www.w3.org/1999/02/22-rdf-syntax-ns#Property +rdf:Property rdf:type owl:Class ; + :catalogId "0"^^xsd:nonNegativeInteger . + + +### http://www.w3.org/2000/01/rdf-schema#Class +rdfs:Class rdf:type owl:Class ; + :catalogId "1"^^xsd:nonNegativeInteger . + + ### http://www.w3.org/ns/ldp#BasicContainer ldp:BasicContainer rdf:type owl:Class ; - rdfs:subClassOf :ReadOnly . + :readOnly "true"^^xsd:boolean . ### http://www.w3.org/ns/ldp#Container ldp:Container rdf:type owl:Class ; - rdfs:subClassOf :ReadOnly . + :readOnly "true"^^xsd:boolean . ### http://www.w3.org/ns/ldp#RDFSource ldp:RDFSource rdf:type owl:Class ; - rdfs:subClassOf :ReadOnly . + :readOnly "true"^^xsd:boolean . ### http://www.w3.org/ns/ldp#Resource ldp:Resource rdf:type owl:Class ; - rdfs:subClassOf :ReadOnly . + :readOnly "true"^^xsd:boolean . ### https://graphofliberty.org/2026/04/ont/AudioBook :AudioBook rdf:type owl:Class ; rdfs:subClassOf :Entity ; rdfs:label "Audio Book"@en ; - :catalogId "1"^^xsd:positiveInteger . + :catalogId "2"^^xsd:nonNegativeInteger . ### https://graphofliberty.org/2026/04/ont/Book :Book rdf:type owl:Class ; rdfs:subClassOf :Entity ; rdfs:label "Book"@en ; - :catalogId "2"^^xsd:positiveInteger . + :catalogId "3"^^xsd:nonNegativeInteger . ### https://graphofliberty.org/2026/04/ont/Document :Document rdf:type owl:Class ; rdfs:subClassOf :Entity ; rdfs:label "Document"@en ; - :catalogId "3"^^xsd:positiveInteger . + :catalogId "4"^^xsd:nonNegativeInteger . ### https://graphofliberty.org/2026/04/ont/Entity @@ -240,78 +278,48 @@ ldp:Resource rdf:type owl:Class ; :Meme rdf:type owl:Class ; rdfs:subClassOf :Entity ; rdfs:label "Meme"@en ; - :catalogId "4"^^xsd:positiveInteger . + :catalogId "5"^^xsd:nonNegativeInteger . ### https://graphofliberty.org/2026/04/ont/Movie :Movie rdf:type owl:Class ; rdfs:subClassOf :Entity ; rdfs:label "Movie"@en ; - :catalogId "5"^^xsd:positiveInteger . + :catalogId "6"^^xsd:nonNegativeInteger . ### https://graphofliberty.org/2026/04/ont/Music :Music rdf:type owl:Class ; rdfs:subClassOf :Entity ; rdfs:label "Music"@en ; - :catalogId "6"^^xsd:positiveInteger . + :catalogId "7"^^xsd:nonNegativeInteger . ### https://graphofliberty.org/2026/04/ont/Person :Person rdf:type owl:Class ; rdfs:subClassOf :Entity ; rdfs:label "Person"@en ; - :catalogId "7"^^xsd:positiveInteger . + :catalogId "8"^^xsd:nonNegativeInteger . ### https://graphofliberty.org/2026/04/ont/Podcast :Podcast rdf:type owl:Class ; rdfs:subClassOf :Entity ; rdfs:label "Podcast"@en ; - :catalogId "8"^^xsd:positiveInteger . - - -### https://graphofliberty.org/2026/04/ont/ReadOnly -:ReadOnly rdf:type owl:Class ; - rdfs:label "Read Only"@en . + :catalogId "9"^^xsd:nonNegativeInteger . ### https://graphofliberty.org/2026/04/ont/TVShow :TVShow rdf:type owl:Class ; rdfs:subClassOf :Entity ; rdfs:label "TV Show"@en ; - :catalogId "9"^^xsd:positiveInteger . + :catalogId "10"^^xsd:nonNegativeInteger . ################################################################# # Individuals ################################################################# -### http://fedora.info/definitions/v4/repository#created -fedora:created rdf:type owl:NamedIndividual , - :ReadOnly . - - -### http://fedora.info/definitions/v4/repository#createdBy -fedora:createdBy rdf:type owl:NamedIndividual , - :ReadOnly . - - -### http://fedora.info/definitions/v4/repository#hasParent -fedora:hasParent rdf:type owl:NamedIndividual , - :ReadOnly . - - -### http://fedora.info/definitions/v4/repository#lastModified -fedora:lastModified rdf:type owl:NamedIndividual , - :ReadOnly . - - -### http://fedora.info/definitions/v4/repository#lastModifiedBy -fedora:lastModifiedBy rdf:type owl:NamedIndividual , - :ReadOnly . - - ### http://rdaregistry.info/Elements/a/P50094 rdaa:P50094 rdf:type owl:NamedIndividual ; owl:sameAs rdaa:identifierForPerson.en . @@ -347,10 +355,8 @@ rdaa:surname.en rdf:type owl:NamedIndividual . ### http://rdaregistry.info/Elements/c/C10007 rdac:C10007 rdf:type owl:NamedIndividual ; - :template _:genid1 . - -_:genid1 rdf:type rdac:C10007 ; - rdam:uniformResourceLocator.en : . + :template [ rdf:type rdac:C10007 + ] . ### http://rdaregistry.info/Elements/m/P30154 @@ -364,32 +370,10 @@ rdam:P30154 rdf:type owl:NamedIndividual ; rdam:uniformResourceLocator.en rdf:type owl:NamedIndividual . -### http://www.w3.org/ns/ldp#contains -ldp:contains rdf:type owl:NamedIndividual , - :ReadOnly ; - rdfs:label "contains"@en . - - -### https://graphofliberty.org/2026/04/ont/ -: rdf:type owl:NamedIndividual . - - ################################################################# # Annotations ################################################################# -fedora:created rdfs:label "created"@en . - - -fedora:createdBy rdfs:label "created by"@en . - - -fedora:lastModified rdfs:label "last modified"@en . - - -fedora:lastModifiedBy rdfs:label "last modified by"@en . - - rdaa:P50094 rdfs:label "has identifier for person"@en . diff --git a/publish/src/rdf/ontologies/owl.ttl b/publish/src/rdf/ontologies/owl.ttl new file mode 100644 index 0000000..5f78546 --- /dev/null +++ b/publish/src/rdf/ontologies/owl.ttl @@ -0,0 +1,552 @@ +@prefix dc: . +@prefix grddl: . +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@prefix xml: . +@prefix xsd: . + + a owl:Ontology ; + dc:title "The OWL 2 Schema vocabulary (OWL 2)" ; + rdfs:comment """ + This ontology partially describes the built-in classes and + properties that together form the basis of the RDF/XML syntax of OWL 2. + The content of this ontology is based on Tables 6.1 and 6.2 + in Section 6.4 of the OWL 2 RDF-Based Semantics specification, + available at http://www.w3.org/TR/owl2-rdf-based-semantics/. + Please note that those tables do not include the different annotations + (labels, comments and rdfs:isDefinedBy links) used in this file. + Also note that the descriptions provided in this ontology do not + provide a complete and correct formal description of either the syntax + or the semantics of the introduced terms (please see the OWL 2 + recommendations for the complete and normative specifications). + Furthermore, the information provided by this ontology may be + misleading if not used with care. This ontology SHOULD NOT be imported + into OWL ontologies. Importing this file into an OWL 2 DL ontology + will cause it to become an OWL 2 Full ontology and may have other, + unexpected, consequences. + """ ; + rdfs:isDefinedBy + , + , + ; + rdfs:seeAlso , + ; + owl:imports ; + owl:versionIRI ; + owl:versionInfo "$Date: 2009/11/15 10:54:12 $" ; + grddl:namespaceTransformation . + + +owl:AllDifferent a rdfs:Class ; + rdfs:label "AllDifferent" ; + rdfs:comment "The class of collections of pairwise different individuals." ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdfs:Resource . + +owl:AllDisjointClasses a rdfs:Class ; + rdfs:label "AllDisjointClasses" ; + rdfs:comment "The class of collections of pairwise disjoint classes." ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdfs:Resource . + +owl:AllDisjointProperties a rdfs:Class ; + rdfs:label "AllDisjointProperties" ; + rdfs:comment "The class of collections of pairwise disjoint properties." ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdfs:Resource . + +owl:Annotation a rdfs:Class ; + rdfs:label "Annotation" ; + rdfs:comment "The class of annotated annotations for which the RDF serialization consists of an annotated subject, predicate and object." ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdfs:Resource . + +owl:AnnotationProperty a rdfs:Class ; + rdfs:label "AnnotationProperty" ; + rdfs:comment "The class of annotation properties." ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdf:Property . + +owl:AsymmetricProperty a rdfs:Class ; + rdfs:label "AsymmetricProperty" ; + rdfs:comment "The class of asymmetric properties." ; + rdfs:isDefinedBy ; + rdfs:subClassOf owl:ObjectProperty . + +owl:Axiom a rdfs:Class ; + rdfs:label "Axiom" ; + rdfs:comment "The class of annotated axioms for which the RDF serialization consists of an annotated subject, predicate and object." ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdfs:Resource . + +owl:Class a rdfs:Class ; + rdfs:label "Class" ; + rdfs:comment "The class of OWL classes." ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdfs:Class . + +owl:DataRange a rdfs:Class ; + rdfs:label "DataRange" ; + rdfs:comment "The class of OWL data ranges, which are special kinds of datatypes. Note: The use of the IRI owl:DataRange has been deprecated as of OWL 2. The IRI rdfs:Datatype SHOULD be used instead." ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdfs:Datatype . + +owl:DatatypeProperty a rdfs:Class ; + rdfs:label "DatatypeProperty" ; + rdfs:comment "The class of data properties." ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdf:Property . + +owl:DeprecatedClass a rdfs:Class ; + rdfs:label "DeprecatedClass" ; + rdfs:comment "The class of deprecated classes." ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdfs:Class . + +owl:DeprecatedProperty a rdfs:Class ; + rdfs:label "DeprecatedProperty" ; + rdfs:comment "The class of deprecated properties." ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdf:Property . + +owl:FunctionalProperty a rdfs:Class ; + rdfs:label "FunctionalProperty" ; + rdfs:comment "The class of functional properties." ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdf:Property . + +owl:InverseFunctionalProperty a rdfs:Class ; + rdfs:label "InverseFunctionalProperty" ; + rdfs:comment "The class of inverse-functional properties." ; + rdfs:isDefinedBy ; + rdfs:subClassOf owl:ObjectProperty . + +owl:IrreflexiveProperty a rdfs:Class ; + rdfs:label "IrreflexiveProperty" ; + rdfs:comment "The class of irreflexive properties." ; + rdfs:isDefinedBy ; + rdfs:subClassOf owl:ObjectProperty . + +owl:NamedIndividual a rdfs:Class ; + rdfs:label "NamedIndividual" ; + rdfs:comment "The class of named individuals." ; + rdfs:isDefinedBy ; + rdfs:subClassOf owl:Thing . + +owl:NegativePropertyAssertion a rdfs:Class ; + rdfs:label "NegativePropertyAssertion" ; + rdfs:comment "The class of negative property assertions." ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdfs:Resource . + +owl:Nothing a owl:Class ; + rdfs:label "Nothing" ; + rdfs:comment "This is the empty class." ; + rdfs:isDefinedBy ; + rdfs:subClassOf owl:Thing . + +owl:ObjectProperty a rdfs:Class ; + rdfs:label "ObjectProperty" ; + rdfs:comment "The class of object properties." ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdf:Property . + +owl:Ontology a rdfs:Class ; + rdfs:label "Ontology" ; + rdfs:comment "The class of ontologies." ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdfs:Resource . + +owl:OntologyProperty a rdfs:Class ; + rdfs:label "OntologyProperty" ; + rdfs:comment "The class of ontology properties." ; + rdfs:isDefinedBy ; + rdfs:subClassOf rdf:Property . + +owl:ReflexiveProperty a rdfs:Class ; + rdfs:label "ReflexiveProperty" ; + rdfs:comment "The class of reflexive properties." ; + rdfs:isDefinedBy ; + rdfs:subClassOf owl:ObjectProperty . + +owl:Restriction a rdfs:Class ; + rdfs:label "Restriction" ; + rdfs:comment "The class of property restrictions." ; + rdfs:isDefinedBy ; + rdfs:subClassOf owl:Class . + +owl:SymmetricProperty a rdfs:Class ; + rdfs:label "SymmetricProperty" ; + rdfs:comment "The class of symmetric properties." ; + rdfs:isDefinedBy ; + rdfs:subClassOf owl:ObjectProperty . + +owl:TransitiveProperty a rdfs:Class ; + rdfs:label "TransitiveProperty" ; + rdfs:comment "The class of transitive properties." ; + rdfs:isDefinedBy ; + rdfs:subClassOf owl:ObjectProperty . + +owl:Thing a owl:Class ; + rdfs:label "Thing" ; + rdfs:comment "The class of OWL individuals." ; + rdfs:isDefinedBy . + +owl:allValuesFrom a rdf:Property ; + rdfs:label "allValuesFrom" ; + rdfs:comment "The property that determines the class that a universal property restriction refers to." ; + rdfs:domain owl:Restriction ; + rdfs:isDefinedBy ; + rdfs:range rdfs:Class . + +owl:annotatedProperty a rdf:Property ; + rdfs:label "annotatedProperty" ; + rdfs:comment "The property that determines the predicate of an annotated axiom or annotated annotation." ; + rdfs:domain rdfs:Resource ; + rdfs:isDefinedBy ; + rdfs:range rdfs:Resource . + +owl:annotatedSource a rdf:Property ; + rdfs:label "annotatedSource" ; + rdfs:comment "The property that determines the subject of an annotated axiom or annotated annotation." ; + rdfs:domain rdfs:Resource ; + rdfs:isDefinedBy ; + rdfs:range rdfs:Resource . + +owl:annotatedTarget a rdf:Property ; + rdfs:label "annotatedTarget" ; + rdfs:comment "The property that determines the object of an annotated axiom or annotated annotation." ; + rdfs:domain rdfs:Resource ; + rdfs:isDefinedBy ; + rdfs:range rdfs:Resource . + +owl:assertionProperty a rdf:Property ; + rdfs:label "assertionProperty" ; + rdfs:comment "The property that determines the predicate of a negative property assertion." ; + rdfs:domain owl:NegativePropertyAssertion ; + rdfs:isDefinedBy ; + rdfs:range rdf:Property . + +owl:backwardCompatibleWith a owl:AnnotationProperty, owl:OntologyProperty ; + rdfs:label "backwardCompatibleWith" ; + rdfs:comment "The annotation property that indicates that a given ontology is backward compatible with another ontology." ; + rdfs:domain owl:Ontology ; + rdfs:isDefinedBy ; + rdfs:range owl:Ontology . + +owl:bottomDataProperty a owl:DatatypeProperty ; + rdfs:label "bottomDataProperty" ; + rdfs:comment "The data property that does not relate any individual to any data value." ; + rdfs:domain owl:Thing ; + rdfs:isDefinedBy ; + rdfs:range rdfs:Literal . + +owl:bottomObjectProperty a owl:ObjectProperty ; + rdfs:label "bottomObjectProperty" ; + rdfs:comment "The object property that does not relate any two individuals." ; + rdfs:domain owl:Thing ; + rdfs:isDefinedBy ; + rdfs:range owl:Thing . + +owl:cardinality a rdf:Property ; + rdfs:label "cardinality" ; + rdfs:comment "The property that determines the cardinality of an exact cardinality restriction." ; + rdfs:domain owl:Restriction ; + rdfs:isDefinedBy ; + rdfs:range xsd:nonNegativeInteger . + +owl:complementOf a rdf:Property ; + rdfs:label "complementOf" ; + rdfs:comment "The property that determines that a given class is the complement of another class." ; + rdfs:domain owl:Class ; + rdfs:isDefinedBy ; + rdfs:range owl:Class . + +owl:datatypeComplementOf a rdf:Property ; + rdfs:label "datatypeComplementOf" ; + rdfs:comment "The property that determines that a given data range is the complement of another data range with respect to the data domain." ; + rdfs:domain rdfs:Datatype ; + rdfs:isDefinedBy ; + rdfs:range rdfs:Datatype . + +owl:deprecated a owl:AnnotationProperty ; + rdfs:label "deprecated" ; + rdfs:comment "The annotation property that indicates that a given entity has been deprecated." ; + rdfs:domain rdfs:Resource ; + rdfs:isDefinedBy ; + rdfs:range rdfs:Resource . + +owl:differentFrom a rdf:Property ; + rdfs:label "differentFrom" ; + rdfs:comment "The property that determines that two given individuals are different." ; + rdfs:domain owl:Thing ; + rdfs:isDefinedBy ; + rdfs:range owl:Thing . + +owl:disjointUnionOf a rdf:Property ; + rdfs:label "disjointUnionOf" ; + rdfs:comment "The property that determines that a given class is equivalent to the disjoint union of a collection of other classes." ; + rdfs:domain owl:Class ; + rdfs:isDefinedBy ; + rdfs:range rdf:List . + +owl:disjointWith a rdf:Property ; + rdfs:label "disjointWith" ; + rdfs:comment "The property that determines that two given classes are disjoint." ; + rdfs:domain owl:Class ; + rdfs:isDefinedBy ; + rdfs:range owl:Class . + +owl:distinctMembers a rdf:Property ; + rdfs:label "distinctMembers" ; + rdfs:comment "The property that determines the collection of pairwise different individuals in a owl:AllDifferent axiom." ; + rdfs:domain owl:AllDifferent ; + rdfs:isDefinedBy ; + rdfs:range rdf:List . + +owl:equivalentClass a rdf:Property ; + rdfs:label "equivalentClass" ; + rdfs:comment "The property that determines that two given classes are equivalent, and that is used to specify datatype definitions." ; + rdfs:domain rdfs:Class ; + rdfs:isDefinedBy ; + rdfs:range rdfs:Class . + +owl:equivalentProperty a rdf:Property ; + rdfs:label "equivalentProperty" ; + rdfs:comment "The property that determines that two given properties are equivalent." ; + rdfs:domain rdf:Property ; + rdfs:isDefinedBy ; + rdfs:range rdf:Property . + +owl:hasKey a rdf:Property ; + rdfs:label "hasKey" ; + rdfs:comment "The property that determines the collection of properties that jointly build a key." ; + rdfs:domain owl:Class ; + rdfs:isDefinedBy ; + rdfs:range rdf:List . + +owl:hasSelf a rdf:Property ; + rdfs:label "hasSelf" ; + rdfs:comment "The property that determines the property that a self restriction refers to." ; + rdfs:domain owl:Restriction ; + rdfs:isDefinedBy ; + rdfs:range rdfs:Resource . + +owl:hasValue a rdf:Property ; + rdfs:label "hasValue" ; + rdfs:comment "The property that determines the individual that a has-value restriction refers to." ; + rdfs:domain owl:Restriction ; + rdfs:isDefinedBy ; + rdfs:range rdfs:Resource . + +owl:imports a owl:OntologyProperty ; + rdfs:label "imports" ; + rdfs:comment "The property that is used for importing other ontologies into a given ontology." ; + rdfs:domain owl:Ontology ; + rdfs:isDefinedBy ; + rdfs:range owl:Ontology . + +owl:incompatibleWith a owl:AnnotationProperty, owl:OntologyProperty ; + rdfs:label "incompatibleWith" ; + rdfs:comment "The annotation property that indicates that a given ontology is incompatible with another ontology." ; + rdfs:domain owl:Ontology ; + rdfs:isDefinedBy ; + rdfs:range owl:Ontology . + +owl:intersectionOf a rdf:Property ; + rdfs:label "intersectionOf" ; + rdfs:comment "The property that determines the collection of classes or data ranges that build an intersection." ; + rdfs:domain rdfs:Class ; + rdfs:isDefinedBy ; + rdfs:range rdf:List . + +owl:inverseOf a rdf:Property ; + rdfs:label "inverseOf" ; + rdfs:comment "The property that determines that two given properties are inverse." ; + rdfs:domain owl:ObjectProperty ; + rdfs:isDefinedBy ; + rdfs:range owl:ObjectProperty . + +owl:maxCardinality a rdf:Property ; + rdfs:label "maxCardinality" ; + rdfs:comment "The property that determines the cardinality of a maximum cardinality restriction." ; + rdfs:domain owl:Restriction ; + rdfs:isDefinedBy ; + rdfs:range xsd:nonNegativeInteger . + +owl:maxQualifiedCardinality a rdf:Property ; + rdfs:label "maxQualifiedCardinality" ; + rdfs:comment "The property that determines the cardinality of a maximum qualified cardinality restriction." ; + rdfs:domain owl:Restriction ; + rdfs:isDefinedBy ; + rdfs:range xsd:nonNegativeInteger . + +owl:members a rdf:Property ; + rdfs:label "members" ; + rdfs:comment "The property that determines the collection of members in either a owl:AllDifferent, owl:AllDisjointClasses or owl:AllDisjointProperties axiom." ; + rdfs:domain rdfs:Resource ; + rdfs:isDefinedBy ; + rdfs:range rdf:List . + +owl:minCardinality a rdf:Property ; + rdfs:label "minCardinality" ; + rdfs:comment "The property that determines the cardinality of a minimum cardinality restriction." ; + rdfs:domain owl:Restriction ; + rdfs:isDefinedBy ; + rdfs:range xsd:nonNegativeInteger . + +owl:minQualifiedCardinality a rdf:Property ; + rdfs:label "minQualifiedCardinality" ; + rdfs:comment "The property that determines the cardinality of a minimum qualified cardinality restriction." ; + rdfs:domain owl:Restriction ; + rdfs:isDefinedBy ; + rdfs:range xsd:nonNegativeInteger . + +owl:onClass a rdf:Property ; + rdfs:label "onClass" ; + rdfs:comment "The property that determines the class that a qualified object cardinality restriction refers to." ; + rdfs:domain owl:Restriction ; + rdfs:isDefinedBy ; + rdfs:range owl:Class . + +owl:onDataRange a rdf:Property ; + rdfs:label "onDataRange" ; + rdfs:comment "The property that determines the data range that a qualified data cardinality restriction refers to." ; + rdfs:domain owl:Restriction ; + rdfs:isDefinedBy ; + rdfs:range rdfs:Datatype . + +owl:onDatatype a rdf:Property ; + rdfs:label "onDatatype" ; + rdfs:comment "The property that determines the datatype that a datatype restriction refers to." ; + rdfs:domain rdfs:Datatype ; + rdfs:isDefinedBy ; + rdfs:range rdfs:Datatype . + +owl:oneOf a rdf:Property ; + rdfs:label "oneOf" ; + rdfs:comment "The property that determines the collection of individuals or data values that build an enumeration." ; + rdfs:domain rdfs:Class ; + rdfs:isDefinedBy ; + rdfs:range rdf:List . + +owl:onProperties a rdf:Property ; + rdfs:label "onProperties" ; + rdfs:comment "The property that determines the n-tuple of properties that a property restriction on an n-ary data range refers to." ; + rdfs:domain owl:Restriction ; + rdfs:isDefinedBy ; + rdfs:range rdf:List . + +owl:onProperty a rdf:Property ; + rdfs:label "onProperty" ; + rdfs:comment "The property that determines the property that a property restriction refers to." ; + rdfs:domain owl:Restriction ; + rdfs:isDefinedBy ; + rdfs:range rdf:Property . + +owl:priorVersion a owl:AnnotationProperty, owl:OntologyProperty ; + rdfs:label "priorVersion" ; + rdfs:comment "The annotation property that indicates the predecessor ontology of a given ontology." ; + rdfs:domain owl:Ontology ; + rdfs:isDefinedBy ; + rdfs:range owl:Ontology . + +owl:propertyChainAxiom a rdf:Property ; + rdfs:label "propertyChainAxiom" ; + rdfs:comment "The property that determines the n-tuple of properties that build a sub property chain of a given property." ; + rdfs:domain owl:ObjectProperty ; + rdfs:isDefinedBy ; + rdfs:range rdf:List . + +owl:propertyDisjointWith a rdf:Property ; + rdfs:label "propertyDisjointWith" ; + rdfs:comment "The property that determines that two given properties are disjoint." ; + rdfs:domain rdf:Property ; + rdfs:isDefinedBy ; + rdfs:range rdf:Property . + +owl:qualifiedCardinality a rdf:Property ; + rdfs:label "qualifiedCardinality" ; + rdfs:comment "The property that determines the cardinality of an exact qualified cardinality restriction." ; + rdfs:domain owl:Restriction ; + rdfs:isDefinedBy ; + rdfs:range xsd:nonNegativeInteger . + +owl:sameAs a rdf:Property ; + rdfs:label "sameAs" ; + rdfs:comment "The property that determines that two given individuals are equal." ; + rdfs:domain owl:Thing ; + rdfs:isDefinedBy ; + rdfs:range owl:Thing . + +owl:someValuesFrom a rdf:Property ; + rdfs:label "someValuesFrom" ; + rdfs:comment "The property that determines the class that an existential property restriction refers to." ; + rdfs:domain owl:Restriction ; + rdfs:isDefinedBy ; + rdfs:range rdfs:Class . + +owl:sourceIndividual a rdf:Property ; + rdfs:label "sourceIndividual" ; + rdfs:comment "The property that determines the subject of a negative property assertion." ; + rdfs:domain owl:NegativePropertyAssertion ; + rdfs:isDefinedBy ; + rdfs:range owl:Thing . + +owl:targetIndividual a rdf:Property ; + rdfs:label "targetIndividual" ; + rdfs:comment "The property that determines the object of a negative object property assertion." ; + rdfs:domain owl:NegativePropertyAssertion ; + rdfs:isDefinedBy ; + rdfs:range owl:Thing . + +owl:targetValue a rdf:Property ; + rdfs:label "targetValue" ; + rdfs:comment "The property that determines the value of a negative data property assertion." ; + rdfs:domain owl:NegativePropertyAssertion ; + rdfs:isDefinedBy ; + rdfs:range rdfs:Literal . + +owl:topDataProperty a owl:DatatypeProperty ; + rdfs:label "topDataProperty" ; + rdfs:comment "The data property that relates every individual to every data value." ; + rdfs:domain owl:Thing ; + rdfs:isDefinedBy ; + rdfs:range rdfs:Literal . + +owl:topObjectProperty a owl:ObjectProperty ; + rdfs:label "topObjectProperty" ; + rdfs:comment "The object property that relates every two individuals." ; + rdfs:domain owl:Thing ; + rdfs:isDefinedBy ; + rdfs:range owl:Thing . + +owl:unionOf a rdf:Property ; + rdfs:label "unionOf" ; + rdfs:comment "The property that determines the collection of classes or data ranges that build a union." ; + rdfs:domain rdfs:Class ; + rdfs:isDefinedBy ; + rdfs:range rdf:List . + +owl:versionInfo a owl:AnnotationProperty ; + rdfs:label "versionInfo" ; + rdfs:comment "The annotation property that provides version information for an ontology or another OWL construct." ; + rdfs:domain rdfs:Resource ; + rdfs:isDefinedBy ; + rdfs:range rdfs:Resource . + +owl:versionIRI a owl:OntologyProperty ; + rdfs:label "versionIRI" ; + rdfs:comment "The property that identifies the version IRI of an ontology." ; + rdfs:domain owl:Ontology ; + rdfs:isDefinedBy ; + rdfs:range owl:Ontology . + +owl:withRestrictions a rdf:Property ; + rdfs:label "withRestrictions" ; + rdfs:comment "The property that determines the collection of facet-value pairs that define a datatype restriction." ; + rdfs:domain rdfs:Datatype ; + rdfs:isDefinedBy ; + rdfs:range rdf:List . + \ No newline at end of file diff --git a/publish/src/rdf/ontology.rs b/publish/src/rdf/ontology.rs index 47f27ba..ce6f494 100644 --- a/publish/src/rdf/ontology.rs +++ b/publish/src/rdf/ontology.rs @@ -7,6 +7,8 @@ use oxigraph::model::{ }; use oxigraph::sparql::{QueryResults, SparqlEvaluator}; use std::collections::{HashMap, HashSet}; +use tracing::debug; +use crate::rdf::vocab::{gl, owl}; const PREFIXES: &[(&str, &str)] = &[ ("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"), @@ -41,26 +43,11 @@ const PREFIXES: &[(&str, &str)] = &[ const RDF_ONT: &[u8] = include_bytes!("ontologies/22-rdf-syntax-ns.ttl"); const RDFS_ONT: &[u8] = include_bytes!("ontologies/rdf-schema.ttl"); +const OWL_ONT: &[u8] = include_bytes!("ontologies/owl.ttl"); const LDP_ONT: &[u8] = include_bytes!("ontologies/ldp.ttl"); const FEDORA_ONT: &[u8] = include_bytes!("ontologies/fedora.xml"); const GL_ONT: &[u8] = include_bytes!("ontologies/ontology.ttl"); -const GL_READ_ONLY: NamedNodeRef = - NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/ReadOnly"); -const GL_TEMPLATE: NamedNodeRef = - NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/template"); -const GL_INDEXED_BY_FIELD: NamedNodeRef = - NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/indexedByField"); -const GL_CATALOG_ID: NamedNodeRef = - NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/catalogId"); -const OWL_SAME_AS: NamedNodeRef = - NamedNodeRef::new_unchecked("http://www.w3.org/2002/07/owl#sameAs"); - -pub const RDA_ENTITY: NamedNodeRef = - NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/c/C10013"); -pub const GL_ENTITY: NamedNodeRef = - NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/Entity"); - pub struct OntologyBuilder<'a> { ontologies: Vec<(RdfFormat, &'a [u8])>, } @@ -74,6 +61,7 @@ impl<'a> OntologyBuilder<'a> { pub fn with_default_ontologies(self) -> Self { self.with_ontology_bytes(RdfFormat::Turtle, RDF_ONT) .with_ontology_bytes(RdfFormat::Turtle, RDFS_ONT) + .with_ontology_bytes(RdfFormat::Turtle, OWL_ONT) .with_ontology_bytes(RdfFormat::Turtle, LDP_ONT) .with_ontology_bytes(RdfFormat::RdfXml, FEDORA_ONT) .with_ontology_bytes(RdfFormat::Turtle, GL_ONT) @@ -81,7 +69,7 @@ impl<'a> OntologyBuilder<'a> { fn materialize_same_as(dataset: &mut Dataset) { let additional_quads = dataset - .quads_for_pattern(None, Some(OWL_SAME_AS), None, None) + .quads_for_pattern(None, Some(owl::SAME_AS), None, None) .fold(Dataset::new(), |mut new_dataset, alias| { if let NamedOrBlankNodeRef::NamedNode(x) = alias.subject && let TermRef::NamedNode(y) = alias.object @@ -118,8 +106,50 @@ impl<'a> OntologyBuilder<'a> { } } + fn lookup_iri_information<'b>(dataset: &Dataset, classes: impl IntoIterator>) -> HashMap { + let class_filter = classes + .into_iter() + .map(|node| node.to_string()) + .collect::>() + .join(","); + + let query = SparqlEvaluator::new() + .parse_query(format!(r#"PREFIX gl: +PREFIX rdfs: + +SELECT DISTINCT ?subject ?label ?comment ?read_only {{ + ?subject a ?class . + FILTER(?class IN ({class_filter})) + OPTIONAL {{ ?subject rdfs:label ?label }} + OPTIONAL {{ ?subject rdfs:comment ?comment }} + OPTIONAL {{ ?subject gl:readOnly ?read_only }} +}}"#).as_str()).expect("Unable to parse property 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 label = solution.get("label").and_then(term_to_string); + let comment = solution.get("comment").and_then(term_to_string); + let read_only = solution.get("read_only").and_then(term_to_boolean).unwrap_or(false); + + if let Some(subject) = subject { + let info = IriInformation { + label: label.map(String::from), + comment: comment.map(String::from), + read_only, + }; + results.insert(subject.to_owned(), info); + } + } + } + results + } + fn memoize(ontology: &mut Ontology) { - // Read-only properties + /*// Read-only properties for quad in ontology.dataset.quads_for_pattern( None, Some(rdf::TYPE), @@ -165,12 +195,12 @@ impl<'a> OntologyBuilder<'a> { if let NamedOrBlankNodeRef::NamedNode(subject) = quad.subject && let TermRef::Literal(literal) = quad.object { - if literal.datatype() == xsd::POSITIVE_INTEGER { - let value: u64 = literal.value().parse().expect("Failed to parse catalog ID from ontology. It ought to be a positive integer."); + if literal.datatype() == xsd::NON_NEGATIVE_INTEGER { + let value: u64 = literal.value().parse().expect("Failed to parse catalog ID from ontology. It ought to be a non-negative integer."); ontology.catalog_ids.insert(subject.into_owned(), value); } } - } + }*/ } pub fn build(&mut self) -> error::Result { @@ -188,9 +218,25 @@ impl<'a> OntologyBuilder<'a> { } Self::materialize_same_as(&mut dataset); + let properties = Self::lookup_iri_information(&dataset, [ + rdf::PROPERTY, + owl::ANNOTATION_PROPERTY, + owl::DATATYPE_PROPERTY, + owl::FUNCTIONAL_PROPERTY, + owl::OBJECT_PROPERTY, + owl::ONTOLOGY_PROPERTY, + ]); + + let classes = Self::lookup_iri_information(&dataset, [ + rdfs::CLASS, + owl::CLASS, + ]); + let mut ontology = Ontology { dataset, prefixes, + properties, + classes, read_only_properties: HashSet::new(), read_only_classes: HashSet::new(), field_map: HashMap::new(), @@ -202,20 +248,45 @@ impl<'a> OntologyBuilder<'a> { } } +#[derive(Debug)] +struct IriInformation { + label: Option, + comment: Option, + read_only: bool, +} + pub struct Ontology { dataset: Dataset, prefixes: HashMap, + properties: HashMap, + classes: HashMap, read_only_properties: HashSet, read_only_classes: HashSet, field_map: HashMap, catalog_ids: HashMap, } -fn term_to_string(term: &Term) -> Option { - match term { - Term::Literal(literal) => Some(literal.value().to_string()), - _ => None, - } +fn term_to_named_node(term: &Term) -> Option<&NamedNode> { + if let Term::NamedNode(node) = term { + Some(node) + } else { None } +} + +fn term_to_string(term: &Term) -> Option<&str> { + if let Term::Literal(literal) = term { + match literal.datatype() { + xsd::STRING | xsd::NORMALIZED_STRING => Some(literal.value()), + _ => None, + } + } else { None } +} + +fn term_to_boolean(term: &Term) -> Option { + if let Term::Literal(literal) = term { + if literal.datatype() == xsd::BOOLEAN { + literal.value().parse().ok() + } else { None } + } else { None } } impl Ontology { @@ -384,7 +455,7 @@ SELECT ?class {{ .dataset .quads_for_pattern( Some(NamedOrBlankNodeRef::NamedNode(class)), - Some(GL_TEMPLATE), + Some(gl::TEMPLATE), None, None, ) diff --git a/publish/src/rdf/vocab.rs b/publish/src/rdf/vocab.rs new file mode 100644 index 0000000..aab594d --- /dev/null +++ b/publish/src/rdf/vocab.rs @@ -0,0 +1,34 @@ +pub mod gl { + use oxigraph::model::NamedNodeRef; + + pub const READ_ONLY: NamedNodeRef = + NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/ReadOnly"); + pub const TEMPLATE: NamedNodeRef = + NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/template"); + pub const INDEXED_BY_FIELD: NamedNodeRef = + NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/indexedByField"); + pub const CATALOG_ID: NamedNodeRef = + NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/catalogId"); + + pub const ENTITY: NamedNodeRef = + NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/Entity"); +} + +pub mod owl { + use oxigraph::model::NamedNodeRef; + + pub const SAME_AS: NamedNodeRef = NamedNodeRef::new_unchecked("http://www.w3.org/2002/07/owl#sameAs"); + pub const CLASS: NamedNodeRef = NamedNodeRef::new_unchecked("http://www.w3.org/2002/07/owl#Class"); + pub const OBJECT_PROPERTY: NamedNodeRef = NamedNodeRef::new_unchecked("http://www.w3.org/2002/07/owl#ObjectProperty"); + pub const DATATYPE_PROPERTY: NamedNodeRef = NamedNodeRef::new_unchecked("http://www.w3.org/2002/07/owl#DatatypeProperty"); + pub const ANNOTATION_PROPERTY: NamedNodeRef = NamedNodeRef::new_unchecked("http://www.w3.org/2002/07/owl#AnnotationProperty"); + pub const FUNCTIONAL_PROPERTY: NamedNodeRef = NamedNodeRef::new_unchecked("http://www.w3.org/2002/07/owl#FunctionalProperty"); + pub const ONTOLOGY_PROPERTY: NamedNodeRef = NamedNodeRef::new_unchecked("http://www.w3.org/2002/07/owl#OntologyProperty"); +} + +pub mod rda { + use oxigraph::model::NamedNodeRef; + + pub const ENTITY: NamedNodeRef = + NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/c/C10013"); +} \ No newline at end of file diff --git a/search/src/index.rs b/search/src/index.rs index 6134aa9..9ee8d63 100644 --- a/search/src/index.rs +++ b/search/src/index.rs @@ -84,12 +84,12 @@ impl SearchIndex { default_fields: Vec, ) -> error::Result> { let doc_type_term = Term::from_field_u64(Schema::type_field(), type_); - let doc_type_query = Box::new(TermQuery::new(doc_type_term, IndexRecordOption::Basic)); + //let doc_type_query = Box::new(TermQuery::new(doc_type_term, IndexRecordOption::Basic)); let parser = QueryParser::for_index(&self.index, default_fields); let (user_query, _) = parser.parse_query_lenient(user_query); let query = BooleanQuery::new(vec![ - (Occur::Must, doc_type_query), + //(Occur::Must, doc_type_query), (Occur::Must, user_query), ]); let searcher = self.reader.searcher();