1use async_trait::async_trait;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use uuid::Uuid;
7
8use crate::types::{BatchWriteSummary, DeleteMode, Page, PageRequest, SqlValue, StorageResult};
9
10#[derive(Clone, Debug, Serialize, Deserialize)]
12pub struct Note {
13 pub id: Uuid,
14 pub namespace: String,
15 pub kind: String,
16 pub status: String,
17 pub name: Option<String>,
18 pub content: String,
19 pub salience: Option<f64>,
20 pub decay_factor: Option<f64>,
21 pub expires_at: Option<i64>,
22 pub properties: Option<Value>,
23 pub created_at: i64,
24 pub updated_at: i64,
25 pub deleted_at: Option<i64>,
26}
27
28impl Note {
29 pub fn new(
30 namespace: impl Into<String>,
31 kind: impl Into<String>,
32 content: impl Into<String>,
33 ) -> Self {
34 let now = chrono::Utc::now().timestamp_micros();
35 Self {
36 id: Uuid::new_v4(),
37 namespace: namespace.into(),
38 kind: kind.into(),
39 status: "active".to_string(),
40 name: None,
41 content: content.into(),
42 salience: None,
43 decay_factor: None,
44 expires_at: None,
45 properties: None,
46 created_at: now,
47 updated_at: now,
48 deleted_at: None,
49 }
50 }
51
52 pub fn with_name(mut self, n: impl Into<String>) -> Self {
53 self.name = Some(n.into());
54 self
55 }
56
57 pub fn with_salience(mut self, s: f64) -> Self {
58 self.salience = Some(s.clamp(0.0, 1.0));
59 self
60 }
61
62 pub fn with_decay(mut self, d: f64) -> Self {
63 self.decay_factor = Some(d.max(0.0));
64 self
65 }
66
67 pub fn with_properties(mut self, p: Value) -> Self {
68 self.properties = Some(p);
69 self
70 }
71}
72
73#[derive(Clone, Debug, Serialize, Deserialize)]
75#[serde(rename_all = "snake_case")]
76pub enum SortDir {
77 Asc,
78 Desc,
79}
80
81#[derive(Clone, Debug, Serialize, Deserialize)]
83#[serde(rename_all = "snake_case")]
84pub enum FilterOp {
85 Eq,
86 EqOrMissing,
89 Ne,
90 Lt,
91 Lte,
92 Gt,
93 Gte,
94}
95
96#[derive(Clone, Debug, Serialize, Deserialize)]
101pub struct PropertyFilter {
102 pub json_path: String,
103 pub op: FilterOp,
104 pub value: SqlValue,
105}
106
107#[derive(Clone, Debug, Default, Serialize, Deserialize)]
112pub struct NoteFilter {
113 pub kind: Option<String>,
114 #[serde(default)]
115 pub property_filters: Vec<PropertyFilter>,
116 pub order_by: Option<(String, SortDir)>,
118}
119
120#[async_trait]
121pub trait NoteStore: Send + Sync + 'static {
122 async fn upsert_note(&self, note: Note) -> StorageResult<()>;
123 async fn upsert_notes(&self, notes: Vec<Note>) -> StorageResult<BatchWriteSummary>;
124 async fn get_note(&self, id: Uuid) -> StorageResult<Option<Note>>;
125 async fn delete_note(&self, id: Uuid, mode: DeleteMode) -> StorageResult<bool>;
126 async fn query_notes(
127 &self,
128 namespace: &str,
129 kind: Option<&str>,
130 page: PageRequest,
131 ) -> StorageResult<Page<Note>>;
132 async fn query_notes_filtered(
133 &self,
134 namespace: &str,
135 filter: &NoteFilter,
136 page: PageRequest,
137 ) -> StorageResult<Page<Note>>;
138 async fn count_notes(&self, namespace: &str, kind: Option<&str>) -> StorageResult<u64>;
139
140 async fn get_notes_batch(&self, ids: &[Uuid]) -> StorageResult<Vec<Note>> {
141 let mut out = Vec::with_capacity(ids.len());
142 for &id in ids {
143 if let Some(n) = self.get_note(id).await? {
144 out.push(n);
145 }
146 }
147 Ok(out)
148 }
149}