1use crate::hierarchy::TaxonomyHierarchy;
4use ontologos_core::{EntityId, Ontology};
5
6use crate::{Error, Result};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum QueryAtom {
11 Type {
13 var: String,
15 class: String,
17 },
18 Subsumed {
20 var: String,
22 superclass: String,
24 },
25}
26
27#[derive(Debug, Clone, Default)]
29pub struct ConjunctiveQuery {
30 pub atoms: Vec<QueryAtom>,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct QueryAnswer {
37 pub bindings: Vec<(String, EntityId)>,
39}
40
41pub fn evaluate(
43 engine: &TaxonomyHierarchy<'_>,
44 ontology: &Ontology,
45 query: &ConjunctiveQuery,
46) -> Result<Vec<QueryAnswer>> {
47 if query.atoms.len() > 1 {
48 return Err(crate::Error::Parse(
49 "conjunctive queries with more than one atom are not supported yet".into(),
50 ));
51 }
52 if query.atoms.is_empty() {
53 return Ok(Vec::new());
54 }
55 let atom = &query.atoms[0];
56 match atom {
57 QueryAtom::Type { var, class } => {
58 let class_id = ontology
59 .lookup_entity(class)
60 .ok_or_else(|| Error::UnknownClass(class.clone()))?;
61 let subs = engine.direct_subclasses(class_id)?;
62 let mut answers = Vec::new();
63 for sub in subs {
64 answers.push(QueryAnswer {
65 bindings: vec![(var.clone(), sub)],
66 });
67 }
68 Ok(answers)
69 }
70 QueryAtom::Subsumed { var, superclass } => {
71 let sup_id = ontology
72 .lookup_entity(superclass)
73 .ok_or_else(|| Error::UnknownClass(superclass.clone()))?;
74 let subs = engine.direct_subclasses(sup_id)?;
75 let mut answers = Vec::new();
76 for sub in subs {
77 answers.push(QueryAnswer {
78 bindings: vec![(var.clone(), sub)],
79 });
80 }
81 Ok(answers)
82 }
83 }
84}