Skip to main content

nodedb_fts/index/
stats.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Corpus statistics for BM25 scoring.
4
5use crate::backend::FtsBackend;
6use crate::index::FtsIndex;
7
8impl<B: FtsBackend> FtsIndex<B> {
9    /// Get total document count and average document length for a collection.
10    ///
11    /// Returns `(total_docs, avg_doc_len)`. If the collection is empty,
12    /// returns `(0, 1.0)` to avoid division by zero.
13    pub fn index_stats(
14        &self,
15        database_id: u64,
16        tid: u64,
17        collection: &str,
18    ) -> Result<(u32, f32), B::Error> {
19        let (count, total_len) = self
20            .backend
21            .collection_stats(database_id, tid, collection)?;
22        let avg = if count > 0 {
23            total_len as f32 / count as f32
24        } else {
25            1.0
26        };
27        Ok((count, avg))
28    }
29}
30
31#[cfg(test)]
32mod tests {
33    use crate::backend::memory::MemoryBackend;
34    use crate::index::FtsIndex;
35    use nodedb_types::Surrogate;
36
37    const DB: u64 = 0;
38    const T: u64 = 1;
39
40    #[test]
41    fn empty_collection_stats() {
42        let idx: FtsIndex<MemoryBackend> = FtsIndex::new(MemoryBackend::new());
43        let (count, avg) = idx.index_stats(DB, T, "empty").unwrap();
44        assert_eq!(count, 0);
45        assert!((avg - 1.0).abs() < f32::EPSILON);
46    }
47
48    #[test]
49    fn stats_after_indexing() {
50        let idx = FtsIndex::new(MemoryBackend::new());
51        idx.index_document(DB, T, "docs", Surrogate(1), "hello world greeting")
52            .unwrap();
53        idx.index_document(DB, T, "docs", Surrogate(2), "hello rust")
54            .unwrap();
55
56        let (count, avg) = idx.index_stats(DB, T, "docs").unwrap();
57        assert_eq!(count, 2);
58        assert!(avg > 0.0);
59    }
60}