From 405af09fc8dd22d42af8e9b6026b75ac5ed41131 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Tue, 29 Mar 2022 11:27:53 -0700 Subject: [PATCH 1/3] Hard limit the number of results returned by a search --- meilisearch-lib/src/index/search.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/meilisearch-lib/src/index/search.rs b/meilisearch-lib/src/index/search.rs index 644b75468..757a5ec00 100644 --- a/meilisearch-lib/src/index/search.rs +++ b/meilisearch-lib/src/index/search.rs @@ -34,6 +34,10 @@ pub const fn default_crop_length() -> usize { DEFAULT_CROP_LENGTH } +/// The maximimum number of results that the engine +/// will be able to return in one search call. +pub const HARD_RESULT_LIMIT: usize = 1000; + #[derive(Deserialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase", deny_unknown_fields)] pub struct SearchQuery { @@ -124,6 +128,8 @@ impl Index { .. } = search.execute()?; + let documents_ids: Vec<_> = documents_ids.into_iter().take(HARD_RESULT_LIMIT).collect(); + let fields_ids_map = self.fields_ids_map(&rtxn).unwrap(); let displayed_ids = self From 8bc6e8dcf90d40bb7744acd2479634389c3b3019 Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Wed, 30 Mar 2022 10:06:15 -0700 Subject: [PATCH 2/3] Make sure that offsets are clamped too --- meilisearch-lib/src/index/search.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/meilisearch-lib/src/index/search.rs b/meilisearch-lib/src/index/search.rs index 757a5ec00..1498b70bd 100644 --- a/meilisearch-lib/src/index/search.rs +++ b/meilisearch-lib/src/index/search.rs @@ -1,3 +1,4 @@ +use std::cmp::min; use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::str::FromStr; use std::time::Instant; @@ -101,8 +102,13 @@ impl Index { search.query(query); } - search.limit(query.limit); - search.offset(query.offset.unwrap_or_default()); + // Make sure that a user can't get more documents than the hard limit, + // we align that on the offset too. + let offset = min(query.offset.unwrap_or(0), HARD_RESULT_LIMIT); + let limit = min(query.limit, HARD_RESULT_LIMIT.saturating_sub(offset)); + + search.offset(offset); + search.limit(limit); if let Some(ref filter) = query.filter { if let Some(facets) = parse_filter(filter)? { @@ -128,8 +134,6 @@ impl Index { .. } = search.execute()?; - let documents_ids: Vec<_> = documents_ids.into_iter().take(HARD_RESULT_LIMIT).collect(); - let fields_ids_map = self.fields_ids_map(&rtxn).unwrap(); let displayed_ids = self From ffafd5b976653a250cb48083b794ee4ab8094bed Mon Sep 17 00:00:00 2001 From: Kerollmops Date: Wed, 30 Mar 2022 16:32:37 -0700 Subject: [PATCH 3/3] Add tests for the hard limit --- meilisearch-http/tests/search/mod.rs | 76 ++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/meilisearch-http/tests/search/mod.rs b/meilisearch-http/tests/search/mod.rs index 7c7924c34..eab8f0a87 100644 --- a/meilisearch-http/tests/search/mod.rs +++ b/meilisearch-http/tests/search/mod.rs @@ -267,3 +267,79 @@ async fn displayed_attributes() { assert_eq!(code, 200, "{}", response); assert!(response["hits"].get("title").is_none()); } + +#[actix_rt::test] +async fn placeholder_search_is_hard_limited() { + let server = Server::new().await; + let index = server.index("test"); + + let documents: Vec<_> = (0..1200) + .map(|i| json!({ "id": i, "text": "I am unique!" })) + .collect(); + index.add_documents(documents.into(), None).await; + index.wait_task(0).await; + + index + .search( + json!({ + "limit": 1500, + }), + |response, code| { + assert_eq!(code, 200, "{}", response); + assert_eq!(response["hits"].as_array().unwrap().len(), 1000); + }, + ) + .await; + + index + .search( + json!({ + "offset": 800, + "limit": 400, + }), + |response, code| { + assert_eq!(code, 200, "{}", response); + assert_eq!(response["hits"].as_array().unwrap().len(), 200); + }, + ) + .await; +} + +#[actix_rt::test] +async fn search_is_hard_limited() { + let server = Server::new().await; + let index = server.index("test"); + + let documents: Vec<_> = (0..1200) + .map(|i| json!({ "id": i, "text": "I am unique!" })) + .collect(); + index.add_documents(documents.into(), None).await; + index.wait_task(0).await; + + index + .search( + json!({ + "q": "unique", + "limit": 1500, + }), + |response, code| { + assert_eq!(code, 200, "{}", response); + assert_eq!(response["hits"].as_array().unwrap().len(), 1000); + }, + ) + .await; + + index + .search( + json!({ + "q": "unique", + "offset": 800, + "limit": 400, + }), + |response, code| { + assert_eq!(code, 200, "{}", response); + assert_eq!(response["hits"].as_array().unwrap().len(), 200); + }, + ) + .await; +}