Add a test on filterable attributes rules priority

**Changes:**
- Add a new test playing with filterable attributes rules priority
- Optimize the faceted field selector avoiding to match false positives
This commit is contained in:
ManyTheFish 2025-03-05 09:44:52 +01:00
parent a7a62e5e4c
commit 5fa4b5c50a
2 changed files with 154 additions and 6 deletions

View File

@ -623,3 +623,136 @@ async fn search_with_pattern_filter_settings_scenario_1() {
)
.await;
}
#[actix_rt::test]
async fn test_filterable_attributes_priority() {
// Test that the filterable attributes priority is respected
// check if doggos.name is filterable
test_settings_documents_indexing_swapping_and_search(
&NESTED_DOCUMENTS,
&json!({"filterableAttributes": [
// deactivated filter
{"patterns": ["doggos.a*"], "features": {"facetSearch": false, "filter": {"equality": false, "comparison": false}}},
// activated filter
{"patterns": ["doggos.*"]},
]}),
&json!({
"filter": "doggos.name = bobby"
}),
|response, code| {
snapshot!(code, @"200 OK");
snapshot!(json_string!(response["hits"]), @r###"
[
{
"id": 852,
"father": "jean",
"mother": "michelle",
"doggos": [
{
"name": "bobby",
"age": 2
},
{
"name": "buddy",
"age": 4
}
],
"cattos": "pésti"
}
]
"###);
},
)
.await;
// check if doggos.name is filterable 2
test_settings_documents_indexing_swapping_and_search(
&NESTED_DOCUMENTS,
&json!({"filterableAttributes": [
// deactivated filter
{"patterns": ["doggos"], "features": {"facetSearch": false, "filter": {"equality": false, "comparison": false}}},
// activated filter
{"patterns": ["doggos.*"]},
]}),
&json!({
"filter": "doggos.name = bobby"
}),
|response, code| {
snapshot!(code, @"200 OK");
snapshot!(json_string!(response["hits"]), @r###"
[
{
"id": 852,
"father": "jean",
"mother": "michelle",
"doggos": [
{
"name": "bobby",
"age": 2
},
{
"name": "buddy",
"age": 4
}
],
"cattos": "pésti"
}
]
"###);
},
)
.await;
// check if doggos.age is not filterable
test_settings_documents_indexing_swapping_and_search(
&NESTED_DOCUMENTS,
&json!({"filterableAttributes": [
// deactivated filter
{"patterns": ["doggos.a*"], "features": {"facetSearch": false, "filter": {"equality": false, "comparison": false}}},
// activated filter
{"patterns": ["doggos.*"]},
]}),
&json!({
"filter": "doggos.age > 2"
}),
|response, code| {
snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###"
{
"message": "Index `test`: Attribute `doggos.age` is not filterable. Available filterable attributes are: `doggos.age`, `doggos.name`.\n1:11 doggos.age > 2",
"code": "invalid_search_filter",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"
}
"###);
},
)
.await;
// check if doggos is not filterable
test_settings_documents_indexing_swapping_and_search(
&NESTED_DOCUMENTS,
&json!({"filterableAttributes": [
// deactivated filter
{"patterns": ["doggos"], "features": {"facetSearch": false, "filter": {"equality": false, "comparison": false}}},
// activated filter
{"patterns": ["doggos.*"]},
]}),
&json!({
"filter": "doggos EXISTS"
}),
|response, code| {
snapshot!(code, @"400 Bad Request");
snapshot!(json_string!(response), @r###"
{
"message": "Index `test`: Attribute `doggos` is not filterable. Available filterable attributes are: `doggos.age`, `doggos.name`.\n1:7 doggos EXISTS",
"code": "invalid_search_filter",
"type": "invalid_request",
"link": "https://docs.meilisearch.com/errors#invalid_search_filter"
}
"###);
},
)
.await;
}

View File

@ -342,15 +342,30 @@ fn match_pattern_by_features(
filter: &impl Fn(&FilterableAttributesFeatures) -> bool,
) -> PatternMatch {
let mut selection = PatternMatch::NoMatch;
// `can_match` becomes false if the field name matches (PatternMatch::Match) any pattern that is not facet searchable or filterable,
// this ensures that the field doesn't match a pattern with a lower priority, however it can still match a pattern for a nested field as a parent (PatternMatch::Parent).
// See the test `search::filters::test_filterable_attributes_priority` for more details.
let mut can_match = true;
// Check if the field name matches any pattern that is facet searchable or filterable
for pattern in filterable_attributes {
let features = pattern.features();
if filter(&features) {
match pattern.match_str(field_name) {
PatternMatch::Match => return PatternMatch::Match,
PatternMatch::Parent => selection = PatternMatch::Parent,
PatternMatch::NoMatch => (),
match pattern.match_str(field_name) {
PatternMatch::Match => {
let features = pattern.features();
if filter(&features) && can_match {
return PatternMatch::Match;
} else {
can_match = false;
}
}
PatternMatch::Parent => {
let features = pattern.features();
if filter(&features) {
selection = PatternMatch::Parent;
}
}
PatternMatch::NoMatch => (),
}
}