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