This commit is contained in:
Alex Wied
2026-06-17 20:50:26 -04:00
parent 6df00eb5a3
commit f5ca8dd9ae
7 changed files with 352 additions and 160 deletions
+88 -45
View File
@@ -1,17 +1,16 @@
use crate::rdf::ontology::Ontology;
use std::collections::HashSet;
use crate::rdf::ontology::{LabeledIri, Ontology};
use crate::rdf::term_helper::{TermHelper, TermHelperMut};
use crate::rdf::vocab::{gl, rda};
use gl_search::{Schema, SearchDocument, SearchIndex, doc};
use gl_search::{Schema, SearchDocument, SearchIndex, doc, NamedFieldDocument, OwnedValue};
use http::StatusCode;
use iced::alignment::Horizontal;
use iced::widget::button::Style;
use iced::widget::grid::Sizing;
use iced::widget::{
button, center, column, combo_box, container, grid, mouse_area, opaque, row, scrollable, space,
stack, table, text, text_input, toggler,
};
use iced::widget::{button, center, column, combo_box, container, grid, mouse_area, opaque, pick_list, row, scrollable, space, stack, table, text, text_input, toggler};
use iced::window::Settings;
use iced::{Background, Color, Element, Length, Subscription, Task, color, window};
use iced::widget::text::Wrapping;
use ldp::middleware::BasicAuthMiddleware;
use ldp::model::{KeyedDataset, QuadKey};
use ldp::reqwest::{Client, Url};
@@ -20,7 +19,7 @@ use ldp::traverse::Traverse;
use ldp::{RdfSource, RdfSourceUpdateResponse, ResourceRequestBuilder, SerializationOptions};
use oxigraph::io::RdfFormat;
use oxigraph::model::vocab::{rdf, rdfs};
use oxigraph::model::{BaseDirection, Dataset, NamedNode, Quad, Term, TermRef};
use oxigraph::model::{BaseDirection, Dataset, NamedNode, NamedNodeRef, Quad, Term, TermRef};
use tracing::{debug, error, info};
#[derive(Debug, Clone)]
@@ -44,6 +43,7 @@ pub(crate) enum Message {
HoverRow(QuadKey),
UnhoverRow(QuadKey),
QueryUpdated(String),
QueryTypeUpdated(NamedNode),
SearchResultClicked(NamedNode),
DatatypeUpdated(QuadKey, Option<String>),
LanguageUpdated(QuadKey, Option<String>),
@@ -74,7 +74,8 @@ struct SearchState {
window_id: window::Id,
action: SearchResultClickAction,
query: String,
results: Vec<NamedNode>,
type_: LabeledIri,
results: Vec<NamedFieldDocument>,
}
pub(crate) struct Publisher {
@@ -113,12 +114,14 @@ impl Publisher {
Schema::iri_field() => iri.as_str(),
);
if let Some(label) = &info.label {
document.add_text(Schema::field("label"), label.to_lowercase());
if let Some(label) = &info.label &&
let Some(field) = ontology.field_for_property(&rdfs::LABEL.into_owned()) {
document.add_text(Schema::field(&field.name), label);
}
if let Some(comment) = &info.comment {
document.add_text(Schema::field("comment"), comment.to_lowercase());
if let Some(comment) = &info.comment &&
let Some(field) = ontology.field_for_property(&rdfs::COMMENT.into_owned()) {
document.add_text(Schema::field(&field.name), comment);
}
index.add(document).expect("Unable to add annotated IRI to search index");
@@ -192,9 +195,9 @@ impl Publisher {
&& let TermRef::Literal(literal) = quad.object
{
let field = Schema::schema()
.get_field(&field.key)
.get_field(field.name.as_str())
.expect("Field not found in schema");
document.add_text(field, literal.value().to_lowercase());
document.add_text(field, literal.value());
}
}
@@ -306,19 +309,29 @@ impl Publisher {
if let Some(search_state) = &mut self.search_state {
search_state.action = action;
} else {
let (id, window_task) = window::open(Settings::default());
self.search_state = Some(SearchState {
window_id: id,
action,
query: String::new(),
results: Vec::new(),
});
task = window_task.map(|_| Message::None)
if let Some(type_) = self.ontology.labeled_entities().next() {
let (id, window_task) = window::open(Settings::default());
self.search_state = Some(SearchState {
window_id: id,
action,
query: String::new(),
type_,
results: Vec::new(),
});
task = window_task.map(|_| Message::None)
};
}
}
Message::QueryTypeUpdated(type_) => {
if let Some(search_state) = &mut self.search_state {
if let Some(type_) = self.ontology.labeled_entity(&type_) {
search_state.type_ = type_;
}
}
}
Message::QueryUpdated(new_query) => {
if let Some(search_state) = &mut self.search_state {
let type_ = match search_state.action {
let catalog_id = match search_state.action {
SearchResultClickAction::Predicate(_) => self.ontology.catalog_id(&rdf::PROPERTY.into_owned()),
SearchResultClickAction::Object(key) => {
self.document
@@ -334,11 +347,8 @@ impl Publisher {
search_state.results = self
.index
.query(type_, new_query.as_str(), Schema::all_fields())
.expect("Error encountered while querying index")
.iter()
.map(NamedNode::new_unchecked)
.collect();
.query(catalog_id, new_query.as_str(), Schema::all_fields())
.expect("Error encountered while querying index");
search_state.query = new_query;
};
}
@@ -670,7 +680,41 @@ impl Publisher {
let search_input =
text_input("Query", &search_state.query).on_input(Message::QueryUpdated);
let label_column = table::column("Label", |result: &NamedNode| {
let mut entities = self.ontology
.labeled_entities()
.collect::<Vec<_>>();
entities.sort_by(|a, b| Ord::cmp(&a.label, &b.label));
let type_selector = pick_list(Some(&search_state.type_), entities, ToString::to_string)
.on_select(|selection| Message::QueryTypeUpdated(selection.iri));
let mut columns = vec![];
for field in self.ontology.fields_for_class(&search_state.type_.iri) {
let header_text = field.label.as_ref().unwrap_or(&field.name);
columns.push(table::column(text(header_text), |document: &NamedFieldDocument| {
let cell_value = document.0
.get(&field.name)
.and_then(|values| values.first())
.map(|value| match value {
OwnedValue::Str(string) => string,
_ => "???",
}).unwrap_or("");
let iri = document.0
.get("iri")
.and_then(|values| values.first())
.map(|value| match value {
OwnedValue::Str(string) => string,
_ => "???",
}).unwrap_or("");
button(text(cell_value).wrapping(Wrapping::Word))
.on_press(Message::SearchResultClicked(NamedNode::new_unchecked(iri)))
.style(button::text)
}).width(Length::Fixed(256.0)));
}
/*let label_column = table::column("Label", |result: &NamedNode| {
let header_text = self
.ontology
.info(result.as_ref())
@@ -695,7 +739,7 @@ impl Publisher {
.style(button::text)
});
/*let description_column = table::column("Description", |result: &NamedNode| {
let description_column = table::column("Description", |result: &NamedNode| {
let header_text = self.ontology.info(result.as_ref())
.and_then(|info| info.comment.clone())
.unwrap_or("Unknown".to_string());
@@ -703,25 +747,24 @@ impl Publisher {
button(text(header_text))
.on_press(Message::SearchResultClicked(result.clone()))
.style(button::text)
});*/
});
let iri_column = table::column("IRI", |result: &NamedNode| {
button(text(result.as_str()))
.on_press(Message::SearchResultClicked(result.clone()))
.style(button::text)
});
});*/
let results_table = scrollable(table(
[
label_column,
//description_column,
type_column,
iri_column,
],
&search_state.results,
));
let results_table = if columns.is_empty() {
None
} else {
Some(scrollable(table(columns, &search_state.results)))
};
return column![search_input, results_table].into();
return column![
row![search_input, type_selector],
results_table
].into();
}
let add_row_button = button("Add row").on_press(Message::AddRow(None));
@@ -742,8 +785,8 @@ impl Publisher {
let body: Element<Message> = if self.show_new_document_buttons {
column![
self.view_new_entity_buttons(self.ontology.subclass_of(gl::ENTITY)),
self.view_new_entity_buttons(self.ontology.subclass_of(rda::ENTITY)),
//self.view_new_entity_buttons(self.ontology.subclass_of(gl::ENTITY)),
//self.view_new_entity_buttons(self.ontology.subclass_of(rda::ENTITY)),
]
.into()
} else {
@@ -818,4 +861,4 @@ where
)
]
.into()
}
}