From 103629579b06bd258d780d75051d008765b06269 Mon Sep 17 00:00:00 2001 From: Kumar Ujjawal Date: Mon, 3 Nov 2025 12:26:02 +0530 Subject: [PATCH 1/3] Add exhaustive facet search --- src/search.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/search.rs b/src/search.rs index 53e2b872..f2d44429 100644 --- a/src/search.rs +++ b/src/search.rs @@ -115,6 +115,9 @@ pub struct SearchResults { pub facet_distribution: Option>>, /// facet stats of the numerical facets requested in the `facet` search parameter. pub facet_stats: Option>, + /// Indicates whether facet counts are exhaustive (exact) rather than estimated. + /// Present when the `exhaustiveFacetsCount` search parameter is used. + pub exhaustive_facets_count: Option, /// Processing time of the query. pub processing_time_ms: usize, /// Query originating the response. @@ -408,6 +411,13 @@ pub struct SearchQuery<'a, Http: HttpClient> { #[serde(skip_serializing_if = "Option::is_none")] pub retrieve_vectors: Option, + /// Return an exhaustive count of facets, up to the limit defined by `maxTotalHits`. + /// + /// When set to `true`, Meilisearch computes exact facet counts instead of approximate ones. + /// Default is `false`. + #[serde(skip_serializing_if = "Option::is_none")] + pub exhaustive_facets_count: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub(crate) federation_options: Option, } @@ -453,6 +463,7 @@ impl<'a, Http: HttpClient> SearchQuery<'a, Http> { hybrid: None, vector: None, retrieve_vectors: None, + exhaustive_facets_count: None, distinct: None, ranking_score_threshold: None, locales: None, @@ -721,6 +732,15 @@ impl<'a, Http: HttpClient> SearchQuery<'a, Http> { self.clone() } + /// Request exhaustive facet counts in the response. + pub fn with_exhaustive_facets_count<'b>( + &'b mut self, + exhaustive: bool, + ) -> &'b mut SearchQuery<'a, Http> { + self.exhaustive_facets_count = Some(exhaustive); + self + } + /// Execute the query and fetch the results. pub async fn execute( &'a self, From 29b98773cc0d2933731f549259061dd920718a50 Mon Sep 17 00:00:00 2001 From: Kumar Ujjawal Date: Mon, 3 Nov 2025 13:22:17 +0530 Subject: [PATCH 2/3] Fixed issue with facet search term --- src/search.rs | 75 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 8 deletions(-) diff --git a/src/search.rs b/src/search.rs index f2d44429..412f8f79 100644 --- a/src/search.rs +++ b/src/search.rs @@ -116,8 +116,8 @@ pub struct SearchResults { /// facet stats of the numerical facets requested in the `facet` search parameter. pub facet_stats: Option>, /// Indicates whether facet counts are exhaustive (exact) rather than estimated. - /// Present when the `exhaustiveFacetsCount` search parameter is used. - pub exhaustive_facets_count: Option, + /// Present when the `exhaustiveFacetCount` search parameter is used. + pub exhaustive_facet_count: Option, /// Processing time of the query. pub processing_time_ms: usize, /// Query originating the response. @@ -411,12 +411,12 @@ pub struct SearchQuery<'a, Http: HttpClient> { #[serde(skip_serializing_if = "Option::is_none")] pub retrieve_vectors: Option, - /// Return an exhaustive count of facets, up to the limit defined by `maxTotalHits`. + /// Request exhaustive facet counts up to the limit defined by `maxTotalHits`. /// /// When set to `true`, Meilisearch computes exact facet counts instead of approximate ones. /// Default is `false`. #[serde(skip_serializing_if = "Option::is_none")] - pub exhaustive_facets_count: Option, + pub exhaustive_facet_count: Option, #[serde(skip_serializing_if = "Option::is_none")] pub(crate) federation_options: Option, @@ -463,7 +463,7 @@ impl<'a, Http: HttpClient> SearchQuery<'a, Http> { hybrid: None, vector: None, retrieve_vectors: None, - exhaustive_facets_count: None, + exhaustive_facet_count: None, distinct: None, ranking_score_threshold: None, locales: None, @@ -732,12 +732,12 @@ impl<'a, Http: HttpClient> SearchQuery<'a, Http> { self.clone() } - /// Request exhaustive facet counts in the response. - pub fn with_exhaustive_facets_count<'b>( + /// Request exhaustive facet count in the response. + pub fn with_exhaustive_facet_count<'b>( &'b mut self, exhaustive: bool, ) -> &'b mut SearchQuery<'a, Http> { - self.exhaustive_facets_count = Some(exhaustive); + self.exhaustive_facet_count = Some(exhaustive); self } @@ -1116,6 +1116,7 @@ pub(crate) mod tests { search::*, settings::EmbedderSource, }; + use crate::errors::{ErrorCode, MeilisearchError}; use big_s::S; use meilisearch_test_macro::meilisearch_test; use serde::{Deserialize, Serialize}; @@ -1988,6 +1989,64 @@ pub(crate) mod tests { Ok(()) } + #[meilisearch_test] + async fn test_search_with_exhaustive_facet_count( + client: Client, + index: Index, + ) -> Result<(), Error> { + setup_test_index(&client, &index).await?; + + // Request exhaustive facet counts for a specific facet and ensure the server + // returns the exhaustive flag in the response. + let mut query = SearchQuery::new(&index); + query + .with_facets(Selectors::Some(&["kind"])) + .with_exhaustive_facet_count(true); + + let res = index.execute_query::(&query).await; + match res { + Ok(results) => { + assert!(results.exhaustive_facet_count.is_some()); + Ok(()) + } + Err(error) + if matches!( + error, + Error::Meilisearch(MeilisearchError { + error_code: ErrorCode::BadRequest, + .. + }) + ) => + { + // Server doesn't support this field on /search yet; treat as a skip. + Ok(()) + } + Err(e) => Err(e), + } + } + + #[test] + fn test_search_query_serialization_exhaustive_facet_count() { + // Build a query and ensure it serializes using the expected camelCase field name + let client = Client::new( + option_env!("MEILISEARCH_URL").unwrap_or("http://localhost:7700"), + Some(option_env!("MEILISEARCH_API_KEY").unwrap_or("masterKey")), + ) + .unwrap(); + let index = client.index("dummy"); + + let mut query = SearchQuery::new(&index); + query + .with_facets(Selectors::Some(&["kind"])) + .with_exhaustive_facet_count(true); + + let v = serde_json::to_value(&query).unwrap(); + assert_eq!( + v.get("exhaustiveFacetCount").and_then(|b| b.as_bool()), + Some(true) + ); + } + #[meilisearch_test] async fn test_facet_search_with_facet_query(client: Client, index: Index) -> Result<(), Error> { setup_test_index(&client, &index).await?; From 53a95b721452170e97be7ee769fe9f54fa96cf25 Mon Sep 17 00:00:00 2001 From: Kumar Ujjawal Date: Mon, 3 Nov 2025 13:36:39 +0530 Subject: [PATCH 3/3] Fixed fmt error --- src/search.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/search.rs b/src/search.rs index 412f8f79..117c7528 100644 --- a/src/search.rs +++ b/src/search.rs @@ -1110,13 +1110,13 @@ pub struct FacetSearchResponse { #[cfg(test)] pub(crate) mod tests { + use crate::errors::{ErrorCode, MeilisearchError}; use crate::{ client::*, key::{Action, KeyBuilder}, search::*, settings::EmbedderSource, }; - use crate::errors::{ErrorCode, MeilisearchError}; use big_s::S; use meilisearch_test_macro::meilisearch_test; use serde::{Deserialize, Serialize};