1use async_trait::async_trait;
5use lc_shared::splitter::{RecursiveCharacterSplitter, TextSplitter};
6use rusqlite::Connection;
7use std::path::Path;
8use std::sync::Arc;
9use tokio::sync::Mutex;
10use uuid::Uuid;
11
12use crate::document_store::{ChunkDocument, ChunkedDocumentStoreTrait, DocumentStore};
13use crate::{Document, VectorStoreError};
14
15#[derive(Debug, Clone)]
16pub struct SQLiteStoreConfig {
17 pub db_path: String,
18}
19
20impl Default for SQLiteStoreConfig {
21 fn default() -> Self {
22 Self {
23 db_path: "langchainrust.db".to_string(),
24 }
25 }
26}
27
28impl SQLiteStoreConfig {
29 pub fn new(path: impl Into<String>) -> Self {
30 Self {
31 db_path: path.into(),
32 }
33 }
34}
35
36pub struct SQLiteDocumentStore {
37 conn: Arc<Mutex<Connection>>,
38}
39
40impl SQLiteDocumentStore {
41 pub fn new(config: SQLiteStoreConfig) -> Result<Self, VectorStoreError> {
42 let conn = Connection::open(&config.db_path)
43 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
44 conn.execute_batch(
45 "CREATE TABLE IF NOT EXISTS documents (
46 id TEXT PRIMARY KEY, content TEXT NOT NULL,
47 metadata TEXT NOT NULL DEFAULT '{}'
48 );
49 CREATE TABLE IF NOT EXISTS chunks (
50 chunk_id TEXT PRIMARY KEY, parent_id TEXT NOT NULL,
51 content TEXT NOT NULL, segment INTEGER NOT NULL,
52 metadata TEXT NOT NULL DEFAULT '{}'
53 );
54 CREATE INDEX IF NOT EXISTS idx_chunks_parent ON chunks(parent_id);
55 CREATE INDEX IF NOT EXISTS idx_chunks_segment ON chunks(parent_id, segment);",
56 )
57 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
58 Ok(Self {
59 conn: Arc::new(Mutex::new(conn)),
60 })
61 }
62}
63
64#[async_trait]
65impl DocumentStore for SQLiteDocumentStore {
66 async fn add_document(&self, document: Document) -> Result<String, VectorStoreError> {
67 let id = document
68 .id
69 .clone()
70 .unwrap_or_else(|| Uuid::new_v4().to_string());
71 let meta = serde_json::to_string(&document.metadata).unwrap_or_else(|_| "{}".to_string());
72 let conn = self.conn.lock().await;
73 conn.execute(
74 "INSERT OR REPLACE INTO documents (id, content, metadata) VALUES (?1, ?2, ?3)",
75 rusqlite::params![id, document.content, meta],
76 )
77 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
78 Ok(id)
79 }
80
81 async fn add_documents(
82 &self,
83 documents: Vec<Document>,
84 ) -> Result<Vec<String>, VectorStoreError> {
85 let conn = self.conn.lock().await;
86 let mut ids = Vec::new();
87 for doc in documents {
88 let id = doc.id.clone().unwrap_or_else(|| Uuid::new_v4().to_string());
89 let meta = serde_json::to_string(&doc.metadata).unwrap_or_else(|_| "{}".to_string());
90 conn.execute(
91 "INSERT OR REPLACE INTO documents (id, content, metadata) VALUES (?1, ?2, ?3)",
92 rusqlite::params![id, doc.content, meta],
93 )
94 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
95 ids.push(id);
96 }
97 Ok(ids)
98 }
99
100 async fn get_document(&self, id: &str) -> Result<Option<Document>, VectorStoreError> {
101 let conn = self.conn.lock().await;
102 let mut stmt = conn
103 .prepare("SELECT id, content, metadata FROM documents WHERE id = ?1")
104 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
105 let result = stmt.query_row(rusqlite::params![id], |row| {
106 let id: String = row.get(0)?;
107 let content: String = row.get(1)?;
108 let meta_str: String = row.get(2)?;
109 Ok(Document {
110 id: Some(id),
111 content,
112 metadata: serde_json::from_str(&meta_str).unwrap_or_default(),
113 })
114 });
115 match result {
116 Ok(doc) => Ok(Some(doc)),
117 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
118 Err(e) => Err(VectorStoreError::StorageError(e.to_string())),
119 }
120 }
121
122 async fn delete_document(&self, id: &str) -> Result<(), VectorStoreError> {
123 let conn = self.conn.lock().await;
124 conn.execute(
125 "DELETE FROM chunks WHERE parent_id = ?1",
126 rusqlite::params![id],
127 )
128 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
129 conn.execute("DELETE FROM documents WHERE id = ?1", rusqlite::params![id])
130 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
131 Ok(())
132 }
133
134 async fn count(&self) -> usize {
135 let conn = self.conn.lock().await;
136 conn.query_row("SELECT COUNT(*) FROM documents", [], |r| r.get(0))
137 .unwrap_or(0)
138 }
139
140 async fn clear(&self) -> Result<(), VectorStoreError> {
141 let conn = self.conn.lock().await;
142 conn.execute_batch("DELETE FROM chunks; DELETE FROM documents;")
143 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
144 Ok(())
145 }
146}
147
148#[async_trait]
149impl ChunkedDocumentStoreTrait for SQLiteDocumentStore {
150 async fn add_parent_document(
151 &self,
152 document: Document,
153 chunk_size: usize,
154 ) -> Result<(String, Vec<String>), VectorStoreError> {
155 let splitter = RecursiveCharacterSplitter::new(chunk_size, chunk_size / 10);
156 let chunks_text = splitter.split_text(&document.content);
157 let parent_id = document
158 .id
159 .clone()
160 .unwrap_or_else(|| Uuid::new_v4().to_string());
161 let meta = serde_json::to_string(&document.metadata).unwrap_or_else(|_| "{}".to_string());
162
163 let conn = self.conn.lock().await;
164 conn.execute(
165 "INSERT OR REPLACE INTO documents (id, content, metadata) VALUES (?1, ?2, ?3)",
166 rusqlite::params![parent_id, document.content, meta],
167 )
168 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
169
170 let mut chunk_ids = Vec::new();
171 for (i, text) in chunks_text.iter().enumerate() {
172 let cid = format!("{}:chunk:{}", parent_id, i);
173 conn.execute("INSERT OR REPLACE INTO chunks (chunk_id, parent_id, content, segment, metadata) VALUES (?1, ?2, ?3, ?4, ?5)",
174 rusqlite::params![cid, parent_id, text, i, "{}"])
175 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
176 chunk_ids.push(cid);
177 }
178 Ok((parent_id, chunk_ids))
179 }
180
181 async fn add_parent_documents(
182 &self,
183 documents: Vec<Document>,
184 chunk_size: usize,
185 ) -> Result<Vec<(String, Vec<String>)>, VectorStoreError> {
186 let mut results = Vec::new();
187 for doc in documents {
188 results.push(self.add_parent_document(doc, chunk_size).await?);
189 }
190 Ok(results)
191 }
192
193 async fn get_parent_document(
194 &self,
195 parent_id: &str,
196 ) -> Result<Option<Document>, VectorStoreError> {
197 self.get_document(parent_id).await
198 }
199
200 async fn get_chunk(&self, chunk_id: &str) -> Result<Option<ChunkDocument>, VectorStoreError> {
201 let conn = self.conn.lock().await;
202 let mut stmt = conn.prepare("SELECT chunk_id, parent_id, content, segment, metadata FROM chunks WHERE chunk_id = ?1")
203 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
204 let result = stmt.query_row(rusqlite::params![chunk_id], |row| {
205 Ok(ChunkDocument {
206 chunk_id: row.get(0)?,
207 parent_id: row.get(1)?,
208 content: row.get(2)?,
209 segment: row.get(3)?,
210 metadata: serde_json::from_str::<std::collections::HashMap<String, String>>(
211 &row.get::<_, String>(4)?,
212 )
213 .unwrap_or_default(),
214 })
215 });
216 match result {
217 Ok(chunk) => Ok(Some(chunk)),
218 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
219 Err(e) => Err(VectorStoreError::StorageError(e.to_string())),
220 }
221 }
222
223 async fn get_chunk_document(
224 &self,
225 chunk_id: &str,
226 ) -> Result<Option<Document>, VectorStoreError> {
227 Ok(self.get_chunk(chunk_id).await?.map(|c| c.to_document()))
228 }
229
230 async fn get_chunks_for_parent(
231 &self,
232 parent_id: &str,
233 ) -> Result<Vec<ChunkDocument>, VectorStoreError> {
234 let conn = self.conn.lock().await;
235 let mut stmt = conn.prepare("SELECT chunk_id, parent_id, content, segment, metadata FROM chunks WHERE parent_id = ?1 ORDER BY segment")
236 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
237 let chunks = stmt
238 .query_map(rusqlite::params![parent_id], |row| {
239 Ok(ChunkDocument {
240 chunk_id: row.get(0)?,
241 parent_id: row.get(1)?,
242 content: row.get(2)?,
243 segment: row.get(3)?,
244 metadata: serde_json::from_str(&row.get::<_, String>(4)?).unwrap_or_default(),
245 })
246 })
247 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
248 .filter_map(|r| r.ok())
249 .collect();
250 Ok(chunks)
251 }
252
253 async fn get_chunk_documents_for_parent(
254 &self,
255 parent_id: &str,
256 ) -> Result<Vec<Document>, VectorStoreError> {
257 Ok(self
258 .get_chunks_for_parent(parent_id)
259 .await?
260 .into_iter()
261 .map(|c| c.to_document())
262 .collect())
263 }
264
265 async fn delete_parent_document(&self, parent_id: &str) -> Result<(), VectorStoreError> {
266 self.delete_document(parent_id).await
267 }
268
269 async fn parent_count(&self) -> usize {
270 let conn = self.conn.lock().await;
271 conn.query_row("SELECT COUNT(*) FROM documents", [], |r| r.get(0))
272 .unwrap_or(0)
273 }
274
275 async fn chunk_count(&self) -> usize {
276 let conn = self.conn.lock().await;
277 conn.query_row("SELECT COUNT(*) FROM chunks", [], |r| r.get(0))
278 .unwrap_or(0)
279 }
280
281 async fn get_all_chunks(&self) -> Result<Vec<ChunkDocument>, VectorStoreError> {
282 let conn = self.conn.lock().await;
283 let mut stmt = conn
284 .prepare("SELECT chunk_id, parent_id, content, segment, metadata FROM chunks")
285 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
286 let chunks = stmt
287 .query_map([], |row| {
288 Ok(ChunkDocument {
289 chunk_id: row.get(0)?,
290 parent_id: row.get(1)?,
291 content: row.get(2)?,
292 segment: row.get(3)?,
293 metadata: serde_json::from_str(&row.get::<_, String>(4)?).unwrap_or_default(),
294 })
295 })
296 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
297 .filter_map(|r| r.ok())
298 .collect();
299 Ok(chunks)
300 }
301
302 async fn clear(&self) -> Result<(), VectorStoreError> {
303 let conn = self.conn.lock().await;
304 conn.execute_batch("DELETE FROM chunks; DELETE FROM documents;")
305 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
306 Ok(())
307 }
308
309 async fn save(&self, _path: impl AsRef<Path> + Send) -> Result<(), VectorStoreError> {
310 Ok(()) }
312
313 fn add_parent_document_blocking(
315 &self,
316 document: Document,
317 chunk_size: usize,
318 ) -> Result<(String, Vec<String>), VectorStoreError> {
319 let conn = self.conn.blocking_lock();
320 let splitter = RecursiveCharacterSplitter::new(chunk_size, chunk_size / 10);
321 let chunks_text = splitter.split_text(&document.content);
322 let parent_id = document
323 .id
324 .clone()
325 .unwrap_or_else(|| Uuid::new_v4().to_string());
326 let meta = serde_json::to_string(&document.metadata).unwrap_or_else(|_| "{}".to_string());
327
328 conn.execute(
329 "INSERT OR REPLACE INTO documents (id, content, metadata) VALUES (?1, ?2, ?3)",
330 rusqlite::params![parent_id, document.content, meta],
331 )
332 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
333
334 let mut chunk_ids = Vec::new();
335 for (i, text) in chunks_text.iter().enumerate() {
336 let cid = format!("{}:chunk:{}", parent_id, i);
337 conn.execute("INSERT OR REPLACE INTO chunks (chunk_id, parent_id, content, segment, metadata) VALUES (?1, ?2, ?3, ?4, ?5)",
338 rusqlite::params![cid, parent_id, text, i, "{}"])
339 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
340 chunk_ids.push(cid);
341 }
342 Ok((parent_id, chunk_ids))
343 }
344
345 fn get_parent_document_blocking(
346 &self,
347 parent_id: &str,
348 ) -> Result<Option<Document>, VectorStoreError> {
349 let conn = self.conn.blocking_lock();
350 let mut stmt = conn
351 .prepare("SELECT id, content, metadata FROM documents WHERE id = ?1")
352 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
353 let result = stmt.query_row(rusqlite::params![parent_id], |row| {
354 Ok(Document {
355 id: Some(row.get(0)?),
356 content: row.get(1)?,
357 metadata: serde_json::from_str(&row.get::<_, String>(2)?).unwrap_or_default(),
358 })
359 });
360 match result {
361 Ok(doc) => Ok(Some(doc)),
362 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
363 Err(e) => Err(VectorStoreError::StorageError(e.to_string())),
364 }
365 }
366
367 fn get_chunk_blocking(
368 &self,
369 chunk_id: &str,
370 ) -> Result<Option<ChunkDocument>, VectorStoreError> {
371 let conn = self.conn.blocking_lock();
372 let mut stmt = conn.prepare("SELECT chunk_id, parent_id, content, segment, metadata FROM chunks WHERE chunk_id = ?1")
373 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
374 let result = stmt.query_row(rusqlite::params![chunk_id], |row| {
375 Ok(ChunkDocument {
376 chunk_id: row.get(0)?,
377 parent_id: row.get(1)?,
378 content: row.get(2)?,
379 segment: row.get(3)?,
380 metadata: serde_json::from_str(&row.get::<_, String>(4)?).unwrap_or_default(),
381 })
382 });
383 match result {
384 Ok(chunk) => Ok(Some(chunk)),
385 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
386 Err(e) => Err(VectorStoreError::StorageError(e.to_string())),
387 }
388 }
389
390 fn blocking_get_chunks_for_parent(
391 &self,
392 parent_id: &str,
393 ) -> Result<Vec<ChunkDocument>, VectorStoreError> {
394 let conn = self.conn.blocking_lock();
395 let mut stmt = conn.prepare("SELECT chunk_id, parent_id, content, segment, metadata FROM chunks WHERE parent_id = ?1 ORDER BY segment")
396 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
397 let chunks = stmt
398 .query_map(rusqlite::params![parent_id], |row| {
399 Ok(ChunkDocument {
400 chunk_id: row.get(0)?,
401 parent_id: row.get(1)?,
402 content: row.get(2)?,
403 segment: row.get(3)?,
404 metadata: serde_json::from_str(&row.get::<_, String>(4)?).unwrap_or_default(),
405 })
406 })
407 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
408 .filter_map(|r| r.ok())
409 .collect();
410 Ok(chunks)
411 }
412}