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}