1use std::fmt;
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use uuid::Uuid;
9
10use khive_types::{EdgeRelation, SubstrateKind};
11
12use crate::error::StorageError;
13
14pub type StorageResult<T> = Result<T, StorageError>;
15
16#[derive(Clone, Debug, Default, Serialize, Deserialize)]
17pub struct BatchWriteSummary {
18 pub attempted: u64,
19 pub affected: u64,
20 pub failed: u64,
21 #[serde(default, skip_serializing_if = "String::is_empty")]
22 pub first_error: String,
23}
24
25#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
26#[serde(rename_all = "snake_case")]
27pub enum DeleteMode {
28 Soft,
29 Hard,
30}
31
32#[derive(Clone, Debug, Serialize, Deserialize)]
35#[serde(rename_all = "snake_case")]
36pub enum SqlValue {
37 Null,
38 Bool(bool),
39 Integer(i64),
40 Float(f64),
41 Text(String),
42 Blob(Vec<u8>),
43 Json(Value),
44 Uuid(Uuid),
45 Timestamp(DateTime<Utc>),
46}
47
48#[derive(Clone, Debug, Serialize, Deserialize)]
49pub struct SqlStatement {
50 pub sql: String,
51 pub params: Vec<SqlValue>,
52 pub label: Option<String>,
53}
54
55#[derive(Clone, Debug, Serialize, Deserialize)]
56pub struct SqlColumn {
57 pub name: String,
58 pub value: SqlValue,
59}
60
61#[derive(Clone, Debug, Serialize, Deserialize)]
62pub struct SqlRow {
63 pub columns: Vec<SqlColumn>,
64}
65
66impl SqlRow {
67 pub fn get(&self, name: &str) -> Option<&SqlValue> {
68 self.columns
69 .iter()
70 .find(|c| c.name == name)
71 .map(|c| &c.value)
72 }
73}
74
75#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
76#[serde(rename_all = "snake_case")]
77pub enum SqlIsolation {
78 Default,
79 ReadCommitted,
80 RepeatableRead,
81 Serializable,
82}
83
84#[derive(Clone, Debug, Serialize, Deserialize)]
85pub struct SqlTxOptions {
86 pub read_only: bool,
87 pub isolation: SqlIsolation,
88 pub label: Option<String>,
89}
90
91impl Default for SqlTxOptions {
92 fn default() -> Self {
93 Self {
94 read_only: false,
95 isolation: SqlIsolation::Default,
96 label: None,
97 }
98 }
99}
100
101#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
104#[serde(rename_all = "snake_case")]
105pub enum VectorIndexKind {
106 Hnsw,
107 SqliteVec,
108 Flat,
109}
110
111#[derive(Clone, Debug, Serialize, Deserialize)]
112pub struct VectorRecord {
113 pub subject_id: Uuid,
114 pub kind: SubstrateKind,
115 pub namespace: String,
116 pub embedding: Vec<f32>,
117 pub updated_at: DateTime<Utc>,
118}
119
120#[derive(Clone, Debug, Serialize, Deserialize)]
121pub struct VectorSearchRequest {
122 pub query_embedding: Vec<f32>,
123 pub top_k: u32,
124 pub namespace: Option<String>,
125 pub kind: Option<SubstrateKind>,
126}
127
128#[derive(Clone, Debug, Serialize, Deserialize)]
129pub struct VectorSearchHit {
130 pub subject_id: Uuid,
131 pub score: khive_score::DeterministicScore,
132 pub rank: u32,
133}
134
135#[derive(Clone, Debug, Serialize, Deserialize)]
136pub struct VectorStoreInfo {
137 pub model_name: String,
138 pub dimensions: usize,
139 pub index_kind: VectorIndexKind,
140 pub entry_count: u64,
141 pub needs_rebuild: bool,
142 pub last_rebuild_at: Option<DateTime<Utc>>,
143}
144
145#[derive(Clone, Debug, Serialize, Deserialize)]
148pub struct TextDocument {
149 pub subject_id: Uuid,
150 pub kind: SubstrateKind,
151 pub namespace: String,
152 pub title: Option<String>,
153 pub body: String,
154 pub tags: Vec<String>,
155 pub metadata: Option<Value>,
156 pub updated_at: DateTime<Utc>,
157}
158
159#[derive(Clone, Debug, Default, Serialize, Deserialize)]
160pub struct TextFilter {
161 pub ids: Vec<Uuid>,
162 pub kinds: Vec<SubstrateKind>,
163 pub namespaces: Vec<String>,
164}
165
166#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
167#[serde(rename_all = "snake_case")]
168pub enum TextQueryMode {
169 Plain,
170 Phrase,
171}
172
173#[derive(Clone, Debug, Serialize, Deserialize)]
174pub struct TextSearchRequest {
175 pub query: String,
176 pub mode: TextQueryMode,
177 pub filter: Option<TextFilter>,
178 pub top_k: u32,
179 pub snippet_chars: usize,
180}
181
182#[derive(Clone, Debug, Serialize, Deserialize)]
183pub struct TextSearchHit {
184 pub subject_id: Uuid,
185 pub score: khive_score::DeterministicScore,
186 pub rank: u32,
187 pub title: Option<String>,
188 pub snippet: Option<String>,
189}
190
191#[derive(Clone, Debug, Serialize, Deserialize)]
192pub struct TextIndexStats {
193 pub document_count: u64,
194 pub needs_rebuild: bool,
195 pub last_rebuild_at: Option<DateTime<Utc>>,
196}
197
198#[derive(Clone, Debug, Serialize, Deserialize)]
199#[serde(rename_all = "snake_case")]
200pub enum IndexRebuildScope {
201 Full,
202 Entities(Vec<Uuid>),
203}
204
205#[derive(Clone, Debug, Serialize, Deserialize)]
208pub struct PageRequest {
209 pub offset: u64,
210 pub limit: u32,
211}
212
213impl Default for PageRequest {
214 fn default() -> Self {
215 Self {
216 offset: 0,
217 limit: 50,
218 }
219 }
220}
221
222#[derive(Clone, Debug, Serialize, Deserialize)]
223pub struct Page<T> {
224 pub items: Vec<T>,
225 pub total: Option<u64>,
226}
227
228#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
232pub struct LinkId(pub Uuid);
233
234impl From<Uuid> for LinkId {
235 fn from(u: Uuid) -> Self {
236 Self(u)
237 }
238}
239
240impl From<LinkId> for Uuid {
241 fn from(l: LinkId) -> Uuid {
242 l.0
243 }
244}
245
246impl fmt::Display for LinkId {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 self.0.fmt(f)
249 }
250}
251
252#[derive(Clone, Debug, Serialize, Deserialize)]
254pub struct Edge {
255 pub id: LinkId,
256 pub source_id: Uuid,
257 pub target_id: Uuid,
258 pub relation: EdgeRelation,
259 pub weight: f64,
260 pub created_at: DateTime<Utc>,
261 pub metadata: Option<Value>,
262}
263
264#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
265#[serde(rename_all = "snake_case")]
266pub enum Direction {
267 #[default]
268 Out,
269 In,
270 Both,
271}
272
273#[derive(Clone, Debug, Default, Serialize, Deserialize)]
274pub struct TimeRange {
275 pub start: Option<DateTime<Utc>>,
276 pub end: Option<DateTime<Utc>>,
277}
278
279#[derive(Clone, Debug, Default, Serialize, Deserialize)]
280pub struct EdgeFilter {
281 pub ids: Vec<LinkId>,
282 pub source_ids: Vec<Uuid>,
283 pub target_ids: Vec<Uuid>,
284 pub relations: Vec<EdgeRelation>,
285 pub min_weight: Option<f64>,
286 pub max_weight: Option<f64>,
287 pub created_at: Option<TimeRange>,
288}
289
290#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
291#[serde(rename_all = "snake_case")]
292pub enum EdgeSortField {
293 CreatedAt,
294 Weight,
295 Relation,
296}
297
298#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
299#[serde(rename_all = "snake_case")]
300pub enum SortDirection {
301 Asc,
302 Desc,
303}
304
305#[derive(Clone, Debug, Serialize, Deserialize)]
306pub struct SortOrder<F> {
307 pub field: F,
308 pub direction: SortDirection,
309}
310
311#[derive(Clone, Debug, Serialize, Deserialize)]
312pub struct NeighborQuery {
313 pub direction: Direction,
314 pub relations: Option<Vec<EdgeRelation>>,
315 pub limit: Option<u32>,
316 pub min_weight: Option<f64>,
317}
318
319#[derive(Clone, Debug, Serialize, Deserialize)]
320pub struct NeighborHit {
321 pub node_id: Uuid,
322 pub edge_id: Uuid,
323 pub relation: EdgeRelation,
324 pub weight: f64,
325}
326
327#[derive(Clone, Debug, Default, Serialize, Deserialize)]
328pub struct TraversalOptions {
329 pub max_depth: usize,
330 pub direction: Direction,
331 pub relations: Option<Vec<EdgeRelation>>,
332 pub min_weight: Option<f64>,
333 pub limit: Option<u32>,
334}
335
336impl TraversalOptions {
337 pub fn new(max_depth: usize) -> Self {
338 Self {
339 max_depth,
340 ..Default::default()
341 }
342 }
343
344 pub fn with_direction(mut self, d: Direction) -> Self {
345 self.direction = d;
346 self
347 }
348}
349
350#[derive(Clone, Debug, Serialize, Deserialize)]
351pub struct TraversalRequest {
352 pub roots: Vec<Uuid>,
353 pub options: TraversalOptions,
354 pub include_roots: bool,
355}
356
357#[derive(Clone, Debug, Serialize, Deserialize)]
358pub struct PathNode {
359 pub node_id: Uuid,
360 pub via_edge: Option<Uuid>,
361 pub depth: usize,
362}
363
364#[derive(Clone, Debug, Serialize, Deserialize)]
365pub struct GraphPath {
366 pub root_id: Uuid,
367 pub nodes: Vec<PathNode>,
368 pub total_weight: f64,
369}