use crate::search::conf::SuggestionItem;
use crate::search::tokenizer::levenshtein_distance;
use rapidhash::RapidHashMap;
use std::cmp::Ordering;
#[derive(Debug, Clone, Default)]
pub struct SuggestionDict {
pub entries: RapidHashMap<String, (f64, Option<String>)>,
}
impl SuggestionDict {
pub fn new() -> Self {
Self::default()
}
pub fn sug_add(
&mut self,
string: &str,
score: f64,
incr: bool,
payload: Option<String>,
) -> usize {
let entry = self
.entries
.entry(string.to_string())
.or_insert((0.0, None));
if incr {
entry.0 += score;
} else {
entry.0 = score;
}
if payload.is_some() {
entry.1 = payload;
}
self.entries.len()
}
pub fn sug_get(
&self,
prefix: &str,
fuzzy: bool,
withscores: bool,
withpayloads: bool,
max: Option<usize>,
) -> Vec<SuggestionItem> {
let limit = max.unwrap_or(10);
let prefix_lower = prefix.to_lowercase();
let mut matched: Vec<SuggestionItem> = self
.entries
.iter()
.filter(|(s, _)| {
let s_lower = s.to_lowercase();
if s_lower.starts_with(&prefix_lower) {
true
} else if fuzzy {
levenshtein_distance(&s_lower, &prefix_lower) <= 1
} else {
false
}
})
.map(|(s, (score, payload))| SuggestionItem {
string: s.clone(),
score: if withscores { *score } else { 0.0 },
payload: if withpayloads { payload.clone() } else { None },
})
.collect();
if matched.len() > limit {
matched.select_nth_unstable_by(limit, |a, b| {
b.score
.partial_cmp(&a.score)
.unwrap_or(Ordering::Equal)
.then_with(|| a.string.cmp(&b.string))
});
matched.truncate(limit);
}
matched.sort_by(|a, b| {
b.score
.partial_cmp(&a.score)
.unwrap_or(Ordering::Equal)
.then_with(|| a.string.cmp(&b.string))
});
matched
}
pub fn sug_del(&mut self, string: &str) -> bool {
self.entries.remove(string).is_some()
}
pub fn sug_len(&self) -> usize {
self.entries.len()
}
}