use std::sync::Arc;
use laurus::Document;
use laurus::lexical::query::Query;
use laurus::lexical::query::fuzzy::FuzzyQuery;
use laurus::lexical::query::prefix::PrefixQuery;
use laurus::lexical::query::regexp::RegexpQuery;
use laurus::lexical::query::wildcard::WildcardQuery;
use laurus::lexical::{LexicalIndexConfig, LexicalSearchRequest, LexicalStore};
use laurus::storage::Storage;
use laurus::storage::memory::{MemoryStorage, MemoryStorageConfig};
fn doc(body: &str) -> Document {
Document::builder().add_text("body", body).build()
}
const CORPUS: [(u64, &str); 4] = [
(1, "programming in rust"),
(2, "a programmer at work"),
(3, "program notes for the concert"),
(4, "python coding session"),
];
fn store_with_segments(n_segments: usize) -> (LexicalStore, Arc<dyn Storage>) {
let storage: Arc<dyn Storage> = Arc::new(MemoryStorage::new(MemoryStorageConfig::default()));
let config = LexicalIndexConfig::builder().max_segments(1000).build();
let store = LexicalStore::new(storage.clone(), config).unwrap();
let chunk = CORPUS.len().div_ceil(n_segments);
for group in CORPUS.chunks(chunk) {
for (id, body) in group {
store.upsert_document(*id, doc(body)).unwrap();
}
store.commit().unwrap();
}
(store, storage)
}
fn search_ids(store: &LexicalStore, query: Box<dyn Query>) -> Vec<u64> {
let mut ids: Vec<u64> = store
.search(LexicalSearchRequest::new(query).limit(10))
.unwrap()
.hits
.iter()
.map(|h| h.doc_id)
.collect();
ids.sort_unstable();
ids
}
fn assert_multi_term_hits(store: &LexicalStore, label: &str) {
assert_eq!(
search_ids(store, Box::new(PrefixQuery::new("body", "program"))),
vec![1, 2, 3],
"{label}: PrefixQuery(body, program)"
);
assert_eq!(
search_ids(
store,
Box::new(WildcardQuery::new("body", "program*").unwrap())
),
vec![1, 2, 3],
"{label}: WildcardQuery(body, program*)"
);
assert_eq!(
search_ids(
store,
Box::new(FuzzyQuery::new("body", "programing").max_edits(1))
),
vec![1],
"{label}: FuzzyQuery(body, programing, 1 edit)"
);
assert_eq!(
search_ids(
store,
Box::new(RegexpQuery::new("body", "program.*").unwrap())
),
vec![1, 2, 3],
"{label}: RegexpQuery(body, program.*)"
);
}
#[test]
fn multi_term_queries_hit_on_single_segment() {
let (store, storage) = store_with_segments(1);
let segs = storage
.list_files()
.unwrap()
.iter()
.filter(|f| f.starts_with("segment_") && f.ends_with(".meta"))
.count();
assert_eq!(segs, 1, "harness must produce exactly 1 segment");
assert_multi_term_hits(&store, "single-segment");
}
#[test]
fn multi_term_queries_hit_on_multi_segment() {
let (store, storage) = store_with_segments(2);
let segs = storage
.list_files()
.unwrap()
.iter()
.filter(|f| f.starts_with("segment_") && f.ends_with(".meta"))
.count();
assert_eq!(segs, 2, "harness must produce exactly 2 segments");
assert_multi_term_hits(&store, "multi-segment");
}