Make search types serialize and deserialize so that reading from a proxy is possible

This commit is contained in:
Louis Dureuil 2025-01-29 10:57:03 +01:00
parent b232bf935b
commit ce9b5e0029
No known key found for this signature in database
3 changed files with 23 additions and 18 deletions

View File

@ -4,13 +4,14 @@ use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use deserr::Deserr; use deserr::Deserr;
use serde::Serialize;
use utoipa::ToSchema; use utoipa::ToSchema;
use crate::error::{Code, ErrorCode}; use crate::error::{Code, ErrorCode};
/// An index uid is composed of only ascii alphanumeric characters, - and _, between 1 and 400 /// An index uid is composed of only ascii alphanumeric characters, - and _, between 1 and 400
/// bytes long /// bytes long
#[derive(Debug, Clone, PartialEq, Eq, Deserr, PartialOrd, Ord, ToSchema)] #[derive(Debug, Clone, PartialEq, Eq, Deserr, PartialOrd, Ord, Serialize, ToSchema)]
#[deserr(try_from(String) = IndexUid::try_from -> IndexUidFormatError)] #[deserr(try_from(String) = IndexUid::try_from -> IndexUidFormatError)]
#[schema(value_type = String, example = "movies")] #[schema(value_type = String, example = "movies")]
pub struct IndexUid(String); pub struct IndexUid(String);

View File

@ -30,7 +30,7 @@ use milli::{
MatchBounds, MatcherBuilder, SortError, TermsMatchingStrategy, DEFAULT_VALUES_PER_FACET, MatchBounds, MatcherBuilder, SortError, TermsMatchingStrategy, DEFAULT_VALUES_PER_FACET,
}; };
use regex::Regex; use regex::Regex;
use serde::Serialize; use serde::{Deserialize, Serialize};
use serde_json::{json, Value}; use serde_json::{json, Value};
#[cfg(test)] #[cfg(test)]
mod mod_test; mod mod_test;
@ -119,7 +119,7 @@ pub struct SearchQuery {
pub locales: Option<Vec<Locale>>, pub locales: Option<Vec<Locale>>,
} }
#[derive(Debug, Clone, Copy, PartialEq, Deserr, ToSchema)] #[derive(Debug, Clone, Copy, PartialEq, Deserr, ToSchema, Serialize)]
#[deserr(try_from(f64) = TryFrom::try_from -> InvalidSearchRankingScoreThreshold)] #[deserr(try_from(f64) = TryFrom::try_from -> InvalidSearchRankingScoreThreshold)]
pub struct RankingScoreThreshold(f64); pub struct RankingScoreThreshold(f64);
impl std::convert::TryFrom<f64> for RankingScoreThreshold { impl std::convert::TryFrom<f64> for RankingScoreThreshold {
@ -275,11 +275,13 @@ impl fmt::Debug for SearchQuery {
} }
} }
#[derive(Debug, Clone, Default, PartialEq, Deserr, ToSchema)] #[derive(Debug, Clone, Default, PartialEq, Deserr, ToSchema, Serialize)]
#[deserr(error = DeserrJsonError<InvalidSearchHybridQuery>, rename_all = camelCase, deny_unknown_fields)] #[deserr(error = DeserrJsonError<InvalidSearchHybridQuery>, rename_all = camelCase, deny_unknown_fields)]
#[serde(rename_all = "camelCase")]
pub struct HybridQuery { pub struct HybridQuery {
#[deserr(default, error = DeserrJsonError<InvalidSearchSemanticRatio>, default)] #[deserr(default, error = DeserrJsonError<InvalidSearchSemanticRatio>, default)]
#[schema(value_type = f32, default)] #[schema(value_type = f32, default)]
#[serde(default)]
pub semantic_ratio: SemanticRatio, pub semantic_ratio: SemanticRatio,
#[deserr(error = DeserrJsonError<InvalidSearchEmbedder>)] #[deserr(error = DeserrJsonError<InvalidSearchEmbedder>)]
pub embedder: String, pub embedder: String,
@ -369,7 +371,7 @@ impl SearchKind {
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Deserr)] #[derive(Debug, Clone, Copy, PartialEq, Deserr, Serialize)]
#[deserr(try_from(f32) = TryFrom::try_from -> InvalidSearchSemanticRatio)] #[deserr(try_from(f32) = TryFrom::try_from -> InvalidSearchSemanticRatio)]
pub struct SemanticRatio(f32); pub struct SemanticRatio(f32);
@ -411,8 +413,9 @@ impl SearchQuery {
// This struct contains the fields of `SearchQuery` inline. // This struct contains the fields of `SearchQuery` inline.
// This is because neither deserr nor serde support `flatten` when using `deny_unknown_fields. // This is because neither deserr nor serde support `flatten` when using `deny_unknown_fields.
// The `From<SearchQueryWithIndex>` implementation ensures both structs remain up to date. // The `From<SearchQueryWithIndex>` implementation ensures both structs remain up to date.
#[derive(Debug, Clone, PartialEq, Deserr, ToSchema)] #[derive(Debug, Clone, Serialize, PartialEq, Deserr, ToSchema)]
#[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)] #[deserr(error = DeserrJsonError, rename_all = camelCase, deny_unknown_fields)]
#[serde(rename_all = "camelCase")]
#[schema(rename_all = "camelCase")] #[schema(rename_all = "camelCase")]
pub struct SearchQueryWithIndex { pub struct SearchQueryWithIndex {
#[deserr(error = DeserrJsonError<InvalidIndexUid>, missing_field_error = DeserrJsonError::missing_index_uid)] #[deserr(error = DeserrJsonError<InvalidIndexUid>, missing_field_error = DeserrJsonError::missing_index_uid)]
@ -620,8 +623,9 @@ impl TryFrom<Value> for ExternalDocumentId {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserr, ToSchema)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Deserr, ToSchema, Serialize)]
#[deserr(rename_all = camelCase)] #[deserr(rename_all = camelCase)]
#[serde(rename_all = "camelCase")]
pub enum MatchingStrategy { pub enum MatchingStrategy {
/// Remove query words from last to first /// Remove query words from last to first
Last, Last,
@ -667,19 +671,19 @@ impl From<FacetValuesSort> for OrderBy {
} }
} }
#[derive(Debug, Clone, Serialize, PartialEq, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ToSchema)]
pub struct SearchHit { pub struct SearchHit {
#[serde(flatten)] #[serde(flatten)]
#[schema(additional_properties, inline, value_type = HashMap<String, Value>)] #[schema(additional_properties, inline, value_type = HashMap<String, Value>)]
pub document: Document, pub document: Document,
#[serde(rename = "_formatted", skip_serializing_if = "Document::is_empty")] #[serde(default, rename = "_formatted", skip_serializing_if = "Document::is_empty")]
#[schema(additional_properties, value_type = HashMap<String, Value>)] #[schema(additional_properties, value_type = HashMap<String, Value>)]
pub formatted: Document, pub formatted: Document,
#[serde(rename = "_matchesPosition", skip_serializing_if = "Option::is_none")] #[serde(default, rename = "_matchesPosition", skip_serializing_if = "Option::is_none")]
pub matches_position: Option<MatchesPosition>, pub matches_position: Option<MatchesPosition>,
#[serde(rename = "_rankingScore", skip_serializing_if = "Option::is_none")] #[serde(default, rename = "_rankingScore", skip_serializing_if = "Option::is_none")]
pub ranking_score: Option<f64>, pub ranking_score: Option<f64>,
#[serde(rename = "_rankingScoreDetails", skip_serializing_if = "Option::is_none")] #[serde(default, rename = "_rankingScoreDetails", skip_serializing_if = "Option::is_none")]
pub ranking_score_details: Option<serde_json::Map<String, serde_json::Value>>, pub ranking_score_details: Option<serde_json::Map<String, serde_json::Value>>,
} }
@ -767,7 +771,7 @@ pub struct SearchResultWithIndex {
pub result: SearchResult, pub result: SearchResult,
} }
#[derive(Serialize, Debug, Clone, PartialEq, Eq, ToSchema)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, ToSchema)]
#[serde(untagged)] #[serde(untagged)]
pub enum HitsInfo { pub enum HitsInfo {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -778,7 +782,7 @@ pub enum HitsInfo {
OffsetLimit { limit: usize, offset: usize, estimated_total_hits: usize }, OffsetLimit { limit: usize, offset: usize, estimated_total_hits: usize },
} }
#[derive(Serialize, Debug, Clone, PartialEq, ToSchema)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, ToSchema)]
pub struct FacetStats { pub struct FacetStats {
pub min: f64, pub min: f64,
pub max: f64, pub max: f64,
@ -1061,7 +1065,7 @@ pub fn perform_search(
Ok(result) Ok(result)
} }
#[derive(Debug, Clone, Default, Serialize, ToSchema)] #[derive(Debug, Clone, Default, Serialize, Deserialize, ToSchema)]
pub struct ComputedFacets { pub struct ComputedFacets {
#[schema(value_type = BTreeMap<String, BTreeMap<String, u64>>)] #[schema(value_type = BTreeMap<String, BTreeMap<String, u64>>)]
pub distribution: BTreeMap<String, IndexMap<String, u64>>, pub distribution: BTreeMap<String, IndexMap<String, u64>>,

View File

@ -11,7 +11,7 @@ use either::Either;
pub use matching_words::MatchingWords; pub use matching_words::MatchingWords;
use matching_words::{MatchType, PartialMatch}; use matching_words::{MatchType, PartialMatch};
use r#match::{Match, MatchPosition}; use r#match::{Match, MatchPosition};
use serde::Serialize; use serde::{Deserialize, Serialize};
use simple_token_kind::SimpleTokenKind; use simple_token_kind::SimpleTokenKind;
use utoipa::ToSchema; use utoipa::ToSchema;
@ -101,11 +101,11 @@ impl FormatOptions {
} }
} }
#[derive(Serialize, Debug, Clone, PartialEq, Eq, ToSchema)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, ToSchema)]
pub struct MatchBounds { pub struct MatchBounds {
pub start: usize, pub start: usize,
pub length: usize, pub length: usize,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none", default)]
pub indices: Option<Vec<usize>>, pub indices: Option<Vec<usize>>,
} }