diff --git a/CHANGELOG.md b/CHANGELOG.md index ba786c2f..69f09f2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If - Default calendars and address books are not subscribed by default. - Unchanged immutable `id` property is rejected on `/set`. - `filter: null` rejected as `notRequest` on `/query` and `/queryChanges`. + - `Email/query` total miscount when `collapseThreads` is enabled. - OIDC: Add default domain name to groups that are not email addresses. - RocksDB: Enable blob garbage collection to reclaim disk space from deleted blobs. diff --git a/crates/jmap/src/email/query.rs b/crates/jmap/src/email/query.rs index f9de329c..a5cedbb1 100644 --- a/crates/jmap/src/email/query.rs +++ b/crates/jmap/src/email/query.rs @@ -349,15 +349,30 @@ impl EmailQuery for Server { ) .await?; + let collapse_threads = request.arguments.collapse_threads.unwrap_or(false); + let total_results = if collapse_threads { + let mut seen_thread_ids = AHashSet::new(); + results + .iter() + .filter_map(|document_id| { + cached_messages + .email_by_id(document_id) + .map(|email| email.thread_id) + }) + .filter(|thread_id| seen_thread_ids.insert(*thread_id)) + .count() + } else { + results.len() + }; + let mut response = QueryResponseBuilder::new( - results.len(), + total_results, self.core.jmap.query_max_results, cached_messages.get_state(false), &request, ); if !results.is_empty() { - let collapse_threads = request.arguments.collapse_threads.unwrap_or(false); let mut seen_thread_ids = AHashSet::new(); for document_id in results {