use crate::explain_digest::predicate_summary;
use crate::memory::forget::predicate_fields;
use crate::{GraphDb, PredicateSummary, RuleDef, SuggestConfig, SUGGEST_DEFAULT_SEED};
use core_storage::fs::Fs;
use serde_json::{json, Value as Js};
pub const BOOKKEEPING_FIELDS: [&str; 8] = [
"ns",
"kind",
"ts",
"source",
"provisional",
"id",
"aliases",
"alias_keys",
];
#[must_use]
pub fn round_to(x: f64, places: i32) -> f64 {
let scale = 10f64.powi(places);
(x * scale).round() / scale
}
#[must_use]
pub fn create_rule_args(def: &RuleDef) -> Js {
let mut v = serde_json::to_value(def).unwrap_or(Js::Null);
if let Some(obj) = v.as_object_mut() {
obj.retain(|_, x| !x.is_null());
if obj.get("approximate") == Some(&Js::Bool(false)) {
obj.remove("approximate");
}
obj.entry("weight_prop").or_insert_with(|| json!("weight"));
}
v
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct Suggestion {
pub name: String,
pub src_label: String,
pub dst_label: String,
pub edge_type: String,
pub predicate: String,
pub est_edges: u64,
pub examples: Vec<(String, String, f64)>,
pub rationale: String,
pub create_rule_args: Js,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct FilteredSuggestions {
pub suggestions: Vec<Suggestion>,
pub total: usize,
pub bookkeeping_hidden: usize,
pub truncated: bool,
}
pub fn filtered_suggestions<F: Fs>(db: &GraphDb<F>) -> FilteredSuggestions {
let report = db.suggest_rules_with_config(&SuggestConfig::default(), SUGGEST_DEFAULT_SEED);
let mut kept: Vec<Suggestion> = Vec::new();
let mut hidden = 0usize;
for s in report.suggestions {
let summary = PredicateSummary::from(&s.def.predicate);
if predicate_fields(&summary)
.iter()
.any(|f| BOOKKEEPING_FIELDS.contains(&f.as_str()))
{
hidden += 1;
continue;
}
kept.push(Suggestion {
create_rule_args: create_rule_args(&s.def),
predicate: predicate_summary(&summary),
name: s.def.name,
src_label: s.def.src_label,
dst_label: s.def.dst_label,
edge_type: s.def.edge_type,
est_edges: s.est_edges,
examples: s
.examples
.into_iter()
.map(|(a, b, score)| (a, b, round_to(score, 2)))
.collect(),
rationale: s.rationale,
});
}
FilteredSuggestions {
total: kept.len(),
suggestions: kept,
bookkeeping_hidden: hidden,
truncated: report.truncated,
}
}