atuin_history/sort.rs
1use atuin_client::history::History;
2
3type ScoredHistory = (f64, History);
4
5// Fuzzy search already comes sorted by minspan
6// This sorting should be applicable to all search modes, and solve the more "obvious" issues
7// first.
8// Later on, we can pass in context and do some boosts there too.
9pub fn sort(query: &str, input: Vec<History>) -> Vec<History> {
10 // This can totally be extended. We need to be _careful_ that it's not slow.
11 // We also need to balance sorting db-side with sorting here. SQLite can do a lot,
12 // but some things are just much easier/more doable in Rust.
13
14 let mut scored = input
15 .into_iter()
16 .map(|h| {
17 // If history is _prefixed_ with the query, score it more highly
18 let score = if h.command.starts_with(query) {
19 2.0
20 } else if h.command.contains(query) {
21 1.75
22 } else {
23 1.0
24 };
25
26 // calculate how long ago the history was, in seconds
27 let now = time::OffsetDateTime::now_utc().unix_timestamp();
28 let time = h.timestamp.unix_timestamp();
29 let diff = std::cmp::max(1, now - time); // no /0 please
30
31 // prefer newer history, but not hugely so as to offset the other scoring
32 // the numbers will get super small over time, but I don't want time to overpower other
33 // scoring
34 #[allow(clippy::cast_precision_loss)]
35 let time_score = 1.0 + (1.0 / diff as f64);
36 let score = score * time_score;
37
38 (score, h)
39 })
40 .collect::<Vec<ScoredHistory>>();
41
42 scored.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap().reverse());
43
44 // Remove the scores and return the history
45 scored.into_iter().map(|(_, h)| h).collect::<Vec<History>>()
46}