This commit is contained in:
2026-08-23 17:01:13 -04:00
parent 37248f6ff3
commit 70fca07790
4 changed files with 54 additions and 38 deletions
+3 -3
View File
@@ -144,9 +144,9 @@ SELECT ?subject ?class WHERE {{
Ok(results)
}
fn selector_to_sparql_pattern<'a>(selector: ResourceSelector, nodes: impl IntoIterator<Item = NamedNodeRef<'a>>) -> String {
fn selector_to_sparql_pattern(selector: ResourceSelector, nodes: impl IntoIterator<Item = NamedNode>) -> 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<Item = NamedNodeRef<'a>>) -> crate::Result<HashMap<NamedNode, ResourceDescription>> {
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");
+43 -23
View File
@@ -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<NamedNode, ResourceDescription>),
CacheReadOnlyEntities(HashSet<ReadOnlyEntity>),
PopulateCaches,
LookupResourceDescription(NamedNode),
CacheResourceDescription(NamedNode, ResourceDescription),
LookupResourceDescriptions(Vec<NamedNode>),
CacheResourceDescriptions(HashMap<NamedNode, ResourceDescription>),
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) {
Message::LookupResourceDescriptions(nodes) => {
let provided_nodes: HashSet<NamedNode> = 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_description(ontology.clone(), node)
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::<Vec<_>>();
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<NamedNode> = 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<gl_search::Field, gl_search::OwnedValue>| {
let columns = vec![table::column(text("CURIE"), |document: &HashMap<gl_search::Field, gl_search::OwnedValue>| {
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],
+3 -3
View File
@@ -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(|| {
+4 -8
View File
@@ -13,21 +13,17 @@ pub(crate) fn connect_to_ontology_service(builder: OntologyBuilder) -> Task<Mess
})
}
pub(crate) fn lookup_resource_description(mut ontology: ConnectedOntology, subject: NamedNode) -> Task<Message> {
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<Item = NamedNode> + Send + 'static) -> Task<Message> {
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<Message> {
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()),