Additional search tests

This commit is contained in:
Antoine Gersant 2024-09-21 22:51:23 -07:00
parent 390ee03020
commit 409d79d8a2

View file

@ -83,7 +83,7 @@ impl Search {
Literal::Text(s) => { Literal::Text(s) => {
let mut songs = IntSet::default(); let mut songs = IntSet::default();
for field in self.text_fields.values() { for field in self.text_fields.values() {
songs.extend(field.find_like(strings, s)); songs.extend(field.find_like(s));
} }
songs songs
} }
@ -113,7 +113,7 @@ impl Search {
match operator { match operator {
TextOp::Eq => field_index.find_exact(strings, value), TextOp::Eq => field_index.find_exact(strings, value),
TextOp::Like => field_index.find_like(strings, value), TextOp::Like => field_index.find_like(value),
} }
} }
@ -150,7 +150,7 @@ impl TextFieldIndex {
self.exact.entry(value).or_default().insert(key); self.exact.entry(value).or_default().insert(key);
} }
pub fn find_like(&self, strings: &RodeoReader, value: &str) -> IntSet<SongKey> { pub fn find_like(&self, value: &str) -> IntSet<SongKey> {
let characters = value.chars().collect::<Vec<_>>(); let characters = value.chars().collect::<Vec<_>>();
let empty_set = IntSet::default(); let empty_set = IntSet::default();
@ -172,7 +172,9 @@ impl TextFieldIndex {
candidates[0] candidates[0]
.iter() .iter()
.filter(move |c| candidates[1..].iter().all(|s| s.contains(c))) .filter(move |c| candidates[1..].iter().all(|s| s.contains(c)))
.filter(|s| strings.resolve(&s.virtual_path.0).contains(value)) // Note: matching all the n-grams doesn't actually guarantee a substring match
// We should theoretically resolve the underlying field value and compare with the query string
// Unlikely to cause issues for realistic use cases.
.copied() .copied()
.collect() .collect()
} }
@ -370,4 +372,79 @@ mod test {
assert_eq!(songs.len(), 1); assert_eq!(songs.len(), 1);
assert!(songs.contains(&PathBuf::from("seasons.mp3"))); assert!(songs.contains(&PathBuf::from("seasons.mp3")));
} }
#[test]
fn can_find_field_exact() {
let (search, strings) = setup_test(vec![
scanner::Song {
virtual_path: PathBuf::from("seasons.mp3"),
title: Some("Seasons".to_owned()),
artists: vec!["Dragonforce".to_owned()],
..Default::default()
},
scanner::Song {
virtual_path: PathBuf::from("potd.mp3"),
title: Some("Power of the Dragonflame".to_owned()),
artists: vec!["Rhapsody".to_owned()],
..Default::default()
},
]);
let songs = search.find_songs(&strings, "artist = Dragon").unwrap();
assert!(songs.is_empty());
let songs = search.find_songs(&strings, "artist = Dragonforce").unwrap();
assert_eq!(songs.len(), 1);
assert!(songs.contains(&PathBuf::from("seasons.mp3")));
}
#[test]
fn can_use_and_operator() {
let (search, strings) = setup_test(vec![
scanner::Song {
virtual_path: PathBuf::from("whale.mp3"),
..Default::default()
},
scanner::Song {
virtual_path: PathBuf::from("space.mp3"),
..Default::default()
},
scanner::Song {
virtual_path: PathBuf::from("whales in space.mp3"),
..Default::default()
},
]);
let songs = search.find_songs(&strings, "space && whale").unwrap();
assert_eq!(songs.len(), 1);
assert!(songs.contains(&PathBuf::from("whales in space.mp3")));
let songs = search.find_songs(&strings, "space whale").unwrap();
assert_eq!(songs.len(), 1);
assert!(songs.contains(&PathBuf::from("whales in space.mp3")));
}
#[test]
fn can_use_or_operator() {
let (search, strings) = setup_test(vec![
scanner::Song {
virtual_path: PathBuf::from("whale.mp3"),
..Default::default()
},
scanner::Song {
virtual_path: PathBuf::from("space.mp3"),
..Default::default()
},
scanner::Song {
virtual_path: PathBuf::from("whales in space.mp3"),
..Default::default()
},
]);
let songs = search.find_songs(&strings, "space || whale").unwrap();
assert_eq!(songs.len(), 3);
assert!(songs.contains(&PathBuf::from("whale.mp3")));
assert!(songs.contains(&PathBuf::from("space.mp3")));
assert!(songs.contains(&PathBuf::from("whales in space.mp3")));
}
} }