Skip to main content

context69_contracts/
search.rs

1use chrono::{DateTime, Utc};
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use utoipa::ToSchema;
6use uuid::Uuid;
7
8use crate::Visibility;
9
10#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
11pub struct SearchRequest {
12    pub query: String,
13    #[serde(default, skip_serializing_if = "Option::is_none")]
14    pub locale: Option<String>,
15    #[serde(default = "default_limit")]
16    pub limit: usize,
17    #[serde(default = "default_page")]
18    pub page: usize,
19    #[serde(default)]
20    pub source_key: Option<String>,
21    #[serde(default)]
22    pub group_path: Option<String>,
23    #[serde(default)]
24    pub published_after: Option<DateTime<Utc>>,
25    #[serde(default)]
26    pub published_before: Option<DateTime<Utc>>,
27    #[serde(default)]
28    pub metadata_filters: Vec<crate::MetadataFilter>,
29}
30
31fn default_limit() -> usize {
32    8
33}
34
35fn default_page() -> usize {
36    1
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
40#[serde(rename_all = "snake_case")]
41pub enum SearchMode {
42    Vector,
43    Hybrid,
44}
45
46impl SearchMode {
47    pub fn as_str(self) -> &'static str {
48        match self {
49            Self::Vector => "vector",
50            Self::Hybrid => "hybrid",
51        }
52    }
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
56pub struct SearchHit {
57    pub chunk_id: Uuid,
58    pub document_id: i64,
59    pub group_key: String,
60    pub group_path: String,
61    pub visibility: Visibility,
62    pub source_key: String,
63    pub external_id: String,
64    pub title: String,
65    pub summary: Option<String>,
66    pub source_uri: String,
67    pub published_at: Option<DateTime<Utc>>,
68    pub chunk_index: i32,
69    pub chunk_text: String,
70    pub score: f32,
71    #[serde(default, skip_serializing_if = "Option::is_none")]
72    pub vector_score: Option<f32>,
73    #[serde(default, skip_serializing_if = "Option::is_none")]
74    pub keyword_score: Option<f32>,
75    #[serde(default, skip_serializing_if = "Option::is_none")]
76    pub rerank_score: Option<f32>,
77    #[serde(default, skip_serializing_if = "Option::is_none")]
78    pub match_reason: Option<String>,
79    #[serde(default)]
80    #[schema(value_type = Object)]
81    pub metadata_json: Value,
82    #[serde(default, skip_serializing_if = "Option::is_none")]
83    pub library_file_id: Option<Uuid>,
84    #[serde(default, skip_serializing_if = "Option::is_none")]
85    pub library_section_label: Option<String>,
86    #[serde(default, skip_serializing_if = "Option::is_none")]
87    pub library_path: Option<String>,
88    #[serde(default)]
89    pub is_library_file: bool,
90    #[serde(default, skip_serializing_if = "Option::is_none")]
91    pub requested_locale: Option<String>,
92    #[serde(default, skip_serializing_if = "Option::is_none")]
93    pub content_locale: Option<String>,
94    #[serde(default, skip_serializing_if = "Option::is_none")]
95    pub translation_status: Option<crate::TranslationStatus>,
96    #[serde(default)]
97    pub is_fallback: bool,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
101pub struct SearchResponse {
102    pub query: String,
103    pub items: Vec<SearchHit>,
104    pub pagination: crate::Pagination,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
108pub struct DocumentResponse {
109    pub document_id: i64,
110    pub group_key: String,
111    pub group_path: String,
112    pub visibility: Visibility,
113    pub source_key: String,
114    pub external_id: String,
115    pub title: String,
116    pub summary: Option<String>,
117    pub source_uri: String,
118    pub published_at: Option<DateTime<Utc>>,
119    pub updated_at: DateTime<Utc>,
120    pub record_hash: String,
121    #[serde(default)]
122    #[schema(value_type = Object)]
123    pub metadata_json: Value,
124    #[serde(default, skip_serializing_if = "Option::is_none")]
125    pub library_file_id: Option<Uuid>,
126    #[serde(default, skip_serializing_if = "Option::is_none")]
127    pub library_section_label: Option<String>,
128    #[serde(default, skip_serializing_if = "Option::is_none")]
129    pub library_path: Option<String>,
130    #[serde(default)]
131    pub is_library_file: bool,
132    pub chunks: Vec<DocumentChunkResponse>,
133    #[serde(default, skip_serializing_if = "Option::is_none")]
134    pub requested_locale: Option<String>,
135    #[serde(default, skip_serializing_if = "Option::is_none")]
136    pub content_locale: Option<String>,
137    #[serde(default, skip_serializing_if = "Option::is_none")]
138    pub translation_status: Option<crate::TranslationStatus>,
139    #[serde(default)]
140    pub is_fallback: bool,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)]
144pub struct DocumentChunkResponse {
145    pub chunk_id: Uuid,
146    pub chunk_index: i32,
147    pub text: String,
148}