fix most of the tests

This commit is contained in:
Tamo 2021-11-06 01:32:12 +01:00
parent 070ec9bd97
commit b249989bef
No known key found for this signature in database
GPG Key ID: 20CD8020AFA88D69
4 changed files with 66 additions and 302 deletions

View File

@ -14,7 +14,7 @@ use crate::heed_codec::facet::{
}; };
use crate::{distance_between_two_points, CboRoaringBitmapCodec, FieldId, Index, Result}; use crate::{distance_between_two_points, CboRoaringBitmapCodec, FieldId, Index, Result};
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct Filter<'a> { pub struct Filter<'a> {
condition: FilterCondition<'a>, condition: FilterCondition<'a>,
} }
@ -45,7 +45,7 @@ impl<'a> Display for FilterError<'a> {
), ),
Self::BadGeo(keyword) => write!(f, "`{}` is a reserved keyword and thus can't be used as a filter expression. Use the _geoRadius(latitude, longitude, distance) built-in rule to filter on _geo field coordinates.", keyword), Self::BadGeo(keyword) => write!(f, "`{}` is a reserved keyword and thus can't be used as a filter expression. Use the _geoRadius(latitude, longitude, distance) built-in rule to filter on _geo field coordinates.", keyword),
Self::BadGeoLat(lat) => write!(f, "Bad latitude `{}`. Latitude must be contained between -90 and 90 degrees. ", lat), Self::BadGeoLat(lat) => write!(f, "Bad latitude `{}`. Latitude must be contained between -90 and 90 degrees. ", lat),
Self::BadGeoLng(lng) => write!(f, "Bad longitude `{}`. Latitude must be contained between -180 and 180 degrees. ", lng), Self::BadGeoLng(lng) => write!(f, "Bad longitude `{}`. Longitude must be contained between -180 and 180 degrees. ", lng),
} }
} }
} }
@ -426,275 +426,64 @@ mod tests {
use crate::update::Settings; use crate::update::Settings;
use crate::Index; use crate::Index;
#[test]
fn number() {
let path = tempfile::tempdir().unwrap();
let mut options = EnvOpenOptions::new();
options.map_size(10 * 1024 * 1024); // 10 MB
let index = Index::new(options, &path).unwrap();
// Set the filterable fields to be the channel.
let mut wtxn = index.write_txn().unwrap();
let mut map = index.fields_ids_map(&wtxn).unwrap();
map.insert("timestamp");
index.put_fields_ids_map(&mut wtxn, &map).unwrap();
let mut builder = Settings::new(&mut wtxn, &index, 0);
builder.set_filterable_fields(hashset! { "timestamp".into() });
builder.execute(|_, _| ()).unwrap();
wtxn.commit().unwrap();
// Test that the facet condition is correctly generated.
let rtxn = index.read_txn().unwrap();
let condition = FilterCondition::from_str(&rtxn, &index, "timestamp 22 TO 44").unwrap();
let expected = FilterCondition::Operator(0, Between(22.0, 44.0));
assert_eq!(condition, expected);
let condition = FilterCondition::from_str(&rtxn, &index, "NOT timestamp 22 TO 44").unwrap();
let expected = FilterCondition::Or(
Box::new(FilterCondition::Operator(0, LowerThan(22.0))),
Box::new(FilterCondition::Operator(0, GreaterThan(44.0))),
);
assert_eq!(condition, expected);
}
#[test]
fn compare() {
let path = tempfile::tempdir().unwrap();
let mut options = EnvOpenOptions::new();
options.map_size(10 * 1024 * 1024); // 10 MB
let index = Index::new(options, &path).unwrap();
let mut wtxn = index.write_txn().unwrap();
let mut builder = Settings::new(&mut wtxn, &index, 0);
builder.set_searchable_fields(vec![S("channel"), S("timestamp"), S("id")]); // to keep the fields order
builder.set_filterable_fields(hashset! { S("channel"), S("timestamp") ,S("id")});
builder.execute(|_, _| ()).unwrap();
wtxn.commit().unwrap();
let rtxn = index.read_txn().unwrap();
let condition = FilterCondition::from_str(&rtxn, &index, "channel < 20").unwrap();
let expected = FilterCondition::Operator(0, LowerThan(20.0));
assert_eq!(condition, expected);
let rtxn = index.read_txn().unwrap();
let condition = FilterCondition::from_str(&rtxn, &index, "id < 200").unwrap();
let expected = FilterCondition::Operator(2, LowerThan(200.0));
assert_eq!(condition, expected);
}
#[test]
fn parentheses() {
let path = tempfile::tempdir().unwrap();
let mut options = EnvOpenOptions::new();
options.map_size(10 * 1024 * 1024); // 10 MB
let index = Index::new(options, &path).unwrap();
// Set the filterable fields to be the channel.
let mut wtxn = index.write_txn().unwrap();
let mut builder = Settings::new(&mut wtxn, &index, 0);
builder.set_searchable_fields(vec![S("channel"), S("timestamp")]); // to keep the fields order
builder.set_filterable_fields(hashset! { S("channel"), S("timestamp") });
builder.execute(|_, _| ()).unwrap();
wtxn.commit().unwrap();
// Test that the facet condition is correctly generated.
let rtxn = index.read_txn().unwrap();
let condition = FilterCondition::from_str(
&rtxn,
&index,
"channel = gotaga OR (timestamp 22 TO 44 AND channel != ponce)",
)
.unwrap();
let expected = FilterCondition::Or(
Box::new(FilterCondition::Operator(0, Operator::Equal(None, S("gotaga")))),
Box::new(FilterCondition::And(
Box::new(FilterCondition::Operator(1, Between(22.0, 44.0))),
Box::new(FilterCondition::Operator(0, Operator::NotEqual(None, S("ponce")))),
)),
);
assert_eq!(condition, expected);
let condition = FilterCondition::from_str(
&rtxn,
&index,
"channel = gotaga OR NOT (timestamp 22 TO 44 AND channel != ponce)",
)
.unwrap();
let expected = FilterCondition::Or(
Box::new(FilterCondition::Operator(0, Operator::Equal(None, S("gotaga")))),
Box::new(FilterCondition::Or(
Box::new(FilterCondition::Or(
Box::new(FilterCondition::Operator(1, LowerThan(22.0))),
Box::new(FilterCondition::Operator(1, GreaterThan(44.0))),
)),
Box::new(FilterCondition::Operator(0, Operator::Equal(None, S("ponce")))),
)),
);
assert_eq!(condition, expected);
}
#[test] #[test]
fn from_array() { fn from_array() {
let path = tempfile::tempdir().unwrap();
let mut options = EnvOpenOptions::new();
options.map_size(10 * 1024 * 1024); // 10 MB
let index = Index::new(options, &path).unwrap();
// Set the filterable fields to be the channel.
let mut wtxn = index.write_txn().unwrap();
let mut builder = Settings::new(&mut wtxn, &index, 0);
builder.set_searchable_fields(vec![S("channel"), S("timestamp")]); // to keep the fields order
builder.set_filterable_fields(hashset! { S("channel"), S("timestamp") });
builder.execute(|_, _| ()).unwrap();
wtxn.commit().unwrap();
// Simple array with Left // Simple array with Left
let rtxn = index.read_txn().unwrap(); let condition = Filter::from_array(vec![Either::Left(["channel = mv"])]).unwrap().unwrap();
let condition = FilterCondition::from_array::<_, _, _, &str>( let expected = Filter::from_str("channel = mv").unwrap();
&rtxn,
&index,
vec![Either::Left(["channel = mv"])],
)
.unwrap()
.unwrap();
let expected = FilterCondition::from_str(&rtxn, &index, "channel = mv").unwrap();
assert_eq!(condition, expected); assert_eq!(condition, expected);
// Simple array with Right // Simple array with Right
let rtxn = index.read_txn().unwrap(); let condition = Filter::from_array::<_, Option<&str>>(vec![Either::Right("channel = mv")])
let condition = FilterCondition::from_array::<_, Option<&str>, _, _>(
&rtxn,
&index,
vec![Either::Right("channel = mv")],
)
.unwrap() .unwrap()
.unwrap(); .unwrap();
let expected = FilterCondition::from_str(&rtxn, &index, "channel = mv").unwrap(); let expected = Filter::from_str("channel = mv").unwrap();
assert_eq!(condition, expected); assert_eq!(condition, expected);
// Array with Left and escaped quote // Array with Left and escaped quote
let rtxn = index.read_txn().unwrap(); let condition =
let condition = FilterCondition::from_array::<_, _, _, &str>( Filter::from_array(vec![Either::Left(["channel = \"Mister Mv\""])]).unwrap().unwrap();
&rtxn, let expected = Filter::from_str("channel = \"Mister Mv\"").unwrap();
&index,
vec![Either::Left(["channel = \"Mister Mv\""])],
)
.unwrap()
.unwrap();
let expected = FilterCondition::from_str(&rtxn, &index, "channel = \"Mister Mv\"").unwrap();
assert_eq!(condition, expected); assert_eq!(condition, expected);
// Array with Right and escaped quote // Array with Right and escaped quote
let rtxn = index.read_txn().unwrap(); let condition =
let condition = FilterCondition::from_array::<_, Option<&str>, _, _>( Filter::from_array::<_, Option<&str>>(vec![Either::Right("channel = \"Mister Mv\"")])
&rtxn,
&index,
vec![Either::Right("channel = \"Mister Mv\"")],
)
.unwrap() .unwrap()
.unwrap(); .unwrap();
let expected = FilterCondition::from_str(&rtxn, &index, "channel = \"Mister Mv\"").unwrap(); let expected = Filter::from_str("channel = \"Mister Mv\"").unwrap();
assert_eq!(condition, expected); assert_eq!(condition, expected);
// Array with Left and escaped simple quote // Array with Left and escaped simple quote
let rtxn = index.read_txn().unwrap(); let condition =
let condition = FilterCondition::from_array::<_, _, _, &str>( Filter::from_array(vec![Either::Left(["channel = 'Mister Mv'"])]).unwrap().unwrap();
&rtxn, let expected = Filter::from_str("channel = 'Mister Mv'").unwrap();
&index,
vec![Either::Left(["channel = 'Mister Mv'"])],
)
.unwrap()
.unwrap();
let expected = FilterCondition::from_str(&rtxn, &index, "channel = 'Mister Mv'").unwrap();
assert_eq!(condition, expected); assert_eq!(condition, expected);
// Array with Right and escaped simple quote // Array with Right and escaped simple quote
let rtxn = index.read_txn().unwrap(); let condition =
let condition = FilterCondition::from_array::<_, Option<&str>, _, _>( Filter::from_array::<_, Option<&str>>(vec![Either::Right("channel = 'Mister Mv'")])
&rtxn,
&index,
vec![Either::Right("channel = 'Mister Mv'")],
)
.unwrap() .unwrap()
.unwrap(); .unwrap();
let expected = FilterCondition::from_str(&rtxn, &index, "channel = 'Mister Mv'").unwrap(); let expected = Filter::from_str("channel = 'Mister Mv'").unwrap();
assert_eq!(condition, expected); assert_eq!(condition, expected);
// Simple with parenthesis // Simple with parenthesis
let rtxn = index.read_txn().unwrap(); let condition =
let condition = FilterCondition::from_array::<_, _, _, &str>( Filter::from_array(vec![Either::Left(["(channel = mv)"])]).unwrap().unwrap();
&rtxn, let expected = Filter::from_str("(channel = mv)").unwrap();
&index,
vec![Either::Left(["(channel = mv)"])],
)
.unwrap()
.unwrap();
let expected = FilterCondition::from_str(&rtxn, &index, "(channel = mv)").unwrap();
assert_eq!(condition, expected); assert_eq!(condition, expected);
// Test that the facet condition is correctly generated. // Test that the facet condition is correctly generated.
let rtxn = index.read_txn().unwrap(); let condition = Filter::from_array(vec![
let condition = FilterCondition::from_array(
&rtxn,
&index,
vec![
Either::Right("channel = gotaga"), Either::Right("channel = gotaga"),
Either::Left(vec!["timestamp = 44", "channel != ponce"]), Either::Left(vec!["timestamp = 44", "channel != ponce"]),
], ])
)
.unwrap() .unwrap()
.unwrap(); .unwrap();
let expected = FilterCondition::from_str( let expected =
&rtxn, Filter::from_str("channel = gotaga AND (timestamp = 44 OR channel != ponce)").unwrap();
&index, println!("\nExpecting: {:#?}\nGot: {:#?}\n", expected, condition);
"channel = gotaga AND (timestamp = 44 OR channel != ponce)",
)
.unwrap();
assert_eq!(condition, expected);
}
#[test]
fn geo_radius() {
let path = tempfile::tempdir().unwrap();
let mut options = EnvOpenOptions::new();
options.map_size(10 * 1024 * 1024); // 10 MB
let index = Index::new(options, &path).unwrap();
// Set the filterable fields to be the channel.
let mut wtxn = index.write_txn().unwrap();
let mut builder = Settings::new(&mut wtxn, &index, 0);
builder.set_searchable_fields(vec![S("_geo"), S("price")]); // to keep the fields order
builder.set_filterable_fields(hashset! { S("_geo"), S("price") });
builder.execute(|_, _| ()).unwrap();
wtxn.commit().unwrap();
let rtxn = index.read_txn().unwrap();
// basic test
let condition =
FilterCondition::from_str(&rtxn, &index, "_geoRadius(12, 13.0005, 2000)").unwrap();
let expected = FilterCondition::Operator(0, GeoLowerThan([12., 13.0005], 2000.));
assert_eq!(condition, expected);
// test the negation of the GeoLowerThan
let condition =
FilterCondition::from_str(&rtxn, &index, "NOT _geoRadius(50, 18, 2000.500)").unwrap();
let expected = FilterCondition::Operator(0, GeoGreaterThan([50., 18.], 2000.500));
assert_eq!(condition, expected);
// composition of multiple operations
let condition = FilterCondition::from_str(
&rtxn,
&index,
"(NOT _geoRadius(1, 2, 300) AND _geoRadius(1.001, 2.002, 1000.300)) OR price <= 10",
)
.unwrap();
let expected = FilterCondition::Or(
Box::new(FilterCondition::And(
Box::new(FilterCondition::Operator(0, GeoGreaterThan([1., 2.], 300.))),
Box::new(FilterCondition::Operator(0, GeoLowerThan([1.001, 2.002], 1000.300))),
)),
Box::new(FilterCondition::Operator(1, LowerThanOrEqual(10.))),
);
assert_eq!(condition, expected); assert_eq!(condition, expected);
} }
@ -715,62 +504,40 @@ mod tests {
let rtxn = index.read_txn().unwrap(); let rtxn = index.read_txn().unwrap();
// georadius don't have any parameters // georadius have a bad latitude
let result = FilterCondition::from_str(&rtxn, &index, "_geoRadius"); let filter = Filter::from_str("_geoRadius(-100, 150, 10)").unwrap();
assert!(result.is_err()); let error = filter.evaluate(&rtxn, &index).unwrap_err();
let error = result.unwrap_err();
assert!(error.to_string().contains("The `_geoRadius` filter expect three arguments: `_geoRadius(latitude, longitude, radius)`"));
// georadius don't have any parameters
let result = FilterCondition::from_str(&rtxn, &index, "_geoRadius()");
assert!(result.is_err());
let error = result.unwrap_err();
assert!(error.to_string().contains("The `_geoRadius` filter expect three arguments: `_geoRadius(latitude, longitude, radius)`"));
// georadius don't have enough parameters
let result = FilterCondition::from_str(&rtxn, &index, "_geoRadius(1, 2)");
assert!(result.is_err());
let error = result.unwrap_err();
assert!(error.to_string().contains("The `_geoRadius` filter expect three arguments: `_geoRadius(latitude, longitude, radius)`"));
// georadius have too many parameters
let result =
FilterCondition::from_str(&rtxn, &index, "_geoRadius(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)");
assert!(result.is_err());
let error = result.unwrap_err();
assert!(error.to_string().contains("The `_geoRadius` filter expect three arguments: `_geoRadius(latitude, longitude, radius)`"));
let result = FilterCondition::from_str(&rtxn, &index, "_geoRadius(-100, 150, 10)");
assert!(result.is_err());
let error = result.unwrap_err();
assert!( assert!(
error.to_string().contains("Latitude must be contained between -90 and 90 degrees."), error.to_string().starts_with(
"Bad latitude `-100`. Latitude must be contained between -90 and 90 degrees."
),
"{}", "{}",
error.to_string() error.to_string()
); );
// georadius have a bad latitude // georadius have a bad latitude
let result = FilterCondition::from_str(&rtxn, &index, "_geoRadius(-90.0000001, 150, 10)"); let filter = Filter::from_str("_geoRadius(-90.0000001, 150, 10)").unwrap();
assert!(result.is_err()); let error = filter.evaluate(&rtxn, &index).unwrap_err();
let error = result.unwrap_err(); assert!(error.to_string().contains(
assert!(error "Bad latitude `-90.0000001`. Latitude must be contained between -90 and 90 degrees."
.to_string() ));
.contains("Latitude must be contained between -90 and 90 degrees."));
// georadius have a bad longitude // georadius have a bad longitude
let result = FilterCondition::from_str(&rtxn, &index, "_geoRadius(-10, 250, 10)"); let filter = Filter::from_str("_geoRadius(-10, 250, 10)").unwrap();
assert!(result.is_err()); let error = filter.evaluate(&rtxn, &index).unwrap_err();
let error = result.unwrap_err(); assert!(
assert!(error error.to_string().contains(
.to_string() "Bad longitude `250`. Longitude must be contained between -180 and 180 degrees."
.contains("Longitude must be contained between -180 and 180 degrees.")); ),
"{}",
error.to_string(),
);
// georadius have a bad longitude // georadius have a bad longitude
let result = FilterCondition::from_str(&rtxn, &index, "_geoRadius(-10, 180.000001, 10)"); let filter = Filter::from_str("_geoRadius(-10, 180.000001, 10)").unwrap();
assert!(result.is_err()); let error = filter.evaluate(&rtxn, &index).unwrap_err();
let error = result.unwrap_err(); assert!(error.to_string().contains(
assert!(error "Bad longitude `180.000001`. Longitude must be contained between -180 and 180 degrees."
.to_string() ));
.contains("Longitude must be contained between -180 and 180 degrees."));
} }
} }

View File

@ -567,7 +567,7 @@ mod tests {
use super::*; use super::*;
use crate::update::{IndexDocuments, Settings}; use crate::update::{IndexDocuments, Settings};
use crate::FilterCondition; use crate::Filter;
#[test] #[test]
fn delete_documents_with_numbers_as_primary_key() { fn delete_documents_with_numbers_as_primary_key() {
@ -667,7 +667,7 @@ mod tests {
builder.delete_external_id("1_4"); builder.delete_external_id("1_4");
builder.execute().unwrap(); builder.execute().unwrap();
let filter = FilterCondition::from_str(&wtxn, &index, "label = sign").unwrap(); let filter = Filter::from_str("label = sign").unwrap();
let results = index.search(&wtxn).filter(filter).execute().unwrap(); let results = index.search(&wtxn).filter(filter).execute().unwrap();
assert!(results.documents_ids.is_empty()); assert!(results.documents_ids.is_empty());

View File

@ -524,7 +524,7 @@ mod tests {
use super::*; use super::*;
use crate::error::Error; use crate::error::Error;
use crate::update::IndexDocuments; use crate::update::IndexDocuments;
use crate::{Criterion, FilterCondition, SearchResult}; use crate::{Criterion, Filter, SearchResult};
#[test] #[test]
fn set_and_reset_searchable_fields() { fn set_and_reset_searchable_fields() {
@ -1066,7 +1066,8 @@ mod tests {
wtxn.commit().unwrap(); wtxn.commit().unwrap();
let rtxn = index.read_txn().unwrap(); let rtxn = index.read_txn().unwrap();
FilterCondition::from_str(&rtxn, &index, "toto = 32").unwrap_err(); let filter = Filter::from_str("toto = 32").unwrap();
let _ = filter.evaluate(&rtxn, &index).unwrap_err();
} }
#[test] #[test]

View File

@ -1,5 +1,5 @@
use either::{Either, Left, Right}; use either::{Either, Left, Right};
use milli::{Criterion, FilterCondition, Search, SearchResult}; use milli::{Criterion, Filter, Search, SearchResult};
use Criterion::*; use Criterion::*;
use crate::search::{self, EXTERNAL_DOCUMENTS_IDS}; use crate::search::{self, EXTERNAL_DOCUMENTS_IDS};
@ -13,11 +13,7 @@ macro_rules! test_filter {
let rtxn = index.read_txn().unwrap(); let rtxn = index.read_txn().unwrap();
let filter_conditions = let filter_conditions =
FilterCondition::from_array::<Vec<Either<Vec<&str>, &str>>, _, _, _>( Filter::from_array::<Vec<Either<Vec<&str>, &str>>, _>($filter).unwrap().unwrap();
&rtxn, &index, $filter,
)
.unwrap()
.unwrap();
let mut search = Search::new(&rtxn, &index); let mut search = Search::new(&rtxn, &index);
search.query(search::TEST_QUERY); search.query(search::TEST_QUERY);