use crate::core::db::{Database, DbError};
use super::FactIssue;
const INCOMPATIBLE_PREDICATES: &[(&str, &str)] = &[
("husband_of", "brother_of"),
("husband_of", "father_of"),
("husband_of", "son_of"),
("wife_of", "sister_of"),
("wife_of", "mother_of"),
("wife_of", "daughter_of"),
("mother_of", "wife_of"),
("father_of", "husband_of"),
("employee_of", "founder_of"),
("employee_of", "owner_of"),
("reports_to", "manages"),
("subordinate_of", "manager_of"),
];
pub fn are_incompatible(p1: &str, p2: &str) -> bool {
INCOMPATIBLE_PREDICATES
.iter()
.any(|(a, b)| (a == &p1 && b == &p2) || (a == &p2 && b == &p1))
}
pub fn detect_relation_contradictions(
db: &Database,
text_triples: &[(String, String, String)],
) -> Result<Vec<FactIssue>, DbError> {
let mut issues = Vec::new();
for (subject, text_pred, object) in text_triples {
let active = db.query_triples(Some(subject), None, Some(object), true)?;
for kg in active {
if are_incompatible(text_pred, &kg.predicate) {
issues.push(FactIssue::RelationContradiction {
subject: subject.clone(),
text_claim: text_pred.clone(),
kg_fact: kg.predicate.clone(),
triple_id: kg.id.clone(),
source_drawer: kg.source_drawer.clone(),
});
}
}
}
Ok(issues)
}
pub fn detect_stale_facts(
db: &Database,
text_triples: &[(String, String, String)],
now_unix_secs: u64,
) -> Result<Vec<FactIssue>, DbError> {
let mut issues = Vec::new();
for (subject, predicate, object) in text_triples {
let rows = db.query_triples(Some(subject), Some(predicate), Some(object), false)?;
for kg in rows {
let Some(valid_to) = kg.valid_to.as_deref() else {
continue;
};
let Ok(expiry_secs) = valid_to.parse::<u64>() else {
continue;
};
if expiry_secs < now_unix_secs {
issues.push(FactIssue::StaleFact {
subject: kg.subject.clone(),
predicate: kg.predicate.clone(),
object: kg.object.clone(),
valid_to: valid_to.to_string(),
triple_id: kg.id.clone(),
});
}
}
}
Ok(issues)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_incompatible_predicates_symmetric() {
assert!(are_incompatible("husband_of", "brother_of"));
assert!(are_incompatible("brother_of", "husband_of"));
assert!(!are_incompatible("husband_of", "wife_of"));
}
#[test]
fn test_incompatible_unknown_predicates_returns_false() {
assert!(!are_incompatible("foo_of", "bar_of"));
}
}