From b5514a6e24f8d35a993b7e109bab393e7812f241 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Thu, 26 Aug 2021 15:02:20 +0200 Subject: [PATCH] feat(service): Improved search result sorting via Jaro-Winkler --- plugins/src/desktop_entries/mod.rs | 23 +++++-------- service/src/lib.rs | 53 +++++++++++++++--------------- 2 files changed, 35 insertions(+), 41 deletions(-) diff --git a/plugins/src/desktop_entries/mod.rs b/plugins/src/desktop_entries/mod.rs index efc7ccb..01605b5 100644 --- a/plugins/src/desktop_entries/mod.rs +++ b/plugins/src/desktop_entries/mod.rs @@ -41,19 +41,14 @@ pub async fn main() { while let Some(result) = requests.next().await { match result { - Ok(request) => { - tracing::debug!("received request: {:?}", request); - match request { - Request::Activate(id) => app.activate(id).await, - Request::ActivateContext { id, context } => { - app.activate_context(id, context).await - } - Request::Context(id) => app.context(id).await, - Request::Search(query) => app.search(&query).await, - Request::Exit => break, - _ => (), - } - } + Ok(request) => match request { + Request::Activate(id) => app.activate(id).await, + Request::ActivateContext { id, context } => app.activate_context(id, context).await, + Request::Context(id) => app.context(id).await, + Request::Search(query) => app.search(&query).await, + Request::Exit => break, + _ => (), + }, Err(why) => { tracing::error!("malformed JSON request: {}", why); @@ -231,7 +226,7 @@ impl App { || query .split_ascii_whitespace() .any(|query| search_interest.contains(&*query)) - || strsim::damerau_levenshtein(&*query, &*search_interest) < 3; + || strsim::jaro_winkler(&*query, &*search_interest) > 0.6; if append { let desc_source = path_string(&entry.src); diff --git a/service/src/lib.rs b/service/src/lib.rs index c46874a..59698a4 100644 --- a/service/src/lib.rs +++ b/service/src/lib.rs @@ -422,8 +422,9 @@ impl + Unpin> Service { *no_sort = false; } else { active_search.sort_by(|a, b| { - fn calculate_weight(meta: &PluginSearchResult, query: &str) -> usize { - let mut weight = 0; + // Weight is calculated between 0.0 and 1.0, with higher values being most similar + fn calculate_weight(meta: &PluginSearchResult, query: &str) -> f64 { + let mut weight: f64 = 0.0; let name = meta.name.to_ascii_lowercase(); let description = meta.description.to_ascii_lowercase(); @@ -433,50 +434,48 @@ impl + Unpin> Service { .map(|exec| exec.to_ascii_lowercase()) .unwrap_or_default(); - if !name.starts_with(query) { - weight = 1; - - if !name.contains(query) { - weight = strsim::damerau_levenshtein(&name, query) - .min(strsim::damerau_levenshtein(&description, query)); - - if let Some(keywords) = meta.keywords.as_ref() { - for keyword in keywords.iter() { - let keyword = keyword.to_ascii_lowercase(); - weight = if keyword.starts_with(query) - || keyword.contains(query) - { - 1 - } else { - weight.min(strsim::damerau_levenshtein(query, &keyword) + 1) - } - } - } + for name in name.split_ascii_whitespace() { + if name.starts_with(query) { + return 1.0; } } if exec.contains(query) { - weight = if exec.starts_with(query) { - weight.min(2) + if exec.starts_with(query) { + return 1.0; } else { - weight.min(strsim::damerau_levenshtein(query, &exec)) + weight = strsim::jaro_winkler(query, &exec) - 0.1; } } weight + .max(strsim::jaro_winkler(&name, query)) + .max(strsim::jaro_winkler(&description, query) - 0.1) + .max(match meta.keywords.as_ref() { + Some(keywords) => keywords + .iter() + .flat_map(|word| word.split_ascii_whitespace()) + .fold(0.0, |acc, keyword| { + let keyword = keyword.to_ascii_lowercase(); + acc.max(strsim::jaro_winkler(query, &keyword) - 0.1) + }), + None => 0.0, + }) } let a_weight = calculate_weight(&a.1, query); let b_weight = calculate_weight(&b.1, query); - match a_weight.cmp(&b_weight) { - Ordering::Equal => { + match a_weight.partial_cmp(&b_weight) { + Some(Ordering::Equal) => { let a_len = a.1.name.len(); let b_len = b.1.name.len(); a_len.cmp(&b_len) } - other => other, + Some(Ordering::Less) => Ordering::Greater, + Some(Ordering::Greater) => Ordering::Less, + None => Ordering::Greater, } });