1use async_trait::async_trait;
5use lc_shared::splitter::{RecursiveCharacterSplitter, TextSplitter};
6use rusqlite::Connection;
7use std::sync::Arc;
8use tokio::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> {
83 Ok(())
84 }
85}
86
87#[async_trait]
88impl DocumentStore for SQLiteDocumentStore {
89 async fn add_document(&self, document: Document) -> Result<String, VectorStoreError> {
90 let id = document
91 .id
92 .clone()
93 .unwrap_or_else(|| Uuid::new_v4().to_string());
94 let meta = serde_json::to_string(&document.metadata).unwrap_or_else(|_| "{}".to_string());
95 let conn = self.conn.lock().await;
96 conn.execute(
97 "INSERT OR REPLACE INTO documents (id, content, metadata) VALUES (?1, ?2, ?3)",
98 rusqlite::params![id, document.content, meta],
99 )
100 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
101 Ok(id)
102 }
103
104 async fn add_documents(
105 &self,
106 documents: Vec<Document>,
107 ) -> Result<Vec<String>, VectorStoreError> {
108 let conn = self.conn.lock().await;
109 let mut ids = Vec::new();
110 for doc in documents {
111 let id = doc.id.clone().unwrap_or_else(|| Uuid::new_v4().to_string());
112 let meta = serde_json::to_string(&doc.metadata).unwrap_or_else(|_| "{}".to_string());
113 conn.execute(
114 "INSERT OR REPLACE INTO documents (id, content, metadata) VALUES (?1, ?2, ?3)",
115 rusqlite::params![id, doc.content, meta],
116 )
117 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
118 ids.push(id);
119 }
120 Ok(ids)
121 }
122
123 async fn get_document(&self, id: &str) -> Result<Option<Document>, VectorStoreError> {
124 let conn = self.conn.lock().await;
125 let mut stmt = conn
126 .prepare("SELECT id, content, metadata FROM documents WHERE id = ?1")
127 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
128 let result = stmt.query_row(rusqlite::params![id], |row| {
129 let id: String = row.get(0)?;
130 let content: String = row.get(1)?;
131 let meta_str: String = row.get(2)?;
132 Ok(Document {
133 id: Some(id),
134 content,
135 metadata: parse_metadata_or_default(&meta_str),
136 })
137 });
138 match result {
139 Ok(doc) => Ok(Some(doc)),
140 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
141 Err(e) => Err(VectorStoreError::StorageError(e.to_string())),
142 }
143 }
144
145 async fn delete_document(&self, id: &str) -> Result<(), VectorStoreError> {
146 let conn = self.conn.lock().await;
147 conn.execute(
148 "DELETE FROM chunks WHERE parent_id = ?1",
149 rusqlite::params![id],
150 )
151 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
152 conn.execute("DELETE FROM documents WHERE id = ?1", rusqlite::params![id])
153 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
154 Ok(())
155 }
156
157 async fn count(&self) -> usize {
158 let conn = self.conn.lock().await;
159 match conn.query_row("SELECT COUNT(*) FROM documents", [], |r| r.get(0)) {
160 Ok(count) => count,
161 Err(e) => {
164 log::error!("SQLite count(documents) query failed, returning 0: {}", e);
165 0
166 }
167 }
168 }
169
170 async fn clear(&self) -> Result<(), VectorStoreError> {
171 let conn = self.conn.lock().await;
172 conn.execute_batch("DELETE FROM chunks; DELETE FROM documents;")
173 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
174 Ok(())
175 }
176}
177
178#[async_trait]
179impl ChunkedDocumentStoreTrait for SQLiteDocumentStore {
180 async fn add_parent_document(
181 &self,
182 document: Document,
183 chunk_size: usize,
184 ) -> Result<(String, Vec<String>), VectorStoreError> {
185 let splitter = RecursiveCharacterSplitter::new(chunk_size, chunk_size / 10);
186 let chunks_text = splitter.split_text(&document.content);
187 let parent_id = document
188 .id
189 .clone()
190 .unwrap_or_else(|| Uuid::new_v4().to_string());
191 let meta = serde_json::to_string(&document.metadata).unwrap_or_else(|_| "{}".to_string());
192
193 let conn = self.conn.lock().await;
194 conn.execute(
195 "INSERT OR REPLACE INTO documents (id, content, metadata) VALUES (?1, ?2, ?3)",
196 rusqlite::params![parent_id, document.content, meta],
197 )
198 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
199
200 let mut chunk_ids = Vec::new();
201 for (i, text) in chunks_text.iter().enumerate() {
202 let cid = format!("{}:chunk:{}", parent_id, i);
203 conn.execute("INSERT OR REPLACE INTO chunks (chunk_id, parent_id, content, segment, metadata) VALUES (?1, ?2, ?3, ?4, ?5)",
204 rusqlite::params![cid, parent_id, text, i, "{}"])
205 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
206 chunk_ids.push(cid);
207 }
208 Ok((parent_id, chunk_ids))
209 }
210
211 async fn add_parent_documents(
212 &self,
213 documents: Vec<Document>,
214 chunk_size: usize,
215 ) -> Result<Vec<(String, Vec<String>)>, VectorStoreError> {
216 let mut results = Vec::new();
217 for doc in documents {
218 results.push(self.add_parent_document(doc, chunk_size).await?);
219 }
220 Ok(results)
221 }
222
223 async fn get_parent_document(
224 &self,
225 parent_id: &str,
226 ) -> Result<Option<Document>, VectorStoreError> {
227 self.get_document(parent_id).await
228 }
229
230 async fn get_chunk(&self, chunk_id: &str) -> Result<Option<ChunkDocument>, VectorStoreError> {
231 let conn = self.conn.lock().await;
232 let mut stmt = conn.prepare("SELECT chunk_id, parent_id, content, segment, metadata FROM chunks WHERE chunk_id = ?1")
233 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
234 let result = stmt.query_row(rusqlite::params![chunk_id], |row| {
235 Ok(ChunkDocument {
236 chunk_id: row.get(0)?,
237 parent_id: row.get(1)?,
238 content: row.get(2)?,
239 segment: row.get(3)?,
240 metadata: parse_metadata_or_default(&row.get::<_, String>(4)?),
241 })
242 });
243 match result {
244 Ok(chunk) => Ok(Some(chunk)),
245 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
246 Err(e) => Err(VectorStoreError::StorageError(e.to_string())),
247 }
248 }
249
250 async fn get_chunk_document(
251 &self,
252 chunk_id: &str,
253 ) -> Result<Option<Document>, VectorStoreError> {
254 Ok(self.get_chunk(chunk_id).await?.map(|c| c.to_document()))
255 }
256
257 async fn get_chunks_for_parent(
258 &self,
259 parent_id: &str,
260 ) -> Result<Vec<ChunkDocument>, VectorStoreError> {
261 let conn = self.conn.lock().await;
262 let mut stmt = conn.prepare("SELECT chunk_id, parent_id, content, segment, metadata FROM chunks WHERE parent_id = ?1 ORDER BY segment")
263 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
264 let chunks = stmt
265 .query_map(rusqlite::params![parent_id], |row| {
266 Ok(ChunkDocument {
267 chunk_id: row.get(0)?,
268 parent_id: row.get(1)?,
269 content: row.get(2)?,
270 segment: row.get(3)?,
271 metadata: parse_metadata_or_default(&row.get::<_, String>(4)?),
272 })
273 })
274 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
275 .filter_map(|r| match r {
276 Ok(chunk) => Some(chunk),
277 Err(e) => {
278 log::error!(
280 "SQLite store: a row failed to deserialize and was dropped from the query result: {}",
281 e
282 );
283 None
284 }
285 })
286 .collect();
287 Ok(chunks)
288 }
289
290 async fn get_chunk_documents_for_parent(
291 &self,
292 parent_id: &str,
293 ) -> Result<Vec<Document>, VectorStoreError> {
294 Ok(self
295 .get_chunks_for_parent(parent_id)
296 .await?
297 .into_iter()
298 .map(|c| c.to_document())
299 .collect())
300 }
301
302 async fn delete_parent_document(&self, parent_id: &str) -> Result<(), VectorStoreError> {
303 self.delete_document(parent_id).await
304 }
305
306 async fn parent_count(&self) -> usize {
307 let conn = self.conn.lock().await;
308 match conn.query_row("SELECT COUNT(*) FROM documents", [], |r| r.get(0)) {
309 Ok(count) => count,
310 Err(e) => {
312 log::error!("SQLite parent_count query failed, returning 0: {}", e);
313 0
314 }
315 }
316 }
317
318 async fn chunk_count(&self) -> usize {
319 let conn = self.conn.lock().await;
320 match conn.query_row("SELECT COUNT(*) FROM chunks", [], |r| r.get(0)) {
321 Ok(count) => count,
322 Err(e) => {
324 log::error!("SQLite chunk_count query failed, returning 0: {}", e);
325 0
326 }
327 }
328 }
329
330 async fn get_all_chunks(&self) -> Result<Vec<ChunkDocument>, VectorStoreError> {
331 let conn = self.conn.lock().await;
332 let mut stmt = conn
333 .prepare("SELECT chunk_id, parent_id, content, segment, metadata FROM chunks")
334 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
335 let chunks = stmt
336 .query_map([], |row| {
337 Ok(ChunkDocument {
338 chunk_id: row.get(0)?,
339 parent_id: row.get(1)?,
340 content: row.get(2)?,
341 segment: row.get(3)?,
342 metadata: parse_metadata_or_default(&row.get::<_, String>(4)?),
343 })
344 })
345 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
346 .filter_map(|r| match r {
347 Ok(chunk) => Some(chunk),
348 Err(e) => {
349 log::error!(
351 "SQLite store: a row failed to deserialize and was dropped from the query result: {}",
352 e
353 );
354 None
355 }
356 })
357 .collect();
358 Ok(chunks)
359 }
360
361 async fn clear(&self) -> Result<(), VectorStoreError> {
362 let conn = self.conn.lock().await;
363 conn.execute_batch("DELETE FROM chunks; DELETE FROM documents;")
364 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
365 Ok(())
366 }
367
368 fn add_parent_document_blocking(
370 &self,
371 document: Document,
372 chunk_size: usize,
373 ) -> Result<(String, Vec<String>), VectorStoreError> {
374 let conn = self.conn.blocking_lock();
375 let splitter = RecursiveCharacterSplitter::new(chunk_size, chunk_size / 10);
376 let chunks_text = splitter.split_text(&document.content);
377 let parent_id = document
378 .id
379 .clone()
380 .unwrap_or_else(|| Uuid::new_v4().to_string());
381 let meta = serde_json::to_string(&document.metadata).unwrap_or_else(|_| "{}".to_string());
382
383 conn.execute(
384 "INSERT OR REPLACE INTO documents (id, content, metadata) VALUES (?1, ?2, ?3)",
385 rusqlite::params![parent_id, document.content, meta],
386 )
387 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
388
389 let mut chunk_ids = Vec::new();
390 for (i, text) in chunks_text.iter().enumerate() {
391 let cid = format!("{}:chunk:{}", parent_id, i);
392 conn.execute("INSERT OR REPLACE INTO chunks (chunk_id, parent_id, content, segment, metadata) VALUES (?1, ?2, ?3, ?4, ?5)",
393 rusqlite::params![cid, parent_id, text, i, "{}"])
394 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
395 chunk_ids.push(cid);
396 }
397 Ok((parent_id, chunk_ids))
398 }
399
400 fn get_parent_document_blocking(
401 &self,
402 parent_id: &str,
403 ) -> Result<Option<Document>, VectorStoreError> {
404 let conn = self.conn.blocking_lock();
405 let mut stmt = conn
406 .prepare("SELECT id, content, metadata FROM documents WHERE id = ?1")
407 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
408 let result = stmt.query_row(rusqlite::params![parent_id], |row| {
409 Ok(Document {
410 id: Some(row.get(0)?),
411 content: row.get(1)?,
412 metadata: parse_metadata_or_default(&row.get::<_, String>(2)?),
413 })
414 });
415 match result {
416 Ok(doc) => Ok(Some(doc)),
417 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
418 Err(e) => Err(VectorStoreError::StorageError(e.to_string())),
419 }
420 }
421
422 fn get_chunk_blocking(
423 &self,
424 chunk_id: &str,
425 ) -> Result<Option<ChunkDocument>, VectorStoreError> {
426 let conn = self.conn.blocking_lock();
427 let mut stmt = conn.prepare("SELECT chunk_id, parent_id, content, segment, metadata FROM chunks WHERE chunk_id = ?1")
428 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
429 let result = stmt.query_row(rusqlite::params![chunk_id], |row| {
430 Ok(ChunkDocument {
431 chunk_id: row.get(0)?,
432 parent_id: row.get(1)?,
433 content: row.get(2)?,
434 segment: row.get(3)?,
435 metadata: parse_metadata_or_default(&row.get::<_, String>(4)?),
436 })
437 });
438 match result {
439 Ok(chunk) => Ok(Some(chunk)),
440 Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
441 Err(e) => Err(VectorStoreError::StorageError(e.to_string())),
442 }
443 }
444
445 fn blocking_get_chunks_for_parent(
446 &self,
447 parent_id: &str,
448 ) -> Result<Vec<ChunkDocument>, VectorStoreError> {
449 let conn = self.conn.blocking_lock();
450 let mut stmt = conn.prepare("SELECT chunk_id, parent_id, content, segment, metadata FROM chunks WHERE parent_id = ?1 ORDER BY segment")
451 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
452 let chunks = stmt
453 .query_map(rusqlite::params![parent_id], |row| {
454 Ok(ChunkDocument {
455 chunk_id: row.get(0)?,
456 parent_id: row.get(1)?,
457 content: row.get(2)?,
458 segment: row.get(3)?,
459 metadata: parse_metadata_or_default(&row.get::<_, String>(4)?),
460 })
461 })
462 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
463 .filter_map(|r| match r {
464 Ok(chunk) => Some(chunk),
465 Err(e) => {
466 log::error!(
468 "SQLite store: a row failed to deserialize and was dropped from the query result: {}",
469 e
470 );
471 None
472 }
473 })
474 .collect();
475 Ok(chunks)
476 }
477}