Final completeness by time window: ranges keep their posting span, release_final() over blocks of done ranges
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3d24db5565
commit
ce43099188
12 files changed
+259
-9
No files matched your search
@@ -78,6 +78,9 @@ pub struct Batch {
|
||||
pub from: i64,
|
||||
pub next: i64,
|
||||
pub headers: u64,
|
||||
/// postedLow / postedHigh: the batch's posting times without the earliest and latest 1% (postedSpan).
|
||||
pub posted_low: Option<i64>,
|
||||
pub posted_high: Option<i64>,
|
||||
pub releases: Vec<ReleaseChunk>,
|
||||
pub samples: Vec<(ReleaseChunk, &'static str, String)>,
|
||||
pub candidates: Vec<Candidate>,
|
||||
@@ -277,6 +280,16 @@ fn first_track(text: &str) -> Option<usize> {
|
||||
None
|
||||
}
|
||||
|
||||
/// postedSpan: posting times without the earliest and latest 1% (floor(n / 100) at each end).
|
||||
pub fn posted_span(mut dates: Vec<i64>) -> (Option<i64>, Option<i64>) {
|
||||
if dates.is_empty() {
|
||||
return (None, None);
|
||||
}
|
||||
dates.sort_unstable();
|
||||
let k = dates.len() / 100;
|
||||
(Some(dates[k]), Some(dates[dates.len() - 1 - k]))
|
||||
}
|
||||
|
||||
/// nameBesideQuotes (lib/headers/subject.ts): a file name elsewhere when the quoted text has no extension.
|
||||
pub fn name_beside_quotes(quoted: &str, before_counter: &str) -> Option<String> {
|
||||
if has_extension(quoted) {
|
||||
@@ -633,6 +646,8 @@ pub struct RangeIndexer {
|
||||
count_order: Vec<(&'static str, String)>,
|
||||
completed: FxHashMap<String, Vec<Vec<Seg>>>,
|
||||
completed_order: Vec<String>,
|
||||
/// Posting times of this batch's segments (posted_span at take).
|
||||
dates: Vec<i64>,
|
||||
}
|
||||
|
||||
impl RangeIndexer {
|
||||
@@ -653,6 +668,7 @@ impl RangeIndexer {
|
||||
count_order: Vec::new(),
|
||||
completed: FxHashMap::default(),
|
||||
completed_order: Vec::new(),
|
||||
dates: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,6 +699,7 @@ impl RangeIndexer {
|
||||
});
|
||||
}
|
||||
self.batch.headers += headers;
|
||||
self.dates.extend(segments.iter().filter_map(|s| s.posted_at));
|
||||
for owner in crossposts {
|
||||
self.count("crosspost", owner, 1, 0);
|
||||
}
|
||||
@@ -725,6 +742,7 @@ impl RangeIndexer {
|
||||
self.scanner.files.clear();
|
||||
let next = self.batch.next;
|
||||
let mut out = std::mem::replace(&mut self.batch, Batch { from: next, next, ..Default::default() });
|
||||
(out.posted_low, out.posted_high) = posted_span(std::mem::take(&mut self.dates));
|
||||
for id in self.count_order.drain(..) {
|
||||
let (articles, files) = self.counts.remove(&id).unwrap();
|
||||
out.counts.push((id.0, id.1, articles, files));
|
||||
@@ -1015,3 +1033,18 @@ mod beside_quotes_tests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod posted_span_tests {
|
||||
use super::posted_span;
|
||||
|
||||
#[test]
|
||||
fn without_the_earliest_and_latest_percent() {
|
||||
assert_eq!(posted_span(vec![]), (None, None));
|
||||
let mut dates: Vec<i64> = (0..200).map(|i| 1_000_000 + i).collect();
|
||||
dates.push(5);
|
||||
dates.push(9_999_999_999);
|
||||
// 202 dates: 2 trimmed at each end, so the two wild ones go (postedSpan).
|
||||
assert_eq!(posted_span(dates), (Some(1_000_001), Some(1_000_198)));
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -96,7 +96,11 @@ fn dump_batch(batch: &Batch, out: &mut impl Write) {
|
||||
out.write_all(line.as_bytes()).unwrap();
|
||||
line.clear();
|
||||
};
|
||||
line.push_str(&format!("{{\"t\":\"b\",\"from\":{},\"next\":{},\"headers\":{}}}", batch.from, batch.next, batch.headers));
|
||||
let opt = |t: Option<i64>| t.map_or("null".to_string(), |t| t.to_string());
|
||||
line.push_str(&format!(
|
||||
"{{\"t\":\"b\",\"from\":{},\"next\":{},\"headers\":{},\"postedLow\":{},\"postedHigh\":{}}}",
|
||||
batch.from, batch.next, batch.headers, opt(batch.posted_low), opt(batch.posted_high)
|
||||
));
|
||||
emit(&mut line);
|
||||
let release = |r: &indexer::ReleaseChunk, kind: &str, line: &mut String| {
|
||||
line.push_str(&format!("{{\"t\":\"{kind}\",\"upload\":{},\"key\":", r.upload));
|
||||
|
||||
@@ -74,10 +74,13 @@ struct WireFragment<'a> {
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WireBatch<'a> {
|
||||
from: i64,
|
||||
next: i64,
|
||||
headers: u64,
|
||||
posted_low: Option<i64>,
|
||||
posted_high: Option<i64>,
|
||||
releases: Vec<WireRelease<'a>>,
|
||||
samples: Vec<WireRelease<'a>>,
|
||||
candidates: Vec<WireCandidate<'a>>,
|
||||
@@ -134,6 +137,8 @@ pub fn to_json(batch: &Batch) -> Vec<u8> {
|
||||
from: batch.from,
|
||||
next: batch.next,
|
||||
headers: batch.headers,
|
||||
posted_low: batch.posted_low,
|
||||
posted_high: batch.posted_high,
|
||||
releases: batch.releases.iter().map(|r| release(r, None)).collect(),
|
||||
samples: batch.samples.iter().map(|(r, rule, key)| release(r, Some((rule, key.as_str())))).collect(),
|
||||
candidates: batch.candidates.iter().map(candidate).collect(),
|
||||
|
||||
Reference in new issue
Block a user