Compare commits

...
18 Commits
Author SHA256 Message Date
alex 6d75248bde . 2026-09-02 23:05:29 -04:00
alex abe3ac97b5 . 2026-08-31 13:45:15 -04:00
alex ebad6e6097 . 2026-08-30 18:50:48 -04:00
alex 0c46d08e1d . 2026-08-30 15:00:52 -04:00
alex 1a122d6ecc . 2026-08-28 15:57:59 -04:00
alex 277d1d2a2d . 2026-08-27 13:00:36 -04:00
alex a8938176be . 2026-08-26 20:43:01 -04:00
alex 948b415d5a . 2026-08-26 19:44:18 -04:00
alex 35477a3658 . 2026-08-25 17:07:40 -04:00
alex 78243f53f5 . 2026-08-24 21:44:23 -04:00
alex 70fca07790 . 2026-08-23 17:01:13 -04:00
alex 37248f6ff3 . 2026-08-23 16:13:09 -04:00
alex 8f6e2b3ac6 . 2026-08-23 14:20:48 -04:00
alex e28cfd1c53 . 2026-08-22 23:31:14 -04:00
alex 631bb539f1 . 2026-08-20 21:27:38 -04:00
alex fcb1da7e0d . 2026-08-20 20:43:18 -04:00
alex 025392b129 . 2026-08-16 21:42:14 -04:00
alex 72a39e5baa . 2026-08-16 15:50:10 -04:00
40 changed files with 4727 additions and 1295 deletions
Generated
+260 -237
View File
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -19,8 +19,8 @@ bytes = "1.12"
clap = { version = "4.6", features = ["derive"] }
color-eyre = "0.6"
futures = "0.3"
http = "1.4"
iced = { git = "https://github.com/iced-rs/iced.git", branch = "master", features = ["advanced", "tokio"] }
http = "1.5"
iced = { git = "https://github.com/iced-rs/iced.git", branch = "master", features = ["advanced", "sipper", "tokio"] }
num-traits = "0.2"
oxigraph = { version = "0.5", features = ["rdf-12"] }
oxilangtag = "0.1"
@@ -28,8 +28,10 @@ parse_link_header = "0.4"
pin-project-lite = "0.2"
prost = "0.14"
rand = "0.10"
rayon = "1.12"
rfd = "0.17"
slotmap = "1.1"
spargebra = "0.4"
subtitler = "2.6"
tantivy = "0.26"
tar = "0.4"
+4 -1
View File
@@ -9,5 +9,8 @@ gl-search.workspace = true
oxigraph.workspace = true
oxilangtag.workspace = true
rayon.workspace = true
spargebra.workspace = true
thiserror.workspace = true
tracing.workspace = true
tracing.workspace = true
url = "2.5.8"
+4 -4
View File
@@ -1,5 +1,5 @@
use oxigraph::model::NamedNodeRef;
use crate::vocab;
use oxigraph::model::NamedNodeRef;
pub enum Category {
AudioBook = 0,
@@ -8,15 +8,15 @@ pub enum Category {
impl Category {
pub fn to_named_node(&self) -> NamedNodeRef<'_> {
match self {
Category::AudioBook => vocab::gl::AUDIO_BOOK,
Category::AudioBook => vocab::glo::AUDIO_BOOK,
}
}
pub fn try_from_named_node<'a>(node: impl Into<NamedNodeRef<'a>>) -> Option<Self> {
let node = node.into();
match node {
vocab::gl::AUDIO_BOOK => Some(Category::AudioBook),
vocab::glo::AUDIO_BOOK => Some(Category::AudioBook),
_ => None,
}
}
}
}
+215 -2
View File
@@ -1,6 +1,11 @@
use oxigraph::model::NamedNodeRef;
use crate::vocab;
use gl_search::Schema;
use oxigraph::model::{Graph, LiteralRef, NamedNode, NamedNodeRef, Triple};
use oxilangtag::LanguageTag;
use std::fmt::Display;
use url::Url;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Class {
RdfProperty = 0,
RdfsClass = 1,
@@ -12,7 +17,41 @@ pub enum Class {
Manifestation = 10007,
}
impl Display for Class {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Class::RdfProperty => f.write_str("RDF Property"),
Class::RdfsClass => f.write_str("RDFS Class"),
Class::SkosConcept => f.write_str("SKOS Concept"),
Class::Work => f.write_str("Work"),
Class::Person => f.write_str("Person"),
Class::CorporateBody => f.write_str("Corporate Body"),
Class::Expression => f.write_str("Expression"),
Class::Manifestation => f.write_str("Manifestation"),
}
}
}
impl Class {
pub const ALL: &[Class] = &[
Class::RdfProperty,
Class::RdfsClass,
Class::SkosConcept,
Class::Work,
Class::Person,
Class::CorporateBody,
Class::Expression,
Class::Manifestation,
];
pub const CREATABLE: &[Class] = &[
Class::Work,
Class::Person,
Class::CorporateBody,
Class::Expression,
Class::Manifestation,
];
pub fn to_named_node(&self) -> NamedNodeRef<'_> {
match self {
Class::RdfProperty => vocab::rdf::PROPERTY,
@@ -40,4 +79,178 @@ impl Class {
_ => None,
}
}
}
pub fn fields(&self, language: LanguageTag<String>) -> Vec<(gl_search::Field, String)> {
match self {
Class::RdfProperty => vec![
(
Schema::field("label", Some(language.primary_language())),
String::from("Label"),
),
(
Schema::field("definition", Some(language.primary_language())),
String::from("Definition"),
),
],
Class::RdfsClass => vec![
(
Schema::field("label", Some(language.primary_language())),
String::from("Label"),
),
(
Schema::field("definition", Some(language.primary_language())),
String::from("Definition"),
),
],
Class::SkosConcept => vec![
(
Schema::field("label", Some(language.primary_language())),
String::from("Label"),
),
(
Schema::field("definition", Some(language.primary_language())),
String::from("Definition"),
),
],
Class::Work => vec![(
Schema::field("title", Some(language.primary_language())),
String::from("Title"),
)],
Class::Person => vec![
(
Schema::field("given_name", Some(language.primary_language())),
String::from("Given Name"),
),
(
Schema::field("surname", Some(language.primary_language())),
String::from("Surname"),
),
],
Class::CorporateBody => vec![(
Schema::field("corporate_body", Some(language.primary_language())),
String::from("Corporate Body"),
)],
Class::Expression => vec![(
Schema::field("title", Some(language.primary_language())),
String::from("Title"),
)],
Class::Manifestation => vec![(
Schema::field("title", Some(language.primary_language())),
String::from("Title"),
)],
}
}
pub fn next_url(&self) -> Option<Url> {
match self {
Class::Work => Some(Url::parse("http://fedora.quill.lan/rest/#Work").unwrap()),
Class::Person => Some(Url::parse("http://fedora.quill.lan/rest/#Agent").unwrap()),
Class::CorporateBody => {
Some(Url::parse("http://fedora.quill.lan/rest/#Agent").unwrap())
}
Class::Expression => {
Some(Url::parse("http://fedora.quill.lan/rest/#Expression").unwrap())
}
Class::Manifestation => {
Some(Url::parse("http://fedora.quill.lan/rest/#Manifestation").unwrap())
}
_ => None,
}
}
pub fn template(&self, subject: NamedNode) -> Graph {
let subject = subject.as_ref();
let empty_english_string_literal =
LiteralRef::new_language_tagged_literal_unchecked("", "en");
let empty_string_literal = LiteralRef::new_simple_literal("");
let empty_node = NamedNodeRef::new_unchecked("");
let hash_node = NamedNodeRef::new_unchecked("#");
match self {
Class::RdfProperty => Graph::new(),
Class::RdfsClass => Graph::new(),
Class::SkosConcept => Graph::new(),
Class::Work => Graph::from_iter([
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([
Triple::new(
subject,
vocab::rdaad::GIVEN_NAME,
empty_english_string_literal,
),
Triple::new(subject, vocab::rdaad::SURNAME, empty_english_string_literal),
Triple::new(subject, vocab::rdaao::IDENTIFIER_FOR_PERSON, 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::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),
]),
}
}
}
+39 -24
View File
@@ -1,6 +1,6 @@
use oxigraph::model::{GraphNameRef, NamedNodeRef};
use std::collections::BTreeMap;
use std::sync::LazyLock;
use url::Url;
const ONTOLOGY_PREFIX: &str = "https://graphofliberty.org/2026/04/ont/";
@@ -19,12 +19,16 @@ pub static PREFIXES: LazyLock<BTreeMap<String, String>> = LazyLock::new(|| {
"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/"),
("lcid", "http://id.loc.gov/vocabulary/identifiers/"),
("lca", "http://id.loc.gov/authorities/"),
("lcn", "http://id.loc.gov/authorities/names/"),
("lclang", "http://id.loc.gov/vocabulary/languages/"),
("viaf", "http://viaf.org/viaf/"),
("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#"),
("lrmer", "http://iflastandards.info/ns/lrm/lrmer/"),
("rdaa", "http://rdaregistry.info/Elements/a/"),
("rdac", "http://rdaregistry.info/Elements/c/"),
("rdae", "http://rdaregistry.info/Elements/e/"),
@@ -58,32 +62,43 @@ impl CurieHelper {
Self { prefixes }
}
pub fn abbreviate(&self, base: Option<&str>, iri: &str) -> Option<String> {
if let Some(base) = base
&& let Some(local_name) = iri.strip_prefix(base)
{
return Some(format!(":{local_name}"));
}
pub fn abbreviate(&self, base: Option<&Url>, iri: &str) -> Option<String> {
let relative_iri = base.and_then(|base| {
let iri = Url::parse(iri).ok()?;
base.make_relative(&iri)
});
for (name, base) in &self.prefixes {
if let Some(local_name) = iri.strip_prefix(base) {
return Some(format!("{name}:{local_name}"));
if relative_iri.is_some() {
relative_iri
} else {
for (name, prefix) in &self.prefixes {
if let Some(local_name) = iri.strip_prefix(prefix) {
return Some(format!("{name}:{local_name}"));
}
}
None
}
None
}
pub fn expand(&self, base: Option<&str>, abbreviated_iri: &str) -> Option<String> {
let (prefix, name) = abbreviated_iri.split_once(':')?;
pub fn expand(&self, base: Option<&Url>, abbreviated_iri: &str) -> Option<String> {
let absolute_iri = base
.and_then(|base| {
base.join(abbreviated_iri)
.ok()
.map(|absolute| absolute.as_str().to_string())
})
.unwrap_or_else(|| abbreviated_iri.to_string());
if prefix == ""
&& let Some(base) = base
{
Some(format!("{base}{name}"))
} else {
self.prefixes
.get(prefix)
.map(|base| format!("{base}{name}"))
}
absolute_iri.split_once(':').and_then(|(prefix, name)| {
if prefix.is_empty()
&& let Some(base) = base
{
Some(format!("{base}{name}"))
} else {
self.prefixes
.get(prefix)
.map(|base| format!("{base}{name}"))
}
})
}
}
+15
View File
@@ -4,6 +4,9 @@ pub type Result<R> = std::result::Result<R, Error>;
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
IriParse(#[from] oxigraph::model::IriParseError),
@@ -16,6 +19,18 @@ pub enum Error {
#[error(transparent)]
QueryEvaluation(#[from] oxigraph::sparql::QueryEvaluationError),
#[error(transparent)]
QueryResultsSyntax(#[from] oxigraph::sparql::results::QueryResultsSyntaxError),
#[error(transparent)]
UpdateEvaluation(#[from] oxigraph::sparql::UpdateEvaluationError),
#[error(transparent)]
TonicTransport(#[from] gl_inference::tonic::transport::Error),
#[error(transparent)]
TonicStatus(#[from] gl_inference::tonic::Status),
#[error("Ontology client is not connected")]
NotConnected,
}
+3 -11
View File
@@ -1,6 +1,6 @@
use oxigraph::model::{NamedNode, NamedNodeRef, Term, TermRef};
use oxigraph::model::vocab::xsd;
use crate::vocab::rdf;
use oxigraph::model::vocab::xsd;
use oxigraph::model::{NamedNode, Term, TermRef};
pub(crate) fn term_to_named_node(term: &Term) -> Option<&NamedNode> {
if let Term::NamedNode(node) = term {
@@ -10,14 +10,6 @@ pub(crate) fn term_to_named_node(term: &Term) -> Option<&NamedNode> {
}
}
pub(crate) fn term_ref_as_named_node(term: TermRef<'_>) -> Option<NamedNodeRef<'_>> {
if let TermRef::NamedNode(node) = term {
Some(node)
} else {
None
}
}
pub(crate) fn term_as_str(term: &Term) -> Option<&str> {
if let Term::Literal(literal) = term {
match literal.datatype() {
@@ -42,4 +34,4 @@ pub(crate) fn term_ref_as_str(term: TermRef<'_>) -> Option<&str> {
} else {
None
}
}
}
-95
View File
@@ -1,95 +0,0 @@
use std::collections::HashMap;
use oxigraph::model::NamedNode;
use oxigraph::sparql::results::{QueryResultsFormat, QueryResultsParser, SliceQueryResultsParserOutput};
use gl_inference::tonic::transport::Channel;
use gl_inference::proto::ontology_client::OntologyClient;
use gl_inference::proto::OntologyQueryRequest;
use gl_search::{Field, OwnedValue, Schema};
use crate::class::Class;
use crate::{curie, CurieHelper};
use crate::helpers::{term_as_str, term_to_named_node};
use crate::language::LanguageCondition;
pub async fn index_ontology(
client: &mut OntologyClient<Channel>,
language: &LanguageCondition,
) -> crate::Result<Vec<HashMap<Field, OwnedValue>>> {
let curie_helper = CurieHelper::new((&*curie::PREFIXES).clone());
let label_filter = language.to_filter_expression("label");
let description_filter = language.to_filter_expression("description");
let mut request = OntologyQueryRequest::default();
request.sparql_query = Some(format!(r#"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
SELECT ?class ?subject ?label ?description WHERE {{
VALUES ?class {{ rdf:Property rdfs:Class skos:Concept }}
?subject a ?class ;
rdfs:label ?label .
{label_filter}
OPTIONAL {{ ?subject rdfs:comment ?comment }}
OPTIONAL {{ ?subject skos:definition ?definition }}
BIND(COALESCE(?definition, ?comment) AS ?description)
{description_filter}
}}"#));
let response = client.query(request).await.unwrap();
let parser_output = QueryResultsParser::from_format(QueryResultsFormat::Json)
.for_slice(&response.get_ref().results)
.unwrap();
let mut results = Vec::new();
if let SliceQueryResultsParserOutput::Solutions(solutions) = parser_output {
let primary_language = language.primary_language();
let label_field = Schema::field("label", primary_language);
let definition_field = Schema::field("definition", primary_language);
for solution in solutions.filter_map(Result::ok) {
let mut document = HashMap::with_capacity(4);
let discriminant = solution
.get("class")
.and_then(term_to_named_node)
.and_then(Class::try_from_named_node)
.map(|doc_type| doc_type as u64)
.map(OwnedValue::U64)
.unwrap_or(OwnedValue::Null);
document.insert(Schema::discriminant_field(), discriminant);
let subject_str = solution
.get("subject")
.and_then(term_to_named_node)
.map(NamedNode::as_str);
let curie = subject_str
.and_then(|subject| curie_helper.abbreviate(None, subject))
.map(OwnedValue::Str)
.unwrap_or(OwnedValue::Null);
document.insert(Schema::curie_field(), curie);
let subject = subject_str
.map(OwnedValue::from)
.unwrap_or(OwnedValue::Null);
document.insert(Schema::iri_field(), subject);
let label = solution
.get("label")
.and_then(term_as_str)
.map(OwnedValue::from)
.unwrap_or(OwnedValue::Null);
document.insert(label_field, label);
let definition = solution
.get("description")
.and_then(term_as_str)
.map(OwnedValue::from)
.unwrap_or(OwnedValue::Null);
document.insert(definition_field, definition);
results.push(document);
}
}
Ok(results)
}
+176
View File
@@ -0,0 +1,176 @@
use crate::class::Class;
use crate::language::LanguageCondition;
use crate::ontology::ResourceDescription;
use crate::{CurieHelper, helpers, vocab};
use gl_search::{Field, OwnedValue, Schema};
use oxigraph::model::{Graph, NamedNode, NamedNodeRef, NamedOrBlankNodeRef, TermRef};
use rayon::iter::IntoParallelRefIterator;
use rayon::iter::ParallelIterator;
use std::collections::{HashMap, HashSet};
pub struct Indexer<'a> {
language: LanguageCondition,
curie_helper: &'a CurieHelper,
}
impl<'a> Indexer<'a> {
pub fn new(language: LanguageCondition, curie_helper: &'a CurieHelper) -> Self {
Self {
language,
curie_helper,
}
}
fn extract_string(
&self,
graph: &Graph,
subject: NamedNodeRef,
predicate: NamedNodeRef,
) -> OwnedValue {
graph
.object_for_subject_predicate(subject, predicate)
.filter(|term| self.language.primary_matches_term(*term))
.and_then(helpers::term_ref_as_str)
.map(String::from)
.map(OwnedValue::Str)
.unwrap_or_else(|| OwnedValue::Null)
}
fn person(&self, graph: &Graph, subject: NamedNodeRef) -> HashMap<Field, OwnedValue> {
HashMap::from_iter([
(
Schema::field("given_name", self.language.primary_language()),
self.extract_string(graph, subject, vocab::rdaad::GIVEN_NAME),
),
(
Schema::field("surname", self.language.primary_language()),
self.extract_string(graph, subject, vocab::rdaad::SURNAME),
),
])
}
fn corporate_body(&self, graph: &Graph, subject: NamedNodeRef) -> HashMap<Field, OwnedValue> {
HashMap::from_iter([(
Schema::field("corporate_name", self.language.primary_language()),
self.extract_string(graph, subject, vocab::rdaad::NAME_OF_CORPORATE_BODY),
)])
}
fn work(&self, graph: &Graph, subject: NamedNodeRef) -> HashMap<Field, OwnedValue> {
HashMap::from_iter([(
Schema::field("title", self.language.primary_language()),
self.extract_string(graph, subject, vocab::rdawd::TITLE_OF_WORK),
)])
}
fn expression(&self, graph: &Graph, subject: NamedNodeRef) -> HashMap<Field, OwnedValue> {
HashMap::from_iter([(
Schema::field("title", self.language.primary_language()),
self.extract_string(graph, subject, vocab::rdaed::TITLE_OF_EXPRESSION),
)])
}
fn manifestation(&self, graph: &Graph, subject: NamedNodeRef) -> HashMap<Field, OwnedValue> {
HashMap::from_iter([(
Schema::field("title", self.language.primary_language()),
self.extract_string(graph, subject, vocab::rdamd::TITLE_OF_MANIFESTATION),
)])
}
pub fn graph(&self, graph: &Graph) -> Vec<HashMap<Field, OwnedValue>> {
let mut entities_and_types = HashSet::new();
let triples = graph.triples_for_predicate(vocab::rdf::TYPE);
for triple in triples {
if let NamedOrBlankNodeRef::NamedNode(subject) = triple.subject
&& let TermRef::NamedNode(class) = triple.object
{
entities_and_types.insert((subject, class));
}
}
entities_and_types
.par_iter()
.filter_map(|(subject, class)| {
Class::try_from_named_node(*class).and_then(|class| {
match class {
Class::RdfProperty => None,
Class::RdfsClass => None,
Class::SkosConcept => None,
Class::Work => Some(self.work(graph, *subject)),
Class::Person => Some(self.person(graph, *subject)),
Class::CorporateBody => Some(self.corporate_body(graph, *subject)),
Class::Expression => Some(self.expression(graph, *subject)),
Class::Manifestation => Some(self.manifestation(graph, *subject)),
}
.map(|mut document| {
document
.insert(Schema::discriminant_field(), OwnedValue::from(class as u64));
document.insert(
Schema::iri_field(),
OwnedValue::Str(subject.as_str().to_string()),
);
let curie = self
.curie_helper
.abbreviate(None, subject.as_str())
.map(OwnedValue::Str)
.unwrap_or(OwnedValue::Null);
document.insert(Schema::curie_field(), curie);
document
})
})
})
.collect()
}
pub fn ontology(
&self,
entities: HashMap<NamedNode, ResourceDescription>,
) -> Vec<HashMap<Field, OwnedValue>> {
let label_field = Schema::field("label", self.language.primary_language());
let definition_field = Schema::field("definition", self.language.primary_language());
entities
.iter()
.map(|(subject, description)| {
let mut document = HashMap::with_capacity(4);
let discriminant = description
.class
.as_ref()
.and_then(|node| Class::try_from_named_node(node.as_ref()))
.map(|doc_type| doc_type as u64)
.map(OwnedValue::U64)
.unwrap_or(OwnedValue::Null);
document.insert(Schema::discriminant_field(), discriminant);
let curie = self
.curie_helper
.abbreviate(None, subject.as_str())
.map(OwnedValue::Str)
.unwrap_or(OwnedValue::Null);
document.insert(Schema::curie_field(), curie);
let subject = OwnedValue::from(subject.as_str());
document.insert(Schema::iri_field(), subject);
let label = description
.label
.clone()
.map(OwnedValue::from)
.unwrap_or(OwnedValue::Null);
document.insert(label_field, label);
let definition = description
.description
.clone()
.map(OwnedValue::from)
.unwrap_or(OwnedValue::Null);
document.insert(definition_field, definition);
document
})
.collect()
}
}
+44 -16
View File
@@ -1,15 +1,16 @@
use oxigraph::model::TermRef;
use oxigraph::model::{Literal, TermRef};
use oxilangtag::LanguageTag;
use spargebra::algebra::{Expression, Function, GraphPattern};
use spargebra::term::Variable;
use std::sync::LazyLock;
pub const ENGLISH_PRIMARY: &str = "en";
pub static ENGLISH_TAG: LazyLock<LanguageTag<String>> =
LazyLock::new(|| LanguageTag::parse("en".to_string()).unwrap());
pub static ENGLISH_OR_UNTAGGED: LazyLock<LanguageCondition> = LazyLock::new(|| {
LanguageCondition::ExactMatchOrUntagged(
LanguageTag::parse(ENGLISH_PRIMARY.to_string()).unwrap(),
)
});
pub static ENGLISH_OR_UNTAGGED: LazyLock<LanguageCondition> =
LazyLock::new(|| LanguageCondition::ExactMatchOrUntagged(ENGLISH_TAG.clone()));
#[derive(Clone, Debug)]
pub enum LanguageCondition {
ExactMatchOnly(LanguageTag<String>),
ExactMatchOrUntagged(LanguageTag<String>),
@@ -48,16 +49,43 @@ impl LanguageCondition {
}
}
pub fn to_filter_expression(&self, var: &str) -> String {
pub fn filter(&self, variable: impl Into<String>) -> String {
let variable = variable.into();
let exact = |language: &LanguageTag<String>| {
Expression::FunctionCall(
Function::LangMatches,
vec![
Expression::FunctionCall(
Function::Lang,
vec![Expression::Variable(Variable::new_unchecked(&variable))],
),
Expression::Literal(Literal::new_simple_literal(language.to_string())),
],
)
};
let untagged = Expression::Not(Box::new(Expression::FunctionCall(
Function::HasLang,
vec![Expression::Variable(Variable::new_unchecked(&variable))],
)));
match self {
LanguageCondition::ExactMatchOnly(language) => {
format!(r#"FILTER(langMATCHES(LANG(?{var}), "{language}"))"#)
}
LanguageCondition::ExactMatchOrUntagged(language) => {
format!(r#"FILTER(langMATCHES(LANG(?{var}), "{language}") || !hasLANG(?{var}))"#)
}
LanguageCondition::UntaggedOnly => format!("FILTER(!hasLANG(?{var}))"),
LanguageCondition::AnyOrNone => "".to_string(),
Self::ExactMatchOnly(language) => Some(exact(language)),
Self::ExactMatchOrUntagged(language) => Some(Expression::Or(
Box::new(exact(language)),
Box::new(untagged),
)),
Self::UntaggedOnly => Some(untagged),
Self::AnyOrNone => None,
}
.map(|expr| {
GraphPattern::Filter {
expr,
inner: Box::new(GraphPattern::Bgp { patterns: vec![] }),
}
.to_string()
})
.unwrap_or_default()
}
}
+5 -5
View File
@@ -1,13 +1,13 @@
mod curie;
//mod materialize;
mod error;
pub mod vocab;
pub mod class;
pub mod category;
pub mod language;
pub mod class;
mod helpers;
pub mod index;
pub mod indexer;
pub mod language;
pub mod ontology;
pub mod vocab;
pub use curie::{CurieHelper, PREFIXES};
pub use error::{Error, Result};
+264
View File
@@ -0,0 +1,264 @@
use crate::helpers::{term_as_str, term_to_named_node};
use crate::language::LanguageCondition;
use crate::vocab;
use gl_inference::proto::OntologyQueryRequest;
use gl_inference::proto::ontology_client::OntologyClient;
use gl_inference::tonic::transport::{Channel, Endpoint};
use oxigraph::io::{RdfFormat, RdfParser, RdfSerializer};
use oxigraph::model::{Graph, NamedNode, Triple};
use oxigraph::sparql::results::{
QueryResultsFormat, QueryResultsParser, SliceQueryResultsParserOutput,
};
use spargebra::algebra::GraphPattern;
use spargebra::term::{GroundTerm, NamedNodePattern, TermPattern, TriplePattern, Variable};
use std::collections::{HashMap, HashSet};
use std::fmt::Debug;
use std::str::FromStr;
#[derive(Clone, Debug, Default)]
pub struct ResourceDescription {
pub label: Option<String>,
pub description: Option<String>,
pub class: Option<NamedNode>,
}
pub enum ResourceSelector {
Classes,
Subjects,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum ReadOnlyEntity {
Property(NamedNode),
Class(NamedNode),
}
pub struct OntologyBuilder {
endpoint: Endpoint,
language: LanguageCondition,
}
impl OntologyBuilder {
pub fn from_string(endpoint: &str, language: LanguageCondition) -> crate::Result<Self> {
let endpoint = Endpoint::from_str(endpoint)?
.connect_timeout(std::time::Duration::from_secs(10))
.tcp_keepalive(Some(std::time::Duration::from_secs(30)))
.http2_keep_alive_interval(std::time::Duration::from_secs(15))
.keep_alive_timeout(std::time::Duration::from_secs(20))
.keep_alive_while_idle(true);
Ok(Self { endpoint, language })
}
pub async fn connect(self) -> crate::Result<ConnectedOntology> {
let client = OntologyClient::connect(self.endpoint)
.await?
.max_decoding_message_size(1024 * 1024 * 1024);
Ok(ConnectedOntology {
client,
language: self.language,
})
}
}
#[derive(Clone)]
pub struct ConnectedOntology {
client: OntologyClient<Channel>,
language: LanguageCondition,
}
impl Debug for ConnectedOntology {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("ontology")
}
}
impl ConnectedOntology {
pub async fn run_inference(&mut self, graph: &Graph) -> crate::Result<Graph> {
let mut output_buffer = Vec::new();
let mut serializer =
RdfSerializer::from_format(RdfFormat::Turtle).for_writer(output_buffer);
for triple in graph {
serializer.serialize_triple(triple)?;
}
output_buffer = serializer.finish()?;
let turtle = String::from_utf8_lossy(&output_buffer).to_string();
let request = OntologyQueryRequest {
turtle: Some(turtle),
inferences_only: true,
..Default::default()
};
let response = self.client.query(request).await?;
Ok(RdfParser::from_format(RdfFormat::Turtle)
.for_slice(&response.get_ref().results)
.filter_map(Result::ok)
.map(Triple::from)
.collect::<Graph>())
}
pub async fn list_read_only(&mut self) -> crate::Result<HashSet<ReadOnlyEntity>> {
let sparql_query = r#"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX gl: <https://graphofliberty.org/2026/04/ont/>
SELECT ?subject ?class WHERE {
VALUES ?class { rdf:Property rdfs:Class }
?subject a ?class ;
gl:readOnly "true"^^xsd:boolean .
}"#
.to_owned();
let request = OntologyQueryRequest {
sparql_query: Some(sparql_query),
..Default::default()
};
let response = self.client.query(request).await?;
let parser_output = QueryResultsParser::from_format(QueryResultsFormat::Json)
.for_slice(&response.get_ref().results)?;
let mut results = HashSet::new();
if let SliceQueryResultsParserOutput::Solutions(solutions) = parser_output {
for solution in solutions.filter_map(Result::ok) {
let subject = solution
.get("subject")
.and_then(term_to_named_node)
.cloned();
let class = solution.get("class").and_then(term_to_named_node).cloned();
if let Some(subject) = subject
&& let Some(class) = class
{
let entry = match class.as_ref() {
vocab::rdf::PROPERTY => ReadOnlyEntity::Property(subject),
vocab::rdfs::CLASS => ReadOnlyEntity::Class(subject),
_ => continue,
};
results.insert(entry);
}
}
}
Ok(results)
}
fn selector_to_sparql_pattern(
selector: ResourceSelector,
nodes: impl IntoIterator<Item = NamedNode>,
) -> String {
let bindings = nodes
.into_iter()
.map(GroundTerm::NamedNode)
.map(Some)
.map(|item| vec![item])
.collect();
let variable = match selector {
ResourceSelector::Classes => Variable::new_unchecked("class"),
ResourceSelector::Subjects => Variable::new_unchecked("subject"),
};
let values_pattern = GraphPattern::Values {
variables: vec![variable.clone()],
bindings,
};
let triple_pattern = match selector {
ResourceSelector::Classes => vec![TriplePattern {
subject: TermPattern::Variable(Variable::new_unchecked("subject")),
predicate: NamedNodePattern::NamedNode(vocab::rdf::TYPE.into_owned()),
object: TermPattern::Variable(variable),
}],
ResourceSelector::Subjects => vec![],
};
let basic_pattern = GraphPattern::Bgp {
patterns: triple_pattern,
};
GraphPattern::Join {
left: Box::new(basic_pattern),
right: Box::new(values_pattern),
}
.to_string()
}
pub async fn resource_descriptions(
&mut self,
selector: ResourceSelector,
nodes: impl IntoIterator<Item = NamedNode>,
) -> crate::Result<HashMap<NamedNode, ResourceDescription>> {
let pattern = Self::selector_to_sparql_pattern(selector, nodes);
let label_filter = self.language.filter("label");
let comment_filter = self.language.filter("comment");
let definition_filter = self.language.filter("definition");
let sparql_query = format!(
r#"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
SELECT ?subject ?label ?description ?class WHERE {{
{pattern}
OPTIONAL {{
?subject rdfs:label ?label .
{label_filter}
}} OPTIONAL {{
?subject rdfs:comment ?comment .
{comment_filter}
}} OPTIONAL {{
?subject skos:definition ?definition .
{definition_filter}
}}
BIND(COALESCE(?definition, ?comment) AS ?description)
}}"#
);
let request = OntologyQueryRequest {
sparql_query: Some(sparql_query),
..Default::default()
};
let response = self.client.query(request).await?;
let parser_output = QueryResultsParser::from_format(QueryResultsFormat::Json)
.for_slice(&response.get_ref().results)?;
let mut results = HashMap::new();
if let SliceQueryResultsParserOutput::Solutions(solutions) = parser_output {
for solution in solutions.filter_map(Result::ok) {
let class = solution.get("class").and_then(term_to_named_node).cloned();
let subject = solution
.get("subject")
.and_then(term_to_named_node)
.cloned();
if let Some(subject) = subject {
let label = solution
.get("label")
.and_then(term_as_str)
.map(String::from);
let description = solution
.get("description")
.and_then(term_as_str)
.map(String::from);
results.insert(
subject,
ResourceDescription {
label,
description,
class,
},
);
}
}
}
Ok(results)
}
}
+120 -4
View File
@@ -1,7 +1,7 @@
pub use oxigraph::model::vocab::rdf;
pub use oxigraph::model::vocab::rdfs;
pub mod gl {
pub mod glo {
use oxigraph::model::NamedNodeRef;
pub const AUDIO_BOOK: NamedNodeRef =
@@ -9,13 +9,16 @@ pub mod gl {
pub const READ_ONLY: NamedNodeRef =
NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/readOnly");
pub const NEXT_URL: NamedNodeRef =
NamedNodeRef::new_unchecked("https://graphofliberty.org/2026/04/ont/nextUrl");
}
pub mod skos {
pub mod lclang {
use oxigraph::model::NamedNodeRef;
pub const CONCEPT: NamedNodeRef =
NamedNodeRef::new_unchecked("http://www.w3.org/2004/02/skos/core#Concept");
pub const ENGLISH: NamedNodeRef =
NamedNodeRef::new_unchecked("http://id.loc.gov/vocabulary/languages/eng");
}
pub mod owl {
@@ -44,6 +47,13 @@ pub mod rdac {
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 {
use oxigraph::model::NamedNodeRef;
@@ -56,3 +66,109 @@ pub mod rdaad {
pub const NAME_OF_CORPORATE_BODY: NamedNodeRef =
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/a/datatype/P50032");
}
pub mod rdaao {
use oxigraph::model::NamedNodeRef;
pub const IDENTIFIER_FOR_PERSON: NamedNodeRef =
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 {
use oxigraph::model::NamedNodeRef;
pub const TITLE_OF_WORK: NamedNodeRef =
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 {
use oxigraph::model::NamedNodeRef;
pub const TITLE_OF_EXPRESSION: NamedNodeRef =
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 {
use oxigraph::model::NamedNodeRef;
pub const TITLE_OF_MANIFESTATION: NamedNodeRef =
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 {
use oxigraph::model::NamedNodeRef;
pub const NOMEN_STRING: NamedNodeRef =
NamedNodeRef::new_unchecked("http://rdaregistry.info/Elements/n/datatype/P80068");
}
pub mod rdano {
use oxigraph::model::NamedNodeRef;
pub const SCHEME_OF_NOMEN: NamedNodeRef =
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 -1
View File
@@ -4,4 +4,4 @@ fn main() {
println!("cargo::rerun-if-changed=build.rs");
println!("cargo::rerun-if-changed=proto/inference.proto");
}
}
+3 -3
View File
@@ -9,13 +9,13 @@ pub enum Error {
#[error(transparent)]
SparqlSyntax(#[from] oxigraph::sparql::SparqlSyntaxError),
#[error(transparent)]
QueryEvaluation(#[from] oxigraph::sparql::QueryEvaluationError),
#[error(transparent)]
UpdateEvaluation(#[from] oxigraph::sparql::UpdateEvaluationError),
#[error(transparent)]
Storage(#[from] oxigraph::store::StorageError),
}
}
+1 -1
View File
@@ -2,4 +2,4 @@ pub mod proto {
tonic::include_proto!("org.graphofliberty.inference");
}
pub use tonic;
pub use tonic;
+29 -10
View File
@@ -1,5 +1,5 @@
use oxigraph::model::GraphNameRef;
use oxigraph::sparql::{SparqlEvaluator};
use oxigraph::sparql::SparqlEvaluator;
use oxigraph::store::Store;
use tracing::debug_span;
@@ -30,24 +30,43 @@ const DOMAIN_UPDATE: &str = r#"INSERT {
?x ?p ?y .
}"#;
fn run_update(query_name: &str, query: &str, store: &Store, graph_name: GraphNameRef<'_>) -> crate::error::Result<()> {
fn run_update(
query_name: &str,
query: &str,
store: &Store,
graph_name: GraphNameRef<'_>,
) -> crate::error::Result<()> {
let _span = debug_span!("Update", name = query_name).entered();
SparqlEvaluator::new()
.with_prefix("rdfs", RDF_SCHEMA_PREFIX)?
.with_prefix("gl", GL_PREFIX)?
.parse_update(&format!("WITH {graph_name} {query}"))?
.on_store(store).execute()?;
.on_store(store)
.execute()?;
Ok(())
}
pub(crate) fn infer(inferences: usize, store: &Store, graph_name: GraphNameRef<'_>) -> crate::error::Result<usize> {
let old_count = store.quads_for_pattern(None, None, None, Some(graph_name)).count();
run_update("rdfs:subPropertyOf", SUB_PROPERTY_OF_UPDATE, &store, graph_name)?;
run_update("rdfs:subClassOf", SUB_CLASS_OF_UPDATE, &store, graph_name)?;
run_update("rdfs:domain", DOMAIN_UPDATE, &store, graph_name)?;
let new_count = store.quads_for_pattern(None, None, None, Some(graph_name)).count();
pub(crate) fn infer(
inferences: usize,
store: &Store,
graph_name: GraphNameRef<'_>,
) -> crate::error::Result<usize> {
let old_count = store
.quads_for_pattern(None, None, None, Some(graph_name))
.count();
run_update(
"rdfs:subPropertyOf",
SUB_PROPERTY_OF_UPDATE,
store,
graph_name,
)?;
run_update("rdfs:subClassOf", SUB_CLASS_OF_UPDATE, store, graph_name)?;
run_update("rdfs:domain", DOMAIN_UPDATE, store, graph_name)?;
let new_count = store
.quads_for_pattern(None, None, None, Some(graph_name))
.count();
let new_inferences_total = inferences + (new_count - old_count);
if new_count > old_count {
@@ -55,4 +74,4 @@ pub(crate) fn infer(inferences: usize, store: &Store, graph_name: GraphNameRef<'
} else {
Ok(new_inferences_total)
}
}
}
+6 -6
View File
@@ -1,14 +1,14 @@
use crate::service::OntologyService;
use gl_inference::proto::ontology_server::OntologyServer;
use tonic::transport::Server;
use tracing_subscriber::{fmt, EnvFilter};
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use gl_inference::proto::ontology_server::OntologyServer;
use crate::service::OntologyService;
use tracing_subscriber::{EnvFilter, fmt};
mod service;
mod logic;
mod error;
mod logic;
mod service;
#[tokio::main]
async fn main() -> color_eyre::Result<()> {
@@ -35,4 +35,4 @@ async fn main() -> color_eyre::Result<()> {
.await?;
Ok(())
}
}
+109 -42
View File
@@ -1,13 +1,16 @@
use gl_inference::proto::ontology_server::Ontology;
use gl_inference::proto::{
OntologyClearResponse, OntologyLoadRequest, OntologyLoadResponse, OntologyQueryRequest,
OntologyQueryResponse,
};
use num_traits::ToPrimitive;
use oxigraph::io::{RdfFormat, RdfParser, RdfSerializer};
use oxigraph::model::{BlankNode, Dataset, GraphName, GraphNameRef, NamedNode, NamedNodeRef};
use oxigraph::sparql::{QueryResults, SparqlEvaluator};
use oxigraph::sparql::results::{QueryResultsFormat, QueryResultsSerializer};
use oxigraph::sparql::{QueryResults, SparqlEvaluator};
use oxigraph::store::Store;
use tonic::{Request, Response, Status};
use tracing::{debug_span, field};
use gl_inference::proto::ontology_server::Ontology;
use gl_inference::proto::{OntologyClearResponse, OntologyLoadRequest, OntologyLoadResponse, OntologyQueryRequest, OntologyQueryResponse};
use tracing::{debug, debug_span, field};
const ONTOLOGY_GRAPH: GraphNameRef = GraphNameRef::NamedNode(NamedNodeRef::new_unchecked(
"https://graphofliberty.org/ontology",
@@ -27,79 +30,124 @@ impl OntologyService {
#[tonic::async_trait]
impl Ontology for OntologyService {
async fn load(&self, request: Request<OntologyLoadRequest>) -> Result<Response<OntologyLoadResponse>, Status> {
let span = debug_span!("Load Ontology", input_size = field::Empty, old_size = field::Empty, new_size = field::Empty).entered();
async fn load(
&self,
request: Request<OntologyLoadRequest>,
) -> Result<Response<OntologyLoadResponse>, Status> {
let span = debug_span!(
"Load Ontology",
old_size = field::Empty,
input_size = field::Empty,
inferences = field::Empty,
new_size = field::Empty
)
.entered();
let request = request.get_ref();
let mut response = OntologyLoadResponse::default();
response.old_size = self.ontology.len()
let old_size = self
.ontology
.len()
.map_err(|err| Status::internal(err.to_string()))?
.to_u64()
.unwrap_or(u64::MAX);
let path = std::path::Path::new(&request.path);
let store = Store::open_read_only(path).unwrap();
response.input_size = store.len()
let input_size = store
.len()
.map_err(|err| Status::internal(err.to_string()))?
.to_u64()
.unwrap_or(u64::MAX);
let quads = store.iter()
.filter_map(Result::ok)
.map(|mut quad| {
quad.graph_name = ONTOLOGY_GRAPH.into_owned();
quad
});
let quads = store.iter().filter_map(Result::ok).map(|mut quad| {
quad.graph_name = ONTOLOGY_GRAPH.into_owned();
quad
});
self.ontology.extend(quads).unwrap();
response.new_size = self.ontology.len()
let inferences = if request.infer {
crate::logic::infer(0, &self.ontology, ONTOLOGY_GRAPH)
.map_err(|err| Status::internal(err.to_string()))?
.to_u64()
.unwrap_or(u64::MAX)
} else {
0
};
let new_size = self
.ontology
.len()
.map_err(|err| Status::internal(err.to_string()))?
.to_u64()
.unwrap_or(u64::MAX);
span.record("input_size", response.input_size);
let response = OntologyLoadResponse {
old_size,
input_size,
inferences,
new_size,
};
span.record("old_size", response.old_size);
span.record("input_size", response.input_size);
span.record("inferences", response.inferences);
span.record("new_size", response.new_size);
Ok(Response::new(response))
}
async fn clear(&self, _request: Request<()>) -> Result<Response<OntologyClearResponse>, Status> {
async fn clear(
&self,
_request: Request<()>,
) -> Result<Response<OntologyClearResponse>, Status> {
let span = debug_span!("Clear Ontology", size = field::Empty).entered();
let mut response = OntologyClearResponse::default();
response.size = self.ontology.len()
let size = self
.ontology
.len()
.map_err(|err| Status::internal(err.to_string()))?
.to_u64()
.unwrap_or(u64::MAX);
self.ontology.clear()
self.ontology
.clear()
.map_err(|err| Status::internal(err.to_string()))?;
let response = OntologyClearResponse { size };
span.record("size", response.size);
Ok(Response::new(response))
}
async fn query(&self, request: Request<OntologyQueryRequest>) -> Result<Response<OntologyQueryResponse>, Status> {
async fn query(
&self,
request: Request<OntologyQueryRequest>,
) -> Result<Response<OntologyQueryResponse>, Status> {
let span = debug_span!("Query", inferences = field::Empty).entered();
let request = request.get_ref();
let mut response = OntologyQueryResponse::default();
let provided_dataset;
let graph_name = if let Some(turtle) = &request.turtle {
let random_graph_identifier = BlankNode::default();
let graph_name = GraphName::NamedNode(NamedNode::new_unchecked(format!("https://graphofliberty.org/inference/{}", random_graph_identifier.as_str())));
let dataset = RdfParser::from_format(RdfFormat::Turtle)
let graph_name = GraphName::NamedNode(NamedNode::new_unchecked(format!(
"https://graphofliberty.org/inference/{}",
random_graph_identifier.as_str()
)));
provided_dataset = RdfParser::from_format(RdfFormat::Turtle)
.for_slice(&turtle)
.filter_map(Result::ok)
.map(|mut quad| {
quad.graph_name = graph_name.clone();
quad
}).collect::<Dataset>();
})
.collect::<Dataset>();
self.ontology.extend(&dataset)
self.ontology
.extend(&provided_dataset)
.map_err(|err| Status::internal(err.to_string()))?;
let inferences = crate::logic::infer(0, &self.ontology, graph_name.as_ref())
@@ -107,41 +155,52 @@ impl Ontology for OntologyService {
span.record("inferences", inferences);
graph_name
} else {
provided_dataset = Dataset::new();
ONTOLOGY_GRAPH.into_owned()
};
let mut output_buffer = Vec::new();
if let Some(sparql_query) = &request.sparql_query {
debug!("SPARQL Query: {sparql_query}");
let mut evaluator = SparqlEvaluator::new();
for (name, iri) in &request.prefixes {
evaluator = evaluator.with_prefix(name, iri)
evaluator = evaluator
.with_prefix(name, iri)
.map_err(|err| Status::internal(err.to_string()))?;
}
if let Some(base) = &request.base {
evaluator = evaluator.with_base_iri(base)
evaluator = evaluator
.with_base_iri(base)
.map_err(|err| Status::internal(err.to_string()))?;
}
let mut query = evaluator.parse_query(sparql_query)
let mut query = evaluator
.parse_query(sparql_query)
.map_err(|err| Status::internal(err.to_string()))?;
query.dataset_mut().set_default_graph(vec![graph_name.clone()]);
query
.dataset_mut()
.set_default_graph(vec![graph_name.clone()]);
let results = query.on_store(&self.ontology)
let results = query
.on_store(&self.ontology)
.execute()
.map_err(|err| Status::internal(err.to_string()))?;
match results {
QueryResults::Boolean(result) => {
let serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Json);
output_buffer = serializer.serialize_boolean_to_writer(output_buffer, result)
output_buffer = serializer
.serialize_boolean_to_writer(output_buffer, result)
.map_err(|err| Status::internal(err.to_string()))?;
}
QueryResults::Solutions(solutions) => {
let serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Json);
let variables = Vec::from_iter(solutions.variables().iter().cloned());
let mut writer = serializer.serialize_solutions_to_writer(output_buffer, variables)?;
let mut writer =
serializer.serialize_solutions_to_writer(output_buffer, variables)?;
for solution in solutions.filter_map(Result::ok) {
writer.serialize(&solution)?;
}
@@ -150,11 +209,13 @@ impl Ontology for OntologyService {
QueryResults::Graph(graph) => {
let mut serializer = RdfSerializer::from_format(RdfFormat::Turtle);
for (name, iri) in &request.prefixes {
serializer = serializer.with_prefix(name, iri)
serializer = serializer
.with_prefix(name, iri)
.map_err(|err| Status::internal(err.to_string()))?;
}
if let Some(base) = &request.base {
serializer = serializer.with_base_iri(base)
serializer = serializer
.with_base_iri(base)
.map_err(|err| Status::internal(err.to_string()))?;
}
@@ -168,20 +229,25 @@ impl Ontology for OntologyService {
} else {
let mut serializer = RdfSerializer::from_format(RdfFormat::Turtle);
for (name, iri) in &request.prefixes {
serializer = serializer.with_prefix(name, iri)
serializer = serializer
.with_prefix(name, iri)
.map_err(|err| Status::internal(err.to_string()))?;
}
if let Some(base) = &request.base {
serializer = serializer.with_base_iri(base)
serializer = serializer
.with_base_iri(base)
.map_err(|err| Status::internal(err.to_string()))?;
}
let mut serializer = serializer.for_writer(output_buffer);
let graph = self.ontology
let resulting_dataset = self
.ontology
.quads_for_pattern(None, None, None, Some(graph_name.as_ref()))
.filter_map(Result::ok);
for triple in graph {
serializer.serialize_triple(triple.as_ref())?;
for quad in resulting_dataset {
if !(request.inferences_only && provided_dataset.contains(quad.as_ref())) {
serializer.serialize_triple(quad.as_ref())?;
}
}
output_buffer = serializer.finish()?;
}
@@ -189,10 +255,11 @@ impl Ontology for OntologyService {
response.results = String::from_utf8_lossy(&output_buffer).to_string();
if request.turtle.is_some() {
self.ontology.clear_graph(graph_name.as_ref())
self.ontology
.clear_graph(graph_name.as_ref())
.map_err(|err| Status::internal(err.to_string()))?;
}
Ok(Response::new(response))
}
}
}
+2
View File
@@ -6,6 +6,8 @@ oxigraph load -l ~/.local/share/org.graphofliberty.desktop/ontology -f ldp.ttl -
oxigraph load -l ~/.local/share/org.graphofliberty.desktop/ontology -f owl.ttl --graph http://www.w3.org/2002/07/owl
oxigraph load -l ~/.local/share/org.graphofliberty.desktop/ontology -f skos.rdf --graph https://www.w3.org/TR/skos-reference/skos.rdf
oxigraph load -l ~/.local/share/org.graphofliberty.desktop/ontology -f prov.ttl --graph http://www.w3.org/ns/prov
oxigraph load -l ~/.local/share/org.graphofliberty.desktop/ontology -f premis-1-0-0.rdf --graph https://id.loc.gov/ontologies/premis-1-0-0.rdf
oxigraph load -l ~/.local/share/org.graphofliberty.desktop/ontology -f premis-3-0-0.rdf --graph https://id.loc.gov/ontologies/premis-3-0-0.rdf
# https://github.com/RDARegistry/RDA-Vocabularies/archive/v5.4.13.tar.gz
find Elements -type f -name '*.xml' -exec oxigraph load -l ~/.local/share/org.graphofliberty.desktop/ontology --graph http://rdaregistry.info/Elements -f {} \;
+20 -2
View File
@@ -22,7 +22,8 @@
@prefix premis3: <http://www.loc.gov/premis/rdf/v3/> .
@base <https://graphofliberty.org/2026/04/ont/> .
<https://graphofliberty.org/2026/04/ont> rdf:type owl:Ontology .
<https://graphofliberty.org/2026/04/ont> rdf:type owl:Ontology ;
owl:imports <http://www.w3.org/2004/02/skos/core> .
#################################################################
# Annotation properties
@@ -94,6 +95,12 @@ xsd:unsignedShort rdf:type rdfs:Datatype ;
rdfs:label "derived with"@en .
### https://graphofliberty.org/2026/04/ont/nextUrl
:nextUrl rdf:type owl:ObjectProperty ;
rdfs:comment "The next URL to use when creating a new document."@en ;
rdfs:label "Next URL"@en .
#################################################################
# Data properties
#################################################################
@@ -110,6 +117,7 @@ xsd:unsignedShort rdf:type rdfs:Datatype ;
### https://graphofliberty.org/2026/04/ont/Category
:Category rdf:type owl:Class ;
rdfs:subClassOf skos:Concept ;
rdfs:label "Category"@en .
@@ -133,6 +141,11 @@ fedora:Container rdf:type owl:NamedIndividual ;
:readOnly "true"^^xsd:boolean .
### http://fedora.info/definitions/v4/repository#RepositoryRoot
fedora:RepositoryRoot 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 .
@@ -158,6 +171,11 @@ fedora:hasParent rdf:type owl:NamedIndividual ;
:readOnly "true"^^xsd:boolean .
### http://fedora.info/definitions/v4/repository#hasTransactionProvider
fedora:hasTransactionProvider 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 .
@@ -221,7 +239,7 @@ ldp:contains rdf:type owl:NamedIndividual ;
### https://graphofliberty.org/2026/04/ont/AudioBook
:AudioBook rdf:type owl:NamedIndividual ,
:Category ;
rdfs:label "Audio Book"@en .
rdfs:label "audio book"@en .
### Generated by the OWL API (version 4.5.29.2024-05-13T12:11:03Z) https://github.com/owlcs/owlapi
File diff suppressed because it is too large Load Diff
+578
View File
@@ -0,0 +1,578 @@
<rdf:RDF xml:base="http://www.loc.gov/premis/rdf/v3/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:bf="http://id.loc.gov/ontologies/bibframe/" xmlns:bflc="http://id.loc.gov/ontologies/bflc/" xmlns:owl="http://www.w3.org/2002/07/owl#" xmlns:skos="http://www.w3.org/2004/02/skos/core#" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:cc="http://creativecommons.org/ns#" xmlns:foaf="http://xmlns.com/foaf/0.1/">
<owl:Ontology rdf:about="">
<rdfs:label xml:lang="en">PREMIS 3 Ontology</rdfs:label>
<rdfs:comment xml:lang="en">Ontology for PREMIS 3, the international standard
for metadata to support the preservation of digital objects and ensure their
long-term usability.</rdfs:comment>
<dcterms:modified rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2018-10-12</dcterms:modified>
<!-- Replaced long note and version variation mechanism with actual bonafide conclusion to version debate. -->
<owl:versionInfo rdf:datatype="http://www.w3.org/2001/XMLSchema#string">3.0.0</owl:versionInfo>
<owl:versionIRI rdf:resource="https://id.loc.gov/ontologies/premis-3-0-0"/>
<owl:priorVersion rdf:resource="https://id.loc.gov/ontologies/premis-1-0-0"/>
<rdfs:seeAlso rdf:resource="http://www.loc.gov/standards/premis/"/>
<rdfs:seeAlso rdf:resource="https://github.com/PREMIS-OWL-Revision-Team/revise-premis-owl/"/>
<owl:imports rdf:resource="http://www.w3.org/ns/prov-o-20130430"/>
</owl:Ontology>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Action" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Action</rdfs:label>
<rdfs:comment xml:lang="en">Operation type to perform on an Object. Effectively performing this action may produce an Event.</rdfs:comment>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<rdfs:seeAlso rdf:resource="http://id.loc.gov/vocabulary/preservation/actionsGranted"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Agent" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Agent</rdfs:label>
<rdfs:comment xml:lang="en">Actor (human, machine, or software) associated with one or more Event and/or Rights
statement associated with a digital object.</rdfs:comment>
<rdfs:subClassOf rdf:resource="http://www.w3.org/ns/prov#Agent"/>
<rdfs:subClassOf rdf:resource="http://xmlns.com/foaf/0.1/Agent"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Bitstream" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Bitstream</rdfs:label>
<rdfs:comment xml:lang="en">Contiguous or non-contiguous data within a file that has meaningful properties for
preservation purposes.</rdfs:comment>
<rdfs:subClassOf rdf:resource="http://www.loc.gov/premis/rdf/v3/Object"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/File"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/IntellectualEntity"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Representation"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Copyright" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Copyright</rdfs:label>
<rdfs:comment xml:lang="en">Copyright law.</rdfs:comment>
<rdfs:subClassOf rdf:resource="http://www.loc.gov/premis/rdf/v3/RightsBasis"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/InstitutionalPolicy"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/License"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Statute"/>
<rdfs:seeAlso rdf:resource="http://id.loc.gov/vocabulary/preservation/copyrightStatus"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Dependency" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Dependency</rdfs:label>
<rdfs:comment xml:lang="en">Relationship where one Object requires another Object to support its function, delivery, or
the coherence of its content.</rdfs:comment>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/EnvironmentCharacteristic" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Environment characteristic</rdfs:label>
<rdfs:comment xml:lang="en">An assessment of the extent to which the described environment supports its
purpose.</rdfs:comment>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<rdfs:seeAlso rdf:resource="http://id.loc.gov/vocabulary/preservation/environmentCharacteristic"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Event" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Event</rdfs:label>
<rdfs:comment xml:lang="en">Action performed within or outside the repository that affects its capability to preserve Objects over the long term.</rdfs:comment>
<rdfs:subClassOf rdf:resource="http://www.w3.org/ns/prov#Activity"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<rdfs:seeAlso rdf:resource="http://id.loc.gov/vocabulary/preservation/eventType"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/File" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">File</rdfs:label>
<rdfs:comment xml:lang="en">Named and ordered sequence of bytes that is known to an operating system.</rdfs:comment>
<rdfs:subClassOf rdf:resource="http://www.loc.gov/premis/rdf/v3/Object"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Bitstream"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/IntellectualEntity"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Representation"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Fixity" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Fixity</rdfs:label>
<rdfs:comment xml:lang="en">Information used to verify whether an object has been altered in an undocumented or
unauthorized way.</rdfs:comment>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<rdfs:seeAlso rdf:resource="http://id.loc.gov/vocabulary/preservation/cryptographicHashFunctions"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/HardwareAgent" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Hardware agent</rdfs:label>
<rdfs:subClassOf rdf:resource="http://www.loc.gov/premis/rdf/v3/Agent"/>
<owl:sameAs rdf:resource="http://id.loc.gov/vocabulary/preservation/agentType/har"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Organization"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Person"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/SoftwareAgent"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Identifier" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Identifier</rdfs:label>
<rdfs:comment xml:lang="en">An unambiguous reference to the PREMIS entity within the preservation
repository.</rdfs:comment>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Inhibitor" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Inhibitor</rdfs:label>
<rdfs:comment xml:lang="en">Feature of a Digital Object intended to inhibit access, copying, dissemination, or
migration. Common Inhibitors are encryption and password protection.</rdfs:comment>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<rdfs:seeAlso rdf:resource="http://id.loc.gov/vocabulary/preservation/inhibitorType"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/InstitutionalPolicy" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Institutional policy</rdfs:label>
<rdfs:comment xml:lang="en">A policy decision made by an organization.</rdfs:comment>
<rdfs:subClassOf rdf:resource="http://www.loc.gov/premis/rdf/v3/RightsBasis"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Copyright"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/License"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Statute"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/IntellectualEntity" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Intellectual entity</rdfs:label>
<rdfs:comment xml:lang="en">A set of content that is considered a single intellectual unit for purposes of management and
description: for example, a particular book, map, photograph, database, or piece of hardware or
software. An Intellectual Entity can include other Intellectual Entities; for example, a web site can
include a web page; a web page can include an image. An Intellectual Entity may have one or more digital
representations. An Intellectual Entity may also describe an environment, defined as technology
supporting a digital object in some way (e.g. by rendering or executing it). Environments can consist of
software, hardware, or a combination of both.</rdfs:comment>
<rdfs:subClassOf rdf:resource="http://www.loc.gov/premis/rdf/v3/Object"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Bitstream"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/File"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Representation"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<rdfs:seeAlso rdf:resource="http://id.loc.gov/vocabulary/preservation/environmentFunctionType"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/License" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">License</rdfs:label>
<rdfs:comment xml:lang="en">A license agreement or other legal document that grants rights.</rdfs:comment>
<rdfs:subClassOf rdf:resource="http://www.loc.gov/premis/rdf/v3/RightsBasis"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Copyright"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/InstitutionalPolicy"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Statute"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Object" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Object</rdfs:label>
<rdfs:comment xml:lang="en">Discrete unit of information subject to digital preservation. Subclasses of Object are
Intellectual Entity, Representation, File and Bitstream.</rdfs:comment>
<rdfs:subClassOf rdf:resource="http://www.w3.org/ns/prov#Entity"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Organization" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Organization</rdfs:label>
<rdfs:subClassOf rdf:resource="http://www.loc.gov/premis/rdf/v3/Agent"/>
<rdfs:subClassOf rdf:resource="http://xmlns.com/foaf/0.1/Organization"/>
<rdfs:subClassOf rdf:resource="http://www.w3.org/ns/prov#Organization"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/HardwareAgent"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Person"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/SoftwareAgent"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/OutcomeStatus" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Outcome status</rdfs:label>
<rdfs:comment xml:lang="en">Overall result of the Event in terms of success, partial success, or failure.</rdfs:comment>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<rdfs:seeAlso rdf:resource="http://id.loc.gov/vocabulary/preservation/eventOutcome"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Person" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Person</rdfs:label>
<rdfs:subClassOf rdf:resource="http://www.loc.gov/premis/rdf/v3/Agent"/>
<rdfs:subClassOf rdf:resource="http://www.w3.org/ns/prov#Person"/>
<rdfs:subClassOf rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/HardwareAgent"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Organization"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/SoftwareAgent"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/PreservationPolicy" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Preservation policy</rdfs:label>
<rdfs:comment xml:lang="en">Information indicating the decision or policy on the set of preservation functions to be
applied to an object and the context in which the decision or policy was made. Note that in addition to
subclasses declared at presLevType, SignificantProperties is also a subclass of PreservationPolicy.
Implementers may also wish to create locally-defined subclasses.</rdfs:comment>
<rdfs:subClassOf rdf:resource="http://purl.org/dc/terms/Policy"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<rdfs:seeAlso rdf:resource="http://id.loc.gov/vocabulary/preservation/preservationLevelRole"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Representation" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Representation</rdfs:label>
<rdfs:comment xml:lang="en">Digital or physical Object instantiating or embodying an Intellectual Entity. A digital
representation is the set of stored digital files and structural metadata needed to provide a complete
and reasonable rendition of the Intellectual Entity. A physical representation is an item such as a
manuscript, video cassette, or printed document.</rdfs:comment>
<rdfs:subClassOf rdf:resource="http://www.loc.gov/premis/rdf/v3/Object"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Bitstream"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/File"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/IntellectualEntity"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/RightsBasis" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Rights basis</rdfs:label>
<rdfs:comment xml:lang="en">Designation of the basis for the right or permission governing the Object.</rdfs:comment>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<rdfs:seeAlso rdf:resource="http://id.loc.gov/vocabulary/preservation/rightsBasis"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/RightsStatus" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Rights status</rdfs:label>
<rdfs:comment xml:lang="en">Information about how a RightsBasis applies to a particular object.</rdfs:comment>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Rule" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Rule</rdfs:label>
<rdfs:comment xml:lang="en">Statement about the Actions an Agent is permitted to undertake or prohibited from
undertaking with respect to an Object.</rdfs:comment>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Signature" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Signature</rdfs:label>
<rdfs:comment xml:lang="en">Mathematical technique used to validate the authenticity and integrity of a message,
software or digital document.</rdfs:comment>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<rdfs:seeAlso rdf:resource="http://id.loc.gov/vocabulary/preservation/signatureMethod"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/SignatureEncoding" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Signature encoding</rdfs:label>
<rdfs:comment xml:lang="en">Encoding used for the signature value and key information.</rdfs:comment>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<rdfs:seeAlso rdf:resource="http://id.loc.gov/vocabulary/preservation/signatureEncoding"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/SignificantProperties" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Significant properties</rdfs:label>
<rdfs:comment xml:lang="en">Characteristics of a particular object subjectively determined to be important to maintain
through preservation actions.</rdfs:comment>
<rdfs:subClassOf rdf:resource="http://www.loc.gov/premis/rdf/v3/PreservationPolicy"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/SoftwareAgent" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Software agent</rdfs:label>
<rdfs:subClassOf rdf:resource="http://www.loc.gov/premis/rdf/v3/Agent"/>
<rdfs:subClassOf rdf:resource="http://www.w3.org/ns/prov#SoftwareAgent"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/HardwareAgent"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Organization"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Person"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Statute" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Statute</rdfs:label>
<rdfs:comment xml:lang="en">A law that grants or revokes rights, such as laws governing privacy or orphan
works.</rdfs:comment>
<rdfs:subClassOf rdf:resource="http://www.loc.gov/premis/rdf/v3/RightsBasis"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/Copyright"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/License"/>
<owl:disjointWith rdf:resource="http://www.loc.gov/premis/rdf/v3/InstitutionalPolicy"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/StorageLocation" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Storage location</rdfs:label>
<rdfs:comment xml:lang="en">Information needed to retrieve a physical item from its physical storage location or a file
from the storage system, or to access a bitstream within a file.</rdfs:comment>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<rdfs:seeAlso rdf:resource="http://id.loc.gov/vocabulary/preservation/contentLocationType"/>
</owl:Class>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/StorageMedium" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">Storage medium</rdfs:label>
<rdfs:comment xml:lang="en">The physical medium on which the Object is stored (e.g., magnetic tape, hard disk, CD-ROM,
DVD).</rdfs:comment>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<rdfs:seeAlso rdf:resource="http://id.loc.gov/vocabulary/preservation/storageMedium"/>
</owl:Class>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/act" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">act</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/Rule"/>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/Action"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/allows" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">allows</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/RightsBasis"/>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/Rule"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/governs" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">governs</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/RightsBasis"/>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/Object"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/basis" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has basis</rdfs:label>
<rdfs:comment xml:lang="en">Links from a RightsStatus to the RightsBasis instance that supports or documents
it.</rdfs:comment>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/RightsStatus"/>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/RightsBasis"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/characteristic" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has characteristic</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/Dependency"/>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/EnvironmentCharacteristic"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/dependency" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has dependency</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/Object"/>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/Dependency"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/documentation" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has documentation</rdfs:label>
<rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/encoding" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has encoding</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/Signature"/>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/SignatureEncoding"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/fixity" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has fixity</rdfs:label>
<rdfs:domain>
<owl:Class>
<owl:unionOf rdf:parseType="Collection">
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Bitstream"/>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/File"/>
</owl:unionOf>
</owl:Class>
</rdfs:domain>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/Fixity"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/identifier" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has identifier</rdfs:label>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/Identifier"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/jurisdiction" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has jurisdiction</rdfs:label>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/RightsBasis"/>
<rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Resource"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/medium" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has medium</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/StorageLocation"/>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/StorageMedium"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/outcome" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has outcome</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/Event"/>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/OutcomeStatus"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/policy" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has policy</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/Object"/>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/PreservationPolicy"/>
<rdfs:seeAlso rdf:resource="http://id.loc.gov/vocabulary/preservation/preservationLevelRole"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/purpose" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has purpose</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/Dependency"/>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/Action"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/relationship" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has relationship</rdfs:label>
<rdfs:domain>
<owl:Class>
<owl:unionOf rdf:parseType="Collection">
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Agent"/>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Object"/>
</owl:unionOf>
</owl:Class>
</rdfs:domain>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/Object"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<rdfs:seeAlso rdf:resource="http://id.loc.gov/vocabulary/preservation/relationshipSubType"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/rightsStatus" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has rights status</rdfs:label>
<rdfs:subPropertyOf rdf:resource="http://purl.org/dc/elements/1.1/rights"/>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/Object"/>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/RightsStatus"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/signature" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has signature</rdfs:label>
<rdfs:domain>
<owl:Class>
<owl:unionOf rdf:parseType="Collection">
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Bitstream"/>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/File"/>
</owl:unionOf>
</owl:Class>
</rdfs:domain>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/Signature"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/inhibitedBy" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">inhibited by</rdfs:label>
<rdfs:domain>
<owl:Class>
<owl:unionOf rdf:parseType="Collection">
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Bitstream"/>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/File"/>
</owl:unionOf>
</owl:Class>
</rdfs:domain>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/Inhibitor"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/inhibits" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">inhibits</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/Inhibitor"/>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/Action"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/prohibits" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">prohibits</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/RightsBasis"/>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/Rule"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:ObjectProperty rdf:about="http://www.loc.gov/premis/rdf/v3/storedAt" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">stored at</rdfs:label>
<rdfs:domain>
<owl:Class>
<owl:unionOf rdf:parseType="Collection">
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Bitstream"/>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/File"/>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Representation"/>
</owl:unionOf>
</owl:Class>
</rdfs:domain>
<rdfs:range rdf:resource="http://www.loc.gov/premis/rdf/v3/StorageLocation"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:ObjectProperty>
<owl:DatatypeProperty rdf:about="http://www.loc.gov/premis/rdf/v3/endDate" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">end date</rdfs:label>
<rdfs:domain>
<owl:Class>
<owl:unionOf rdf:parseType="Collection">
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/RightsStatus"/>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Rule"/>
</owl:unionOf>
</owl:Class>
</rdfs:domain>
<rdfs:subPropertyOf rdf:resource="http://purl.org/dc/terms/date"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="http://www.loc.gov/premis/rdf/v3/citation" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has citation</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/RightsBasis"/>
<rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="http://www.loc.gov/premis/rdf/v3/compositionLevel" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has composition level</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/File"/>
<rdfs:range rdf:resource="http://www.w3.org/2001/XMLSchema#nonNegativeInteger"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="http://www.loc.gov/premis/rdf/v3/determinationDate" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has determination date</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/RightsStatus"/>
<rdfs:range rdf:resource="http://www.w3.org/2001/XMLSchema#date"/>
<rdfs:subPropertyOf rdf:resource="http://purl.org/dc/terms/date"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="http://www.loc.gov/premis/rdf/v3/key" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has key</rdfs:label>
<rdfs:domain>
<owl:Class>
<owl:unionOf rdf:parseType="Collection">
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Inhibitor"/>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Signature"/>
</owl:unionOf>
</owl:Class>
</rdfs:domain>
<rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="http://www.loc.gov/premis/rdf/v3/note" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has note</rdfs:label>
<rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
<rdfs:subPropertyOf rdf:resource="http://purl.org/dc/terms/description"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="http://www.loc.gov/premis/rdf/v3/originalName" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has original name</rdfs:label>
<rdfs:domain>
<owl:Class>
<owl:unionOf rdf:parseType="Collection">
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/File"/>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/IntellectualEntity"/>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Representation"/>
</owl:unionOf>
</owl:Class>
</rdfs:domain>
<rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="http://www.loc.gov/premis/rdf/v3/outcomeNote" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has outcome note</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/Event"/>
<rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
<rdfs:subPropertyOf rdf:resource="http://www.loc.gov/premis/rdf/v3/note"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="http://www.loc.gov/premis/rdf/v3/rationale" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has rationale</rdfs:label>
<rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
<rdfs:subPropertyOf rdf:resource="http://www.loc.gov/premis/rdf/v3/note"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="http://www.loc.gov/premis/rdf/v3/restriction" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has restriction</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/Rule"/>
<rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="http://www.loc.gov/premis/rdf/v3/size" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has size</rdfs:label>
<rdfs:domain>
<owl:Class>
<owl:unionOf rdf:parseType="Collection">
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Bitstream"/>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/File"/>
</owl:unionOf>
</owl:Class>
</rdfs:domain>
<rdfs:range rdf:resource="http://www.w3.org/2001/XMLSchema#nonNegativeInteger"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
<skos:narrowMatch rdf:resource="http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#fileSize"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="http://www.loc.gov/premis/rdf/v3/terms" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has terms</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/RightsBasis"/>
<rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
<rdfs:subPropertyOf rdf:resource="http://www.loc.gov/premis/rdf/v3/note"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="http://www.loc.gov/premis/rdf/v3/validationRules" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has validation rules</rdfs:label>
<rdfs:domain rdf:resource="http://www.loc.gov/premis/rdf/v3/Signature"/>
<rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
<rdfs:subPropertyOf rdf:resource="http://www.loc.gov/premis/rdf/v3/note"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="http://www.loc.gov/premis/rdf/v3/version" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">has version</rdfs:label>
<rdfs:domain>
<owl:Class>
<owl:unionOf rdf:parseType="Collection">
<owl:Class rdf:about="http://purl.org/dc/terms/FileFormat"/>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/HardwareAgent"/>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/IntellectualEntity"/>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/SoftwareAgent"/>
</owl:unionOf>
</owl:Class>
</rdfs:domain>
<rdfs:range rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="http://www.loc.gov/premis/rdf/v3/startDate" xmlns="http://www.loc.gov/premis/rdf/v3/" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:prov="http://www.w3.org/ns/prov#">
<rdfs:label xml:lang="en">start date</rdfs:label>
<rdfs:domain>
<owl:Class>
<owl:unionOf rdf:parseType="Collection">
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/RightsStatus"/>
<owl:Class rdf:about="http://www.loc.gov/premis/rdf/v3/Rule"/>
</owl:unionOf>
</owl:Class>
</rdfs:domain>
<rdfs:subPropertyOf rdf:resource="http://purl.org/dc/terms/date"/>
<rdfs:isDefinedBy rdf:resource="http://www.loc.gov/premis/rdf/v3/"/>
</owl:DatatypeProperty>
</rdf:RDF>
+6 -3
View File
@@ -5,12 +5,14 @@ import "google/protobuf/empty.proto";
message OntologyLoadRequest {
string path = 1;
bool infer = 2;
}
message OntologyLoadResponse {
uint64 input_size = 1;
uint64 old_size = 2;
uint64 new_size = 3;
uint64 old_size = 1;
uint64 input_size = 2;
uint64 inferences = 3;
uint64 new_size = 4;
}
message OntologyClearResponse {
@@ -22,6 +24,7 @@ message OntologyQueryRequest {
optional string sparql_query = 2;
map<string, string> prefixes = 3;
optional string base = 4;
bool inferences_only = 5;
}
message OntologyQueryResponse {
-1
View File
@@ -10,7 +10,6 @@ gl-search.workspace = true
clap.workspace = true
color-eyre.workspace = true
csv = "1.4"
http.workspace = true
iced.workspace = true
ldp.workspace = true
+664 -342
View File
File diff suppressed because it is too large Load Diff
+7 -1
View File
@@ -7,10 +7,13 @@ pub(crate) struct QueryArgs {
pub(crate) dataset_path: Option<PathBuf>,
#[arg(short, long, value_name = "QUERY PATH")]
pub(crate) query_path: PathBuf,
pub(crate) query_path: Option<PathBuf>,
#[arg(short, long, value_name = "BASE IRI")]
pub(crate) base: Option<String>,
#[arg(short, long)]
pub(crate) inferences_only: bool,
}
#[derive(Args)]
@@ -18,6 +21,9 @@ pub(crate) struct SearchArgs {
#[arg(short, long, value_name = "DOC TYPE")]
pub(crate) discriminant: Option<u64>,
#[arg(short, long, value_name = "LIMIT")]
pub(crate) limit: Option<usize>,
#[arg(value_name = "QUERY")]
pub(crate) query: String,
}
+13 -1
View File
@@ -42,4 +42,16 @@ pub(crate) enum Error {
#[error(transparent)]
TonicTransport(#[from] tonic::transport::Error),
}
#[error(transparent)]
TonicStatus(#[from] tonic::Status),
#[error(transparent)]
Tantivy(#[from] gl_search::tantivy::TantivyError),
#[error(transparent)]
Reqwest(#[from] ldp::reqwest::Error),
#[error(transparent)]
Ldp(#[from] ldp::Error),
}
+94 -53
View File
@@ -3,33 +3,32 @@ mod args;
mod error;
mod navigator;
mod rdf;
mod tasks;
mod theme;
mod widget;
mod windows;
use std::collections::HashMap;
use crate::app::Publisher;
use crate::args::{AppArgs, Command};
use clap::Parser;
use gl_graph::indexer::Indexer;
use gl_graph::ontology::{OntologyBuilder, ResourceSelector};
use gl_graph::{CurieHelper, language, vocab};
use gl_inference::proto::OntologyQueryRequest;
use gl_inference::proto::ontology_client::OntologyClient;
use gl_search::{Schema, SearchIndex};
use iced::futures::StreamExt;
use ldp::Traverse;
use ldp::middleware::BasicAuthMiddleware;
use ldp::reqwest::Client;
use ldp::reqwest_middleware::ClientBuilder;
use ldp::traverse::Traverse;
use oxigraph::io::{RdfFormat, RdfParser, RdfSerializer};
use oxigraph::model::{Dataset, Graph, Triple, TripleRef};
use tonic::Status;
use tracing::{debug, debug_span, error, field, Instrument};
use oxigraph::model::{Graph, TripleRef};
use std::collections::HashMap;
use tracing::{Instrument, debug_span, error, field};
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{EnvFilter, fmt};
use url::Url;
use gl_graph::index::index_ontology;
use gl_graph::language;
use gl_inference::proto::ontology_client::OntologyClient;
use gl_inference::proto::OntologyQueryRequest;
fn main() -> color_eyre::Result<()> {
let appender = tracing_appender::rolling::never("/tmp", "publisher-log");
@@ -43,25 +42,32 @@ fn main() -> color_eyre::Result<()> {
.init();
color_eyre::install()?;
let mut index = SearchIndex::builder()
.with_path("/home/alex/.local/share/org.graphofliberty.desktop/index")
.build()
.expect("Failed to build search index");
let args = AppArgs::parse();
match args.command {
Some(Command::Query(args)) => {
let raw_query = String::from_utf8(std::fs::read(&args.query_path)?)?;
let raw_query = if let Some(query) = &args.query_path {
Some(String::from_utf8(std::fs::read(query)?)?)
} else {
None
};
let graph = if let Some(dataset_path) = &args.dataset_path {
Some(String::from_utf8(std::fs::read(dataset_path)?)?)
} else { None };
} else {
None
};
let mut request = OntologyQueryRequest::default();
request.sparql_query = Some(raw_query);
request.turtle = graph;
request.prefixes = HashMap::from_iter(gl_graph::PREFIXES.iter().map(|(name, iri)| (name.clone(), iri.clone())));
request.base = args.base.clone();
let request = OntologyQueryRequest {
sparql_query: raw_query,
turtle: graph,
prefixes: HashMap::from_iter(
gl_graph::PREFIXES
.iter()
.map(|(name, iri)| (name.clone(), iri.clone())),
),
base: args.base.clone(),
inferences_only: args.inferences_only,
};
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
@@ -69,17 +75,34 @@ fn main() -> color_eyre::Result<()> {
.build()?;
runtime.block_on(async {
let mut client = OntologyClient::connect("http://[::1]:3000").await.unwrap();
let mut client = OntologyClient::connect("http://[::1]:3000")
.await
.unwrap()
.max_decoding_message_size(1024 * 1024 * 1024);
let response = client.query(request).await.unwrap();
println!("{}", response.get_ref().results);
});
}
Some(Command::Search(args)) => {
for document in index.query(args.discriminant, &args.query, Schema::all_fields(), 500000)? {
let index = SearchIndex::builder()
.with_path("/home/alex/.local/share/org.graphofliberty.desktop/index")
.build()
.expect("Failed to build search index");
let limit = args.limit.unwrap_or(5);
for document in
index.query(args.discriminant, &args.query, Schema::all_fields(), limit)?
{
println!("{}", gl_search::to_json(document));
}
}
Some(Command::Reindex) => {
let mut index = SearchIndex::builder()
.with_path("/home/alex/.local/share/org.graphofliberty.desktop/index")
.build()
.expect("Failed to build search index");
let mut writer = index.writer()?;
debug_span!("Clear Index").in_scope(|| {
writer.delete_all_documents()?;
@@ -92,8 +115,26 @@ fn main() -> color_eyre::Result<()> {
.build()?;
runtime.block_on(async {
let mut client = OntologyClient::connect("http://[::1]:3000").await?;
let documents = index_ontology(&mut client, &language::ENGLISH_OR_UNTAGGED).await?;
let curie_helper = CurieHelper::new(gl_graph::PREFIXES.clone());
let indexer = Indexer::new(language::ENGLISH_OR_UNTAGGED.clone(), &curie_helper);
let mut client = OntologyBuilder::from_string(
"http://[::1]:3000",
language::ENGLISH_OR_UNTAGGED.clone(),
)?
.connect()
.await?;
let resource_descriptions = client
.resource_descriptions(
ResourceSelector::Classes,
[
vocab::rdf::PROPERTY.into_owned(),
vocab::rdfs::CLASS.into_owned(),
vocab::skos::CONCEPT.into_owned(),
],
)
.await?;
let documents = indexer.ontology(resource_descriptions);
debug_span!("Index Ontology", documents = field::Empty).in_scope(|| {
for document in documents {
writer.add_document(document).unwrap();
@@ -111,47 +152,47 @@ fn main() -> color_eyre::Result<()> {
.build();
let starting_url = Url::parse("http://fedora.quill.lan/rest/")?;
let mut dataset = Dataset::new();
let mut graph = Graph::new();
let mut traversal = Traverse::new(http_client, starting_url, None);
let mut rdf_source_count = 0usize;
while let Some(result) = traversal.next().await {
match result {
Ok(rdf_source) => {
dataset.extend(rdf_source.dataset());
let triples = rdf_source.dataset().iter().map(TripleRef::from);
graph.extend(triples);
rdf_source_count += 1;
},
}
Err(err) => error!(?err),
}
}
let span = debug_span!("Index Repository", rdf_sources = rdf_source_count, triples = field::Empty, documents = field::Empty);
let span = debug_span!(
"Index Repository",
rdf_sources = rdf_source_count,
triples = field::Empty,
documents = field::Empty
);
let triple_count = async {
let mut client = OntologyClient::connect("http://[::1]:3000").await.unwrap();
let mut request = OntologyQueryRequest::default();
let curie_helper = CurieHelper::new(gl_graph::PREFIXES.clone());
let indexer =
Indexer::new(language::ENGLISH_OR_UNTAGGED.clone(), &curie_helper);
let mut output_buffer = Vec::new();
let mut serializer = RdfSerializer::from_format(RdfFormat::Turtle)
.for_writer(output_buffer);
for quad in &dataset {
serializer.serialize_triple(TripleRef::from(quad))?;
let mut client = OntologyBuilder::from_string(
"http://[::1]:3000",
language::ENGLISH_OR_UNTAGGED.clone(),
)?
.connect()
.await?;
let graph_with_inferences = client.run_inference(&graph).await?;
for document in indexer.graph(&graph_with_inferences) {
writer.add_document(document)?;
}
output_buffer = serializer.finish()?;
let turtle = String::from_utf8_lossy(&output_buffer).to_string();
request.sparql_query = None;
request.turtle = Some(turtle);
request.prefixes = HashMap::new();
request.base = None;
let response = client.query(request).await.unwrap();
let graph_with_inferences = RdfParser::from_format(RdfFormat::Turtle)
.for_slice(&response.get_ref().results)
.filter_map(Result::ok)
.map(Triple::from)
.collect::<Graph>();
Ok::<usize, crate::error::Error>(graph_with_inferences.len())
}.instrument(span.clone()).await?;
}
.instrument(span.clone())
.await?;
span.record("triples", triple_count);
Ok::<_, crate::error::Error>(writer)
+1 -1
View File
@@ -1,3 +1,3 @@
pub(crate) mod conversion;
pub(crate) mod ontology;
//pub(crate) mod ontology;
pub(crate) mod term_helper;
-396
View File
@@ -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, Term, 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
}
}
}
}
+113
View File
@@ -0,0 +1,113 @@
use crate::app::Message;
use gl_graph::class::Class;
use gl_graph::ontology::{ConnectedOntology, OntologyBuilder, ResourceSelector};
use gl_graph::vocab;
use iced::Task;
use ldp::ResourceRequestBuilder;
use ldp::reqwest_middleware::ClientWithMiddleware;
use oxigraph::io::RdfFormat;
use oxigraph::model::{Dataset, Graph, NamedNode, NamedNodeRef, NamedOrBlankNodeRef, TermRef};
use url::Url;
pub(crate) fn connect_to_ontology_service(builder: OntologyBuilder) -> Task<Message> {
Task::perform(builder.connect(), |result| match result {
Ok(ontology) => Message::ConnectedToOntologyService(ontology),
Err(err) => Message::ShowError(err.to_string()),
})
}
pub(crate) fn lookup_resource_descriptions(
mut ontology: ConnectedOntology,
subjects: impl IntoIterator<Item = NamedNode> + Send + 'static,
) -> Task<Message> {
Task::perform(
async move {
ontology
.resource_descriptions(ResourceSelector::Subjects, subjects)
.await
},
|result| match result {
Ok(descriptions) => Message::CacheResourceDescriptions(descriptions),
Err(err) => Message::ShowError(err.to_string()),
},
)
}
pub(crate) fn list_datatypes(mut ontology: ConnectedOntology) -> Task<Message> {
Task::perform(
async move {
ontology
.resource_descriptions(
ResourceSelector::Classes,
vec![vocab::rdfs::DATATYPE.into_owned()],
)
.await
},
|result| match result {
Ok(datatypes) => Message::CacheDatatypes(datatypes),
Err(err) => Message::ShowError(err.to_string()),
},
)
}
pub(crate) fn list_read_only_entities(mut ontology: ConnectedOntology) -> Task<Message> {
Task::perform(
async move { ontology.list_read_only().await },
|result| match result {
Ok(entities) => Message::CacheReadOnlyEntities(entities),
Err(err) => Message::ShowError(err.to_string()),
},
)
}
pub(crate) fn run_inference(mut ontology: ConnectedOntology, graph: Graph) -> Task<Message> {
Task::perform(
async move { ontology.run_inference(&graph).await },
|result| match result {
Ok(inferences) => Message::SetInferredTriples(inferences),
Err(err) => Message::ShowError(err.to_string()),
},
)
}
pub(crate) fn navigate_to_next_url(client: ClientWithMiddleware, class: Class) -> Task<Message> {
if let Some(lookup_url) = class.next_url() {
Task::future(async move {
let request = ResourceRequestBuilder::with_client_and_url(client, lookup_url.clone())
.accept_rdf_format(RdfFormat::Turtle)
.follow_described_by(true)
.build();
match request.send().await {
Ok(resource) => match resource.into_rdf_source::<Dataset>().await {
Ok(document) => {
let node = NamedNodeRef::new_unchecked(lookup_url.as_str());
let result = document
.dataset()
.quads_for_pattern(
Some(NamedOrBlankNodeRef::NamedNode(node)),
Some(vocab::glo::NEXT_URL),
None,
None,
)
.next();
if let Some(next_url_quad) = result {
if let TermRef::NamedNode(next_url) = next_url_quad.object {
let url = Url::parse(next_url.as_str()).unwrap();
Message::NewDocument(class, Some(url))
} else {
Message::None
}
} else {
Message::None
}
}
_ => Message::None,
},
_ => Message::None,
}
})
} else {
Task::none()
}
}
+25 -25
View File
@@ -7,6 +7,7 @@ use iced::clipboard::Content;
use iced::mouse::{Cursor, Interaction};
use iced::widget::text_input::{Catalog, Status, Style, StyleFn};
use iced::{Element, Event, Length, Rectangle, Size, keyboard, widget};
use url::Url;
pub struct State {
control: bool,
@@ -19,7 +20,7 @@ where
Renderer: text::Renderer,
{
curie_helper: &'a CurieHelper,
base: Option<String>,
base: Option<&'a Url>,
on_control_click: Option<Message>,
on_shift_click: Option<Message>,
text_input: widget::TextInput<'a, Message, Theme, Renderer>,
@@ -34,7 +35,7 @@ where
pub fn new(
curie_helper: &'a CurieHelper,
placeholder: &'a str,
base: Option<&str>,
base: Option<&'a Url>,
iri: &str,
) -> Self {
let display_value = curie_helper
@@ -43,7 +44,7 @@ where
let text_input = widget::TextInput::new(placeholder, display_value);
Self {
curie_helper,
base: base.map(String::from),
base,
on_control_click: None,
on_shift_click: None,
text_input,
@@ -70,10 +71,9 @@ where
#[must_use]
pub fn on_input(mut self, on_input: impl Fn(String) -> Message + 'a) -> Self {
let base_clone = self.base.clone();
let wrapped = move |value: String| {
let expanded_value = self.curie_helper.expand(base_clone.as_deref(), &value);
on_input(expanded_value.unwrap_or_else(|| value))
let expanded_value = self.curie_helper.expand(self.base, &value);
on_input(expanded_value.unwrap_or(value))
};
self.text_input = self.text_input.on_input(wrapped);
@@ -117,7 +117,7 @@ where
pub fn iri_input<'a, Message, Theme, Renderer>(
curie_helper: &'a CurieHelper,
placeholder: &'a str,
base: Option<&str>,
base: Option<&'a Url>,
iri: &str,
) -> IriInput<'a, Message, Theme, Renderer>
where
@@ -213,21 +213,21 @@ where
state.control = modifiers.control();
state.shift = modifiers.shift();
}
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
if cursor.is_over(layout.bounds()) {
if state.control
&& let Some(on_control_click) = &self.on_control_click
{
shell.publish(on_control_click.clone());
shell.capture_event();
}
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
if cursor.is_over(layout.bounds()) =>
{
if state.control
&& let Some(on_control_click) = &self.on_control_click
{
shell.publish(on_control_click.clone());
shell.capture_event();
}
if state.shift
&& let Some(on_shift_click) = &self.on_shift_click
{
shell.publish(on_shift_click.clone());
shell.capture_event();
}
if state.shift
&& let Some(on_shift_click) = &self.on_shift_click
{
shell.publish(on_shift_click.clone());
shell.capture_event();
}
}
_ => {}
@@ -245,10 +245,10 @@ where
);
let clipboard = shell.clipboard_mut();
if let Some(Content::Text(content)) = &clipboard.write {
if let Some(expanded) = self.curie_helper.expand(self.base.as_deref(), &content) {
clipboard.write = Some(Content::Text(expanded));
}
if let Some(Content::Text(content)) = &clipboard.write
&& let Some(expanded) = self.curie_helper.expand(self.base, content)
{
clipboard.write = Some(Content::Text(expanded));
}
}
-1
View File
@@ -1 +0,0 @@
View File
+1 -1
View File
@@ -99,4 +99,4 @@ impl SearchIndex {
pub fn to_json(document: HashMap<Field, OwnedValue>) -> String {
document.to_json(Schema::schema())
}
}
+2 -1
View File
@@ -2,10 +2,11 @@ mod error;
mod index;
mod schema;
pub mod subtitles;
pub use tantivy;
pub use tantivy::indexer::IndexWriter;
pub use tantivy::schema::Field;
pub use tantivy::schema::document::OwnedValue;
pub use error::{Result, SearchError};
pub use index::{SearchIndex, SearchIndexBuilder, to_json};
pub use schema::Schema;
pub use schema::Schema;
+2
View File
@@ -39,6 +39,8 @@ impl Schema {
schema_builder.add_text_field("given_name:en", stored_ngram32.clone());
schema_builder.add_text_field("corporate_name:en", stored_ngram32.clone());
schema_builder.add_text_field("title:en", stored_en_stem.clone());
schema_builder.add_u64_field("subtitle_start", schema::STORED);
schema_builder.add_u64_field("subtitle_end", schema::STORED);
schema_builder.add_text_field("subtitle:en", stored_en_stem.clone());
+3 -3
View File
@@ -1,7 +1,7 @@
use subtitler;
/*use subtitler;
use subtitler::SubtitleFormat;
/*
pub fn bytes_to_documents(data: &[u8]) -> crate::Result<Vec<SearchDocument>> {
let subtitle_start = Schema::field("subtitle_start", None);
let subtitle_end = Schema::field("subtitle_end", None);
@@ -20,4 +20,4 @@ pub fn bytes_to_documents(data: &[u8]) -> crate::Result<Vec<SearchDocument>> {
})
.collect::<Vec<_>>();
Ok(documents)
}*/
}*/