1use async_trait::async_trait;
5use lc_shared::splitter::{RecursiveCharacterSplitter, TextSplitter};
6use rusqlite::Connection;
7use std::sync::Arc;
8use std::sync::Mutex;
9use uuid::Uuid;
10
11use crate::document_store::{ChunkDocument, ChunkedDocumentStoreTrait, DocumentStore};
12use crate::{Document, VectorStoreError};
13
14fn parse_metadata_or_default(raw: &str) -> std::collections::HashMap<String, serde_json::Value> {
16 serde_json::from_str(raw).unwrap_or_else(|e| {
17 log::warn!(
18 "SQLite metadata in store is corrupted, falling back to empty map: {}",
19 e
20 );
21 std::collections::HashMap::new()
22 })
23}
24
25#[derive(Debug, Clone)]
27pub struct SQLiteStoreConfig {
28 pub db_path: String,
30}
31
32impl Default for SQLiteStoreConfig {
33 fn default() -> Self {
34 Self {
35 db_path: "langchainrust.db".to_string(),
36 }
37 }
38}
39
40impl SQLiteStoreConfig {
41 pub fn new(path: impl Into<String>) -> Self {
43 Self {
44 db_path: path.into(),
45 }
46 }
47}
48
49pub struct SQLiteDocumentStore {
51 conn: Arc<Mutex<Connection>>,
52}
53
54impl SQLiteDocumentStore {
55 pub fn new(config: SQLiteStoreConfig) -> Result<Self, VectorStoreError> {
57 let conn = Connection::open(&config.db_path)
58 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
59 conn.execute_batch(
60 "CREATE TABLE IF NOT EXISTS documents (
61 id TEXT PRIMARY KEY, content TEXT NOT NULL,
62 metadata TEXT NOT NULL DEFAULT '{}'
63 );
64 CREATE TABLE IF NOT EXISTS chunks (
65 chunk_id TEXT PRIMARY KEY, parent_id TEXT NOT NULL,
66 content TEXT NOT NULL, segment INTEGER NOT NULL,
67 metadata TEXT NOT NULL DEFAULT '{}'
68 );
69 CREATE INDEX IF NOT EXISTS idx_chunks_parent ON chunks(parent_id);
70 CREATE INDEX IF NOT EXISTS idx_chunks_segment ON chunks(parent_id, segment);",
71 )
72 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
73 Ok(Self {
74 conn: Arc::new(Mutex::new(conn)),
75 })
76 }
77
78 pub async fn save(&self) -> Result<(), VectorStoreError> {
84 Ok(())
85 }
86}
87
88#[async_trait]
89impl DocumentStore for SQLiteDocumentStore {
90 async fn add_document(&self, document: Document) -> Result<String, VectorStoreError> {
91 let id = document
92 .id
93 .clone()
94 .unwrap_or_else(|| Uuid::new_v4().to_string());
95 let meta = serde_json::to_string(&document.metadata).unwrap_or_else(|_| "{}".to_string());
96 let conn = self.conn.lock().map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
97 conn.execute(
98 "INSERT OR REPLACE INTO documents (id, content, metadata) VALUES (?1, ?2, ?3)",
99 rusqlite::params![id, document.content, meta],
100 )
101 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
102 Ok(id)
103 }
104
105 async fn add_documents(
106 &self,
107 documents: Vec<Document>,
108 ) -> Result<Vec<String>, VectorStoreError> {
109 let conn = self.conn.lock().map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
110 let mut ids = Vec::new();
111 for doc in documents {
112 let id = doc.id.clone().unwrap_or_else(|| Uuid::new_v4().to_string());
113 let meta = serde_json::to_string(&doc.metadata).unwrap_or_else(|_| "{}".to_string());
114 conn.execute(
115 "INSERT OR REPLACE INTO documents (id, content, metadata) VALUES (?1, ?2, ?3)",
116 rusqlite::params![id, doc.content, meta],
117 )
118 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
119 ids.push(id);
120 }
121 Ok(ids)
122 }
123
124 async fn get_document(&self, id: &str) -> Result<Option<Document>, VectorStoreError> {
125 let conn = self.conn.lock().map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
126 let mut stmt = conn
127 .prepare("SELECT id, content, metadata FROM documents WHERE id = ?1")
128 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
129 let result = stmt.query_row(rusqlite::params![id], |row| {
130 let id: String = row.get(0)?;
131 let content: String = row.get(1)?;
132 let meta_str: String = row.get(2)?;
133 Ok(Document {
134 id: Some(id),
135 content,
136 metadata: parse_metadata_or_default(&meta_str),
137 })
138 });
139 match result {
140 Ok(doc) => Ok(Some(doc)),
141 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
142 Err(e) => Err(VectorStoreError::StorageError(e.to_string())),
143 }
144 }
145
146 async fn delete_document(&self, id: &str) -> Result<(), VectorStoreError> {
147 let conn = self.conn.lock().map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
148 conn.execute(
149 "DELETE FROM chunks WHERE parent_id = ?1",
150 rusqlite::params![id],
151 )
152 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
153 conn.execute("DELETE FROM documents WHERE id = ?1", rusqlite::params![id])
154 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
155 Ok(())
156 }
157
158 async fn count(&self) -> usize {
159 let conn = match self.conn.lock() {
162 Ok(guard) => guard,
163 Err(e) => {
164 log::error!("SQLite lock poisoned in count(documents), returning 0: {}", e);
165 return 0;
166 }
167 };
168 match conn.query_row("SELECT COUNT(*) FROM documents", [], |r| r.get(0)) {
169 Ok(count) => count,
170 Err(e) => {
174 log::error!("SQLite count(documents) query failed, returning 0: {}", e);
175 0
176 }
177 }
178 }
179
180 async fn clear(&self) -> Result<(), VectorStoreError> {
181 let conn = self.conn.lock().map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
182 conn.execute_batch("DELETE FROM chunks; DELETE FROM documents;")
183 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
184 Ok(())
185 }
186}
187
188#[async_trait]
189impl ChunkedDocumentStoreTrait for SQLiteDocumentStore {
190 async fn add_parent_document(
191 &self,
192 document: Document,
193 chunk_size: usize,
194 ) -> Result<(String, Vec<String>), VectorStoreError> {
195 let splitter = RecursiveCharacterSplitter::new(chunk_size, chunk_size / 10);
196 let chunks_text = splitter.split_text(&document.content);
197 let parent_id = document
198 .id
199 .clone()
200 .unwrap_or_else(|| Uuid::new_v4().to_string());
201 let meta = serde_json::to_string(&document.metadata).unwrap_or_else(|_| "{}".to_string());
202
203 let conn = self.conn.lock().map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
204 conn.execute(
205 "INSERT OR REPLACE INTO documents (id, content, metadata) VALUES (?1, ?2, ?3)",
206 rusqlite::params![parent_id, document.content, meta],
207 )
208 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
209
210 conn.execute(
215 "DELETE FROM chunks WHERE parent_id = ?1",
216 rusqlite::params![parent_id],
217 )
218 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
219
220 let mut chunk_ids = Vec::new();
221 for (i, text) in chunks_text.iter().enumerate() {
222 let cid = format!("{}:chunk:{}", parent_id, i);
223 conn.execute("INSERT OR REPLACE INTO chunks (chunk_id, parent_id, content, segment, metadata) VALUES (?1, ?2, ?3, ?4, ?5)",
224 rusqlite::params![cid, parent_id, text, i, "{}"])
225 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
226 chunk_ids.push(cid);
227 }
228 Ok((parent_id, chunk_ids))
229 }
230
231 async fn add_parent_documents(
232 &self,
233 documents: Vec<Document>,
234 chunk_size: usize,
235 ) -> Result<Vec<(String, Vec<String>)>, VectorStoreError> {
236 let mut results = Vec::new();
237 for doc in documents {
238 results.push(self.add_parent_document(doc, chunk_size).await?);
239 }
240 Ok(results)
241 }
242
243 async fn get_parent_document(
244 &self,
245 parent_id: &str,
246 ) -> Result<Option<Document>, VectorStoreError> {
247 self.get_document(parent_id).await
248 }
249
250 async fn get_chunk(&self, chunk_id: &str) -> Result<Option<ChunkDocument>, VectorStoreError> {
251 let conn = self.conn.lock().map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
252 let mut stmt = conn.prepare("SELECT chunk_id, parent_id, content, segment, metadata FROM chunks WHERE chunk_id = ?1")
253 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
254 let result = stmt.query_row(rusqlite::params![chunk_id], |row| {
255 Ok(ChunkDocument {
256 chunk_id: row.get(0)?,
257 parent_id: row.get(1)?,
258 content: row.get(2)?,
259 segment: row.get(3)?,
260 metadata: parse_metadata_or_default(&row.get::<_, String>(4)?),
261 })
262 });
263 match result {
264 Ok(chunk) => Ok(Some(chunk)),
265 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
266 Err(e) => Err(VectorStoreError::StorageError(e.to_string())),
267 }
268 }
269
270 async fn get_chunk_document(
271 &self,
272 chunk_id: &str,
273 ) -> Result<Option<Document>, VectorStoreError> {
274 Ok(self.get_chunk(chunk_id).await?.map(|c| c.to_document()))
275 }
276
277 async fn get_chunks_for_parent(
278 &self,
279 parent_id: &str,
280 ) -> Result<Vec<ChunkDocument>, VectorStoreError> {
281 let conn = self.conn.lock().map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
282 let mut stmt = conn.prepare("SELECT chunk_id, parent_id, content, segment, metadata FROM chunks WHERE parent_id = ?1 ORDER BY segment")
283 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
284 let chunks = stmt
285 .query_map(rusqlite::params![parent_id], |row| {
286 Ok(ChunkDocument {
287 chunk_id: row.get(0)?,
288 parent_id: row.get(1)?,
289 content: row.get(2)?,
290 segment: row.get(3)?,
291 metadata: parse_metadata_or_default(&row.get::<_, String>(4)?),
292 })
293 })
294 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
295 .filter_map(|r| match r {
296 Ok(chunk) => Some(chunk),
297 Err(e) => {
298 log::error!(
300 "SQLite store: a row failed to deserialize and was dropped from the query result: {}",
301 e
302 );
303 None
304 }
305 })
306 .collect();
307 Ok(chunks)
308 }
309
310 async fn get_chunk_documents_for_parent(
311 &self,
312 parent_id: &str,
313 ) -> Result<Vec<Document>, VectorStoreError> {
314 Ok(self
315 .get_chunks_for_parent(parent_id)
316 .await?
317 .into_iter()
318 .map(|c| c.to_document())
319 .collect())
320 }
321
322 async fn delete_parent_document(&self, parent_id: &str) -> Result<(), VectorStoreError> {
323 self.delete_document(parent_id).await
324 }
325
326 async fn parent_count(&self) -> usize {
327 let conn = self.conn.lock().map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
328 match conn.query_row("SELECT COUNT(*) FROM documents", [], |r| r.get(0)) {
329 Ok(count) => count,
330 Err(e) => {
332 log::error!("SQLite parent_count query failed, returning 0: {}", e);
333 0
334 }
335 }
336 }
337
338 async fn chunk_count(&self) -> usize {
339 let conn = self.conn.lock().map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
340 match conn.query_row("SELECT COUNT(*) FROM chunks", [], |r| r.get(0)) {
341 Ok(count) => count,
342 Err(e) => {
344 log::error!("SQLite chunk_count query failed, returning 0: {}", e);
345 0
346 }
347 }
348 }
349
350 async fn get_all_chunks(&self) -> Result<Vec<ChunkDocument>, VectorStoreError> {
351 let conn = self.conn.lock().map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
352 let mut stmt = conn
353 .prepare("SELECT chunk_id, parent_id, content, segment, metadata FROM chunks")
354 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
355 let chunks = stmt
356 .query_map([], |row| {
357 Ok(ChunkDocument {
358 chunk_id: row.get(0)?,
359 parent_id: row.get(1)?,
360 content: row.get(2)?,
361 segment: row.get(3)?,
362 metadata: parse_metadata_or_default(&row.get::<_, String>(4)?),
363 })
364 })
365 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
366 .filter_map(|r| match r {
367 Ok(chunk) => Some(chunk),
368 Err(e) => {
369 log::error!(
371 "SQLite store: a row failed to deserialize and was dropped from the query result: {}",
372 e
373 );
374 None
375 }
376 })
377 .collect();
378 Ok(chunks)
379 }
380
381 async fn clear(&self) -> Result<(), VectorStoreError> {
382 let conn = self.conn.lock().map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
383 conn.execute_batch("DELETE FROM chunks; DELETE FROM documents;")
384 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
385 Ok(())
386 }
387
388 fn add_parent_document_blocking(
390 &self,
391 document: Document,
392 chunk_size: usize,
393 ) -> Result<(String, Vec<String>), VectorStoreError> {
394 let conn = self.conn.lock().map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
395 let splitter = RecursiveCharacterSplitter::new(chunk_size, chunk_size / 10);
396 let chunks_text = splitter.split_text(&document.content);
397 let parent_id = document
398 .id
399 .clone()
400 .unwrap_or_else(|| Uuid::new_v4().to_string());
401 let meta = serde_json::to_string(&document.metadata).unwrap_or_else(|_| "{}".to_string());
402
403 conn.execute(
404 "INSERT OR REPLACE INTO documents (id, content, metadata) VALUES (?1, ?2, ?3)",
405 rusqlite::params![parent_id, document.content, meta],
406 )
407 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
408
409 conn.execute(
411 "DELETE FROM chunks WHERE parent_id = ?1",
412 rusqlite::params![parent_id],
413 )
414 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
415
416 let mut chunk_ids = Vec::new();
417 for (i, text) in chunks_text.iter().enumerate() {
418 let cid = format!("{}:chunk:{}", parent_id, i);
419 conn.execute("INSERT OR REPLACE INTO chunks (chunk_id, parent_id, content, segment, metadata) VALUES (?1, ?2, ?3, ?4, ?5)",
420 rusqlite::params![cid, parent_id, text, i, "{}"])
421 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
422 chunk_ids.push(cid);
423 }
424 Ok((parent_id, chunk_ids))
425 }
426
427 fn get_parent_document_blocking(
428 &self,
429 parent_id: &str,
430 ) -> Result<Option<Document>, VectorStoreError> {
431 let conn = self.conn.lock().map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
432 let mut stmt = conn
433 .prepare("SELECT id, content, metadata FROM documents WHERE id = ?1")
434 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
435 let result = stmt.query_row(rusqlite::params![parent_id], |row| {
436 Ok(Document {
437 id: Some(row.get(0)?),
438 content: row.get(1)?,
439 metadata: parse_metadata_or_default(&row.get::<_, String>(2)?),
440 })
441 });
442 match result {
443 Ok(doc) => Ok(Some(doc)),
444 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
445 Err(e) => Err(VectorStoreError::StorageError(e.to_string())),
446 }
447 }
448
449 fn get_chunk_blocking(
450 &self,
451 chunk_id: &str,
452 ) -> Result<Option<ChunkDocument>, VectorStoreError> {
453 let conn = self.conn.lock().map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
454 let mut stmt = conn.prepare("SELECT chunk_id, parent_id, content, segment, metadata FROM chunks WHERE chunk_id = ?1")
455 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
456 let result = stmt.query_row(rusqlite::params![chunk_id], |row| {
457 Ok(ChunkDocument {
458 chunk_id: row.get(0)?,
459 parent_id: row.get(1)?,
460 content: row.get(2)?,
461 segment: row.get(3)?,
462 metadata: parse_metadata_or_default(&row.get::<_, String>(4)?),
463 })
464 });
465 match result {
466 Ok(chunk) => Ok(Some(chunk)),
467 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
468 Err(e) => Err(VectorStoreError::StorageError(e.to_string())),
469 }
470 }
471
472 fn blocking_get_chunks_for_parent(
473 &self,
474 parent_id: &str,
475 ) -> Result<Vec<ChunkDocument>, VectorStoreError> {
476 let conn = self.conn.lock().map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
477 let mut stmt = conn.prepare("SELECT chunk_id, parent_id, content, segment, metadata FROM chunks WHERE parent_id = ?1 ORDER BY segment")
478 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
479 let chunks = stmt
480 .query_map(rusqlite::params![parent_id], |row| {
481 Ok(ChunkDocument {
482 chunk_id: row.get(0)?,
483 parent_id: row.get(1)?,
484 content: row.get(2)?,
485 segment: row.get(3)?,
486 metadata: parse_metadata_or_default(&row.get::<_, String>(4)?),
487 })
488 })
489 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
490 .filter_map(|r| match r {
491 Ok(chunk) => Some(chunk),
492 Err(e) => {
493 log::error!(
495 "SQLite store: a row failed to deserialize and was dropped from the query result: {}",
496 e
497 );
498 None
499 }
500 })
501 .collect();
502 Ok(chunks)
503 }
504}