fix: total ordering for Priority
This commit is contained in:
parent
8cc0d28402
commit
476a8fb445
3 changed files with 42 additions and 39 deletions
|
|
@ -572,8 +572,8 @@ impl<O: futures::Sink<Response> + Unpin> Service<O> {
|
||||||
Priority {
|
Priority {
|
||||||
plugin_priority: plg.config.query.priority,
|
plugin_priority: plg.config.query.priority,
|
||||||
match_score: calculate_weight(sr, query),
|
match_score: calculate_weight(sr, query),
|
||||||
recent_use_index: ex.as_ref().map(|s| recent.get_recent(s)).unwrap_or(0),
|
recent_score: ex.as_ref().map(|s| recent.get_recent(s)).unwrap_or(0.),
|
||||||
use_freq: ex.as_ref().map(|s| recent.get_freq(s)).unwrap_or(0),
|
freq_score: ex.as_ref().map(|s| recent.get_freq(s)).unwrap_or(0.),
|
||||||
execlen: sr.name.len(),
|
execlen: sr.name.len(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -6,41 +6,30 @@ use crate::PluginPriority;
|
||||||
pub struct Priority {
|
pub struct Priority {
|
||||||
pub plugin_priority: PluginPriority,
|
pub plugin_priority: PluginPriority,
|
||||||
pub match_score: f64,
|
pub match_score: f64,
|
||||||
pub recent_use_index: usize,
|
pub recent_score: f64,
|
||||||
pub use_freq: usize,
|
pub freq_score: f64,
|
||||||
pub execlen: usize,
|
pub execlen: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signum(val: i32) -> f64 {
|
fn falloff(x: f64) -> f64 {
|
||||||
if val > 0 {
|
x.clamp(0., 1.).powi(3)
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
if val < 0 {
|
|
||||||
return -1.0;
|
|
||||||
}
|
|
||||||
0.0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Priority {
|
impl Priority {
|
||||||
fn compute_value(&self, other: &Self) -> f64 {
|
fn compute_value(&self) -> f64 {
|
||||||
// increases compared jw-score if this search result
|
let score = if self.match_score > 1. {
|
||||||
// was activated more frequent or recent by constant values
|
self.match_score + 0.1
|
||||||
let score = self.match_score
|
} else {
|
||||||
+ 0.06 * signum(self.recent_use_index as i32 - other.recent_use_index as i32)
|
self.match_score
|
||||||
+ 0.03 * signum(self.use_freq as i32 - other.use_freq as i32);
|
};
|
||||||
// score cannot surpass exact matches
|
score + 0.06 * falloff(self.recent_score) + 0.03 * falloff(self.freq_score)
|
||||||
if self.match_score < 1.0 {
|
|
||||||
return score.min(0.99);
|
|
||||||
}
|
|
||||||
|
|
||||||
score
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Priority {
|
impl PartialEq for Priority {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.plugin_priority == other.plugin_priority
|
self.plugin_priority == other.plugin_priority
|
||||||
&& self.compute_value(other) == other.match_score
|
&& self.compute_value() == other.compute_value()
|
||||||
&& self.execlen == other.execlen
|
&& self.execlen == other.execlen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -48,20 +37,19 @@ impl PartialEq for Priority {
|
||||||
impl Eq for Priority {}
|
impl Eq for Priority {}
|
||||||
|
|
||||||
impl PartialOrd for Priority {
|
impl PartialOrd for Priority {
|
||||||
#[allow(clippy::non_canonical_partial_ord_impl)]
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
// todo: what is going on here ?
|
Some(self.cmp(other))
|
||||||
(
|
|
||||||
other.plugin_priority,
|
|
||||||
self.compute_value(other),
|
|
||||||
self.execlen,
|
|
||||||
)
|
|
||||||
.partial_cmp(&(self.plugin_priority, other.match_score, other.execlen))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ord for Priority {
|
impl Ord for Priority {
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
self.partial_cmp(other).unwrap()
|
match other.plugin_priority.cmp(&self.plugin_priority) {
|
||||||
|
Ordering::Equal => match self.compute_value().total_cmp(&other.compute_value()) {
|
||||||
|
Ordering::Equal => self.execlen.cmp(&other.execlen),
|
||||||
|
p => p,
|
||||||
|
},
|
||||||
|
p => p,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,10 @@ const LONGTERM_CAP: usize = 100;
|
||||||
pub struct RecentUseStorage {
|
pub struct RecentUseStorage {
|
||||||
long_term: HashMap<u64, usize>,
|
long_term: HashMap<u64, usize>,
|
||||||
short_term: HashMap<u64, usize>,
|
short_term: HashMap<u64, usize>,
|
||||||
|
/// used for normalizing individual scores
|
||||||
|
max_long_term: usize,
|
||||||
|
/// used for normalizing individual scores
|
||||||
|
max_short_term: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_key<K: Hash>(key: K) -> u64 {
|
fn hash_key<K: Hash>(key: K) -> u64 {
|
||||||
|
|
@ -26,8 +30,11 @@ fn hash_key<K: Hash>(key: K) -> u64 {
|
||||||
impl RecentUseStorage {
|
impl RecentUseStorage {
|
||||||
pub fn add<K: Hash>(&mut self, exec: &K) {
|
pub fn add<K: Hash>(&mut self, exec: &K) {
|
||||||
let key = hash_key(exec);
|
let key = hash_key(exec);
|
||||||
*self.long_term.entry(key).or_insert(0) += 1;
|
let entry = self.long_term.entry(key).or_insert(0);
|
||||||
|
*entry += 1;
|
||||||
|
self.max_long_term = self.max_long_term.max(*entry);
|
||||||
let short_term_idx = self.short_term.values().max().unwrap_or(&0) + 1;
|
let short_term_idx = self.short_term.values().max().unwrap_or(&0) + 1;
|
||||||
|
self.max_short_term = self.max_short_term.max(short_term_idx);
|
||||||
self.short_term.insert(key, short_term_idx);
|
self.short_term.insert(key, short_term_idx);
|
||||||
self.trim();
|
self.trim();
|
||||||
}
|
}
|
||||||
|
|
@ -39,6 +46,8 @@ impl RecentUseStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
while self.long_term.values().sum::<usize>() > LONGTERM_CAP {
|
while self.long_term.values().sum::<usize>() > LONGTERM_CAP {
|
||||||
|
self.max_long_term /= 2;
|
||||||
|
|
||||||
let mut delete_keys = Vec::new();
|
let mut delete_keys = Vec::new();
|
||||||
for (k, v) in &mut self.long_term {
|
for (k, v) in &mut self.long_term {
|
||||||
*v /= 2;
|
*v /= 2;
|
||||||
|
|
@ -52,12 +61,14 @@ impl RecentUseStorage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_recent<K: Hash>(&self, exec: &K) -> usize {
|
pub fn get_recent<K: Hash>(&self, exec: &K) -> f64 {
|
||||||
self.short_term.get(&hash_key(exec)).copied().unwrap_or(0)
|
self.short_term.get(&hash_key(exec)).copied().unwrap_or(0) as f64
|
||||||
|
/ (self.max_short_term.max(1) as f64)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_freq<K: Hash>(&self, exec: &K) -> usize {
|
pub fn get_freq<K: Hash>(&self, exec: &K) -> f64 {
|
||||||
self.long_term.get(&hash_key(exec)).copied().unwrap_or(0)
|
self.long_term.get(&hash_key(exec)).copied().unwrap_or(0) as f64
|
||||||
|
/ (self.max_long_term.max(1) as f64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,9 +91,13 @@ impl<'de> Deserialize<'de> for RecentUseStorage {
|
||||||
type SerType = (HashMap<u64, usize>, Vec<u64>);
|
type SerType = (HashMap<u64, usize>, Vec<u64>);
|
||||||
let (long_term, stv) = SerType::deserialize(deserializer)?;
|
let (long_term, stv) = SerType::deserialize(deserializer)?;
|
||||||
let short_term: HashMap<_, _> = stv.into_iter().enumerate().map(|(v, k)| (k, v)).collect();
|
let short_term: HashMap<_, _> = stv.into_iter().enumerate().map(|(v, k)| (k, v)).collect();
|
||||||
|
let max_long_term = long_term.values().max().copied().unwrap_or(1);
|
||||||
|
let max_short_term = short_term.values().max().copied().unwrap_or(1);
|
||||||
Ok(RecentUseStorage {
|
Ok(RecentUseStorage {
|
||||||
long_term,
|
long_term,
|
||||||
short_term,
|
short_term,
|
||||||
|
max_long_term,
|
||||||
|
max_short_term,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue