mirror of
https://github.com/meilisearch/meilisearch.git
synced 2024-12-02 10:05:05 +08:00
420 lines
10 KiB
Rust
420 lines
10 KiB
Rust
|
//! This module test the search cutoff and ensure a few things:
|
||
|
//! 1. A basic test works and mark the search as degraded
|
||
|
//! 2. A test that ensure the filters are affectively applied even with a cutoff of 0
|
||
|
//! 3. A test that ensure the cutoff works well with the ranking scores
|
||
|
|
||
|
use std::time::Duration;
|
||
|
|
||
|
use big_s::S;
|
||
|
use maplit::hashset;
|
||
|
use meili_snap::snapshot;
|
||
|
|
||
|
use crate::index::tests::TempIndex;
|
||
|
use crate::{Criterion, Filter, Search, TimeBudget};
|
||
|
|
||
|
fn create_index() -> TempIndex {
|
||
|
let index = TempIndex::new();
|
||
|
|
||
|
index
|
||
|
.update_settings(|s| {
|
||
|
s.set_primary_key("id".to_owned());
|
||
|
s.set_searchable_fields(vec!["text".to_owned()]);
|
||
|
s.set_filterable_fields(hashset! { S("id") });
|
||
|
s.set_criteria(vec![Criterion::Words, Criterion::Typo]);
|
||
|
})
|
||
|
.unwrap();
|
||
|
|
||
|
// reverse the ID / insertion order so we see better what was sorted from what got the insertion order ordering
|
||
|
index
|
||
|
.add_documents(documents!([
|
||
|
{
|
||
|
"id": 4,
|
||
|
"text": "hella puppo kefir",
|
||
|
},
|
||
|
{
|
||
|
"id": 3,
|
||
|
"text": "hella puppy kefir",
|
||
|
},
|
||
|
{
|
||
|
"id": 2,
|
||
|
"text": "hello",
|
||
|
},
|
||
|
{
|
||
|
"id": 1,
|
||
|
"text": "hello puppy",
|
||
|
},
|
||
|
{
|
||
|
"id": 0,
|
||
|
"text": "hello puppy kefir",
|
||
|
},
|
||
|
]))
|
||
|
.unwrap();
|
||
|
index
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn basic_degraded_search() {
|
||
|
let index = create_index();
|
||
|
let rtxn = index.read_txn().unwrap();
|
||
|
|
||
|
let mut search = Search::new(&rtxn, &index);
|
||
|
search.query("hello puppy kefir");
|
||
|
search.limit(3);
|
||
|
search.time_budget(TimeBudget::new(Duration::from_millis(0)));
|
||
|
|
||
|
let result = search.execute().unwrap();
|
||
|
assert!(result.degraded);
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn degraded_search_cannot_skip_filter() {
|
||
|
let index = create_index();
|
||
|
let rtxn = index.read_txn().unwrap();
|
||
|
|
||
|
let mut search = Search::new(&rtxn, &index);
|
||
|
search.query("hello puppy kefir");
|
||
|
search.limit(100);
|
||
|
search.time_budget(TimeBudget::new(Duration::from_millis(0)));
|
||
|
let filter_condition = Filter::from_str("id > 2").unwrap().unwrap();
|
||
|
search.filter(filter_condition);
|
||
|
|
||
|
let result = search.execute().unwrap();
|
||
|
assert!(result.degraded);
|
||
|
snapshot!(format!("{:?}\n{:?}", result.candidates, result.documents_ids), @r###"
|
||
|
RoaringBitmap<[0, 1]>
|
||
|
[0, 1]
|
||
|
"###);
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn degraded_search_and_score_details() {
|
||
|
let index = create_index();
|
||
|
let rtxn = index.read_txn().unwrap();
|
||
|
|
||
|
let mut search = Search::new(&rtxn, &index);
|
||
|
search.query("hello puppy kefir");
|
||
|
search.limit(4);
|
||
|
search.time_budget(TimeBudget::max());
|
||
|
|
||
|
let result = search.execute().unwrap();
|
||
|
snapshot!(format!("{:#?}\n{:#?}", result.documents_ids, result.document_scores), @r###"
|
||
|
[
|
||
|
4,
|
||
|
1,
|
||
|
0,
|
||
|
3,
|
||
|
]
|
||
|
[
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
Typo(
|
||
|
Typo {
|
||
|
typo_count: 0,
|
||
|
max_typo_count: 3,
|
||
|
},
|
||
|
),
|
||
|
],
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
Typo(
|
||
|
Typo {
|
||
|
typo_count: 1,
|
||
|
max_typo_count: 3,
|
||
|
},
|
||
|
),
|
||
|
],
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
],
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 2,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
],
|
||
|
]
|
||
|
"###);
|
||
|
|
||
|
// Do ONE loop iteration. Not much can be deduced, almost everyone matched the words first bucket.
|
||
|
search.time_budget(TimeBudget::max().with_stop_after(1));
|
||
|
|
||
|
let result = search.execute().unwrap();
|
||
|
snapshot!(format!("{:#?}\n{:#?}", result.documents_ids, result.document_scores), @r###"
|
||
|
[
|
||
|
0,
|
||
|
1,
|
||
|
4,
|
||
|
2,
|
||
|
]
|
||
|
[
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
Skipped,
|
||
|
],
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
Skipped,
|
||
|
],
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
Skipped,
|
||
|
],
|
||
|
[
|
||
|
Skipped,
|
||
|
],
|
||
|
]
|
||
|
"###);
|
||
|
|
||
|
// Do TWO loop iterations. The first document should be entirely sorted
|
||
|
search.time_budget(TimeBudget::max().with_stop_after(2));
|
||
|
|
||
|
let result = search.execute().unwrap();
|
||
|
snapshot!(format!("{:#?}\n{:#?}", result.documents_ids, result.document_scores), @r###"
|
||
|
[
|
||
|
4,
|
||
|
0,
|
||
|
1,
|
||
|
2,
|
||
|
]
|
||
|
[
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
Typo(
|
||
|
Typo {
|
||
|
typo_count: 0,
|
||
|
max_typo_count: 3,
|
||
|
},
|
||
|
),
|
||
|
],
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
Skipped,
|
||
|
],
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
Skipped,
|
||
|
],
|
||
|
[
|
||
|
Skipped,
|
||
|
],
|
||
|
]
|
||
|
"###);
|
||
|
|
||
|
// Do THREE loop iterations. The second document should be entirely sorted as well
|
||
|
search.time_budget(TimeBudget::max().with_stop_after(3));
|
||
|
|
||
|
let result = search.execute().unwrap();
|
||
|
snapshot!(format!("{:#?}\n{:#?}", result.documents_ids, result.document_scores), @r###"
|
||
|
[
|
||
|
4,
|
||
|
1,
|
||
|
0,
|
||
|
2,
|
||
|
]
|
||
|
[
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
Typo(
|
||
|
Typo {
|
||
|
typo_count: 0,
|
||
|
max_typo_count: 3,
|
||
|
},
|
||
|
),
|
||
|
],
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
Typo(
|
||
|
Typo {
|
||
|
typo_count: 1,
|
||
|
max_typo_count: 3,
|
||
|
},
|
||
|
),
|
||
|
],
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
Skipped,
|
||
|
],
|
||
|
[
|
||
|
Skipped,
|
||
|
],
|
||
|
]
|
||
|
"###);
|
||
|
|
||
|
// Do FOUR loop iterations. The third document should be entirely sorted as well
|
||
|
// The words bucket have still not progressed thus the last document doesn't have any info yet.
|
||
|
search.time_budget(TimeBudget::max().with_stop_after(4));
|
||
|
|
||
|
let result = search.execute().unwrap();
|
||
|
snapshot!(format!("{:#?}\n{:#?}", result.documents_ids, result.document_scores), @r###"
|
||
|
[
|
||
|
4,
|
||
|
1,
|
||
|
0,
|
||
|
2,
|
||
|
]
|
||
|
[
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
Typo(
|
||
|
Typo {
|
||
|
typo_count: 0,
|
||
|
max_typo_count: 3,
|
||
|
},
|
||
|
),
|
||
|
],
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
Typo(
|
||
|
Typo {
|
||
|
typo_count: 1,
|
||
|
max_typo_count: 3,
|
||
|
},
|
||
|
),
|
||
|
],
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
],
|
||
|
[
|
||
|
Skipped,
|
||
|
],
|
||
|
]
|
||
|
"###);
|
||
|
|
||
|
// After FIVE loop iteration. The words ranking rule gave us a new bucket.
|
||
|
// Since we reached the limit we were able to early exit without checking the typo ranking rule.
|
||
|
search.time_budget(TimeBudget::max().with_stop_after(5));
|
||
|
|
||
|
let result = search.execute().unwrap();
|
||
|
snapshot!(format!("{:#?}\n{:#?}", result.documents_ids, result.document_scores), @r###"
|
||
|
[
|
||
|
4,
|
||
|
1,
|
||
|
0,
|
||
|
3,
|
||
|
]
|
||
|
[
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
Typo(
|
||
|
Typo {
|
||
|
typo_count: 0,
|
||
|
max_typo_count: 3,
|
||
|
},
|
||
|
),
|
||
|
],
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
Typo(
|
||
|
Typo {
|
||
|
typo_count: 1,
|
||
|
max_typo_count: 3,
|
||
|
},
|
||
|
),
|
||
|
],
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 3,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
],
|
||
|
[
|
||
|
Words(
|
||
|
Words {
|
||
|
matching_words: 2,
|
||
|
max_matching_words: 3,
|
||
|
},
|
||
|
),
|
||
|
],
|
||
|
]
|
||
|
"###);
|
||
|
}
|