use crate::rowid::RowId;
use std::collections::HashMap;
pub struct SparseIndex {
postings: HashMap<u32, Vec<(RowId, f32)>>,
}
impl SparseIndex {
pub fn new() -> Self {
Self {
postings: HashMap::new(),
}
}
pub fn insert(&mut self, terms: &[(u32, f32)], row_id: RowId) {
for &(token, weight) in terms {
self.postings
.entry(token)
.or_default()
.push((row_id, weight));
}
}
pub fn search(&self, query: &[(u32, f32)], k: usize) -> Vec<(RowId, f32)> {
let mut scores: HashMap<u64, f32> = HashMap::new();
for &(token, q_weight) in query {
if let Some(list) = self.postings.get(&token) {
for &(rid, d_weight) in list {
*scores.entry(rid.0).or_insert(0.0) += q_weight * d_weight;
}
}
}
let mut ranked: Vec<(RowId, f32)> = scores
.into_iter()
.map(|(rid, score)| (RowId(rid), score))
.collect();
ranked.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
ranked.truncate(k);
ranked
}
pub fn is_empty(&self) -> bool {
self.postings.is_empty()
}
pub fn entries(&self) -> Vec<(u32, Vec<(RowId, f32)>)> {
self.postings
.iter()
.map(|(t, list)| (*t, list.clone()))
.collect()
}
pub fn from_entries(entries: Vec<(u32, Vec<(RowId, f32)>)>) -> Self {
let mut postings = HashMap::new();
for (t, list) in entries {
postings.insert(t, list);
}
Self { postings }
}
}
impl Default for SparseIndex {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ranks_by_sparse_overlap() {
let mut idx = SparseIndex::new();
idx.insert(&[(1, 2.0), (2, 1.0)], RowId(0));
idx.insert(&[(1, 1.0), (3, 3.0)], RowId(1));
idx.insert(&[(2, 5.0)], RowId(2));
let top = idx.search(&[(1, 1.0), (2, 1.0)], 3);
assert_eq!(top[0], (RowId(2), 5.0));
assert_eq!(top[1], (RowId(0), 3.0));
assert_eq!(top[2], (RowId(1), 1.0));
}
}