atagen

macltok

public
Star0
Watch0
c9effeac326a64a991e3de0049f2ffa1c7a2d18a
3
ssh://brewery@pls.lobotomise.me:33/atagen/macltok.git

Blame: src/search/index.rs

c9effeac326aatagen2026-10-25 10:32 UTC1
use std::{collections::HashMap, path::PathBuf, sync::Arc};
c9effeac326aatagen2026-10-25 10:32 UTC2
c9effeac326aatagen2026-10-25 10:32 UTC3
use sha2::{Digest, Sha256};
c9effeac326aatagen2026-10-25 10:32 UTC4
use tantivy::{
c9effeac326aatagen2026-10-25 10:32 UTC5
    DocAddress, DocId, DocSet, Index, IndexSettings, ReloadPolicy, Score, Searcher, SegmentReader,
c9effeac326aatagen2026-10-25 10:32 UTC6
    TantivyDocument, Term,
c9effeac326aatagen2026-10-25 10:32 UTC7
    collector::{Collector, SegmentCollector},
c9effeac326aatagen2026-10-25 10:32 UTC8
    directory::{Directory, OwnedBytes, RamDirectory},
c9effeac326aatagen2026-10-25 10:32 UTC9
    fastfield::{AliveBitSet, write_alive_bitset},
c9effeac326aatagen2026-10-25 10:32 UTC10
    indexer::{SingleSegmentIndexWriter, merge_filtered_segments},
c9effeac326aatagen2026-10-25 10:32 UTC11
    query::{BooleanQuery, Occur, Query, QueryParser, TermSetQuery},
c9effeac326aatagen2026-10-25 10:32 UTC12
    schema::{
c9effeac326aatagen2026-10-25 10:32 UTC13
        FAST, Field, IndexRecordOption, STORED, STRING, Schema, TextFieldIndexing, TextOptions,
c9effeac326aatagen2026-10-25 10:32 UTC14
        Value,
c9effeac326aatagen2026-10-25 10:32 UTC15
    },
c9effeac326aatagen2026-10-25 10:32 UTC16
    tokenizer::{
c9effeac326aatagen2026-10-25 10:32 UTC17
        AsciiFoldingFilter, LowerCaser, RemoveLongFilter, SimpleTokenizer, TextAnalyzer,
c9effeac326aatagen2026-10-25 10:32 UTC18
        TokenStream,
c9effeac326aatagen2026-10-25 10:32 UTC19
    },
c9effeac326aatagen2026-10-25 10:32 UTC20
};
c9effeac326aatagen2026-10-25 10:32 UTC21
use tantivy_common::BitSet;
c9effeac326aatagen2026-10-25 10:32 UTC22
c9effeac326aatagen2026-10-25 10:32 UTC23
use super::MessageIndex;
c9effeac326aatagen2026-10-25 10:32 UTC24
use crate::{Error, ErrorKind};
c9effeac326aatagen2026-10-25 10:32 UTC25
c9effeac326aatagen2026-10-25 10:32 UTC26
const TOKENIZER: &str = "macltok";
c9effeac326aatagen2026-10-25 10:32 UTC27
const WRITER_BUDGET: usize = 4_000_000;
c9effeac326aatagen2026-10-25 10:32 UTC28
/// segments of a size class merge once there are this many of them
c9effeac326aatagen2026-10-25 10:32 UTC29
const MERGE_FACTOR: usize = 6;
c9effeac326aatagen2026-10-25 10:32 UTC30
c9effeac326aatagen2026-10-25 10:32 UTC31
#[derive(Clone, Debug, Eq, PartialEq)]
c9effeac326aatagen2026-10-25 10:32 UTC32
pub(crate) struct Doc {
c9effeac326aatagen2026-10-25 10:32 UTC33
    pub(crate) key: String,
c9effeac326aatagen2026-10-25 10:32 UTC34
    pub(crate) room: String,
c9effeac326aatagen2026-10-25 10:32 UTC35
    pub(crate) sender: String,
c9effeac326aatagen2026-10-25 10:32 UTC36
    /// the original's, or the edit's while the original hasn't turned up
c9effeac326aatagen2026-10-25 10:32 UTC37
    pub(crate) ts: u64,
c9effeac326aatagen2026-10-25 10:32 UTC38
    /// the event whose text is indexed, the original or its latest edit
c9effeac326aatagen2026-10-25 10:32 UTC39
    pub(crate) content: String,
c9effeac326aatagen2026-10-25 10:32 UTC40
    pub(crate) content_ts: u64,
c9effeac326aatagen2026-10-25 10:32 UTC41
    /// only kept for provisional docs, read back empty otherwise
c9effeac326aatagen2026-10-25 10:32 UTC42
    pub(crate) text: String,
c9effeac326aatagen2026-10-25 10:32 UTC43
    /// an edit seen before its original
c9effeac326aatagen2026-10-25 10:32 UTC44
    pub(crate) provisional: bool,
c9effeac326aatagen2026-10-25 10:32 UTC45
}
c9effeac326aatagen2026-10-25 10:32 UTC46
c9effeac326aatagen2026-10-25 10:32 UTC47
#[derive(Clone, Debug, Eq, PartialEq)]
c9effeac326aatagen2026-10-25 10:32 UTC48
pub(crate) struct Hit {
c9effeac326aatagen2026-10-25 10:32 UTC49
    pub(crate) key: String,
c9effeac326aatagen2026-10-25 10:32 UTC50
    pub(crate) room: String,
c9effeac326aatagen2026-10-25 10:32 UTC51
    pub(crate) sender: String,
c9effeac326aatagen2026-10-25 10:32 UTC52
    pub(crate) content: String,
c9effeac326aatagen2026-10-25 10:32 UTC53
    pub(crate) ts: u64,
c9effeac326aatagen2026-10-25 10:32 UTC54
    pub(crate) tie: u64,
c9effeac326aatagen2026-10-25 10:32 UTC55
}
c9effeac326aatagen2026-10-25 10:32 UTC56
c9effeac326aatagen2026-10-25 10:32 UTC57
struct SegmentRecord {
c9effeac326aatagen2026-10-25 10:32 UTC58
    id: u64,
c9effeac326aatagen2026-10-25 10:32 UTC59
    del_gen: u64,
c9effeac326aatagen2026-10-25 10:32 UTC60
}
c9effeac326aatagen2026-10-25 10:32 UTC61
c9effeac326aatagen2026-10-25 10:32 UTC62
#[derive(Default)]
c9effeac326aatagen2026-10-25 10:32 UTC63
pub(crate) struct Changes {
c9effeac326aatagen2026-10-25 10:32 UTC64
    pub(crate) puts: Vec<(String, Vec<u8>)>,
c9effeac326aatagen2026-10-25 10:32 UTC65
    pub(crate) deletes: Vec<String>,
c9effeac326aatagen2026-10-25 10:32 UTC66
    files: Vec<u64>,
c9effeac326aatagen2026-10-25 10:32 UTC67
    dels: Vec<(u64, u64)>,
c9effeac326aatagen2026-10-25 10:32 UTC68
    removed: Vec<u64>,
c9effeac326aatagen2026-10-25 10:32 UTC69
}
c9effeac326aatagen2026-10-25 10:32 UTC70
c9effeac326aatagen2026-10-25 10:32 UTC71
#[derive(Clone, Copy)]
c9effeac326aatagen2026-10-25 10:32 UTC72
struct Fields {
c9effeac326aatagen2026-10-25 10:32 UTC73
    key: Field,
c9effeac326aatagen2026-10-25 10:32 UTC74
    room: Field,
c9effeac326aatagen2026-10-25 10:32 UTC75
    sender: Field,
c9effeac326aatagen2026-10-25 10:32 UTC76
    content: Field,
c9effeac326aatagen2026-10-25 10:32 UTC77
    text: Field,
c9effeac326aatagen2026-10-25 10:32 UTC78
    ts: Field,
c9effeac326aatagen2026-10-25 10:32 UTC79
    tie: Field,
c9effeac326aatagen2026-10-25 10:32 UTC80
    content_ts: Field,
c9effeac326aatagen2026-10-25 10:32 UTC81
    pending: Field,
c9effeac326aatagen2026-10-25 10:32 UTC82
}
c9effeac326aatagen2026-10-25 10:32 UTC83
c9effeac326aatagen2026-10-25 10:32 UTC84
struct Segment {
c9effeac326aatagen2026-10-25 10:32 UTC85
    id: u64,
c9effeac326aatagen2026-10-25 10:32 UTC86
    raw: RamDirectory,
c9effeac326aatagen2026-10-25 10:32 UTC87
    index: Index,
c9effeac326aatagen2026-10-25 10:32 UTC88
    searcher: Searcher,
c9effeac326aatagen2026-10-25 10:32 UTC89
    alive: Arc<BitSet>,
c9effeac326aatagen2026-10-25 10:32 UTC90
    max_doc: u32,
c9effeac326aatagen2026-10-25 10:32 UTC91
    del_gen: u64,
c9effeac326aatagen2026-10-25 10:32 UTC92
    persisted: bool,
c9effeac326aatagen2026-10-25 10:32 UTC93
    deletes_dirty: bool,
c9effeac326aatagen2026-10-25 10:32 UTC94
}
c9effeac326aatagen2026-10-25 10:32 UTC95
c9effeac326aatagen2026-10-25 10:32 UTC96
impl Segment {
c9effeac326aatagen2026-10-25 10:32 UTC97
    fn live(&self) -> usize {
c9effeac326aatagen2026-10-25 10:32 UTC98
        self.alive.len()
c9effeac326aatagen2026-10-25 10:32 UTC99
    }
c9effeac326aatagen2026-10-25 10:32 UTC100
}
c9effeac326aatagen2026-10-25 10:32 UTC101
c9effeac326aatagen2026-10-25 10:32 UTC102
pub(crate) struct KeywordIndex {
c9effeac326aatagen2026-10-25 10:32 UTC103
    schema: Schema,
c9effeac326aatagen2026-10-25 10:32 UTC104
    fields: Fields,
c9effeac326aatagen2026-10-25 10:32 UTC105
    settings: IndexSettings,
c9effeac326aatagen2026-10-25 10:32 UTC106
    parser: QueryParser,
c9effeac326aatagen2026-10-25 10:32 UTC107
    segments: Vec<Segment>,
c9effeac326aatagen2026-10-25 10:32 UTC108
    buffer: HashMap<String, Doc>,
c9effeac326aatagen2026-10-25 10:32 UTC109
    next_id: u64,
c9effeac326aatagen2026-10-25 10:32 UTC110
    persistent: bool,
c9effeac326aatagen2026-10-25 10:32 UTC111
    /// merged away since the last commit, with the delete generation they were persisted at
c9effeac326aatagen2026-10-25 10:32 UTC112
    removed: Vec<(u64, bool, u64)>,
c9effeac326aatagen2026-10-25 10:32 UTC113
}
c9effeac326aatagen2026-10-25 10:32 UTC114
c9effeac326aatagen2026-10-25 10:32 UTC115
pub(crate) fn analyzer() -> TextAnalyzer {
c9effeac326aatagen2026-10-25 10:32 UTC116
    TextAnalyzer::builder(SimpleTokenizer::default())
c9effeac326aatagen2026-10-25 10:32 UTC117
        .filter(RemoveLongFilter::limit(40))
c9effeac326aatagen2026-10-25 10:32 UTC118
        .filter(LowerCaser)
c9effeac326aatagen2026-10-25 10:32 UTC119
        .filter(AsciiFoldingFilter)
c9effeac326aatagen2026-10-25 10:32 UTC120
        .build()
c9effeac326aatagen2026-10-25 10:32 UTC121
}
c9effeac326aatagen2026-10-25 10:32 UTC122
c9effeac326aatagen2026-10-25 10:32 UTC123
/// the byte ranges in `text` whose analysed token is one of `terms`
c9effeac326aatagen2026-10-25 10:32 UTC124
pub(crate) fn match_ranges(text: &str, terms: &[String]) -> Vec<(usize, usize)> {
c9effeac326aatagen2026-10-25 10:32 UTC125
    if terms.is_empty() {
c9effeac326aatagen2026-10-25 10:32 UTC126
        return Vec::new();
c9effeac326aatagen2026-10-25 10:32 UTC127
    }
c9effeac326aatagen2026-10-25 10:32 UTC128
    let mut analyzer = analyzer();
c9effeac326aatagen2026-10-25 10:32 UTC129
    let mut stream = analyzer.token_stream(text);
c9effeac326aatagen2026-10-25 10:32 UTC130
    let mut ranges = Vec::new();
c9effeac326aatagen2026-10-25 10:32 UTC131
    while stream.advance() {
c9effeac326aatagen2026-10-25 10:32 UTC132
        let token = stream.token();
c9effeac326aatagen2026-10-25 10:32 UTC133
        if terms.iter().any(|term| *term == token.text) {
c9effeac326aatagen2026-10-25 10:32 UTC134
            ranges.push((token.offset_from, token.offset_to));
c9effeac326aatagen2026-10-25 10:32 UTC135
        }
c9effeac326aatagen2026-10-25 10:32 UTC136
    }
c9effeac326aatagen2026-10-25 10:32 UTC137
    ranges
c9effeac326aatagen2026-10-25 10:32 UTC138
}
c9effeac326aatagen2026-10-25 10:32 UTC139
c9effeac326aatagen2026-10-25 10:32 UTC140
fn schema() -> (Schema, Fields) {
c9effeac326aatagen2026-10-25 10:32 UTC141
    let mut builder = Schema::builder();
c9effeac326aatagen2026-10-25 10:32 UTC142
    let text_options = TextOptions::default().set_indexing_options(
c9effeac326aatagen2026-10-25 10:32 UTC143
        TextFieldIndexing::default()
c9effeac326aatagen2026-10-25 10:32 UTC144
            .set_tokenizer(TOKENIZER)
c9effeac326aatagen2026-10-25 10:32 UTC145
            .set_index_option(IndexRecordOption::WithFreqsAndPositions),
c9effeac326aatagen2026-10-25 10:32 UTC146
    );
c9effeac326aatagen2026-10-25 10:32 UTC147
    let fields = Fields {
c9effeac326aatagen2026-10-25 10:32 UTC148
        key: builder.add_text_field("key", STRING | STORED),
c9effeac326aatagen2026-10-25 10:32 UTC149
        room: builder.add_text_field("room", STRING | STORED),
c9effeac326aatagen2026-10-25 10:32 UTC150
        sender: builder.add_text_field("sender", STRING | STORED),
c9effeac326aatagen2026-10-25 10:32 UTC151
        content: builder.add_text_field("content", STRING | STORED),
c9effeac326aatagen2026-10-25 10:32 UTC152
        text: builder.add_text_field("text", text_options),
c9effeac326aatagen2026-10-25 10:32 UTC153
        ts: builder.add_u64_field("ts", FAST | STORED),
c9effeac326aatagen2026-10-25 10:32 UTC154
        tie: builder.add_u64_field("tie", FAST),
c9effeac326aatagen2026-10-25 10:32 UTC155
        content_ts: builder.add_u64_field("content_ts", STORED),
c9effeac326aatagen2026-10-25 10:32 UTC156
        pending: builder.add_text_field("pending", STORED),
c9effeac326aatagen2026-10-25 10:32 UTC157
    };
c9effeac326aatagen2026-10-25 10:32 UTC158
    (builder.build(), fields)
c9effeac326aatagen2026-10-25 10:32 UTC159
}
c9effeac326aatagen2026-10-25 10:32 UTC160
c9effeac326aatagen2026-10-25 10:32 UTC161
fn tie(key: &str) -> u64 {
c9effeac326aatagen2026-10-25 10:32 UTC162
    let digest = Sha256::digest(key.as_bytes());
c9effeac326aatagen2026-10-25 10:32 UTC163
    u64::from_le_bytes(digest[..8].try_into().expect("a sha256 digest is 32 bytes"))
c9effeac326aatagen2026-10-25 10:32 UTC164
}
c9effeac326aatagen2026-10-25 10:32 UTC165
c9effeac326aatagen2026-10-25 10:32 UTC166
fn settings() -> IndexSettings {
c9effeac326aatagen2026-10-25 10:32 UTC167
    // the dedicated compressor is a thread, and wasm32-unknown-unknown has none
c9effeac326aatagen2026-10-25 10:32 UTC168
    IndexSettings {
c9effeac326aatagen2026-10-25 10:32 UTC169
        docstore_compress_dedicated_thread: false,
c9effeac326aatagen2026-10-25 10:32 UTC170
        ..IndexSettings::default()
c9effeac326aatagen2026-10-25 10:32 UTC171
    }
c9effeac326aatagen2026-10-25 10:32 UTC172
}
c9effeac326aatagen2026-10-25 10:32 UTC173
c9effeac326aatagen2026-10-25 10:32 UTC174
fn tantivy_error(
c9effeac326aatagen2026-10-25 10:32 UTC175
    context: &'static str,
c9effeac326aatagen2026-10-25 10:32 UTC176
    error: impl std::error::Error + Send + Sync + 'static,
c9effeac326aatagen2026-10-25 10:32 UTC177
) -> Error {
c9effeac326aatagen2026-10-25 10:32 UTC178
    Error::with_source(ErrorKind::Store, context, error)
c9effeac326aatagen2026-10-25 10:32 UTC179
}
c9effeac326aatagen2026-10-25 10:32 UTC180
c9effeac326aatagen2026-10-25 10:32 UTC181
fn open_segment(id: u64, raw: RamDirectory, max_doc_hint: Option<u32>) -> Result<Segment, Error> {
c9effeac326aatagen2026-10-25 10:32 UTC182
    let index = Index::open(raw.clone())
c9effeac326aatagen2026-10-25 10:32 UTC183
        .map_err(|error| tantivy_error("opening a search segment", error))?;
c9effeac326aatagen2026-10-25 10:32 UTC184
    index.tokenizers().register(TOKENIZER, analyzer());
c9effeac326aatagen2026-10-25 10:32 UTC185
    let reader = index
c9effeac326aatagen2026-10-25 10:32 UTC186
        .reader_builder()
c9effeac326aatagen2026-10-25 10:32 UTC187
        .reload_policy(ReloadPolicy::Manual)
c9effeac326aatagen2026-10-25 10:32 UTC188
        .try_into()
c9effeac326aatagen2026-10-25 10:32 UTC189
        .map_err(|error| tantivy_error("reading a search segment", error))?;
c9effeac326aatagen2026-10-25 10:32 UTC190
    let searcher = reader.searcher();
c9effeac326aatagen2026-10-25 10:32 UTC191
    let max_doc = searcher
c9effeac326aatagen2026-10-25 10:32 UTC192
        .segment_readers()
c9effeac326aatagen2026-10-25 10:32 UTC193
        .iter()
c9effeac326aatagen2026-10-25 10:32 UTC194
        .map(SegmentReader::max_doc)
c9effeac326aatagen2026-10-25 10:32 UTC195
        .sum();
c9effeac326aatagen2026-10-25 10:32 UTC196
    if searcher.segment_readers().len() > 1 || max_doc_hint.is_some_and(|hint| hint != max_doc) {
c9effeac326aatagen2026-10-25 10:32 UTC197
        return Err(Error::new(
c9effeac326aatagen2026-10-25 10:32 UTC198
            ErrorKind::Store,
c9effeac326aatagen2026-10-25 10:32 UTC199
            "a search segment has an unexpected shape",
c9effeac326aatagen2026-10-25 10:32 UTC200
        ));
c9effeac326aatagen2026-10-25 10:32 UTC201
    }
c9effeac326aatagen2026-10-25 10:32 UTC202
    Ok(Segment {
c9effeac326aatagen2026-10-25 10:32 UTC203
        id,
c9effeac326aatagen2026-10-25 10:32 UTC204
        raw,
c9effeac326aatagen2026-10-25 10:32 UTC205
        index,
c9effeac326aatagen2026-10-25 10:32 UTC206
        searcher,
c9effeac326aatagen2026-10-25 10:32 UTC207
        alive: Arc::new(BitSet::with_max_value_and_full(max_doc)),
c9effeac326aatagen2026-10-25 10:32 UTC208
        max_doc,
c9effeac326aatagen2026-10-25 10:32 UTC209
        del_gen: 0,
c9effeac326aatagen2026-10-25 10:32 UTC210
        persisted: false,
c9effeac326aatagen2026-10-25 10:32 UTC211
        deletes_dirty: false,
c9effeac326aatagen2026-10-25 10:32 UTC212
    })
c9effeac326aatagen2026-10-25 10:32 UTC213
}
c9effeac326aatagen2026-10-25 10:32 UTC214
c9effeac326aatagen2026-10-25 10:32 UTC215
fn pack(files: &[(String, Vec<u8>)]) -> Vec<u8> {
c9effeac326aatagen2026-10-25 10:32 UTC216
    let mut packed = Vec::with_capacity(
c9effeac326aatagen2026-10-25 10:32 UTC217
        files
c9effeac326aatagen2026-10-25 10:32 UTC218
            .iter()
c9effeac326aatagen2026-10-25 10:32 UTC219
            .map(|(name, bytes)| name.len() + bytes.len() + 12)
c9effeac326aatagen2026-10-25 10:32 UTC220
            .sum(),
c9effeac326aatagen2026-10-25 10:32 UTC221
    );
c9effeac326aatagen2026-10-25 10:32 UTC222
    packed.extend_from_slice(&(files.len() as u32).to_le_bytes());
c9effeac326aatagen2026-10-25 10:32 UTC223
    for (name, bytes) in files {
c9effeac326aatagen2026-10-25 10:32 UTC224
        packed.extend_from_slice(&(name.len() as u32).to_le_bytes());
c9effeac326aatagen2026-10-25 10:32 UTC225
        packed.extend_from_slice(name.as_bytes());
c9effeac326aatagen2026-10-25 10:32 UTC226
        packed.extend_from_slice(&(bytes.len() as u64).to_le_bytes());
c9effeac326aatagen2026-10-25 10:32 UTC227
        packed.extend_from_slice(bytes);
c9effeac326aatagen2026-10-25 10:32 UTC228
    }
c9effeac326aatagen2026-10-25 10:32 UTC229
    packed
c9effeac326aatagen2026-10-25 10:32 UTC230
}
c9effeac326aatagen2026-10-25 10:32 UTC231
c9effeac326aatagen2026-10-25 10:32 UTC232
fn unpack(mut bytes: &[u8]) -> Option<Vec<(String, Vec<u8>)>> {
c9effeac326aatagen2026-10-25 10:32 UTC233
    fn take<'a>(bytes: &mut &'a [u8], len: usize) -> Option<&'a [u8]> {
c9effeac326aatagen2026-10-25 10:32 UTC234
        if bytes.len() < len {
c9effeac326aatagen2026-10-25 10:32 UTC235
            return None;
c9effeac326aatagen2026-10-25 10:32 UTC236
        }
c9effeac326aatagen2026-10-25 10:32 UTC237
        let (head, tail) = bytes.split_at(len);
c9effeac326aatagen2026-10-25 10:32 UTC238
        *bytes = tail;
c9effeac326aatagen2026-10-25 10:32 UTC239
        Some(head)
c9effeac326aatagen2026-10-25 10:32 UTC240
    }
c9effeac326aatagen2026-10-25 10:32 UTC241
    let count = u32::from_le_bytes(take(&mut bytes, 4)?.try_into().ok()?);
c9effeac326aatagen2026-10-25 10:32 UTC242
    let mut files = Vec::new();
c9effeac326aatagen2026-10-25 10:32 UTC243
    for _ in 0..count {
c9effeac326aatagen2026-10-25 10:32 UTC244
        let name_len = u32::from_le_bytes(take(&mut bytes, 4)?.try_into().ok()?) as usize;
c9effeac326aatagen2026-10-25 10:32 UTC245
        let name = std::str::from_utf8(take(&mut bytes, name_len)?)
c9effeac326aatagen2026-10-25 10:32 UTC246
            .ok()?
c9effeac326aatagen2026-10-25 10:32 UTC247
            .to_owned();
c9effeac326aatagen2026-10-25 10:32 UTC248
        let len =
c9effeac326aatagen2026-10-25 10:32 UTC249
            usize::try_from(u64::from_le_bytes(take(&mut bytes, 8)?.try_into().ok()?)).ok()?;
c9effeac326aatagen2026-10-25 10:32 UTC250
        files.push((name, take(&mut bytes, len)?.to_vec()));
c9effeac326aatagen2026-10-25 10:32 UTC251
    }
c9effeac326aatagen2026-10-25 10:32 UTC252
    bytes.is_empty().then_some(files)
c9effeac326aatagen2026-10-25 10:32 UTC253
}
c9effeac326aatagen2026-10-25 10:32 UTC254
c9effeac326aatagen2026-10-25 10:32 UTC255
pub(crate) fn segment_key(id: u64) -> String {
c9effeac326aatagen2026-10-25 10:32 UTC256
    format!("seg/{id}")
c9effeac326aatagen2026-10-25 10:32 UTC257
}
c9effeac326aatagen2026-10-25 10:32 UTC258
c9effeac326aatagen2026-10-25 10:32 UTC259
pub(crate) fn deletes_key(id: u64, generation: u64) -> String {
c9effeac326aatagen2026-10-25 10:32 UTC260
    format!("del/{id}/{generation}")
c9effeac326aatagen2026-10-25 10:32 UTC261
}
c9effeac326aatagen2026-10-25 10:32 UTC262
c9effeac326aatagen2026-10-25 10:32 UTC263
impl KeywordIndex {
c9effeac326aatagen2026-10-25 10:32 UTC264
    pub(crate) fn new(persistent: bool) -> Self {
c9effeac326aatagen2026-10-25 10:32 UTC265
        let (schema, fields) = schema();
c9effeac326aatagen2026-10-25 10:32 UTC266
        let parser_index = Index::create_in_ram(schema.clone());
c9effeac326aatagen2026-10-25 10:32 UTC267
        parser_index.tokenizers().register(TOKENIZER, analyzer());
c9effeac326aatagen2026-10-25 10:32 UTC268
        let mut parser = QueryParser::for_index(&parser_index, vec![fields.text]);
c9effeac326aatagen2026-10-25 10:32 UTC269
        parser.set_conjunction_by_default();
c9effeac326aatagen2026-10-25 10:32 UTC270
        Self {
c9effeac326aatagen2026-10-25 10:32 UTC271
            schema,
c9effeac326aatagen2026-10-25 10:32 UTC272
            fields,
c9effeac326aatagen2026-10-25 10:32 UTC273
            settings: settings(),
c9effeac326aatagen2026-10-25 10:32 UTC274
            parser,
c9effeac326aatagen2026-10-25 10:32 UTC275
            segments: Vec::new(),
c9effeac326aatagen2026-10-25 10:32 UTC276
            buffer: HashMap::new(),
c9effeac326aatagen2026-10-25 10:32 UTC277
            next_id: 1,
c9effeac326aatagen2026-10-25 10:32 UTC278
            persistent,
c9effeac326aatagen2026-10-25 10:32 UTC279
            removed: Vec::new(),
c9effeac326aatagen2026-10-25 10:32 UTC280
        }
c9effeac326aatagen2026-10-25 10:32 UTC281
    }
c9effeac326aatagen2026-10-25 10:32 UTC282
c9effeac326aatagen2026-10-25 10:32 UTC283
    /// `blob` hands back what an earlier commit put, any of it missing or unreadable fails the load
c9effeac326aatagen2026-10-25 10:32 UTC284
    pub(crate) fn load(
c9effeac326aatagen2026-10-25 10:32 UTC285
        manifest: &serde_json::Value,
c9effeac326aatagen2026-10-25 10:32 UTC286
        mut blob: impl FnMut(&str) -> Option<Vec<u8>>,
c9effeac326aatagen2026-10-25 10:32 UTC287
    ) -> Result<Self, Error> {
c9effeac326aatagen2026-10-25 10:32 UTC288
        let invalid = || Error::new(ErrorKind::Store, "the search manifest is unreadable");
c9effeac326aatagen2026-10-25 10:32 UTC289
        let records = manifest["segments"]
c9effeac326aatagen2026-10-25 10:32 UTC290
            .as_array()
c9effeac326aatagen2026-10-25 10:32 UTC291
            .ok_or_else(invalid)?
c9effeac326aatagen2026-10-25 10:32 UTC292
            .iter()
c9effeac326aatagen2026-10-25 10:32 UTC293
            .map(|record| {
c9effeac326aatagen2026-10-25 10:32 UTC294
                Some(SegmentRecord {
c9effeac326aatagen2026-10-25 10:32 UTC295
                    id: record.get(0)?.as_u64()?,
c9effeac326aatagen2026-10-25 10:32 UTC296
                    del_gen: record.get(1)?.as_u64()?,
c9effeac326aatagen2026-10-25 10:32 UTC297
                })
c9effeac326aatagen2026-10-25 10:32 UTC298
            })
c9effeac326aatagen2026-10-25 10:32 UTC299
            .collect::<Option<Vec<_>>>()
c9effeac326aatagen2026-10-25 10:32 UTC300
            .ok_or_else(invalid)?;
c9effeac326aatagen2026-10-25 10:32 UTC301
        let next_id = manifest["next"].as_u64().ok_or_else(invalid)?;
c9effeac326aatagen2026-10-25 10:32 UTC302
        let mut index = Self::new(true);
c9effeac326aatagen2026-10-25 10:32 UTC303
        let missing = || {
c9effeac326aatagen2026-10-25 10:32 UTC304
            Error::new(
c9effeac326aatagen2026-10-25 10:32 UTC305
                ErrorKind::Store,
c9effeac326aatagen2026-10-25 10:32 UTC306
                "a search segment is missing or unreadable",
c9effeac326aatagen2026-10-25 10:32 UTC307
            )
c9effeac326aatagen2026-10-25 10:32 UTC308
        };
c9effeac326aatagen2026-10-25 10:32 UTC309
        for record in &records {
c9effeac326aatagen2026-10-25 10:32 UTC310
            let files = blob(&segment_key(record.id))
c9effeac326aatagen2026-10-25 10:32 UTC311
                .and_then(|bytes| unpack(&bytes))
c9effeac326aatagen2026-10-25 10:32 UTC312
                .ok_or_else(missing)?;
c9effeac326aatagen2026-10-25 10:32 UTC313
            let raw = RamDirectory::create();
c9effeac326aatagen2026-10-25 10:32 UTC314
            for (name, bytes) in files {
c9effeac326aatagen2026-10-25 10:32 UTC315
                raw.atomic_write(&PathBuf::from(name), &bytes)
c9effeac326aatagen2026-10-25 10:32 UTC316
                    .map_err(|error| tantivy_error("loading a search segment", error))?;
c9effeac326aatagen2026-10-25 10:32 UTC317
            }
c9effeac326aatagen2026-10-25 10:32 UTC318
            let mut segment = open_segment(record.id, raw, None)?;
c9effeac326aatagen2026-10-25 10:32 UTC319
            if segment.index.schema() != index.schema {
c9effeac326aatagen2026-10-25 10:32 UTC320
                return Err(Error::new(
c9effeac326aatagen2026-10-25 10:32 UTC321
                    ErrorKind::Store,
c9effeac326aatagen2026-10-25 10:32 UTC322
                    "a search segment has another schema",
c9effeac326aatagen2026-10-25 10:32 UTC323
                ));
c9effeac326aatagen2026-10-25 10:32 UTC324
            }
c9effeac326aatagen2026-10-25 10:32 UTC325
            if record.del_gen > 0 {
c9effeac326aatagen2026-10-25 10:32 UTC326
                let bytes = blob(&deletes_key(record.id, record.del_gen)).ok_or_else(missing)?;
c9effeac326aatagen2026-10-25 10:32 UTC327
                let alive = AliveBitSet::open(OwnedBytes::new(bytes));
c9effeac326aatagen2026-10-25 10:32 UTC328
                let mut bits = BitSet::with_max_value(segment.max_doc);
c9effeac326aatagen2026-10-25 10:32 UTC329
                for doc in alive.iter_alive().filter(|doc| *doc < segment.max_doc) {
c9effeac326aatagen2026-10-25 10:32 UTC330
                    bits.insert(doc);
c9effeac326aatagen2026-10-25 10:32 UTC331
                }
c9effeac326aatagen2026-10-25 10:32 UTC332
                segment.alive = Arc::new(bits);
c9effeac326aatagen2026-10-25 10:32 UTC333
            }
c9effeac326aatagen2026-10-25 10:32 UTC334
            segment.del_gen = record.del_gen;
c9effeac326aatagen2026-10-25 10:32 UTC335
            segment.persisted = true;
c9effeac326aatagen2026-10-25 10:32 UTC336
            index.segments.push(segment);
c9effeac326aatagen2026-10-25 10:32 UTC337
        }
c9effeac326aatagen2026-10-25 10:32 UTC338
        index.next_id = next_id.max(
c9effeac326aatagen2026-10-25 10:32 UTC339
            records
c9effeac326aatagen2026-10-25 10:32 UTC340
                .iter()
c9effeac326aatagen2026-10-25 10:32 UTC341
                .map(|record| record.id + 1)
c9effeac326aatagen2026-10-25 10:32 UTC342
                .max()
c9effeac326aatagen2026-10-25 10:32 UTC343
                .unwrap_or(1),
c9effeac326aatagen2026-10-25 10:32 UTC344
        );
c9effeac326aatagen2026-10-25 10:32 UTC345
        Ok(index)
c9effeac326aatagen2026-10-25 10:32 UTC346
    }
c9effeac326aatagen2026-10-25 10:32 UTC347
c9effeac326aatagen2026-10-25 10:32 UTC348
    fn records(&self) -> Vec<SegmentRecord> {
c9effeac326aatagen2026-10-25 10:32 UTC349
        self.segments
c9effeac326aatagen2026-10-25 10:32 UTC350
            .iter()
c9effeac326aatagen2026-10-25 10:32 UTC351
            .filter(|segment| segment.persisted)
c9effeac326aatagen2026-10-25 10:32 UTC352
            .map(|segment| SegmentRecord {
c9effeac326aatagen2026-10-25 10:32 UTC353
                id: segment.id,
c9effeac326aatagen2026-10-25 10:32 UTC354
                del_gen: segment.del_gen,
c9effeac326aatagen2026-10-25 10:32 UTC355
            })
c9effeac326aatagen2026-10-25 10:32 UTC356
            .collect()
c9effeac326aatagen2026-10-25 10:32 UTC357
    }
c9effeac326aatagen2026-10-25 10:32 UTC358
c9effeac326aatagen2026-10-25 10:32 UTC359
    fn document(&self, doc: &Doc) -> TantivyDocument {
c9effeac326aatagen2026-10-25 10:32 UTC360
        let fields = self.fields;
c9effeac326aatagen2026-10-25 10:32 UTC361
        let mut document = TantivyDocument::default();
c9effeac326aatagen2026-10-25 10:32 UTC362
        document.add_text(fields.key, &doc.key);
c9effeac326aatagen2026-10-25 10:32 UTC363
        document.add_text(fields.room, &doc.room);
c9effeac326aatagen2026-10-25 10:32 UTC364
        document.add_text(fields.sender, &doc.sender);
c9effeac326aatagen2026-10-25 10:32 UTC365
        document.add_text(fields.content, &doc.content);
c9effeac326aatagen2026-10-25 10:32 UTC366
        document.add_text(fields.text, &doc.text);
c9effeac326aatagen2026-10-25 10:32 UTC367
        document.add_u64(fields.ts, doc.ts);
c9effeac326aatagen2026-10-25 10:32 UTC368
        document.add_u64(fields.tie, tie(&doc.key));
c9effeac326aatagen2026-10-25 10:32 UTC369
        document.add_u64(fields.content_ts, doc.content_ts);
c9effeac326aatagen2026-10-25 10:32 UTC370
        if doc.provisional {
c9effeac326aatagen2026-10-25 10:32 UTC371
            document.add_text(fields.pending, &doc.text);
c9effeac326aatagen2026-10-25 10:32 UTC372
        }
c9effeac326aatagen2026-10-25 10:32 UTC373
        document
c9effeac326aatagen2026-10-25 10:32 UTC374
    }
c9effeac326aatagen2026-10-25 10:32 UTC375
c9effeac326aatagen2026-10-25 10:32 UTC376
    fn stored(&self, document: &TantivyDocument) -> Option<Doc> {
c9effeac326aatagen2026-10-25 10:32 UTC377
        let fields = self.fields;
c9effeac326aatagen2026-10-25 10:32 UTC378
        let text = |field| {
c9effeac326aatagen2026-10-25 10:32 UTC379
            document
c9effeac326aatagen2026-10-25 10:32 UTC380
                .get_first(field)
c9effeac326aatagen2026-10-25 10:32 UTC381
                .and_then(|value| value.as_str())
c9effeac326aatagen2026-10-25 10:32 UTC382
                .map(str::to_owned)
c9effeac326aatagen2026-10-25 10:32 UTC383
        };
c9effeac326aatagen2026-10-25 10:32 UTC384
        let number = |field| document.get_first(field).and_then(|value| value.as_u64());
c9effeac326aatagen2026-10-25 10:32 UTC385
        let pending = text(fields.pending);
c9effeac326aatagen2026-10-25 10:32 UTC386
        Some(Doc {
c9effeac326aatagen2026-10-25 10:32 UTC387
            key: text(fields.key)?,
c9effeac326aatagen2026-10-25 10:32 UTC388
            room: text(fields.room)?,
c9effeac326aatagen2026-10-25 10:32 UTC389
            sender: text(fields.sender)?,
c9effeac326aatagen2026-10-25 10:32 UTC390
            ts: number(fields.ts)?,
c9effeac326aatagen2026-10-25 10:32 UTC391
            content: text(fields.content)?,
c9effeac326aatagen2026-10-25 10:32 UTC392
            content_ts: number(fields.content_ts)?,
c9effeac326aatagen2026-10-25 10:32 UTC393
            provisional: pending.is_some(),
c9effeac326aatagen2026-10-25 10:32 UTC394
            text: pending.unwrap_or_default(),
c9effeac326aatagen2026-10-25 10:32 UTC395
        })
c9effeac326aatagen2026-10-25 10:32 UTC396
    }
c9effeac326aatagen2026-10-25 10:32 UTC397
c9effeac326aatagen2026-10-25 10:32 UTC398
    /// the live docs in `segment` whose `field` holds `value`
c9effeac326aatagen2026-10-25 10:32 UTC399
    fn find(&self, segment: &Segment, field: Field, value: &str) -> Vec<DocId> {
c9effeac326aatagen2026-10-25 10:32 UTC400
        let term = Term::from_field_text(field, value);
c9effeac326aatagen2026-10-25 10:32 UTC401
        let mut found = Vec::new();
c9effeac326aatagen2026-10-25 10:32 UTC402
        for reader in segment.searcher.segment_readers() {
c9effeac326aatagen2026-10-25 10:32 UTC403
            let Ok(inverted) = reader.inverted_index(field) else {
c9effeac326aatagen2026-10-25 10:32 UTC404
                continue;
c9effeac326aatagen2026-10-25 10:32 UTC405
            };
c9effeac326aatagen2026-10-25 10:32 UTC406
            let Ok(Some(mut postings)) = inverted.read_postings(&term, IndexRecordOption::Basic)
c9effeac326aatagen2026-10-25 10:32 UTC407
            else {
c9effeac326aatagen2026-10-25 10:32 UTC408
                continue;
c9effeac326aatagen2026-10-25 10:32 UTC409
            };
c9effeac326aatagen2026-10-25 10:32 UTC410
            let mut doc = postings.doc();
c9effeac326aatagen2026-10-25 10:32 UTC411
            while doc != tantivy::TERMINATED {
c9effeac326aatagen2026-10-25 10:32 UTC412
                if segment.alive.contains(doc) {
c9effeac326aatagen2026-10-25 10:32 UTC413
                    found.push(doc);
c9effeac326aatagen2026-10-25 10:32 UTC414
                }
c9effeac326aatagen2026-10-25 10:32 UTC415
                doc = postings.advance();
c9effeac326aatagen2026-10-25 10:32 UTC416
            }
c9effeac326aatagen2026-10-25 10:32 UTC417
        }
c9effeac326aatagen2026-10-25 10:32 UTC418
        found
c9effeac326aatagen2026-10-25 10:32 UTC419
    }
c9effeac326aatagen2026-10-25 10:32 UTC420
c9effeac326aatagen2026-10-25 10:32 UTC421
    fn lookup(&self, field: Field, value: &str) -> Option<Doc> {
c9effeac326aatagen2026-10-25 10:32 UTC422
        self.segments.iter().rev().find_map(|segment| {
c9effeac326aatagen2026-10-25 10:32 UTC423
            let doc = *self.find(segment, field, value).first()?;
c9effeac326aatagen2026-10-25 10:32 UTC424
            let document: TantivyDocument = segment.searcher.doc(DocAddress::new(0, doc)).ok()?;
c9effeac326aatagen2026-10-25 10:32 UTC425
            self.stored(&document)
c9effeac326aatagen2026-10-25 10:32 UTC426
        })
c9effeac326aatagen2026-10-25 10:32 UTC427
    }
c9effeac326aatagen2026-10-25 10:32 UTC428
c9effeac326aatagen2026-10-25 10:32 UTC429
    fn flush_buffer(&mut self) -> Result<(), Error> {
c9effeac326aatagen2026-10-25 10:32 UTC430
        if self.buffer.is_empty() {
c9effeac326aatagen2026-10-25 10:32 UTC431
            return Ok(());
c9effeac326aatagen2026-10-25 10:32 UTC432
        }
c9effeac326aatagen2026-10-25 10:32 UTC433
        let raw = RamDirectory::create();
c9effeac326aatagen2026-10-25 10:32 UTC434
        let index = Index::create(raw.clone(), self.schema.clone(), self.settings.clone())
c9effeac326aatagen2026-10-25 10:32 UTC435
            .map_err(|error| tantivy_error("creating a search segment", error))?;
c9effeac326aatagen2026-10-25 10:32 UTC436
        index.tokenizers().register(TOKENIZER, analyzer());
c9effeac326aatagen2026-10-25 10:32 UTC437
        let mut writer = SingleSegmentIndexWriter::new(index, WRITER_BUDGET)
c9effeac326aatagen2026-10-25 10:32 UTC438
            .map_err(|error| tantivy_error("writing a search segment", error))?;
c9effeac326aatagen2026-10-25 10:32 UTC439
        let mut docs: Vec<_> = self.buffer.values().collect();
c9effeac326aatagen2026-10-25 10:32 UTC440
        docs.sort_by_key(|doc| doc.ts);
c9effeac326aatagen2026-10-25 10:32 UTC441
        for doc in docs {
c9effeac326aatagen2026-10-25 10:32 UTC442
            writer
c9effeac326aatagen2026-10-25 10:32 UTC443
                .add_document(self.document(doc))
c9effeac326aatagen2026-10-25 10:32 UTC444
                .map_err(|error| tantivy_error("writing a search segment", error))?;
c9effeac326aatagen2026-10-25 10:32 UTC445
        }
c9effeac326aatagen2026-10-25 10:32 UTC446
        writer
c9effeac326aatagen2026-10-25 10:32 UTC447
            .finalize()
c9effeac326aatagen2026-10-25 10:32 UTC448
            .map_err(|error| tantivy_error("finishing a search segment", error))?;
c9effeac326aatagen2026-10-25 10:32 UTC449
        let segment = open_segment(self.next_id, raw, Some(self.buffer.len() as u32))?;
c9effeac326aatagen2026-10-25 10:32 UTC450
        self.next_id += 1;
c9effeac326aatagen2026-10-25 10:32 UTC451
        self.segments.push(segment);
c9effeac326aatagen2026-10-25 10:32 UTC452
        self.buffer.clear();
c9effeac326aatagen2026-10-25 10:32 UTC453
        Ok(())
c9effeac326aatagen2026-10-25 10:32 UTC454
    }
c9effeac326aatagen2026-10-25 10:32 UTC455
c9effeac326aatagen2026-10-25 10:32 UTC456
    fn class(live: usize) -> u32 {
c9effeac326aatagen2026-10-25 10:32 UTC457
        live.max(1).ilog(MERGE_FACTOR)
c9effeac326aatagen2026-10-25 10:32 UTC458
    }
c9effeac326aatagen2026-10-25 10:32 UTC459
c9effeac326aatagen2026-10-25 10:32 UTC460
    /// merges a size class once it fills up, and rewrites a segment that's mostly deleted
c9effeac326aatagen2026-10-25 10:32 UTC461
    fn compact(&mut self) -> Result<(), Error> {
c9effeac326aatagen2026-10-25 10:32 UTC462
        self.segments.retain(|segment| {
c9effeac326aatagen2026-10-25 10:32 UTC463
            let empty = segment.live() == 0;
c9effeac326aatagen2026-10-25 10:32 UTC464
            if empty && self.persistent {
c9effeac326aatagen2026-10-25 10:32 UTC465
                self.removed
c9effeac326aatagen2026-10-25 10:32 UTC466
                    .push((segment.id, segment.persisted, segment.del_gen));
c9effeac326aatagen2026-10-25 10:32 UTC467
            }
c9effeac326aatagen2026-10-25 10:32 UTC468
            !empty
c9effeac326aatagen2026-10-25 10:32 UTC469
        });
c9effeac326aatagen2026-10-25 10:32 UTC470
        loop {
c9effeac326aatagen2026-10-25 10:32 UTC471
            let mut classes: HashMap<u32, Vec<usize>> = HashMap::new();
c9effeac326aatagen2026-10-25 10:32 UTC472
            for (position, segment) in self.segments.iter().enumerate() {
c9effeac326aatagen2026-10-25 10:32 UTC473
                classes
c9effeac326aatagen2026-10-25 10:32 UTC474
                    .entry(Self::class(segment.live()))
c9effeac326aatagen2026-10-25 10:32 UTC475
                    .or_default()
c9effeac326aatagen2026-10-25 10:32 UTC476
                    .push(position);
c9effeac326aatagen2026-10-25 10:32 UTC477
            }
c9effeac326aatagen2026-10-25 10:32 UTC478
            let full = classes
c9effeac326aatagen2026-10-25 10:32 UTC479
                .into_values()
c9effeac326aatagen2026-10-25 10:32 UTC480
                .find(|positions| positions.len() >= MERGE_FACTOR);
c9effeac326aatagen2026-10-25 10:32 UTC481
            let sparse = self
c9effeac326aatagen2026-10-25 10:32 UTC482
                .segments
c9effeac326aatagen2026-10-25 10:32 UTC483
                .iter()
c9effeac326aatagen2026-10-25 10:32 UTC484
                .position(|segment| {
c9effeac326aatagen2026-10-25 10:32 UTC485
                    segment.max_doc >= 1_000 && segment.live() * 2 < segment.max_doc as usize
c9effeac326aatagen2026-10-25 10:32 UTC486
                })
c9effeac326aatagen2026-10-25 10:32 UTC487
                .map(|position| vec![position]);
c9effeac326aatagen2026-10-25 10:32 UTC488
            let Some(positions) = full.or(sparse) else {
c9effeac326aatagen2026-10-25 10:32 UTC489
                return Ok(());
c9effeac326aatagen2026-10-25 10:32 UTC490
            };
c9effeac326aatagen2026-10-25 10:32 UTC491
            self.merge(&positions)?;
c9effeac326aatagen2026-10-25 10:32 UTC492
        }
c9effeac326aatagen2026-10-25 10:32 UTC493
    }
c9effeac326aatagen2026-10-25 10:32 UTC494
c9effeac326aatagen2026-10-25 10:32 UTC495
    fn merge(&mut self, positions: &[usize]) -> Result<(), Error> {
c9effeac326aatagen2026-10-25 10:32 UTC496
        let mut inputs = Vec::new();
c9effeac326aatagen2026-10-25 10:32 UTC497
        let mut alive = Vec::new();
c9effeac326aatagen2026-10-25 10:32 UTC498
        for position in positions {
c9effeac326aatagen2026-10-25 10:32 UTC499
            let segment = &self.segments[*position];
c9effeac326aatagen2026-10-25 10:32 UTC500
            let searchable = segment
c9effeac326aatagen2026-10-25 10:32 UTC501
                .index
c9effeac326aatagen2026-10-25 10:32 UTC502
                .searchable_segments()
c9effeac326aatagen2026-10-25 10:32 UTC503
                .map_err(|error| tantivy_error("merging search segments", error))?;
c9effeac326aatagen2026-10-25 10:32 UTC504
            let mut bytes = Vec::new();
c9effeac326aatagen2026-10-25 10:32 UTC505
            write_alive_bitset(&segment.alive, &mut bytes)
c9effeac326aatagen2026-10-25 10:32 UTC506
                .map_err(|error| tantivy_error("merging search segments", error))?;
c9effeac326aatagen2026-10-25 10:32 UTC507
            for tantivy_segment in searchable {
c9effeac326aatagen2026-10-25 10:32 UTC508
                inputs.push(tantivy_segment);
c9effeac326aatagen2026-10-25 10:32 UTC509
                alive.push(Some(AliveBitSet::open(OwnedBytes::new(bytes.clone()))));
c9effeac326aatagen2026-10-25 10:32 UTC510
            }
c9effeac326aatagen2026-10-25 10:32 UTC511
        }
c9effeac326aatagen2026-10-25 10:32 UTC512
        let raw = RamDirectory::create();
c9effeac326aatagen2026-10-25 10:32 UTC513
        merge_filtered_segments(&inputs, self.settings.clone(), alive, raw.clone())
c9effeac326aatagen2026-10-25 10:32 UTC514
            .map_err(|error| tantivy_error("merging search segments", error))?;
c9effeac326aatagen2026-10-25 10:32 UTC515
        let merged = open_segment(self.next_id, raw, None)?;
c9effeac326aatagen2026-10-25 10:32 UTC516
        self.next_id += 1;
c9effeac326aatagen2026-10-25 10:32 UTC517
        let mut position = 0;
c9effeac326aatagen2026-10-25 10:32 UTC518
        self.segments.retain(|segment| {
c9effeac326aatagen2026-10-25 10:32 UTC519
            let merging = positions.contains(&position);
c9effeac326aatagen2026-10-25 10:32 UTC520
            position += 1;
c9effeac326aatagen2026-10-25 10:32 UTC521
            if merging && self.persistent {
c9effeac326aatagen2026-10-25 10:32 UTC522
                self.removed
c9effeac326aatagen2026-10-25 10:32 UTC523
                    .push((segment.id, segment.persisted, segment.del_gen));
c9effeac326aatagen2026-10-25 10:32 UTC524
            }
c9effeac326aatagen2026-10-25 10:32 UTC525
            !merging
c9effeac326aatagen2026-10-25 10:32 UTC526
        });
c9effeac326aatagen2026-10-25 10:32 UTC527
        self.segments.push(merged);
c9effeac326aatagen2026-10-25 10:32 UTC528
        Ok(())
c9effeac326aatagen2026-10-25 10:32 UTC529
    }
c9effeac326aatagen2026-10-25 10:32 UTC530
c9effeac326aatagen2026-10-25 10:32 UTC531
    fn segment_files(segment: &Segment) -> Result<Vec<u8>, Error> {
c9effeac326aatagen2026-10-25 10:32 UTC532
        let mut names: Vec<PathBuf> = segment
c9effeac326aatagen2026-10-25 10:32 UTC533
            .index
c9effeac326aatagen2026-10-25 10:32 UTC534
            .searchable_segment_metas()
c9effeac326aatagen2026-10-25 10:32 UTC535
            .map_err(|error| tantivy_error("reading a search segment", error))?
c9effeac326aatagen2026-10-25 10:32 UTC536
            .iter()
c9effeac326aatagen2026-10-25 10:32 UTC537
            .flat_map(|meta| meta.list_files())
c9effeac326aatagen2026-10-25 10:32 UTC538
            .collect();
c9effeac326aatagen2026-10-25 10:32 UTC539
        names.push(PathBuf::from("meta.json"));
c9effeac326aatagen2026-10-25 10:32 UTC540
        let mut files = Vec::new();
c9effeac326aatagen2026-10-25 10:32 UTC541
        for name in names {
c9effeac326aatagen2026-10-25 10:32 UTC542
            if !segment.raw.exists(&name).unwrap_or(false) {
c9effeac326aatagen2026-10-25 10:32 UTC543
                continue;
c9effeac326aatagen2026-10-25 10:32 UTC544
            }
c9effeac326aatagen2026-10-25 10:32 UTC545
            let bytes = segment
c9effeac326aatagen2026-10-25 10:32 UTC546
                .raw
c9effeac326aatagen2026-10-25 10:32 UTC547
                .open_read(&name)
c9effeac326aatagen2026-10-25 10:32 UTC548
                .map_err(|error| tantivy_error("reading a search segment", error))?
c9effeac326aatagen2026-10-25 10:32 UTC549
                .read_bytes()
c9effeac326aatagen2026-10-25 10:32 UTC550
                .map_err(|error| tantivy_error("reading a search segment", error))?;
c9effeac326aatagen2026-10-25 10:32 UTC551
            files.push((
c9effeac326aatagen2026-10-25 10:32 UTC552
                name.to_string_lossy().into_owned(),
c9effeac326aatagen2026-10-25 10:32 UTC553
                bytes.as_slice().to_vec(),
c9effeac326aatagen2026-10-25 10:32 UTC554
            ));
c9effeac326aatagen2026-10-25 10:32 UTC555
        }
c9effeac326aatagen2026-10-25 10:32 UTC556
        Ok(pack(&files))
c9effeac326aatagen2026-10-25 10:32 UTC557
    }
c9effeac326aatagen2026-10-25 10:32 UTC558
}
c9effeac326aatagen2026-10-25 10:32 UTC559
c9effeac326aatagen2026-10-25 10:32 UTC560
impl MessageIndex for KeywordIndex {
c9effeac326aatagen2026-10-25 10:32 UTC561
    fn get(&self, key: &str) -> Option<Doc> {
c9effeac326aatagen2026-10-25 10:32 UTC562
        if let Some(doc) = self.buffer.get(key) {
c9effeac326aatagen2026-10-25 10:32 UTC563
            return Some(doc.clone());
c9effeac326aatagen2026-10-25 10:32 UTC564
        }
c9effeac326aatagen2026-10-25 10:32 UTC565
        self.lookup(self.fields.key, key)
c9effeac326aatagen2026-10-25 10:32 UTC566
    }
c9effeac326aatagen2026-10-25 10:32 UTC567
c9effeac326aatagen2026-10-25 10:32 UTC568
    fn by_content(&self, content: &str) -> Option<Doc> {
c9effeac326aatagen2026-10-25 10:32 UTC569
        if let Some(doc) = self.buffer.values().find(|doc| doc.content == content) {
c9effeac326aatagen2026-10-25 10:32 UTC570
            return Some(doc.clone());
c9effeac326aatagen2026-10-25 10:32 UTC571
        }
c9effeac326aatagen2026-10-25 10:32 UTC572
        self.lookup(self.fields.content, content)
c9effeac326aatagen2026-10-25 10:32 UTC573
    }
c9effeac326aatagen2026-10-25 10:32 UTC574
c9effeac326aatagen2026-10-25 10:32 UTC575
    fn put(&mut self, doc: Doc) {
c9effeac326aatagen2026-10-25 10:32 UTC576
        self.remove(&doc.key);
c9effeac326aatagen2026-10-25 10:32 UTC577
        self.buffer.insert(doc.key.clone(), doc);
c9effeac326aatagen2026-10-25 10:32 UTC578
    }
c9effeac326aatagen2026-10-25 10:32 UTC579
c9effeac326aatagen2026-10-25 10:32 UTC580
    fn remove(&mut self, key: &str) -> bool {
c9effeac326aatagen2026-10-25 10:32 UTC581
        let mut removed = self.buffer.remove(key).is_some();
c9effeac326aatagen2026-10-25 10:32 UTC582
        let key_field = self.fields.key;
c9effeac326aatagen2026-10-25 10:32 UTC583
        for position in 0..self.segments.len() {
c9effeac326aatagen2026-10-25 10:32 UTC584
            let docs = self.find(&self.segments[position], key_field, key);
c9effeac326aatagen2026-10-25 10:32 UTC585
            if docs.is_empty() {
c9effeac326aatagen2026-10-25 10:32 UTC586
                continue;
c9effeac326aatagen2026-10-25 10:32 UTC587
            }
c9effeac326aatagen2026-10-25 10:32 UTC588
            let segment = &mut self.segments[position];
c9effeac326aatagen2026-10-25 10:32 UTC589
            let alive = Arc::make_mut(&mut segment.alive);
c9effeac326aatagen2026-10-25 10:32 UTC590
            for doc in docs {
c9effeac326aatagen2026-10-25 10:32 UTC591
                alive.remove(doc);
c9effeac326aatagen2026-10-25 10:32 UTC592
            }
c9effeac326aatagen2026-10-25 10:32 UTC593
            segment.deletes_dirty = true;
c9effeac326aatagen2026-10-25 10:32 UTC594
            removed = true;
c9effeac326aatagen2026-10-25 10:32 UTC595
        }
c9effeac326aatagen2026-10-25 10:32 UTC596
        removed
c9effeac326aatagen2026-10-25 10:32 UTC597
    }
c9effeac326aatagen2026-10-25 10:32 UTC598
c9effeac326aatagen2026-10-25 10:32 UTC599
    fn buffered(&self) -> usize {
c9effeac326aatagen2026-10-25 10:32 UTC600
        self.buffer.len()
c9effeac326aatagen2026-10-25 10:32 UTC601
    }
c9effeac326aatagen2026-10-25 10:32 UTC602
c9effeac326aatagen2026-10-25 10:32 UTC603
    fn search(
c9effeac326aatagen2026-10-25 10:32 UTC604
        &mut self,
c9effeac326aatagen2026-10-25 10:32 UTC605
        query: &str,
c9effeac326aatagen2026-10-25 10:32 UTC606
        rooms: Option<&[String]>,
c9effeac326aatagen2026-10-25 10:32 UTC607
        before: Option<(u64, u64)>,
c9effeac326aatagen2026-10-25 10:32 UTC608
        limit: usize,
c9effeac326aatagen2026-10-25 10:32 UTC609
    ) -> Result<(Vec<Hit>, Vec<String>), Error> {
c9effeac326aatagen2026-10-25 10:32 UTC610
        self.flush_buffer()?;
c9effeac326aatagen2026-10-25 10:32 UTC611
        let (text_query, _) = self.parser.parse_query_lenient(query);
c9effeac326aatagen2026-10-25 10:32 UTC612
        let mut terms = Vec::new();
c9effeac326aatagen2026-10-25 10:32 UTC613
        text_query.query_terms(&mut |term, _| {
c9effeac326aatagen2026-10-25 10:32 UTC614
            if term.field() == self.fields.text
c9effeac326aatagen2026-10-25 10:32 UTC615
                && let Some(text) = term.value().as_str()
c9effeac326aatagen2026-10-25 10:32 UTC616
            {
c9effeac326aatagen2026-10-25 10:32 UTC617
                terms.push(text.to_owned());
c9effeac326aatagen2026-10-25 10:32 UTC618
            }
c9effeac326aatagen2026-10-25 10:32 UTC619
        });
c9effeac326aatagen2026-10-25 10:32 UTC620
        let query: Box<dyn Query> = match rooms {
c9effeac326aatagen2026-10-25 10:32 UTC621
            None => text_query,
c9effeac326aatagen2026-10-25 10:32 UTC622
            Some(rooms) => Box::new(BooleanQuery::new(vec![
c9effeac326aatagen2026-10-25 10:32 UTC623
                (Occur::Must, text_query),
c9effeac326aatagen2026-10-25 10:32 UTC624
                (
c9effeac326aatagen2026-10-25 10:32 UTC625
                    Occur::Must,
c9effeac326aatagen2026-10-25 10:32 UTC626
                    Box::new(TermSetQuery::new(
c9effeac326aatagen2026-10-25 10:32 UTC627
                        rooms
c9effeac326aatagen2026-10-25 10:32 UTC628
                            .iter()
c9effeac326aatagen2026-10-25 10:32 UTC629
                            .map(|room| Term::from_field_text(self.fields.room, room)),
c9effeac326aatagen2026-10-25 10:32 UTC630
                    )),
c9effeac326aatagen2026-10-25 10:32 UTC631
                ),
c9effeac326aatagen2026-10-25 10:32 UTC632
            ])),
c9effeac326aatagen2026-10-25 10:32 UTC633
        };
c9effeac326aatagen2026-10-25 10:32 UTC634
        let mut found = Vec::new();
c9effeac326aatagen2026-10-25 10:32 UTC635
        for segment in &self.segments {
c9effeac326aatagen2026-10-25 10:32 UTC636
            let collector = Newest {
c9effeac326aatagen2026-10-25 10:32 UTC637
                alive: segment.alive.clone(),
c9effeac326aatagen2026-10-25 10:32 UTC638
                before,
c9effeac326aatagen2026-10-25 10:32 UTC639
                limit,
c9effeac326aatagen2026-10-25 10:32 UTC640
            };
c9effeac326aatagen2026-10-25 10:32 UTC641
            let newest = segment
c9effeac326aatagen2026-10-25 10:32 UTC642
                .searcher
c9effeac326aatagen2026-10-25 10:32 UTC643
                .search(&*query, &collector)
c9effeac326aatagen2026-10-25 10:32 UTC644
                .map_err(|error| tantivy_error("searching messages", error))?;
c9effeac326aatagen2026-10-25 10:32 UTC645
            for (ts, tie, doc) in newest {
c9effeac326aatagen2026-10-25 10:32 UTC646
                found.push((ts, tie, segment.id, doc));
c9effeac326aatagen2026-10-25 10:32 UTC647
            }
c9effeac326aatagen2026-10-25 10:32 UTC648
        }
c9effeac326aatagen2026-10-25 10:32 UTC649
        found.sort_by(|a, b| (b.0, b.1).cmp(&(a.0, a.1)));
c9effeac326aatagen2026-10-25 10:32 UTC650
        found.truncate(limit);
c9effeac326aatagen2026-10-25 10:32 UTC651
        let mut hits = Vec::new();
c9effeac326aatagen2026-10-25 10:32 UTC652
        for (ts, tie, id, doc) in found {
c9effeac326aatagen2026-10-25 10:32 UTC653
            let segment = self
c9effeac326aatagen2026-10-25 10:32 UTC654
                .segments
c9effeac326aatagen2026-10-25 10:32 UTC655
                .iter()
c9effeac326aatagen2026-10-25 10:32 UTC656
                .find(|segment| segment.id == id)
c9effeac326aatagen2026-10-25 10:32 UTC657
                .expect("the segment was just searched");
c9effeac326aatagen2026-10-25 10:32 UTC658
            let document: TantivyDocument = segment
c9effeac326aatagen2026-10-25 10:32 UTC659
                .searcher
c9effeac326aatagen2026-10-25 10:32 UTC660
                .doc(doc)
c9effeac326aatagen2026-10-25 10:32 UTC661
                .map_err(|error| tantivy_error("reading a search hit", error))?;
c9effeac326aatagen2026-10-25 10:32 UTC662
            let Some(stored) = self.stored(&document) else {
c9effeac326aatagen2026-10-25 10:32 UTC663
                continue;
c9effeac326aatagen2026-10-25 10:32 UTC664
            };
c9effeac326aatagen2026-10-25 10:32 UTC665
            hits.push(Hit {
c9effeac326aatagen2026-10-25 10:32 UTC666
                key: stored.key,
c9effeac326aatagen2026-10-25 10:32 UTC667
                room: stored.room,
c9effeac326aatagen2026-10-25 10:32 UTC668
                sender: stored.sender,
c9effeac326aatagen2026-10-25 10:32 UTC669
                content: stored.content,
c9effeac326aatagen2026-10-25 10:32 UTC670
                ts,
c9effeac326aatagen2026-10-25 10:32 UTC671
                tie,
c9effeac326aatagen2026-10-25 10:32 UTC672
            });
c9effeac326aatagen2026-10-25 10:32 UTC673
        }
c9effeac326aatagen2026-10-25 10:32 UTC674
        terms.sort();
c9effeac326aatagen2026-10-25 10:32 UTC675
        terms.dedup();
c9effeac326aatagen2026-10-25 10:32 UTC676
        Ok((hits, terms))
c9effeac326aatagen2026-10-25 10:32 UTC677
    }
c9effeac326aatagen2026-10-25 10:32 UTC678
c9effeac326aatagen2026-10-25 10:32 UTC679
    fn maintain(&mut self) -> Result<(), Error> {
c9effeac326aatagen2026-10-25 10:32 UTC680
        self.flush_buffer()?;
c9effeac326aatagen2026-10-25 10:32 UTC681
        self.compact()
c9effeac326aatagen2026-10-25 10:32 UTC682
    }
c9effeac326aatagen2026-10-25 10:32 UTC683
c9effeac326aatagen2026-10-25 10:32 UTC684
    fn changes(&mut self) -> Result<Changes, Error> {
c9effeac326aatagen2026-10-25 10:32 UTC685
        self.maintain()?;
c9effeac326aatagen2026-10-25 10:32 UTC686
        let mut changes = Changes::default();
c9effeac326aatagen2026-10-25 10:32 UTC687
        for (id, persisted, del_gen) in &self.removed {
c9effeac326aatagen2026-10-25 10:32 UTC688
            if *persisted {
c9effeac326aatagen2026-10-25 10:32 UTC689
                changes.deletes.push(segment_key(*id));
c9effeac326aatagen2026-10-25 10:32 UTC690
            }
c9effeac326aatagen2026-10-25 10:32 UTC691
            if *del_gen > 0 {
c9effeac326aatagen2026-10-25 10:32 UTC692
                changes.deletes.push(deletes_key(*id, *del_gen));
c9effeac326aatagen2026-10-25 10:32 UTC693
            }
c9effeac326aatagen2026-10-25 10:32 UTC694
            changes.removed.push(*id);
c9effeac326aatagen2026-10-25 10:32 UTC695
        }
c9effeac326aatagen2026-10-25 10:32 UTC696
        for segment in &self.segments {
c9effeac326aatagen2026-10-25 10:32 UTC697
            if !segment.persisted {
c9effeac326aatagen2026-10-25 10:32 UTC698
                changes
c9effeac326aatagen2026-10-25 10:32 UTC699
                    .puts
c9effeac326aatagen2026-10-25 10:32 UTC700
                    .push((segment_key(segment.id), Self::segment_files(segment)?));
c9effeac326aatagen2026-10-25 10:32 UTC701
                changes.files.push(segment.id);
c9effeac326aatagen2026-10-25 10:32 UTC702
            }
c9effeac326aatagen2026-10-25 10:32 UTC703
            if segment.deletes_dirty && segment.live() < segment.max_doc as usize {
c9effeac326aatagen2026-10-25 10:32 UTC704
                let generation = segment.del_gen + 1;
c9effeac326aatagen2026-10-25 10:32 UTC705
                let mut bytes = Vec::new();
c9effeac326aatagen2026-10-25 10:32 UTC706
                write_alive_bitset(&segment.alive, &mut bytes)
c9effeac326aatagen2026-10-25 10:32 UTC707
                    .map_err(|error| tantivy_error("saving search deletes", error))?;
c9effeac326aatagen2026-10-25 10:32 UTC708
                changes
c9effeac326aatagen2026-10-25 10:32 UTC709
                    .puts
c9effeac326aatagen2026-10-25 10:32 UTC710
                    .push((deletes_key(segment.id, generation), bytes));
c9effeac326aatagen2026-10-25 10:32 UTC711
                if segment.del_gen > 0 {
c9effeac326aatagen2026-10-25 10:32 UTC712
                    changes
c9effeac326aatagen2026-10-25 10:32 UTC713
                        .deletes
c9effeac326aatagen2026-10-25 10:32 UTC714
                        .push(deletes_key(segment.id, segment.del_gen));
c9effeac326aatagen2026-10-25 10:32 UTC715
                }
c9effeac326aatagen2026-10-25 10:32 UTC716
                changes.dels.push((segment.id, generation));
c9effeac326aatagen2026-10-25 10:32 UTC717
            }
c9effeac326aatagen2026-10-25 10:32 UTC718
        }
c9effeac326aatagen2026-10-25 10:32 UTC719
        Ok(changes)
c9effeac326aatagen2026-10-25 10:32 UTC720
    }
c9effeac326aatagen2026-10-25 10:32 UTC721
c9effeac326aatagen2026-10-25 10:32 UTC722
    fn committed(&mut self, changes: &Changes) {
c9effeac326aatagen2026-10-25 10:32 UTC723
        self.removed
c9effeac326aatagen2026-10-25 10:32 UTC724
            .retain(|(id, ..)| !changes.removed.contains(id));
c9effeac326aatagen2026-10-25 10:32 UTC725
        for segment in &mut self.segments {
c9effeac326aatagen2026-10-25 10:32 UTC726
            if changes.files.contains(&segment.id) {
c9effeac326aatagen2026-10-25 10:32 UTC727
                segment.persisted = true;
c9effeac326aatagen2026-10-25 10:32 UTC728
            }
c9effeac326aatagen2026-10-25 10:32 UTC729
            if let Some((_, generation)) = changes.dels.iter().find(|(id, _)| *id == segment.id) {
c9effeac326aatagen2026-10-25 10:32 UTC730
                segment.del_gen = *generation;
c9effeac326aatagen2026-10-25 10:32 UTC731
                segment.deletes_dirty = false;
c9effeac326aatagen2026-10-25 10:32 UTC732
            }
c9effeac326aatagen2026-10-25 10:32 UTC733
        }
c9effeac326aatagen2026-10-25 10:32 UTC734
    }
c9effeac326aatagen2026-10-25 10:32 UTC735
c9effeac326aatagen2026-10-25 10:32 UTC736
    fn manifest(&self) -> serde_json::Value {
c9effeac326aatagen2026-10-25 10:32 UTC737
        let segments: Vec<_> = self
c9effeac326aatagen2026-10-25 10:32 UTC738
            .records()
c9effeac326aatagen2026-10-25 10:32 UTC739
            .into_iter()
c9effeac326aatagen2026-10-25 10:32 UTC740
            .map(|record| serde_json::json!([record.id, record.del_gen]))
c9effeac326aatagen2026-10-25 10:32 UTC741
            .collect();
c9effeac326aatagen2026-10-25 10:32 UTC742
        serde_json::json!({ "segments": segments, "next": self.next_id })
c9effeac326aatagen2026-10-25 10:32 UTC743
    }
c9effeac326aatagen2026-10-25 10:32 UTC744
}
c9effeac326aatagen2026-10-25 10:32 UTC745
c9effeac326aatagen2026-10-25 10:32 UTC746
/// the `limit` newest live docs older than `before`, by timestamp and then key hash
c9effeac326aatagen2026-10-25 10:32 UTC747
struct Newest {
c9effeac326aatagen2026-10-25 10:32 UTC748
    alive: Arc<BitSet>,
c9effeac326aatagen2026-10-25 10:32 UTC749
    before: Option<(u64, u64)>,
c9effeac326aatagen2026-10-25 10:32 UTC750
    limit: usize,
c9effeac326aatagen2026-10-25 10:32 UTC751
}
c9effeac326aatagen2026-10-25 10:32 UTC752
c9effeac326aatagen2026-10-25 10:32 UTC753
struct NewestSegment {
c9effeac326aatagen2026-10-25 10:32 UTC754
    alive: Arc<BitSet>,
c9effeac326aatagen2026-10-25 10:32 UTC755
    before: Option<(u64, u64)>,
c9effeac326aatagen2026-10-25 10:32 UTC756
    limit: usize,
c9effeac326aatagen2026-10-25 10:32 UTC757
    ts: tantivy::columnar::Column<u64>,
c9effeac326aatagen2026-10-25 10:32 UTC758
    tie: tantivy::columnar::Column<u64>,
c9effeac326aatagen2026-10-25 10:32 UTC759
    segment: u32,
c9effeac326aatagen2026-10-25 10:32 UTC760
    top: std::collections::BinaryHeap<std::cmp::Reverse<(u64, u64, DocAddress)>>,
c9effeac326aatagen2026-10-25 10:32 UTC761
}
c9effeac326aatagen2026-10-25 10:32 UTC762
c9effeac326aatagen2026-10-25 10:32 UTC763
impl Collector for Newest {
c9effeac326aatagen2026-10-25 10:32 UTC764
    type Fruit = Vec<(u64, u64, DocAddress)>;
c9effeac326aatagen2026-10-25 10:32 UTC765
    type Child = NewestSegment;
c9effeac326aatagen2026-10-25 10:32 UTC766
c9effeac326aatagen2026-10-25 10:32 UTC767
    fn for_segment(&self, segment: u32, reader: &SegmentReader) -> tantivy::Result<NewestSegment> {
c9effeac326aatagen2026-10-25 10:32 UTC768
        Ok(NewestSegment {
c9effeac326aatagen2026-10-25 10:32 UTC769
            alive: self.alive.clone(),
c9effeac326aatagen2026-10-25 10:32 UTC770
            before: self.before,
c9effeac326aatagen2026-10-25 10:32 UTC771
            limit: self.limit,
c9effeac326aatagen2026-10-25 10:32 UTC772
            ts: reader.fast_fields().u64("ts")?,
c9effeac326aatagen2026-10-25 10:32 UTC773
            tie: reader.fast_fields().u64("tie")?,
c9effeac326aatagen2026-10-25 10:32 UTC774
            segment,
c9effeac326aatagen2026-10-25 10:32 UTC775
            top: std::collections::BinaryHeap::new(),
c9effeac326aatagen2026-10-25 10:32 UTC776
        })
c9effeac326aatagen2026-10-25 10:32 UTC777
    }
c9effeac326aatagen2026-10-25 10:32 UTC778
c9effeac326aatagen2026-10-25 10:32 UTC779
    fn requires_scoring(&self) -> bool {
c9effeac326aatagen2026-10-25 10:32 UTC780
        false
c9effeac326aatagen2026-10-25 10:32 UTC781
    }
c9effeac326aatagen2026-10-25 10:32 UTC782
c9effeac326aatagen2026-10-25 10:32 UTC783
    fn merge_fruits(&self, fruits: Vec<Self::Fruit>) -> tantivy::Result<Self::Fruit> {
c9effeac326aatagen2026-10-25 10:32 UTC784
        let mut all: Vec<_> = fruits.into_iter().flatten().collect();
c9effeac326aatagen2026-10-25 10:32 UTC785
        all.sort_by(|a, b| (b.0, b.1).cmp(&(a.0, a.1)));
c9effeac326aatagen2026-10-25 10:32 UTC786
        all.truncate(self.limit);
c9effeac326aatagen2026-10-25 10:32 UTC787
        Ok(all)
c9effeac326aatagen2026-10-25 10:32 UTC788
    }
c9effeac326aatagen2026-10-25 10:32 UTC789
}
c9effeac326aatagen2026-10-25 10:32 UTC790
c9effeac326aatagen2026-10-25 10:32 UTC791
impl SegmentCollector for NewestSegment {
c9effeac326aatagen2026-10-25 10:32 UTC792
    type Fruit = Vec<(u64, u64, DocAddress)>;
c9effeac326aatagen2026-10-25 10:32 UTC793
c9effeac326aatagen2026-10-25 10:32 UTC794
    fn collect(&mut self, doc: DocId, _: Score) {
c9effeac326aatagen2026-10-25 10:32 UTC795
        if !self.alive.contains(doc) {
c9effeac326aatagen2026-10-25 10:32 UTC796
            return;
c9effeac326aatagen2026-10-25 10:32 UTC797
        }
c9effeac326aatagen2026-10-25 10:32 UTC798
        let (Some(ts), Some(tie)) = (self.ts.first(doc), self.tie.first(doc)) else {
c9effeac326aatagen2026-10-25 10:32 UTC799
            return;
c9effeac326aatagen2026-10-25 10:32 UTC800
        };
c9effeac326aatagen2026-10-25 10:32 UTC801
        if self.before.is_some_and(|before| (ts, tie) >= before) {
c9effeac326aatagen2026-10-25 10:32 UTC802
            return;
c9effeac326aatagen2026-10-25 10:32 UTC803
        }
c9effeac326aatagen2026-10-25 10:32 UTC804
        let entry = std::cmp::Reverse((ts, tie, DocAddress::new(self.segment, doc)));
c9effeac326aatagen2026-10-25 10:32 UTC805
        if self.top.len() < self.limit {
c9effeac326aatagen2026-10-25 10:32 UTC806
            self.top.push(entry);
c9effeac326aatagen2026-10-25 10:32 UTC807
        } else if self.top.peek().is_some_and(|smallest| entry < *smallest) {
c9effeac326aatagen2026-10-25 10:32 UTC808
            self.top.pop();
c9effeac326aatagen2026-10-25 10:32 UTC809
            self.top.push(entry);
c9effeac326aatagen2026-10-25 10:32 UTC810
        }
c9effeac326aatagen2026-10-25 10:32 UTC811
    }
c9effeac326aatagen2026-10-25 10:32 UTC812
c9effeac326aatagen2026-10-25 10:32 UTC813
    fn harvest(self) -> Self::Fruit {
c9effeac326aatagen2026-10-25 10:32 UTC814
        let mut top: Vec<_> = self
c9effeac326aatagen2026-10-25 10:32 UTC815
            .top
c9effeac326aatagen2026-10-25 10:32 UTC816
            .into_iter()
c9effeac326aatagen2026-10-25 10:32 UTC817
            .map(|std::cmp::Reverse(entry)| entry)
c9effeac326aatagen2026-10-25 10:32 UTC818
            .collect();
c9effeac326aatagen2026-10-25 10:32 UTC819
        top.sort_by(|a, b| (b.0, b.1).cmp(&(a.0, a.1)));
c9effeac326aatagen2026-10-25 10:32 UTC820
        top
c9effeac326aatagen2026-10-25 10:32 UTC821
    }
c9effeac326aatagen2026-10-25 10:32 UTC822
}
c9effeac326aatagen2026-10-25 10:32 UTC823
c9effeac326aatagen2026-10-25 10:32 UTC824
#[cfg(test)]
c9effeac326aatagen2026-10-25 10:32 UTC825
#[path = "index_tests.rs"]
c9effeac326aatagen2026-10-25 10:32 UTC826
mod tests;