From 060562d156656dd120d8befc0aa2ec74db4a3c12 Mon Sep 17 00:00:00 2001 From: ManyTheFish Date: Tue, 30 Sep 2025 15:22:35 +0200 Subject: [PATCH] feat: add metadata field with queryUid to search responses - Add SearchMetadata struct with queryUid field (UUID v7) - Add metadata field to SearchResult for /search route - Add metadata field to FederatedSearchResult for /multi-search route - Update perform_search to generate queryUid and set metadata - Update federated search to generate queryUid for each query - Update multi-search non-federated path to include metadata - Fix pattern matching in analytics and other code The metadata field contains: - For /search: single object with queryUid - For /multi-search: array of objects, one per query - For federated search: array of objects, one per query All queryUid values are generated using Uuid::now_v7() for time-ordered uniqueness. --- .../src/routes/indexes/search_analytics.rs | 1 + .../meilisearch/src/search/federated/perform.rs | 6 ++++++ crates/meilisearch/src/search/federated/types.rs | 8 ++++++++ crates/meilisearch/src/search/mod.rs | 15 +++++++++++++++ 4 files changed, 30 insertions(+) diff --git a/crates/meilisearch/src/routes/indexes/search_analytics.rs b/crates/meilisearch/src/routes/indexes/search_analytics.rs index 9f095b007..09045fc4a 100644 --- a/crates/meilisearch/src/routes/indexes/search_analytics.rs +++ b/crates/meilisearch/src/routes/indexes/search_analytics.rs @@ -235,6 +235,7 @@ impl SearchAggregator { degraded, used_negative_operator, request_uid: _, + metadata: _, } = result; self.total_succeeded = self.total_succeeded.saturating_add(1); diff --git a/crates/meilisearch/src/search/federated/perform.rs b/crates/meilisearch/src/search/federated/perform.rs index bf2c99a55..7e02e6615 100644 --- a/crates/meilisearch/src/search/federated/perform.rs +++ b/crates/meilisearch/src/search/federated/perform.rs @@ -20,6 +20,7 @@ use tokio::task::JoinHandle; use uuid::Uuid; use super::super::ranking_rules::{self, RankingRules}; +use super::super::SearchMetadata; use super::super::{ compute_facet_distribution_stats, prepare_search, AttributesFormat, ComputedFacets, HitMaker, HitsInfo, RetrieveVectors, SearchHit, SearchKind, SearchQuery, SearchQueryWithIndex, @@ -59,7 +60,10 @@ pub async fn perform_federated_search( // 1. partition queries by host and index let mut partitioned_queries = PartitionedQueries::new(); + let mut query_metadata = Vec::new(); for (query_index, federated_query) in queries.into_iter().enumerate() { + let query_uid = Uuid::now_v7(); + query_metadata.push(SearchMetadata { query_uid }); partitioned_queries.partition(federated_query, query_index, &network, features)? } @@ -173,6 +177,7 @@ pub async fn perform_federated_search( facets_by_index, remote_errors: partitioned_queries.has_remote.then_some(remote_errors), request_uid: Some(request_uid), + metadata: Some(query_metadata), }) } @@ -442,6 +447,7 @@ fn merge_metadata( degraded: degraded_for_host, used_negative_operator: host_used_negative_operator, remote_errors: _, + metadata: _, request_uid: _, } in remote_results { diff --git a/crates/meilisearch/src/search/federated/types.rs b/crates/meilisearch/src/search/federated/types.rs index db30314ee..b771223c8 100644 --- a/crates/meilisearch/src/search/federated/types.rs +++ b/crates/meilisearch/src/search/federated/types.rs @@ -18,6 +18,8 @@ use serde::{Deserialize, Serialize}; use utoipa::ToSchema; use uuid::Uuid; +use crate::search::SearchMetadata; + use super::super::{ComputedFacets, FacetStats, HitsInfo, SearchHit, SearchQueryWithIndex}; use crate::milli::vector::Embedding; @@ -134,6 +136,8 @@ pub struct FederatedSearchResult { pub facets_by_index: FederatedFacets, #[serde(default, skip_serializing_if = "Option::is_none")] pub request_uid: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub metadata: Option>, #[serde(default, skip_serializing_if = "Option::is_none")] pub remote_errors: Option>, @@ -160,6 +164,7 @@ impl fmt::Debug for FederatedSearchResult { facets_by_index, remote_errors, request_uid, + metadata, } = self; let mut debug = f.debug_struct("SearchResult"); @@ -195,6 +200,9 @@ impl fmt::Debug for FederatedSearchResult { if let Some(request_uid) = request_uid { debug.field("request_uid", &request_uid); } + if let Some(metadata) = metadata { + debug.field("metadata", &metadata); + } debug.finish() } diff --git a/crates/meilisearch/src/search/mod.rs b/crates/meilisearch/src/search/mod.rs index 329263271..260cbb146 100644 --- a/crates/meilisearch/src/search/mod.rs +++ b/crates/meilisearch/src/search/mod.rs @@ -836,6 +836,13 @@ pub struct SearchHit { pub ranking_score_details: Option>, } +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, ToSchema)] +#[serde(rename_all = "camelCase")] +#[schema(rename_all = "camelCase")] +pub struct SearchMetadata { + pub query_uid: Uuid, +} + #[derive(Serialize, Clone, PartialEq, ToSchema)] #[serde(rename_all = "camelCase")] #[schema(rename_all = "camelCase")] @@ -854,6 +861,8 @@ pub struct SearchResult { pub facet_stats: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub request_uid: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option, #[serde(skip_serializing_if = "Option::is_none")] pub semantic_hit_count: Option, @@ -876,6 +885,7 @@ impl fmt::Debug for SearchResult { facet_distribution, facet_stats, request_uid, + metadata, semantic_hit_count, degraded, used_negative_operator, @@ -908,6 +918,9 @@ impl fmt::Debug for SearchResult { if let Some(request_uid) = request_uid { debug.field("request_uid", &request_uid); } + if let Some(metadata) = metadata { + debug.field("metadata", &metadata); + } debug.finish() } @@ -1234,6 +1247,7 @@ pub fn perform_search( .map(|ComputedFacets { distribution, stats }| (distribution, stats)) .unzip(); + let query_uid = Uuid::now_v7(); let result = SearchResult { hits: documents, hits_info, @@ -1246,6 +1260,7 @@ pub fn perform_search( used_negative_operator, semantic_hit_count, request_uid: Some(request_uid), + metadata: Some(SearchMetadata { query_uid }), }; Ok(result) }