1use async_trait::async_trait;
5use lc_shared::splitter::{RecursiveCharacterSplitter, TextSplitter};
6use std::path::Path;
7
8use crate::document_store::{ChunkDocument, ChunkedDocumentStoreTrait, DocumentStore};
9use crate::{Document, VectorStoreError};
10
11#[derive(Debug, Clone)]
12pub struct RedisStoreConfig {
13 pub url: String,
14 pub key_prefix: String,
15}
16
17impl Default for RedisStoreConfig {
18 fn default() -> Self {
19 Self {
20 url: "redis://127.0.0.1:6379".to_string(),
21 key_prefix: "langchainrust".to_string(),
22 }
23 }
24}
25
26impl RedisStoreConfig {
27 pub fn new(url: impl Into<String>) -> Self {
28 Self {
29 url: url.into(),
30 ..Default::default()
31 }
32 }
33 pub fn with_prefix(mut self, prefix: impl Into<String>) -> Self {
34 self.key_prefix = prefix.into();
35 self
36 }
37}
38
39pub struct RedisDocumentStore {
40 config: RedisStoreConfig,
41 #[allow(dead_code)]
42 client: redis::Client,
43}
44
45impl RedisDocumentStore {
46 pub async fn new(config: RedisStoreConfig) -> Result<Self, VectorStoreError> {
47 let client = redis::Client::open(config.url.as_str())
48 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
49 let _ = client
51 .get_connection()
52 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
53 Ok(Self { config, client })
54 }
55
56 fn doc_key(&self, id: &str) -> String {
57 format!("{}:doc:{}", self.config.key_prefix, id)
58 }
59 fn chunk_key(&self, id: &str) -> String {
60 format!("{}:chunk:{}", self.config.key_prefix, id)
61 }
62 fn parent_chunks_key(&self, pid: &str) -> String {
63 format!("{}:pchunks:{}", self.config.key_prefix, pid)
64 }
65 fn doc_ids_key(&self) -> String {
66 format!("{}:doc_ids", self.config.key_prefix)
67 }
68 fn parent_ids_key(&self) -> String {
69 format!("{}:parent_ids", self.config.key_prefix)
70 }
71 fn all_chunks_key(&self) -> String {
72 format!("{}:all_chunks", self.config.key_prefix)
73 }
74}
75
76#[async_trait]
77impl DocumentStore for RedisDocumentStore {
78 async fn add_document(&self, document: Document) -> Result<String, VectorStoreError> {
79 let id = document
80 .id
81 .clone()
82 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
83 let json = serde_json::to_string(&document)
84 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
85 let config = self.config.clone();
86 let id2 = id.clone();
87
88 tokio::task::spawn_blocking(move || -> Result<(), VectorStoreError> {
89 let mut conn = redis::Client::open(config.url.as_str())
90 .and_then(|c| c.get_connection())
91 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
92 redis::cmd("SET")
93 .arg(format!("{}:doc:{}", config.key_prefix, id2))
94 .arg(&json)
95 .query::<()>(&mut conn)
96 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
97 redis::cmd("SADD")
98 .arg(format!("{}:doc_ids", config.key_prefix))
99 .arg(&id2)
100 .query::<()>(&mut conn)
101 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
102 Ok(())
103 })
104 .await
105 .map_err(|e| VectorStoreError::StorageError(e.to_string()))??;
106
107 Ok(id)
108 }
109
110 async fn add_documents(
111 &self,
112 documents: Vec<Document>,
113 ) -> Result<Vec<String>, VectorStoreError> {
114 let mut ids = Vec::new();
115 for doc in documents {
116 ids.push(self.add_document(doc).await?);
117 }
118 Ok(ids)
119 }
120
121 async fn get_document(&self, id: &str) -> Result<Option<Document>, VectorStoreError> {
122 let result = self.get_str(&self.doc_key(id)).await?;
123 match result {
124 Some(json) => Ok(serde_json::from_str(&json).ok()),
125 None => Ok(None),
126 }
127 }
128
129 async fn delete_document(&self, id: &str) -> Result<(), VectorStoreError> {
130 self.del(&self.doc_key(id)).await?;
131 self.srem(&self.doc_ids_key(), id).await
132 }
133
134 async fn count(&self) -> usize {
135 self.scard(&self.doc_ids_key()).await.unwrap_or(0)
136 }
137
138 async fn clear(&self) -> Result<(), VectorStoreError> {
139 self.clear_with_prefix().await
141 }
142}
143
144impl RedisDocumentStore {
145 async fn get_str(&self, key: &str) -> Result<Option<String>, VectorStoreError> {
146 let config = self.config.clone();
147 let key = key.to_string();
148 tokio::task::spawn_blocking(move || -> Result<Option<String>, VectorStoreError> {
149 let mut conn = redis::Client::open(config.url.as_str())
150 .and_then(|c| c.get_connection())
151 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
152 redis::cmd("GET")
153 .arg(&key)
154 .query(&mut conn)
155 .map_err(|e| VectorStoreError::StorageError(e.to_string()))
156 })
157 .await
158 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
159 }
160
161 async fn del(&self, key: &str) -> Result<(), VectorStoreError> {
162 let config = self.config.clone();
163 let key = key.to_string();
164 tokio::task::spawn_blocking(move || -> Result<(), VectorStoreError> {
165 let mut conn = redis::Client::open(config.url.as_str())
166 .and_then(|c| c.get_connection())
167 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
168 redis::cmd("DEL")
169 .arg(&key)
170 .query::<()>(&mut conn)
171 .map_err(|e| VectorStoreError::StorageError(e.to_string()))
172 })
173 .await
174 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
175 }
176
177 #[allow(dead_code)]
178 async fn sadd(&self, key: &str, member: &str) -> Result<(), VectorStoreError> {
179 let config = self.config.clone();
180 let (k, m) = (key.to_string(), member.to_string());
181 tokio::task::spawn_blocking(move || -> Result<(), VectorStoreError> {
182 let mut conn = redis::Client::open(config.url.as_str())
183 .and_then(|c| c.get_connection())
184 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
185 redis::cmd("SADD")
186 .arg(&k)
187 .arg(&m)
188 .query::<()>(&mut conn)
189 .map_err(|e| VectorStoreError::StorageError(e.to_string()))
190 })
191 .await
192 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
193 }
194
195 async fn srem(&self, key: &str, member: &str) -> Result<(), VectorStoreError> {
196 let config = self.config.clone();
197 let (k, m) = (key.to_string(), member.to_string());
198 tokio::task::spawn_blocking(move || -> Result<(), VectorStoreError> {
199 let mut conn = redis::Client::open(config.url.as_str())
200 .and_then(|c| c.get_connection())
201 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
202 redis::cmd("SREM")
203 .arg(&k)
204 .arg(&m)
205 .query::<()>(&mut conn)
206 .map_err(|e| VectorStoreError::StorageError(e.to_string()))
207 })
208 .await
209 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
210 }
211
212 async fn smembers(&self, key: &str) -> Result<Vec<String>, VectorStoreError> {
213 let config = self.config.clone();
214 let key = key.to_string();
215 tokio::task::spawn_blocking(move || -> Result<Vec<String>, VectorStoreError> {
216 let mut conn = redis::Client::open(config.url.as_str())
217 .and_then(|c| c.get_connection())
218 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
219 redis::cmd("SMEMBERS")
220 .arg(&key)
221 .query(&mut conn)
222 .map_err(|e| VectorStoreError::StorageError(e.to_string()))
223 })
224 .await
225 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
226 }
227
228 async fn scard(&self, key: &str) -> Result<usize, VectorStoreError> {
229 let config = self.config.clone();
230 let key = key.to_string();
231 tokio::task::spawn_blocking(move || -> Result<usize, VectorStoreError> {
232 let mut conn = redis::Client::open(config.url.as_str())
233 .and_then(|c| c.get_connection())
234 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
235 redis::cmd("SCARD")
236 .arg(&key)
237 .query(&mut conn)
238 .map_err(|e| VectorStoreError::StorageError(e.to_string()))
239 })
240 .await
241 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
242 }
243
244 #[allow(dead_code)]
245 async fn flushdb(&self) -> Result<(), VectorStoreError> {
246 let config = self.config.clone();
247 tokio::task::spawn_blocking(move || -> Result<(), VectorStoreError> {
248 let mut conn = redis::Client::open(config.url.as_str())
249 .and_then(|c| c.get_connection())
250 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
251 redis::cmd("FLUSHDB")
252 .query::<()>(&mut conn)
253 .map_err(|e| VectorStoreError::StorageError(e.to_string()))
254 })
255 .await
256 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
257 }
258
259 async fn clear_with_prefix(&self) -> Result<(), VectorStoreError> {
261 let config = self.config.clone();
262 tokio::task::spawn_blocking(move || -> Result<(), VectorStoreError> {
263 let mut conn = redis::Client::open(config.url.as_str())
264 .and_then(|c| c.get_connection())
265 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
266
267 let pattern = format!("{}:*", config.key_prefix);
268 let mut cursor: u64 = 0;
269
270 loop {
271 let (next_cursor, keys): (u64, Vec<String>) = redis::cmd("SCAN")
272 .arg(cursor)
273 .arg("MATCH")
274 .arg(&pattern)
275 .arg("COUNT")
276 .arg(100)
277 .query(&mut conn)
278 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
279
280 if !keys.is_empty() {
281 redis::cmd("DEL")
282 .arg(&keys)
283 .query::<()>(&mut conn)
284 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
285 }
286
287 cursor = next_cursor;
288 if cursor == 0 {
289 break;
290 }
291 }
292
293 Ok(())
294 })
295 .await
296 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
297 }
298}
299
300#[async_trait]
301impl ChunkedDocumentStoreTrait for RedisDocumentStore {
302 async fn add_parent_document(
303 &self,
304 document: Document,
305 chunk_size: usize,
306 ) -> Result<(String, Vec<String>), VectorStoreError> {
307 let splitter = RecursiveCharacterSplitter::new(chunk_size, chunk_size / 10);
308 let chunks_text = splitter.split_text(&document.content);
309 let parent_id = document
310 .id
311 .clone()
312 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
313 let doc_json = serde_json::to_string(&Document {
314 id: Some(parent_id.clone()),
315 ..document
316 })
317 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
318
319 let config = self.config.clone();
321 let pid = parent_id.clone();
322 let chunks = chunks_text.clone();
323
324 tokio::task::spawn_blocking(move || -> Result<(String, Vec<String>), VectorStoreError> {
325 let mut conn = redis::Client::open(config.url.as_str())
326 .and_then(|c| c.get_connection())
327 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
328
329 redis::cmd("SET")
330 .arg(format!("{}:doc:{}", config.key_prefix, pid))
331 .arg(&doc_json)
332 .query::<()>(&mut conn)
333 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
334
335 let mut chunk_ids = Vec::new();
336 for (i, text) in chunks.iter().enumerate() {
337 let cid = format!("{}:chunk:{}", pid, i);
338 let chunk = ChunkDocument::new(cid.clone(), pid.clone(), text.clone(), i);
339 let cjson = serde_json::to_string(&chunk)
340 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
341 redis::cmd("SET")
342 .arg(format!("{}:chunk:{}", config.key_prefix, cid))
343 .arg(&cjson)
344 .query::<()>(&mut conn)
345 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
346 redis::cmd("SADD")
347 .arg(format!("{}:pchunks:{}", config.key_prefix, pid))
348 .arg(&cid)
349 .query::<()>(&mut conn)
350 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
351 redis::cmd("SADD")
352 .arg(format!("{}:all_chunks", config.key_prefix))
353 .arg(&cid)
354 .query::<()>(&mut conn)
355 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
356 chunk_ids.push(cid);
357 }
358 redis::cmd("SADD")
359 .arg(format!("{}:parent_ids", config.key_prefix))
360 .arg(&pid)
361 .query::<()>(&mut conn)
362 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
363 redis::cmd("SADD")
364 .arg(format!("{}:doc_ids", config.key_prefix))
365 .arg(&pid)
366 .query::<()>(&mut conn)
367 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?;
368
369 Ok((pid, chunk_ids))
370 })
371 .await
372 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
373 }
374
375 async fn add_parent_documents(
376 &self,
377 documents: Vec<Document>,
378 chunk_size: usize,
379 ) -> Result<Vec<(String, Vec<String>)>, VectorStoreError> {
380 let mut results = Vec::new();
381 for doc in documents {
382 results.push(self.add_parent_document(doc, chunk_size).await?);
383 }
384 Ok(results)
385 }
386
387 async fn get_parent_document(
388 &self,
389 parent_id: &str,
390 ) -> Result<Option<Document>, VectorStoreError> {
391 self.get_document(parent_id).await
392 }
393
394 async fn get_chunk(&self, chunk_id: &str) -> Result<Option<ChunkDocument>, VectorStoreError> {
395 match self.get_str(&self.chunk_key(chunk_id)).await? {
396 Some(json) => Ok(serde_json::from_str(&json).ok()),
397 None => Ok(None),
398 }
399 }
400
401 async fn get_chunk_document(
402 &self,
403 chunk_id: &str,
404 ) -> Result<Option<Document>, VectorStoreError> {
405 Ok(self.get_chunk(chunk_id).await?.map(|c| c.to_document()))
406 }
407
408 async fn get_chunks_for_parent(
409 &self,
410 parent_id: &str,
411 ) -> Result<Vec<ChunkDocument>, VectorStoreError> {
412 let ids = self.smembers(&self.parent_chunks_key(parent_id)).await?;
413 let mut chunks = Vec::new();
414 for id in ids {
415 if let Some(c) = self.get_chunk(&id).await? {
416 chunks.push(c);
417 }
418 }
419 chunks.sort_by_key(|c| c.segment);
420 Ok(chunks)
421 }
422
423 async fn get_chunk_documents_for_parent(
424 &self,
425 parent_id: &str,
426 ) -> Result<Vec<Document>, VectorStoreError> {
427 Ok(self
428 .get_chunks_for_parent(parent_id)
429 .await?
430 .into_iter()
431 .map(|c| c.to_document())
432 .collect())
433 }
434
435 async fn delete_parent_document(&self, parent_id: &str) -> Result<(), VectorStoreError> {
436 let chunks = self.get_chunks_for_parent(parent_id).await?;
437 for chunk in &chunks {
438 self.del(&self.chunk_key(&chunk.chunk_id)).await?;
439 }
440 self.del(&self.doc_key(parent_id)).await?;
441 self.del(&self.parent_chunks_key(parent_id)).await?;
442 self.srem(&self.doc_ids_key(), parent_id).await?;
443 self.srem(&self.parent_ids_key(), parent_id).await
444 }
445
446 async fn parent_count(&self) -> usize {
447 self.scard(&self.parent_ids_key()).await.unwrap_or(0)
448 }
449
450 async fn chunk_count(&self) -> usize {
451 self.scard(&self.all_chunks_key()).await.unwrap_or(0)
452 }
453
454 async fn get_all_chunks(&self) -> Result<Vec<ChunkDocument>, VectorStoreError> {
455 let ids = self.smembers(&self.all_chunks_key()).await?;
456 let mut chunks = Vec::new();
457 for id in ids {
458 if let Some(c) = self.get_chunk(&id).await? {
459 chunks.push(c);
460 }
461 }
462 Ok(chunks)
463 }
464
465 async fn clear(&self) -> Result<(), VectorStoreError> {
466 self.clear_with_prefix().await
468 }
469
470 async fn save(&self, _path: impl AsRef<Path> + Send) -> Result<(), VectorStoreError> {
471 let config = self.config.clone();
472 tokio::task::spawn_blocking(move || -> Result<(), VectorStoreError> {
473 let mut conn = redis::Client::open(config.url.as_str())
474 .and_then(|c| c.get_connection())
475 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
476 redis::cmd("SAVE")
477 .query::<()>(&mut conn)
478 .map_err(|e| VectorStoreError::StorageError(e.to_string()))
479 })
480 .await
481 .map_err(|e| VectorStoreError::StorageError(e.to_string()))?
482 }
483
484 fn add_parent_document_blocking(
485 &self,
486 _document: Document,
487 _chunk_size: usize,
488 ) -> Result<(String, Vec<String>), VectorStoreError> {
489 Err(VectorStoreError::StorageError(
490 "blocking not supported, use async API".to_string(),
491 ))
492 }
493
494 fn get_parent_document_blocking(
495 &self,
496 _parent_id: &str,
497 ) -> Result<Option<Document>, VectorStoreError> {
498 Err(VectorStoreError::StorageError(
499 "blocking not supported, use async API".to_string(),
500 ))
501 }
502
503 fn get_chunk_blocking(
504 &self,
505 _chunk_id: &str,
506 ) -> Result<Option<ChunkDocument>, VectorStoreError> {
507 Err(VectorStoreError::StorageError(
508 "blocking not supported, use async API".to_string(),
509 ))
510 }
511
512 fn blocking_get_chunks_for_parent(
513 &self,
514 _parent_id: &str,
515 ) -> Result<Vec<ChunkDocument>, VectorStoreError> {
516 Err(VectorStoreError::StorageError(
517 "blocking not supported, use async API".to_string(),
518 ))
519 }
520}