From 9b1771d5848e9b3742c66367e31d26d25bf8eae3 Mon Sep 17 00:00:00 2001 From: Anders Olsson Date: Thu, 4 Jun 2026 11:19:46 +0200 Subject: [PATCH] refactor(search): document+guard visibility filter precondition; drop redundant dev-dep - Remove serde_json from [dev-dependencies] (already in [dependencies]) - Add debug_assert! in search_objects visibility filter as defense-in-depth - Extend search_objects doc-comment with visibility precondition - Clarify estimated_total_hits.unwrap_or(0) is safe under offset/limit pagination - Add brief comment on with_crop_length(20) explaining ~20-word context window Co-Authored-By: Claude Sonnet 4.6 --- crates/search/Cargo.toml | 1 - crates/search/src/lib.rs | 18 +++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/crates/search/Cargo.toml b/crates/search/Cargo.toml index a5da593..2b28ab5 100644 --- a/crates/search/Cargo.toml +++ b/crates/search/Cargo.toml @@ -16,6 +16,5 @@ serde_json.workspace = true [dev-dependencies] tokio.workspace = true uuid.workspace = true -serde_json.workspace = true sqlx.workspace = true domain = { path = "../domain" } diff --git a/crates/search/src/lib.rs b/crates/search/src/lib.rs index a74f202..286a1c3 100644 --- a/crates/search/src/lib.rs +++ b/crates/search/src/lib.rs @@ -176,6 +176,12 @@ impl SearchClient { /// Full-text query returning display-ready hits with highlighted snippets and the /// estimated total match count. `visibility`, when set, filters on the indexed /// `visibility` attribute. Pagination is offset/limit. + /// + /// # Preconditions + /// + /// When `visibility` is `Some`, the value must be one of `"draft"`, `"internal"`, or + /// `"public"`. The caller owns this validation (the API layer enforces it); this + /// method `debug_assert!`s the constraint as defense-in-depth. pub async fn search_objects( &self, query: &str, @@ -185,7 +191,14 @@ impl SearchClient { ) -> Result { let index = self.client.index(&self.index_uid); - let filter = visibility.map(|v| format!("visibility = \"{v}\"")); + let filter = visibility.map(|v| { + debug_assert!( + matches!(v, "draft" | "internal" | "public"), + "visibility filter must be a known value; got {v:?}" + ); + + format!("visibility = \"{v}\"") + }); let highlight: &[&str] = &["object_name", "brief_description", "fields_text"]; let crop: &[(&str, Option)] = &[("brief_description", None), ("fields_text", None)]; @@ -196,6 +209,7 @@ impl SearchClient { .with_limit(limit) .with_attributes_to_highlight(Selectors::Some(highlight)) .with_attributes_to_crop(Selectors::Some(crop)) + // ~20 words gives enough catalogue-description context around a match. .with_crop_length(20) .with_highlight_pre_tag(HL_PRE) .with_highlight_post_tag(HL_POST); @@ -226,6 +240,8 @@ impl SearchClient { Ok(SearchResults { hits, + // estimated_total_hits is always present for offset/limit pagination; + // None only under page-based mode, which we don't use. estimated_total: results.estimated_total_hits.unwrap_or(0), }) }