diff --git a/Cargo.lock b/Cargo.lock index 5db1a7c..e63c983 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1585,6 +1585,7 @@ name = "gl-graph" version = "0.1.0" dependencies = [ "oxigraph", + "thiserror 2.0.19", "tracing", ] diff --git a/graph/Cargo.toml b/graph/Cargo.toml index f9d542f..895894f 100644 --- a/graph/Cargo.toml +++ b/graph/Cargo.toml @@ -5,4 +5,5 @@ edition = "2024" [dependencies] oxigraph.workspace = true +thiserror.workspace = true tracing.workspace = true \ No newline at end of file diff --git a/graph/src/error.rs b/graph/src/error.rs new file mode 100644 index 0000000..e66821f --- /dev/null +++ b/graph/src/error.rs @@ -0,0 +1,21 @@ +use thiserror::Error; + +pub type Result = std::result::Result; + +#[derive(Error, Debug)] +pub enum Error { + #[error(transparent)] + IriParse(#[from] oxigraph::model::IriParseError), + + #[error(transparent)] + Storage(#[from] oxigraph::store::StorageError), + + #[error(transparent)] + SparqlSyntax(#[from] oxigraph::sparql::SparqlSyntaxError), + + #[error(transparent)] + QueryEvaluation(#[from] oxigraph::sparql::QueryEvaluationError), + + #[error(transparent)] + UpdateEvaluation(#[from] oxigraph::sparql::UpdateEvaluationError) +} \ No newline at end of file diff --git a/graph/src/inference.rs b/graph/src/inference.rs new file mode 100644 index 0000000..6abf4ac --- /dev/null +++ b/graph/src/inference.rs @@ -0,0 +1,59 @@ +use oxigraph::model::{Dataset, GraphNameRef, NamedNodeRef, Quad, QuadRef}; +use oxigraph::sparql::{QueryResults, SparqlEvaluator}; +use oxigraph::store::{Store, Transaction}; +use tracing::{debug_span, field}; + +const RDF_SCHEMA_PREFIX: &str = "http://www.w3.org/2000/01/rdf-schema#"; +const GL_PREFIX: &str = "https://graphofliberty.org/"; + +const INFERENCE_GRAPH: GraphNameRef = GraphNameRef::NamedNode(NamedNodeRef::new_unchecked("https://graphofliberty.org/inference")); +const INPUT_GRAPH: GraphNameRef = GraphNameRef::NamedNode(NamedNodeRef::new_unchecked("https://graphofliberty.org/input")); + +pub struct InferenceEngine { + ontology: Store, +} + +/// `prp-spo1` +fn sub_property_of(transaction: Transaction) -> crate::Result { + let span = debug_span!("rdfs:subPropertyOf", inferences = field::Empty).entered(); + + let mut query = SparqlEvaluator::new() + .with_prefix("rdfs", RDF_SCHEMA_PREFIX)? + .with_prefix("gl", GL_PREFIX)? + .parse_query(r#"CONSTRUCT { + ?x ?p2 ?y . +} WHERE { + ?p1 rdfs:subPropertyOf+ ?p2 . + GRAPH gl:input { ?x ?p1 ?y . } +}"#)?; + + query.dataset_mut().set_default_graph_as_union(); + + let inferences = if let QueryResults::Graph(result) = query.on_transaction(&transaction).execute()? { + result.filter_map(Result::ok) + .map(|triple| Quad::new(triple.subject, triple.predicate, triple.object, INFERENCE_GRAPH)) + .collect() + } else { + Dataset::new() + }; + + span.record("inferences", inferences.len()); + Ok(inferences) +} + +impl InferenceEngine { + pub fn new(store: Store) -> Self { + Self { + ontology: store, + } + } + + pub fn run(&self, dataset: &Dataset) -> crate::Result { + let _span = debug_span!("Inference").entered(); + + let mut transaction = self.ontology.start_transaction()?; + let input = dataset.iter().map(|triple| QuadRef::new(triple.subject, triple.predicate, triple.object, INPUT_GRAPH)); + transaction.extend(input); + sub_property_of(transaction) + } +} \ No newline at end of file diff --git a/graph/src/lib.rs b/graph/src/lib.rs index 1206aba..1ebe92a 100644 --- a/graph/src/lib.rs +++ b/graph/src/lib.rs @@ -1,4 +1,9 @@ mod curie; -pub mod vocab; +//mod materialize; +mod error; -pub use curie::{PREFIXES, CurieHelper}; \ No newline at end of file +pub mod vocab; +pub mod inference; + +pub use curie::{PREFIXES, CurieHelper}; +pub use error::{Error, Result}; \ No newline at end of file diff --git a/publish/src/rdf/materialize.rs b/graph/src/materialize.rs similarity index 89% rename from publish/src/rdf/materialize.rs rename to graph/src/materialize.rs index 4ffef2e..fb26012 100644 --- a/publish/src/rdf/materialize.rs +++ b/graph/src/materialize.rs @@ -1,18 +1,17 @@ -use crate::error; -use gl_graph::vocab::owl; +use crate::vocab; use oxigraph::model::{Dataset, GraphName, NamedNodeRef, NamedOrBlankNode, NamedOrBlankNodeRef, Term}; use oxigraph::sparql::SparqlEvaluator; use oxigraph::store::Store; use tracing::{debug, debug_span}; -const INFERENCE_GRAPH: NamedNodeRef = NamedNodeRef::new_unchecked("https://graphofliberty.org/inference"); + const RDF_SCHEMA: NamedNodeRef = NamedNodeRef::new_unchecked("http://www.w3.org/2000/01/rdf-schema#"); -pub fn same_as(store: &mut Store) -> error::Result<()> { +pub fn same_as(store: &mut Store) -> crate::Result<()> { let _span = debug_span!("Materialize owl:sameAs").entered(); let additional_quads = store - .quads_for_pattern(None, Some(owl::SAME_AS), None, None) + .quads_for_pattern(None, Some(vocab::owl::SAME_AS), None, None) .filter_map(Result::ok) .fold(Dataset::new(), |mut new_dataset, alias| { if let NamedOrBlankNode::NamedNode(x) = alias.subject @@ -56,7 +55,7 @@ pub fn same_as(store: &mut Store) -> error::Result<()> { Ok(()) } -pub fn super_properties(store: &mut Store) -> error::Result<()> { +pub fn super_properties(store: &mut Store) -> crate::Result<()> { let _span = debug_span!("Materialize rdfs:subPropertyOf").entered(); let update = SparqlEvaluator::new() @@ -85,7 +84,7 @@ pub fn super_properties(store: &mut Store) -> error::Result<()> { Ok(()) } -pub fn super_classes(store: &mut Store) -> error::Result<()> { +pub fn super_classes(store: &mut Store) -> crate::Result<()> { let _span = debug_span!("Materialize rdfs:subClassOf").entered(); let update = SparqlEvaluator::new() diff --git a/ontology/graphofliberty.ttl b/ontology/graphofliberty.ttl index 274a61d..0301a2d 100644 --- a/ontology/graphofliberty.ttl +++ b/ontology/graphofliberty.ttl @@ -87,11 +87,6 @@ xsd:unsignedShort rdf:type rdfs:Datatype ; # Object Properties ################################################################# -### https://graphofliberty.org/2026/04/ont/associatedProperty -:associatedProperty rdf:type owl:ObjectProperty ; - rdfs:label "associated property"@en . - - ### https://graphofliberty.org/2026/04/ont/derivedWith :derivedWith rdf:type owl:ObjectProperty ; rdfs:range :DerivationMethodology ; @@ -99,62 +94,10 @@ xsd:unsignedShort rdf:type rdfs:Datatype ; rdfs:label "derived with"@en . -### https://graphofliberty.org/2026/04/ont/indexDocument -:indexDocument rdf:type owl:ObjectProperty ; - rdfs:range :IndexDocument ; - rdfs:comment "Relates an entity to an Index Document."@en ; - rdfs:label "has index document"@en . - - -### https://graphofliberty.org/2026/04/ont/indexedByField -:indexedByField rdf:type owl:ObjectProperty ; - rdfs:range :IndexDocumentField ; - rdfs:comment "The property is associated with the given field in a full-text search database."@en ; - rdfs:label "indexed by field"@en . - - -### https://graphofliberty.org/2026/04/ont/subtitles -:subtitles rdf:type owl:ObjectProperty ; - rdfs:comment "Relates an entity to a File which contains subtitles for the entity."@en ; - rdfs:label "has subtitles"@en . - - -### https://graphofliberty.org/2026/04/ont/template -:template rdf:type owl:ObjectProperty ; - rdfs:comment "A default set of triples used during the creation of new entities of the associated class."@en ; - rdfs:label "has template"@en . - - ################################################################# # Data properties ################################################################# -### https://graphofliberty.org/2026/04/ont/categoryId -:categoryId rdf:type owl:DatatypeProperty ; - rdfs:domain :SearchableClass ; - rdfs:range xsd:nonNegativeInteger ; - rdfs:comment "An integer associated with the class for fast lookup in a database."@en ; - rdfs:label "category id" . - - -### https://graphofliberty.org/2026/04/ont/fieldLabel -:fieldLabel rdf:type owl:DatatypeProperty ; - rdfs:domain :IndexDocumentField ; - rdfs:range rdf:dirLangString , - rdf:langString , - xsd:string ; - rdfs:comment "The label of a field, which shall be displayed to the user."@en ; - rdfs:label "field label"@en . - - -### https://graphofliberty.org/2026/04/ont/fieldName -:fieldName rdf:type owl:DatatypeProperty ; - rdfs:domain :IndexDocumentField ; - rdfs:range xsd:string ; - rdfs:comment "The name of the field, as defined in the full-text search document schema."@en ; - rdfs:label "field name"@en . - - ### https://graphofliberty.org/2026/04/ont/readOnly :readOnly rdf:type owl:DatatypeProperty ; rdfs:comment "Indicates that the property or class is read only (server managed) and should not be made editable in user-facing applications."@en ; @@ -165,47 +108,12 @@ xsd:unsignedShort rdf:type rdfs:Datatype ; # Classes ################################################################# -### https://graphofliberty.org/2026/04/ont/AudioBook -:AudioBook rdf:type owl:Class ; - rdfs:subClassOf :Entity . - - ### https://graphofliberty.org/2026/04/ont/DerivationMethodology :DerivationMethodology rdf:type owl:Class ; rdfs:comment "The methodology used to derive content. Individuals of this class should provide enough information to replicate the process by which the content was generated. For example, if Whisper was used derive captions for an audio book, the precise model used (e.g. large v3), a link to the source code (e.g. HuggingFace) and/or a paper should be provided."@en ; rdfs:label "Derivation Methodology"@en . -### https://graphofliberty.org/2026/04/ont/Entity -:Entity rdf:type owl:Class ; - rdfs:comment "An Entity is a first-class citizen of the Graph of Liberty catalog. It is the class of all cultural artifacts which are to be preserved."@en ; - rdfs:label "Graph of Liberty Entity"@en . - - -### https://graphofliberty.org/2026/04/ont/IndexDocument -:IndexDocument rdf:type owl:Class ; - rdfs:comment "A document which is intended to be passed verbatim to a full-text search engine for indexing."@en ; - rdfs:label "Index Document"@en . - - -### https://graphofliberty.org/2026/04/ont/IndexDocumentField -:IndexDocumentField rdf:type owl:Class ; - rdfs:comment "Describes a single field present within a document that is indexed in a full-text search database."@en ; - rdfs:label "Index Document Field"@en . - - -### https://graphofliberty.org/2026/04/ont/SearchableClass -:SearchableClass rdf:type owl:Class ; - rdfs:comment "The class of classes which are to be indexed in the full-text search database."@en ; - rdfs:label "Searchable Class"@en . - - -### https://graphofliberty.org/2026/04/ont/Transcription -:Transcription rdf:type owl:Class ; - rdfs:comment "A structured or unstructured textual file which contains a transcription of an audio file."@en ; - rdfs:label "Transcription"@en . - - ################################################################# # Individuals ################################################################# @@ -255,24 +163,6 @@ fedora:lastModifiedBy rdf:type owl:NamedIndividual ; :readOnly "true"^^xsd:boolean . -### http://rdaregistry.info/Elements/a/datatype/P50291 - rdf:type owl:NamedIndividual ; - :indexedByField :Surname . - - -### http://rdaregistry.info/Elements/a/datatype/P50292 - rdf:type owl:NamedIndividual ; - :indexedByField :GivenName . - - -### http://rdaregistry.info/Elements/c/C10004 -rdac:C10004 rdf:type owl:NamedIndividual , - :SearchableClass ; - :associatedProperty , - ; - :categoryId "3"^^xsd:nonNegativeInteger . - - ### http://www.loc.gov/premis/rdf/v1#hasMessageDigest premis:hasMessageDigest rdf:type owl:NamedIndividual ; :readOnly "true"^^xsd:boolean . @@ -293,52 +183,6 @@ premis3:size rdf:type owl:NamedIndividual ; :readOnly "true"^^xsd:boolean . -### http://www.w3.org/1999/02/22-rdf-syntax-ns#Property -rdf:Property rdf:type owl:NamedIndividual , - :SearchableClass ; - :associatedProperty rdfs:comment , - rdfs:label , - skos:definition ; - :categoryId "0"^^xsd:nonNegativeInteger . - - -### http://www.w3.org/2000/01/rdf-schema#Class -rdfs:Class rdf:type owl:NamedIndividual , - :SearchableClass ; - :associatedProperty rdfs:comment , - rdfs:label , - skos:definition ; - :categoryId "1"^^xsd:nonNegativeInteger . - - -### http://www.w3.org/2000/01/rdf-schema#comment -rdfs:comment rdf:type owl:NamedIndividual ; - :indexedByField :Definition . - - -### http://www.w3.org/2000/01/rdf-schema#label -rdfs:label rdf:type owl:NamedIndividual ; - :indexedByField :Label . - - -### http://www.w3.org/2004/02/skos/core#Concept -skos:Concept rdf:type owl:NamedIndividual , - :SearchableClass ; - :associatedProperty skos:definition , - skos:prefLabel ; - :categoryId "2"^^xsd:nonNegativeInteger . - - -### http://www.w3.org/2004/02/skos/core#definition -skos:definition rdf:type owl:NamedIndividual ; - :indexedByField :Definition . - - -### http://www.w3.org/2004/02/skos/core#prefLabel -skos:prefLabel rdf:type owl:NamedIndividual ; - :indexedByField :Label . - - ### http://www.w3.org/ns/ldp#BasicContainer ldp:BasicContainer rdf:type owl:NamedIndividual ; :readOnly "true"^^xsd:boolean . @@ -369,49 +213,4 @@ ldp:contains rdf:type owl:NamedIndividual ; :readOnly "true"^^xsd:boolean . -### https://graphofliberty.org/2026/04/ont/AudioBook -:AudioBook rdf:type owl:NamedIndividual , - :SearchableClass ; - :categoryId "4"^^xsd:nonNegativeInteger . - - -### https://graphofliberty.org/2026/04/ont/Definition -:Definition rdf:type owl:NamedIndividual , - :IndexDocumentField ; - :fieldLabel "Definition"@en ; - :fieldName "definition" ; - rdfs:label "Definition Field"@en . - - -### https://graphofliberty.org/2026/04/ont/GivenName -:GivenName rdf:type owl:NamedIndividual , - :IndexDocumentField ; - :fieldLabel "Given Name"@en ; - :fieldName "given_name" ; - rdfs:label "Given Name Field"@en . - - -### https://graphofliberty.org/2026/04/ont/Label -:Label rdf:type owl:NamedIndividual , - :IndexDocumentField ; - :fieldLabel "Label"@en ; - :fieldName "label" ; - rdfs:label "Label Field"@en . - - -### https://graphofliberty.org/2026/04/ont/Surname -:Surname rdf:type owl:NamedIndividual , - :IndexDocumentField ; - :fieldLabel "Surname"@en ; - :fieldName "surname" ; - rdfs:label "Surname Field"@en . - - -################################################################# -# Annotations -################################################################# - -:AudioBook rdfs:label "Audio Book"@en . - - ### Generated by the OWL API (version 4.5.29.2024-05-13T12:11:03Z) https://github.com/owlcs/owlapi diff --git a/publish/src/app.rs b/publish/src/app.rs index 13cd20e..3dfae43 100644 --- a/publish/src/app.rs +++ b/publish/src/app.rs @@ -733,10 +733,10 @@ impl Publisher { text_input("Query", &search_state.query).on_input(Message::QueryUpdated) .id("query"); - let entities = self.ontology + let entities = Vec::new(); /*self.ontology .searchable_classes(&*language::ENGLISH_OR_UNTAGGED) .into_iter() - .collect::>(); + .collect::>();*/ let type_selector = pick_list(search_state.entity_selection.as_ref(), entities, ToString::to_string) .on_select(|selection| Message::QueryTypeUpdated(selection)); diff --git a/publish/src/args.rs b/publish/src/args.rs index 05ebc80..510762c 100644 --- a/publish/src/args.rs +++ b/publish/src/args.rs @@ -1,5 +1,15 @@ +use std::path::PathBuf; use clap::{Args, Parser, Subcommand}; +#[derive(Args)] +pub(crate) struct QueryArgs { + #[arg(short, long, value_name = "DATASET PATH")] + pub(crate) dataset_path: PathBuf, + + #[arg(short, long, value_name = "QUERY PATH")] + pub(crate) query_path: PathBuf, +} + #[derive(Args)] pub(crate) struct SearchArgs { #[arg(short, long, value_name = "DOC TYPE")] @@ -11,6 +21,7 @@ pub(crate) struct SearchArgs { #[derive(Subcommand)] pub(crate) enum Command { + Query(QueryArgs), Search(SearchArgs), Reindex, } diff --git a/publish/src/main.rs b/publish/src/main.rs index 29470d1..634e329 100644 --- a/publish/src/main.rs +++ b/publish/src/main.rs @@ -7,18 +7,25 @@ mod navigator; mod theme; mod args; +use std::fs::File; use clap::Parser; use iced::futures::StreamExt; use ldp::middleware::BasicAuthMiddleware; use ldp::reqwest::{Client, Url}; use ldp::reqwest_middleware::ClientBuilder; use ldp::traverse::Traverse; +use oxigraph::io::{RdfFormat, RdfParser, RdfSerializer}; +use oxigraph::model::{Dataset, Graph, GraphName, GraphNameRef, NamedNode, Quad, Triple}; +use oxigraph::sparql::{QueryResults, SparqlEvaluator}; +use oxigraph::sparql::results::{QueryResultsFormat, QueryResultsSerializer}; +use oxigraph::store::Store; use tracing::{debug, debug_span, error, field}; use crate::app::Publisher; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::util::SubscriberInitExt; use tracing_subscriber::{EnvFilter, fmt}; use tracing_subscriber::fmt::format::FmtSpan; +use gl_graph::inference::InferenceEngine; use gl_search::{Document, Schema, SearchIndex}; use crate::args::{AppArgs, Command}; use crate::rdf::ontology::Ontology; @@ -31,10 +38,8 @@ fn main() -> color_eyre::Result<()> { .init(); color_eyre::install()?; - let ontology = Ontology::builder() - .with_path("/home/alex/.local/share/org.graphofliberty.desktop/ontology") - .build() - .expect("Failed to build ontology"); + let store = Store::open("/home/alex/.local/share/org.graphofliberty.desktop/ontology")?; + let inference_engine = InferenceEngine::new(store.clone()); let mut index = SearchIndex::builder() .with_path("/home/alex/.local/share/org.graphofliberty.desktop/index") @@ -43,11 +48,49 @@ fn main() -> color_eyre::Result<()> { let args = AppArgs::parse(); match args.command { - Some(Command::Search(args)) => { - for doc in index.query(args.discriminant, &args.query, Schema::all_fields(), 500000)? { - println!("{}", doc.to_json(Schema::schema())); + Some(Command::Query(args)) => { + let graph_name = GraphName::NamedNode(NamedNode::new_unchecked(format!("file://{}", args.dataset_path.to_string_lossy()))); + let mut dataset = RdfParser::from_format(RdfFormat::Turtle) + .with_default_graph(graph_name) + .for_reader(File::open(&args.dataset_path)?) + .filter_map(Result::ok) + .collect::(); + let inferences = inference_engine.run(&dataset)?; + dataset.extend(&inferences); + + let raw_query = String::from_utf8(std::fs::read(&args.query_path)?)?; + let mut query = SparqlEvaluator::new() + .parse_query(&raw_query)?; + query.dataset_mut().set_default_graph_as_union(); + + match query.on_queryable_dataset(&dataset).execute()? { + QueryResults::Graph(graph) => { + let mut serializer = RdfSerializer::from_format(RdfFormat::Turtle) + .for_writer(std::io::stdout()); + for triple in graph.filter_map(Result::ok) { + serializer.serialize_triple(triple.as_ref())?; + } + serializer.finish()?; + } + QueryResults::Solutions(solutions) => { + let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Json); + let mut writer = json_serializer.serialize_solutions_to_writer(std::io::stdout(), Vec::from_iter(solutions.variables().iter().cloned()))?; + for solution in solutions.filter_map(Result::ok) { + writer.serialize(&solution)?; + } + writer.finish()?; + } + QueryResults::Boolean(result) => { + let json_serializer = QueryResultsSerializer::from_format(QueryResultsFormat::Json); + json_serializer.serialize_boolean_to_writer(std::io::stdout(), result)?; + } } } + Some(Command::Search(args)) => { + /*for doc in index.query(args.discriminant, &args.query, Schema::all_fields(), 500000)? { + println!("{}", doc.to_json(Schema::schema())); + }*/ + } Some(Command::Reindex) => { let mut writer = index.writer()?; debug_span!("Clear Index").in_scope(|| { @@ -57,7 +100,6 @@ fn main() -> color_eyre::Result<()> { { let span = debug_span!("Index Schema", documents = field::Empty).entered(); - let store = ontology.store(); let count = gl_search::rdf::index_schema(store, &*gl_search::language::ENGLISH_OR_UNTAGGED, &writer)?; span.record("documents", count); } @@ -76,17 +118,25 @@ fn main() -> color_eyre::Result<()> { )) .build(); - let mut documents = 0usize; + let mut rdf_source_count = 0usize; + let mut dataset = Dataset::new(); let mut traversal = Traverse::new(http_client, starting_url, None); while let Some(result) = traversal.next().await { match result { - Ok(rdf_source) => { - documents += gl_search::rdf::index_entity(rdf_source.dataset(), &*gl_search::language::ENGLISH_OR_UNTAGGED, &writer)?; - } + Ok(rdf_source) => dataset.extend(rdf_source.dataset()), Err(err) => error!(?err), } + rdf_source_count += 1; } - debug!(documents, "Repository Traversal"); + + let inferences = inference_engine.run(&dataset).unwrap(); + dataset.extend(&inferences); + + let span = debug_span!("Index Repository", rdf_sources = field::Empty, documents = field::Empty).entered(); + let document_count = gl_search::rdf::index_entity(&dataset, &*gl_search::language::ENGLISH_OR_UNTAGGED, &writer)?; + span.record("rdf_sources", rdf_source_count); + span.record("documents", document_count); + Ok::<_, gl_search::SearchError>(writer) }); diff --git a/publish/src/rdf/mod.rs b/publish/src/rdf/mod.rs index 6adaefb..866727d 100644 --- a/publish/src/rdf/mod.rs +++ b/publish/src/rdf/mod.rs @@ -1,4 +1,3 @@ pub(crate) mod ontology; pub(crate) mod term_helper; -pub(crate) mod materialize; pub(crate) mod conversion; \ No newline at end of file diff --git a/publish/src/rdf/ontology.rs b/publish/src/rdf/ontology.rs index bac93fa..afe577a 100644 --- a/publish/src/rdf/ontology.rs +++ b/publish/src/rdf/ontology.rs @@ -1,5 +1,5 @@ use crate::error; -use crate::rdf::{conversion, materialize}; +use crate::rdf::conversion; use gl_graph::vocab::gl; use gl_search::language::LanguageCondition; use iced::futures::TryFutureExt; @@ -91,13 +91,6 @@ impl OntologyBuilder { .map(|(k, v)| (k.to_string(), v.to_string())) .collect::>(); - if self.materialize_inferences { - materialize::same_as(&mut store)?; - materialize::super_properties(&mut store)?; - materialize::super_classes(&mut store)?; - debug_span!("Optimize Ontology").in_scope(|| store.optimize())?; - } - 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) { @@ -297,25 +290,6 @@ WHERE {{ } } - pub fn searchable_classes(&self, language: &LanguageCondition) -> BTreeSet { - self.store.quads_for_pattern(None, Some(rdf::TYPE), Some(gl::SEARCHABLE_CLASS.into()), None) - .filter_map(Result::ok) - .filter_map(|quad| { - let label = self.store.quads_for_pattern(Some(quad.subject.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(); - if let Some(label) = label && let NamedOrBlankNode::NamedNode(subject) = quad.subject { - Some(LabeledIri { - iri: subject, - label, - }) - } else { None } - }).collect() - } - pub fn subclasses_of(&self, class: &NamedNode) -> BTreeSet { self.store.quads_for_pattern(None, Some(rdfs::SUB_CLASS_OF), Some(class.into()), None) .filter_map(Result::ok) diff --git a/search/src/lib.rs b/search/src/lib.rs index 82e1327..c3cee3e 100644 --- a/search/src/lib.rs +++ b/search/src/lib.rs @@ -41,7 +41,7 @@ impl DocType { } } - pub fn from_named_node<'a>(node: impl Into>) -> Option { + pub fn try_from_named_node<'a>(node: impl Into>) -> Option { let node = node.into(); match node { vocab::rdf::PROPERTY => Some(DocType::RdfProperty), diff --git a/search/src/rdf.rs b/search/src/rdf.rs index 557c5ab..985b699 100644 --- a/search/src/rdf.rs +++ b/search/src/rdf.rs @@ -5,7 +5,6 @@ use oxigraph::sparql::{QueryResults, SparqlEvaluator}; use oxigraph::store::Store; use tantivy::IndexWriter; use tantivy::schema::{Field, OwnedValue}; -use tracing::debug; use gl_graph::{vocab, CurieHelper}; use crate::DocType; use crate::language::LanguageCondition; @@ -68,7 +67,7 @@ pub fn index_entity(dataset: &Dataset, language: &LanguageCondition, writer: &In let mut counter = 0usize; for (subject, types) in subject_type_map { for type_ in types { - if let Some(doc_type) = term_ref_as_named_node(type_).and_then(DocType::from_named_node) { + if let Some(doc_type) = term_ref_as_named_node(type_).and_then(DocType::try_from_named_node) { let mut doc = match doc_type { DocType::Person => index_person(dataset, subject, language), DocType::CorporateBody => index_corporate_body(dataset, subject, language), @@ -170,7 +169,7 @@ pub fn index_schema(store: Store, language: &LanguageCondition, writer: &IndexWr let discriminant = solution.get("class") .and_then(term_to_named_node) - .and_then(DocType::from_named_node) + .and_then(DocType::try_from_named_node) .map(|doc_type| doc_type as u64) .map(OwnedValue::U64) .unwrap_or(OwnedValue::Null);