From 41976b82b1c538c8effbff2f0ad1ea5ccf4234e1 Mon Sep 17 00:00:00 2001 From: Louis Dureuil Date: Thu, 30 May 2024 11:22:26 +0200 Subject: [PATCH] Tests for ranking_score_threshold --- meilisearch/tests/search/errors.rs | 34 ++++ meilisearch/tests/search/mod.rs | 232 ++++++++++++++++++++++++++++ meilisearch/tests/similar/errors.rs | 62 ++++++++ meilisearch/tests/similar/mod.rs | 229 +++++++++++++++++++++++++++ 4 files changed, 557 insertions(+) diff --git a/meilisearch/tests/search/errors.rs b/meilisearch/tests/search/errors.rs index 8be70d162..9c7c361b6 100644 --- a/meilisearch/tests/search/errors.rs +++ b/meilisearch/tests/search/errors.rs @@ -321,6 +321,40 @@ async fn search_bad_facets() { // Can't make the `attributes_to_highlight` fail with a get search since it'll accept anything as an array of strings. } +#[actix_rt::test] +async fn search_bad_threshold() { + let server = Server::new().await; + let index = server.index("test"); + + let (response, code) = index.search_post(json!({"rankingScoreThreshold": "doggo"})).await; + snapshot!(code, @"400 Bad Request"); + snapshot!(json_string!(response), @r###" + { + "message": "Invalid value type at `.rankingScoreThreshold`: expected a number, but found a string: `\"doggo\"`", + "code": "invalid_search_ranking_score_threshold", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#invalid_search_ranking_score_threshold" + } + "###); +} + +#[actix_rt::test] +async fn search_invalid_threshold() { + let server = Server::new().await; + let index = server.index("test"); + + let (response, code) = index.search_post(json!({"rankingScoreThreshold": 42})).await; + snapshot!(code, @"400 Bad Request"); + snapshot!(json_string!(response), @r###" + { + "message": "Invalid value at `.rankingScoreThreshold`: the value of `rankingScoreThreshold` is invalid, expected a float between `0.0` and `1.0`.", + "code": "invalid_search_ranking_score_threshold", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#invalid_search_ranking_score_threshold" + } + "###); +} + #[actix_rt::test] async fn search_non_filterable_facets() { let server = Server::new().await; diff --git a/meilisearch/tests/search/mod.rs b/meilisearch/tests/search/mod.rs index 56fa226b2..7774755bc 100644 --- a/meilisearch/tests/search/mod.rs +++ b/meilisearch/tests/search/mod.rs @@ -47,6 +47,31 @@ static DOCUMENTS: Lazy = Lazy::new(|| { ]) }); +static SCORE_DOCUMENTS: Lazy = Lazy::new(|| { + json!([ + { + "title": "Batman the dark knight returns: Part 1", + "id": "A", + }, + { + "title": "Batman the dark knight returns: Part 2", + "id": "B", + }, + { + "title": "Batman Returns", + "id": "C", + }, + { + "title": "Batman", + "id": "D", + }, + { + "title": "Badman", + "id": "E", + } + ]) +}); + static NESTED_DOCUMENTS: Lazy = Lazy::new(|| { json!([ { @@ -959,6 +984,213 @@ async fn test_score_details() { .await; } +#[actix_rt::test] +async fn test_score() { + let server = Server::new().await; + let index = server.index("test"); + + let documents = SCORE_DOCUMENTS.clone(); + + let res = index.add_documents(json!(documents), None).await; + index.wait_task(res.0.uid()).await; + + index + .search( + json!({ + "q": "Badman the dark knight returns 1", + "showRankingScore": true, + }), + |response, code| { + meili_snap::snapshot!(code, @"200 OK"); + meili_snap::snapshot!(meili_snap::json_string!(response["hits"]), @r###" + [ + { + "title": "Batman the dark knight returns: Part 1", + "id": "A", + "_rankingScore": 0.9746605609456898 + }, + { + "title": "Batman the dark knight returns: Part 2", + "id": "B", + "_rankingScore": 0.8055252965383685 + }, + { + "title": "Badman", + "id": "E", + "_rankingScore": 0.16666666666666666 + }, + { + "title": "Batman Returns", + "id": "C", + "_rankingScore": 0.07702020202020202 + }, + { + "title": "Batman", + "id": "D", + "_rankingScore": 0.07702020202020202 + } + ] + "###); + }, + ) + .await; +} + +#[actix_rt::test] +async fn test_score_threshold() { + let query = "Badman dark returns 1"; + let server = Server::new().await; + let index = server.index("test"); + + let documents = SCORE_DOCUMENTS.clone(); + + let res = index.add_documents(json!(documents), None).await; + index.wait_task(res.0.uid()).await; + + index + .search( + json!({ + "q": query, + "showRankingScore": true, + "rankingScoreThreshold": 0.0 + }), + |response, code| { + meili_snap::snapshot!(code, @"200 OK"); + meili_snap::snapshot!(meili_snap::json_string!(response["estimatedTotalHits"]), @"5"); + meili_snap::snapshot!(meili_snap::json_string!(response["hits"]), @r###" + [ + { + "title": "Batman the dark knight returns: Part 1", + "id": "A", + "_rankingScore": 0.93430081300813 + }, + { + "title": "Batman the dark knight returns: Part 2", + "id": "B", + "_rankingScore": 0.6685627880184332 + }, + { + "title": "Badman", + "id": "E", + "_rankingScore": 0.25 + }, + { + "title": "Batman Returns", + "id": "C", + "_rankingScore": 0.11553030303030302 + }, + { + "title": "Batman", + "id": "D", + "_rankingScore": 0.11553030303030302 + } + ] + "###); + }, + ) + .await; + + index + .search( + json!({ + "q": query, + "showRankingScore": true, + "rankingScoreThreshold": 0.2 + }), + |response, code| { + meili_snap::snapshot!(code, @"200 OK"); + meili_snap::snapshot!(meili_snap::json_string!(response["estimatedTotalHits"]), @r###"3"###); + meili_snap::snapshot!(meili_snap::json_string!(response["hits"]), @r###" + [ + { + "title": "Batman the dark knight returns: Part 1", + "id": "A", + "_rankingScore": 0.93430081300813 + }, + { + "title": "Batman the dark knight returns: Part 2", + "id": "B", + "_rankingScore": 0.6685627880184332 + }, + { + "title": "Badman", + "id": "E", + "_rankingScore": 0.25 + } + ] + "###); + }, + ) + .await; + + index + .search( + json!({ + "q": query, + "showRankingScore": true, + "rankingScoreThreshold": 0.5 + }), + |response, code| { + meili_snap::snapshot!(code, @"200 OK"); + meili_snap::snapshot!(meili_snap::json_string!(response["estimatedTotalHits"]), @r###"2"###); + meili_snap::snapshot!(meili_snap::json_string!(response["hits"]), @r###" + [ + { + "title": "Batman the dark knight returns: Part 1", + "id": "A", + "_rankingScore": 0.93430081300813 + }, + { + "title": "Batman the dark knight returns: Part 2", + "id": "B", + "_rankingScore": 0.6685627880184332 + } + ] + "###); + }, + ) + .await; + + index + .search( + json!({ + "q": query, + "showRankingScore": true, + "rankingScoreThreshold": 0.8 + }), + |response, code| { + meili_snap::snapshot!(code, @"200 OK"); + meili_snap::snapshot!(meili_snap::json_string!(response["estimatedTotalHits"]), @r###"1"###); + meili_snap::snapshot!(meili_snap::json_string!(response["hits"]), @r###" + [ + { + "title": "Batman the dark knight returns: Part 1", + "id": "A", + "_rankingScore": 0.93430081300813 + } + ] + "###); + }, + ) + .await; + + index + .search( + json!({ + "q": query, + "showRankingScore": true, + "rankingScoreThreshold": 1.0 + }), + |response, code| { + meili_snap::snapshot!(code, @"200 OK"); + meili_snap::snapshot!(meili_snap::json_string!(response["estimatedTotalHits"]), @r###"0"###); + // nobody is perfect + meili_snap::snapshot!(meili_snap::json_string!(response["hits"]), @"[]"); + }, + ) + .await; +} + #[actix_rt::test] async fn test_degraded_score_details() { let server = Server::new().await; diff --git a/meilisearch/tests/similar/errors.rs b/meilisearch/tests/similar/errors.rs index 64386a7bf..7765b9a85 100644 --- a/meilisearch/tests/similar/errors.rs +++ b/meilisearch/tests/similar/errors.rs @@ -87,6 +87,68 @@ async fn similar_bad_id() { "###); } +#[actix_rt::test] +async fn similar_bad_ranking_score_threshold() { + let server = Server::new().await; + let index = server.index("test"); + server.set_features(json!({"vectorStore": true})).await; + + let (response, code) = index + .update_settings(json!({ + "embedders": { + "manual": { + "source": "userProvided", + "dimensions": 3, + } + }, + "filterableAttributes": ["title"]})) + .await; + snapshot!(code, @"202 Accepted"); + server.wait_task(response.uid()).await; + + let (response, code) = index.similar_post(json!({"rankingScoreThreshold": ["doggo"]})).await; + snapshot!(code, @"400 Bad Request"); + snapshot!(json_string!(response), @r###" + { + "message": "Invalid value type at `.rankingScoreThreshold`: expected a number, but found an array: `[\"doggo\"]`", + "code": "invalid_similar_ranking_score_threshold", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#invalid_similar_ranking_score_threshold" + } + "###); +} + +#[actix_rt::test] +async fn similar_invalid_ranking_score_threshold() { + let server = Server::new().await; + let index = server.index("test"); + server.set_features(json!({"vectorStore": true})).await; + + let (response, code) = index + .update_settings(json!({ + "embedders": { + "manual": { + "source": "userProvided", + "dimensions": 3, + } + }, + "filterableAttributes": ["title"]})) + .await; + snapshot!(code, @"202 Accepted"); + server.wait_task(response.uid()).await; + + let (response, code) = index.similar_post(json!({"rankingScoreThreshold": 42})).await; + snapshot!(code, @"400 Bad Request"); + snapshot!(json_string!(response), @r###" + { + "message": "Invalid value at `.rankingScoreThreshold`: the value of `rankingScoreThreshold` is invalid, expected a float between `0.0` and `1.0`.", + "code": "invalid_similar_ranking_score_threshold", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#invalid_similar_ranking_score_threshold" + } + "###); +} + #[actix_rt::test] async fn similar_invalid_id() { let server = Server::new().await; diff --git a/meilisearch/tests/similar/mod.rs b/meilisearch/tests/similar/mod.rs index ee78917cb..bde23b67f 100644 --- a/meilisearch/tests/similar/mod.rs +++ b/meilisearch/tests/similar/mod.rs @@ -194,6 +194,235 @@ async fn basic() { .await; } +#[actix_rt::test] +async fn ranking_score_threshold() { + let server = Server::new().await; + let index = server.index("test"); + let (value, code) = server.set_features(json!({"vectorStore": true})).await; + snapshot!(code, @"200 OK"); + snapshot!(value, @r###" + { + "vectorStore": true, + "metrics": false, + "logsRoute": false + } + "###); + + let (response, code) = index + .update_settings(json!({ + "embedders": { + "manual": { + "source": "userProvided", + "dimensions": 3, + } + }, + "filterableAttributes": ["title"]})) + .await; + snapshot!(code, @"202 Accepted"); + server.wait_task(response.uid()).await; + + let documents = DOCUMENTS.clone(); + let (value, code) = index.add_documents(documents, None).await; + snapshot!(code, @"202 Accepted"); + index.wait_task(value.uid()).await; + + index + .similar( + json!({"id": 143, "showRankingScore": true, "rankingScoreThreshold": 0}), + |response, code| { + snapshot!(code, @"200 OK"); + meili_snap::snapshot!(meili_snap::json_string!(response["estimatedTotalHits"]), @"4"); + snapshot!(json_string!(response["hits"]), @r###" + [ + { + "title": "Escape Room", + "release_year": 2019, + "id": "522681", + "_vectors": { + "manual": [ + 0.1, + 0.6, + 0.8 + ] + }, + "_rankingScore": 0.890957772731781 + }, + { + "title": "Captain Marvel", + "release_year": 2019, + "id": "299537", + "_vectors": { + "manual": [ + 0.6, + 0.8, + -0.2 + ] + }, + "_rankingScore": 0.39060014486312866 + }, + { + "title": "How to Train Your Dragon: The Hidden World", + "release_year": 2019, + "id": "166428", + "_vectors": { + "manual": [ + 0.7, + 0.7, + -0.4 + ] + }, + "_rankingScore": 0.2819308042526245 + }, + { + "title": "Shazam!", + "release_year": 2019, + "id": "287947", + "_vectors": { + "manual": [ + 0.8, + 0.4, + -0.5 + ] + }, + "_rankingScore": 0.1662663221359253 + } + ] + "###); + }, + ) + .await; + + index + .similar( + json!({"id": 143, "showRankingScore": true, "rankingScoreThreshold": 0.2}), + |response, code| { + snapshot!(code, @"200 OK"); + meili_snap::snapshot!(meili_snap::json_string!(response["estimatedTotalHits"]), @"3"); + snapshot!(json_string!(response["hits"]), @r###" + [ + { + "title": "Escape Room", + "release_year": 2019, + "id": "522681", + "_vectors": { + "manual": [ + 0.1, + 0.6, + 0.8 + ] + }, + "_rankingScore": 0.890957772731781 + }, + { + "title": "Captain Marvel", + "release_year": 2019, + "id": "299537", + "_vectors": { + "manual": [ + 0.6, + 0.8, + -0.2 + ] + }, + "_rankingScore": 0.39060014486312866 + }, + { + "title": "How to Train Your Dragon: The Hidden World", + "release_year": 2019, + "id": "166428", + "_vectors": { + "manual": [ + 0.7, + 0.7, + -0.4 + ] + }, + "_rankingScore": 0.2819308042526245 + } + ] + "###); + }, + ) + .await; + + index + .similar( + json!({"id": 143, "showRankingScore": true, "rankingScoreThreshold": 0.3}), + |response, code| { + snapshot!(code, @"200 OK"); + meili_snap::snapshot!(meili_snap::json_string!(response["estimatedTotalHits"]), @"2"); + snapshot!(json_string!(response["hits"]), @r###" + [ + { + "title": "Escape Room", + "release_year": 2019, + "id": "522681", + "_vectors": { + "manual": [ + 0.1, + 0.6, + 0.8 + ] + }, + "_rankingScore": 0.890957772731781 + }, + { + "title": "Captain Marvel", + "release_year": 2019, + "id": "299537", + "_vectors": { + "manual": [ + 0.6, + 0.8, + -0.2 + ] + }, + "_rankingScore": 0.39060014486312866 + } + ] + "###); + }, + ) + .await; + + index + .similar( + json!({"id": 143, "showRankingScore": true, "rankingScoreThreshold": 0.6}), + |response, code| { + snapshot!(code, @"200 OK"); + meili_snap::snapshot!(meili_snap::json_string!(response["estimatedTotalHits"]), @"1"); + snapshot!(json_string!(response["hits"]), @r###" + [ + { + "title": "Escape Room", + "release_year": 2019, + "id": "522681", + "_vectors": { + "manual": [ + 0.1, + 0.6, + 0.8 + ] + }, + "_rankingScore": 0.890957772731781 + } + ] + "###); + }, + ) + .await; + + index + .similar( + json!({"id": 143, "showRankingScore": true, "rankingScoreThreshold": 0.9}), + |response, code| { + snapshot!(code, @"200 OK"); + snapshot!(json_string!(response["hits"]), @"[]"); + }, + ) + .await; +} + #[actix_rt::test] async fn filter() { let server = Server::new().await;