|
1 | | -use std::collections::HashMap; |
| 1 | +use std::collections::{HashMap, HashSet}; |
2 | 2 | use std::num::NonZeroUsize; |
3 | 3 |
|
| 4 | +use itertools::Itertools; |
4 | 5 | use tracing::instrument; |
5 | 6 |
|
6 | 7 | use crate::network::state::HistoricMessageTargetId; |
@@ -185,32 +186,47 @@ impl<NetworkPolicy: policy::PolicyService> HistoryService |
185 | 186 | before_ts: Option<i64>, |
186 | 187 | limit: Option<NonZeroUsize>, |
187 | 188 | ) -> HashMap<TargetId, i64> { |
| 189 | + // Targets that have an entry whose timestamp is after after_ts |
| 190 | + let mut excluded_targets = HashSet::new(); |
| 191 | + |
188 | 192 | let mut found_targets = HashMap::new(); |
189 | 193 |
|
190 | | - for entry in self.node.history().entries_for_user(user) { |
191 | | - if matches!(before_ts, Some(ts) if entry.timestamp <= ts) { |
192 | | - // Skip over until we hit the timestamp window we're interested in |
| 194 | + for entry in self.node.history().entries_for_user_reverse(user) { |
| 195 | + if matches!(after_ts, Some(ts) if entry.timestamp >= ts) { |
| 196 | + // Skip over until we hit the timestamp window we're interested in; |
| 197 | + // and exclude all targets we find in that window, because they have |
| 198 | + // an entry that is more recent than requested |
| 199 | + if let Some(target_id) = target_id_for_entry(user, entry) { |
| 200 | + excluded_targets.insert(target_id); |
| 201 | + } |
193 | 202 | continue; |
194 | 203 | } |
195 | | - if matches!(after_ts, Some(ts) if entry.timestamp >= ts) { |
196 | | - // We're iterating forwards through time; if we hit this then we've |
| 204 | + if matches!(before_ts, Some(ts) if entry.timestamp <= ts) { |
| 205 | + // We're iterating backwards through time; if we hit this then we've |
197 | 206 | // passed the requested window and should stop |
198 | 207 | break; |
199 | 208 | } |
200 | 209 |
|
201 | 210 | if let Some(target_id) = target_id_for_entry(user, entry) { |
202 | | - found_targets.insert(target_id, entry.timestamp); |
203 | | - } |
204 | | - |
205 | | - // If this pushes us past the requested limit, stop |
206 | | - if matches!(limit, Some(limit) if usize::from(limit) <= found_targets.len()) { |
207 | | - break; |
| 211 | + if !excluded_targets.contains(&target_id) { |
| 212 | + // if the target is already listed, keep the existing timestamp |
| 213 | + // (which is newer than the one of the current entry) |
| 214 | + found_targets.entry(target_id).or_insert(entry.timestamp); |
| 215 | + } |
208 | 216 | } |
209 | 217 | } |
210 | 218 |
|
211 | 219 | tracing::trace!("list_targets local response: {found_targets:?}"); |
212 | 220 |
|
213 | | - found_targets |
| 221 | + if let Some(limit) = limit { |
| 222 | + // Sort by ascending timestamp, and keep the smallest ones (ie. oldest) |
| 223 | + found_targets |
| 224 | + .into_iter() |
| 225 | + .k_smallest_relaxed_by_key(usize::from(limit), |(_target_id, ts)| *ts) |
| 226 | + .collect() |
| 227 | + } else { |
| 228 | + found_targets |
| 229 | + } |
214 | 230 | } |
215 | 231 |
|
216 | 232 | #[instrument(skip(self))] |
|
0 commit comments