diff --git a/crates/meilisearch/src/routes/indexes/facet_search.rs b/crates/meilisearch/src/routes/indexes/facet_search.rs index ff11f1305..ab335c528 100644 --- a/crates/meilisearch/src/routes/indexes/facet_search.rs +++ b/crates/meilisearch/src/routes/indexes/facet_search.rs @@ -11,6 +11,7 @@ use meilisearch_types::index_uid::IndexUid; use meilisearch_types::locales::Locale; use serde_json::Value; use tracing::debug; +use utoipa::{OpenApi, ToSchema}; use crate::analytics::{Aggregate, Analytics}; use crate::extractors::authentication::policies::*; @@ -18,20 +19,33 @@ use crate::extractors::authentication::GuardedData; use crate::routes::indexes::search::search_kind; use crate::search::{ add_search_rules, perform_facet_search, FacetSearchResult, HybridQuery, MatchingStrategy, - RankingScoreThreshold, SearchQuery, DEFAULT_CROP_LENGTH, DEFAULT_CROP_MARKER, + RankingScoreThreshold, SearchQuery, SearchResult, DEFAULT_CROP_LENGTH, DEFAULT_CROP_MARKER, DEFAULT_HIGHLIGHT_POST_TAG, DEFAULT_HIGHLIGHT_PRE_TAG, DEFAULT_SEARCH_LIMIT, DEFAULT_SEARCH_OFFSET, }; use crate::search_queue::SearchQueue; +#[derive(OpenApi)] +#[openapi( + paths(search), + tags( + ( + name = "Facet Search", + description = "The `/facet-search` route allows you to search for facet values. Facet search supports prefix search and typo tolerance. The returned hits are sorted lexicographically in ascending order. You can configure how facets are sorted using the sortFacetValuesBy property of the faceting index settings.", + external_docs(url = "https://www.meilisearch.com/docs/reference/api/facet_search"), + ), + ), +)] +pub struct FacetSearchApi; + pub fn configure(cfg: &mut web::ServiceConfig) { cfg.service(web::resource("").route(web::post().to(search))); } -/// # Important -/// -/// Intentionally don't use `deny_unknown_fields` to ignore search parameters sent by user -#[derive(Debug, Clone, Default, PartialEq, deserr::Deserr)] +// # Important +// +// Intentionally don't use `deny_unknown_fields` to ignore search parameters sent by user +#[derive(Debug, Clone, Default, PartialEq, deserr::Deserr, ToSchema)] #[deserr(error = DeserrJsonError, rename_all = camelCase)] pub struct FacetSearchQuery { #[deserr(default, error = DeserrJsonError)] @@ -158,6 +172,62 @@ impl Aggregate for FacetSearchAggregator { } } +/// Perform a facet search +/// +/// Search for a facet value within a given facet. +#[utoipa::path( + post, + path = "/{indexUid}/facet-search", + tags = ["Indexes", "Facet Search"], + security(("Bearer" = ["search", "*"])), + params( + ("indexUid", example = "movies", description = "Index Unique Identifier", nullable = false), + ), + request_body = FacetSearchQuery, + responses( + (status = 200, description = "The documents are returned", body = SearchResult, content_type = "application/json", example = json!( + { + "hits": [ + { + "id": 2770, + "title": "American Pie 2", + "poster": "https://image.tmdb.org/t/p/w1280/q4LNgUnRfltxzp3gf1MAGiK5LhV.jpg", + "overview": "The whole gang are back and as close as ever. They decide to get even closer by spending the summer together at a beach house. They decide to hold the biggest…", + "release_date": 997405200 + }, + { + "id": 190859, + "title": "American Sniper", + "poster": "https://image.tmdb.org/t/p/w1280/svPHnYE7N5NAGO49dBmRhq0vDQ3.jpg", + "overview": "U.S. Navy SEAL Chris Kyle takes his sole mission—protect his comrades—to heart and becomes one of the most lethal snipers in American history. His pinpoint accuracy not only saves countless lives but also makes him a prime…", + "release_date": 1418256000 + } + ], + "offset": 0, + "limit": 2, + "estimatedTotalHits": 976, + "processingTimeMs": 35, + "query": "american " + } + )), + (status = 404, description = "Index not found", body = ResponseError, content_type = "application/json", example = json!( + { + "message": "Index `movies` not found.", + "code": "index_not_found", + "type": "invalid_request", + "link": "https://docs.meilisearch.com/errors#index_not_found" + } + )), + (status = 401, description = "The authorization header is missing", body = ResponseError, content_type = "application/json", example = json!( + { + "message": "The Authorization header is missing. It must use the bearer authorization method.", + "code": "missing_authorization_header", + "type": "auth", + "link": "https://docs.meilisearch.com/errors#missing_authorization_header" + } + )), + ) +)] pub async fn search( index_scheduler: GuardedData, Data>, search_queue: Data, diff --git a/crates/meilisearch/src/routes/indexes/mod.rs b/crates/meilisearch/src/routes/indexes/mod.rs index 86e08ee2b..76bb68d11 100644 --- a/crates/meilisearch/src/routes/indexes/mod.rs +++ b/crates/meilisearch/src/routes/indexes/mod.rs @@ -41,6 +41,7 @@ mod similar_analytics; #[openapi( nest( (path = "/", api = documents::DocumentsApi), + (path = "/", api = facet_search::FacetSearchApi), ), paths(list_indexes, create_index, get_index, update_index, delete_index, get_index_stats), tags(