From 70fca077904c6a131a2bbcda2d6db44133fa21e4ccddad84b83b8595a1d0f580 Mon Sep 17 00:00:00 2001 From: Alex Wied <2+alex@noreply.code.graphofliberty.org> Date: Sun, 23 Aug 2026 17:01:13 -0400 Subject: [PATCH] . --- graph/src/ontology.rs | 6 ++-- publish/src/app.rs | 68 ++++++++++++++++++++++++++++--------------- publish/src/main.rs | 6 ++-- publish/src/tasks.rs | 12 +++----- 4 files changed, 54 insertions(+), 38 deletions(-) diff --git a/graph/src/ontology.rs b/graph/src/ontology.rs index 1d4835c..0107dcb 100644 --- a/graph/src/ontology.rs +++ b/graph/src/ontology.rs @@ -144,9 +144,9 @@ SELECT ?subject ?class WHERE {{ Ok(results) } - fn selector_to_sparql_pattern<'a>(selector: ResourceSelector, nodes: impl IntoIterator>) -> String { + fn selector_to_sparql_pattern(selector: ResourceSelector, nodes: impl IntoIterator) -> String { let bindings = nodes.into_iter() - .map(|node| GroundTerm::NamedNode(node.into_owned())) + .map(GroundTerm::NamedNode) .map(Some) .map(|item| vec![item]) .collect(); @@ -180,7 +180,7 @@ SELECT ?subject ?class WHERE {{ }.to_string() } - pub async fn resource_descriptions<'a>(&mut self, selector: ResourceSelector, nodes: impl IntoIterator>) -> crate::Result> { + pub async fn resource_descriptions(&mut self, selector: ResourceSelector, nodes: impl IntoIterator) -> crate::Result> { let pattern = Self::selector_to_sparql_pattern(selector, nodes); let label_filter = self.language.filter("label"); let comment_filter = self.language.filter("comment"); diff --git a/publish/src/app.rs b/publish/src/app.rs index bd69c1e..78e7c45 100644 --- a/publish/src/app.rs +++ b/publish/src/app.rs @@ -1,4 +1,5 @@ use std::collections::{HashMap, HashSet}; +use std::ops::Sub; use crate::navigator::Navigator; use crate::rdf::term_helper::{TermHelper, TermHelperMut}; use crate::widget::iri_input::iri_input; @@ -23,7 +24,7 @@ use ldp::reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; use ldp::{RdfSource, RdfSourceUpdateResponse, ResourceRequestBuilder, SerializationOptions}; use oxigraph::io::RdfFormat; use oxigraph::model::vocab::{rdf, rdfs}; -use oxigraph::model::{BaseDirection, Graph, NamedNode, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, Term, TermRef, Triple, TripleRef}; +use oxigraph::model::{BaseDirection, Graph, NamedNode, NamedNodeRef, NamedOrBlankNode, NamedOrBlankNodeRef, Quad, Term, TermRef, Triple, TripleRef}; use tracing::{debug, error, trace}; use gl_graph::class::Class; use gl_graph::ontology::{ConnectedOntology, OntologyBuilder, ReadOnlyEntity, ResourceDescription}; @@ -38,8 +39,8 @@ pub(crate) enum Message { CacheDatatypes(HashMap), CacheReadOnlyEntities(HashSet), PopulateCaches, - LookupResourceDescription(NamedNode), - CacheResourceDescription(NamedNode, ResourceDescription), + LookupResourceDescriptions(Vec), + CacheResourceDescriptions(HashMap), WindowClosed(window::Id), URLInputChanged(String), URLInputSubmitted, @@ -219,15 +220,17 @@ impl Publisher { Message::CacheReadOnlyEntities(entities) => { self.read_only_entities = entities; } - Message::LookupResourceDescription(node) => { - if !self.resource_descriptions.contains_key(&node) { - if let Some(ontology) = &mut self.ontology { - task = tasks::lookup_resource_description(ontology.clone(), node) - } + Message::LookupResourceDescriptions(nodes) => { + let provided_nodes: HashSet = HashSet::from_iter(nodes); + let nodes_with_known_descriptions = HashSet::from_iter(self.resource_descriptions.keys().cloned()); + let nodes_to_look_up = provided_nodes.sub(&nodes_with_known_descriptions); + + if let Some(ontology) = &mut self.ontology { + task = tasks::lookup_resource_descriptions(ontology.clone(), nodes_to_look_up) } } - Message::CacheResourceDescription(node, description) => { - self.resource_descriptions.insert(node, description); + Message::CacheResourceDescriptions(descriptions) => { + self.resource_descriptions.extend(descriptions); } Message::WindowClosed(id) => { if self.window_id == id { @@ -291,7 +294,6 @@ impl Publisher { datatype_state, }; Task::done(Message::AddRow(Some((key, state)))) - .chain(Task::done(Message::LookupResourceDescription(quad.predicate.clone()))) }); task = Task::batch(add_row_tasks) .chain(Task::done(Message::RunInference)) @@ -597,12 +599,20 @@ impl Publisher { } } Message::SetInferredTriples(graph) => { - let messages = graph.iter() - .map(|triple| { - Message::LookupResourceDescription(triple.predicate.into_owned()) - }).map(Task::done) - .collect::>(); - task = Task::batch(messages); + if let Some(ontology) = &mut self.ontology { + let predicates = graph.iter() + .map(|triple| triple.predicate.into_owned()); + + let objects = graph.iter() + .filter_map(|triple| match triple.object { + TermRef::NamedNode(node) => Some(node.into_owned()), + _ => None, + }); + + let mut nodes_to_look_up: HashSet = HashSet::from_iter(predicates); + nodes_to_look_up.extend(objects); + task = tasks::lookup_resource_descriptions(ontology.clone(), nodes_to_look_up); + } self.inferred_triples = graph; } @@ -837,7 +847,7 @@ impl Publisher { ToString::to_string, ).on_select(|selection| Message::UpdateQueryClass(selection)); - let mut columns = vec![table::column(text("CURIE"), |document: &HashMap| { + let columns = vec![table::column(text("CURIE"), |document: &HashMap| { let iri = document.get(&Schema::iri_field()) .and_then(|value| value.as_str()) .unwrap_or_default(); @@ -942,14 +952,24 @@ impl Publisher { }); let object_column = table::column("Object", |triple: TripleRef| { - let displayed_text = match triple.object { + match triple.object { TermRef::NamedNode(node) => { - self.curie_helper.abbreviate(Some(self.document.origin().as_str()), node.as_str()) - } - _ => None - }.unwrap_or(triple.object.to_string()); + let curie = self.curie_helper + .abbreviate(Some(self.document.origin().as_str()), node.as_str()) + .unwrap_or_else(|| node.as_str().to_string()); - text(displayed_text) + let label = self.resource_descriptions + .get(&node.into_owned()) + .and_then(|description| description.label.clone()); + + if let Some(label) = label { + text(format!("{curie} ({label})")) + } else { + text(curie) + } + } + _ => text(triple.object.to_string()), + } }); Some(table( [subject_column, predicate_column, object_column], diff --git a/publish/src/main.rs b/publish/src/main.rs index ca19ed5..32e8d0d 100644 --- a/publish/src/main.rs +++ b/publish/src/main.rs @@ -112,9 +112,9 @@ fn main() -> color_eyre::Result<()> { .connect() .await?; let resource_descriptions = client.resource_descriptions(ResourceSelector::Classes, [ - vocab::rdf::PROPERTY, - vocab::rdfs::CLASS, - vocab::skos::CONCEPT, + 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(|| { diff --git a/publish/src/tasks.rs b/publish/src/tasks.rs index 45e0483..1bf088a 100644 --- a/publish/src/tasks.rs +++ b/publish/src/tasks.rs @@ -13,21 +13,17 @@ pub(crate) fn connect_to_ontology_service(builder: OntologyBuilder) -> Task Task { - let subject_clone = subject.clone(); - Task::perform(async move { ontology.resource_descriptions(ResourceSelector::Subjects, vec![subject_clone.as_ref()]).await }, |result| { +pub(crate) fn lookup_resource_descriptions(mut ontology: ConnectedOntology, subjects: impl IntoIterator + Send + 'static) -> Task { + Task::perform(async move { ontology.resource_descriptions(ResourceSelector::Subjects, subjects).await }, |result| { match result { - Ok(descriptions) => { - let description = descriptions[&subject].clone(); - Message::CacheResourceDescription(subject, description) - }, + Ok(descriptions) => Message::CacheResourceDescriptions(descriptions), Err(err) => Message::ShowError(err.to_string()), } }) } pub(crate) fn list_datatypes(mut ontology: ConnectedOntology) -> Task { - Task::perform(async move { ontology.resource_descriptions(ResourceSelector::Classes, vec![vocab::rdfs::DATATYPE]).await }, |result| { + 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()),