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