diff --git a/Cargo.lock b/Cargo.lock index 1816c2e..67c3735 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3113,7 +3113,6 @@ dependencies = [ "mailparse", "memmap", "notmuch", - "rayon", "rocket 0.5.0", "rocket_contrib", "rocket_cors", diff --git a/server/Cargo.toml b/server/Cargo.toml index 50a5be3..6b0eb5d 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -20,7 +20,6 @@ urlencoding = "2.1.3" async-graphql = { version = "6.0.11", features = ["log"] } async-graphql-rocket = "6.0.11" rocket_cors = "0.6.0" -rayon = "1.8.0" memmap = "0.7.0" mailparse = "0.14.0" ammonia = "3.3.0" diff --git a/server/src/graphql.rs b/server/src/graphql.rs index d63d527..a17e719 100644 --- a/server/src/graphql.rs +++ b/server/src/graphql.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashMap, fs::File, hash::{DefaultHasher, Hash, Hasher}, }; @@ -12,7 +13,7 @@ use log::{error, info, warn}; use mailparse::{parse_mail, MailHeaderMap, ParsedMail}; use memmap::MmapOptions; use notmuch::Notmuch; -use rayon::prelude::*; +use rocket::time::Instant; pub struct QueryRoot; @@ -214,15 +215,32 @@ impl QueryRoot { async fn tags<'ctx>(&self, ctx: &Context<'ctx>) -> FieldResult> { let nm = ctx.data_unchecked::(); - Ok(nm + let now = Instant::now(); + let needs_unread = ctx.look_ahead().field("unread").exists(); + let unread_msg_cnt: HashMap = if needs_unread { + // 10000 is an arbitrary number, if there's more than 10k unread messages, we'll + // get an inaccurate count. + nm.search("is:unread", 0, 10000)? + .0 + .iter() + .fold(HashMap::new(), |mut m, ts| { + ts.tags.iter().for_each(|t| { + m.entry(t.clone()).and_modify(|c| *c += 1).or_insert(1); + }); + m + }) + } else { + HashMap::new() + }; + let tags = nm .tags()? - .into_par_iter() + .into_iter() .map(|tag| { let mut hasher = DefaultHasher::new(); tag.hash(&mut hasher); let hex = format!("#{:06x}", hasher.finish() % (1 << 24)); - let unread = if ctx.look_ahead().field("unread").exists() { - nm.count(&format!("tag:{tag} is:unread")).unwrap_or(0) + let unread = if needs_unread { + *unread_msg_cnt.get(&tag).unwrap_or(&0) } else { 0 }; @@ -233,7 +251,9 @@ impl QueryRoot { unread, } }) - .collect()) + .collect(); + info!("Fetching tags took {}", now.elapsed()); + Ok(tags) } async fn thread<'ctx>(&self, ctx: &Context<'ctx>, thread_id: String) -> Result { // TODO(wathiede): normalize all email addresses through an address book with preferred