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 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,5 @@ serde_json.workspace = true
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
serde_json.workspace = true
|
|
||||||
sqlx.workspace = true
|
sqlx.workspace = true
|
||||||
domain = { path = "../domain" }
|
domain = { path = "../domain" }
|
||||||
|
|||||||
@@ -176,6 +176,12 @@ impl SearchClient {
|
|||||||
/// Full-text query returning display-ready hits with highlighted snippets and the
|
/// Full-text query returning display-ready hits with highlighted snippets and the
|
||||||
/// estimated total match count. `visibility`, when set, filters on the indexed
|
/// estimated total match count. `visibility`, when set, filters on the indexed
|
||||||
/// `visibility` attribute. Pagination is offset/limit.
|
/// `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(
|
pub async fn search_objects(
|
||||||
&self,
|
&self,
|
||||||
query: &str,
|
query: &str,
|
||||||
@@ -185,7 +191,14 @@ impl SearchClient {
|
|||||||
) -> Result<SearchResults, SearchError> {
|
) -> Result<SearchResults, SearchError> {
|
||||||
let index = self.client.index(&self.index_uid);
|
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 highlight: &[&str] = &["object_name", "brief_description", "fields_text"];
|
||||||
let crop: &[(&str, Option<usize>)] = &[("brief_description", None), ("fields_text", None)];
|
let crop: &[(&str, Option<usize>)] = &[("brief_description", None), ("fields_text", None)];
|
||||||
|
|
||||||
@@ -196,6 +209,7 @@ impl SearchClient {
|
|||||||
.with_limit(limit)
|
.with_limit(limit)
|
||||||
.with_attributes_to_highlight(Selectors::Some(highlight))
|
.with_attributes_to_highlight(Selectors::Some(highlight))
|
||||||
.with_attributes_to_crop(Selectors::Some(crop))
|
.with_attributes_to_crop(Selectors::Some(crop))
|
||||||
|
// ~20 words gives enough catalogue-description context around a match.
|
||||||
.with_crop_length(20)
|
.with_crop_length(20)
|
||||||
.with_highlight_pre_tag(HL_PRE)
|
.with_highlight_pre_tag(HL_PRE)
|
||||||
.with_highlight_post_tag(HL_POST);
|
.with_highlight_post_tag(HL_POST);
|
||||||
@@ -226,6 +240,8 @@ impl SearchClient {
|
|||||||
|
|
||||||
Ok(SearchResults {
|
Ok(SearchResults {
|
||||||
hits,
|
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),
|
estimated_total: results.estimated_total_hits.unwrap_or(0),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user