Skip to main content

lc_vector_stores/
sqlite_store.rs

1// lc-vector-stores/src/sqlite_store.rs
2//! SQLite 文档存储实现
3
4use 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(()) // SQLite auto-saves
311    }
312
313    // Blocking methods
314    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}