1use async_trait::async_trait;
8use serde::{Deserialize, Serialize};
9use serde_json::json;
10use std::collections::HashMap;
11
12use crate::{Document, SearchResult, VectorStore, VectorStoreError};
13
14#[derive(Debug, Clone)]
16pub struct ChromaDBConfig {
17 pub host: String,
19 pub collection_name: String,
21 pub vector_size: usize,
23 pub metadata: Option<HashMap<String, String>>,
25}
26
27impl Default for ChromaDBConfig {
28 fn default() -> Self {
29 Self {
30 host: "http://localhost:8000".to_string(),
31 collection_name: "langchainrust".to_string(),
32 vector_size: 1536,
33 metadata: None,
34 }
35 }
36}
37
38impl ChromaDBConfig {
39 pub fn new(
40 host: impl Into<String>,
41 collection_name: impl Into<String>,
42 vector_size: usize,
43 ) -> Self {
44 Self {
45 host: host.into(),
46 collection_name: collection_name.into(),
47 vector_size,
48 metadata: None,
49 }
50 }
51}
52
53#[derive(Debug, Deserialize)]
55#[allow(dead_code)]
56struct ChromaCollection {
57 id: String,
58 name: String,
59 #[serde(default)]
60 metadata: Option<serde_json::Value>,
61}
62
63#[derive(Debug, Serialize)]
65struct ChromaAddRequest {
66 ids: Vec<String>,
67 embeddings: Vec<Vec<f32>>,
68 documents: Vec<String>,
69 #[serde(skip_serializing_if = "Option::is_none")]
70 metadatas: Option<Vec<HashMap<String, String>>>,
71}
72
73#[derive(Debug, Serialize)]
75struct ChromaQueryRequest {
76 query_embeddings: Vec<Vec<f32>>,
77 n_results: usize,
78 #[serde(skip_serializing_if = "Option::is_none")]
79 include: Option<Vec<String>>,
80}
81
82#[derive(Debug, Deserialize)]
84struct ChromaQueryResponse {
85 ids: Vec<Vec<String>>,
86 distances: Vec<Vec<f64>>,
87 documents: Vec<Vec<String>>,
88 #[serde(default)]
89 metadatas: Vec<Vec<Option<HashMap<String, String>>>>,
90}
91
92#[derive(Debug, Deserialize)]
94struct ChromaGetResponse {
95 ids: Vec<String>,
96 documents: Vec<Option<String>>,
97 #[serde(default)]
98 metadatas: Vec<Option<HashMap<String, String>>>,
99 embeddings: Option<Vec<Vec<f32>>>,
100}
101
102pub struct ChromaDBVectorStore {
115 config: ChromaDBConfig,
116 client: reqwest::Client,
117 collection_id: Option<String>,
118}
119
120impl ChromaDBVectorStore {
121 pub async fn new(config: ChromaDBConfig) -> Result<Self, VectorStoreError> {
123 let client = reqwest::Client::new();
124 let mut store = Self {
125 config,
126 client,
127 collection_id: None,
128 };
129 store.init_collection().await?;
130 Ok(store)
131 }
132
133 async fn init_collection(&mut self) -> Result<(), VectorStoreError> {
135 let url = format!(
137 "{}/api/v1/collections/{}",
138 self.config.host, self.config.collection_name
139 );
140 let response = self
141 .client
142 .get(&url)
143 .send()
144 .await
145 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
146
147 if response.status().is_success() {
148 let collection: ChromaCollection = response
149 .json()
150 .await
151 .map_err(|e| VectorStoreError::StorageError(format!("解析集合信息失败: {}", e)))?;
152 self.collection_id = Some(collection.id);
153 return Ok(());
154 }
155
156 let create_url = format!("{}/api/v1/collections", self.config.host);
158 let mut body = json!({
159 "name": self.config.collection_name,
160 });
161
162 if let Some(ref meta) = self.config.metadata {
163 body["metadata"] = serde_json::to_value(meta).unwrap_or(json!({}));
164 }
165
166 let response = self
167 .client
168 .post(&create_url)
169 .json(&body)
170 .send()
171 .await
172 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
173
174 if response.status().is_success() {
175 let collection: ChromaCollection = response.json().await.map_err(|e| {
176 VectorStoreError::StorageError(format!("解析新集合信息失败: {}", e))
177 })?;
178 self.collection_id = Some(collection.id);
179 Ok(())
180 } else {
181 let text = response.text().await.unwrap_or_default();
182 Err(VectorStoreError::StorageError(format!(
183 "创建集合失败: {}",
184 text
185 )))
186 }
187 }
188
189 fn get_collection_id(&self) -> Result<&str, VectorStoreError> {
191 self.collection_id
192 .as_deref()
193 .ok_or_else(|| VectorStoreError::StorageError("集合未初始化".to_string()))
194 }
195
196 fn collection_url(&self, endpoint: &str) -> Result<String, VectorStoreError> {
198 let cid = self.get_collection_id()?;
199 Ok(format!(
200 "{}/api/v1/collections/{}/{}",
201 self.config.host, cid, endpoint
202 ))
203 }
204}
205
206#[async_trait]
207impl VectorStore for ChromaDBVectorStore {
208 async fn add_documents(
209 &self,
210 documents: Vec<Document>,
211 embeddings: Vec<Vec<f32>>,
212 ) -> Result<Vec<String>, VectorStoreError> {
213 if documents.is_empty() {
214 return Ok(Vec::new());
215 }
216
217 let count = documents.len();
218 let ids: Vec<String> = (0..count)
219 .map(|i| {
220 documents[i]
221 .id
222 .clone()
223 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string())
224 })
225 .collect();
226
227 let contents: Vec<String> = documents.iter().map(|d| d.content.clone()).collect();
228 let metadatas: Vec<HashMap<String, String>> =
229 documents.iter().map(|d| d.metadata.clone()).collect();
230 let has_metadata = metadatas.iter().any(|m| !m.is_empty());
231
232 let request = ChromaAddRequest {
233 ids: ids.clone(),
234 embeddings,
235 documents: contents,
236 metadatas: if has_metadata { Some(metadatas) } else { None },
237 };
238
239 let url = self.collection_url("add")?;
240 let response = self
241 .client
242 .post(&url)
243 .json(&request)
244 .send()
245 .await
246 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
247
248 if !response.status().is_success() {
249 let text = response.text().await.unwrap_or_default();
250 return Err(VectorStoreError::StorageError(format!(
251 "添加文档失败: {}",
252 text
253 )));
254 }
255
256 Ok(ids)
257 }
258
259 async fn similarity_search(
260 &self,
261 query_embedding: &[f32],
262 k: usize,
263 ) -> Result<Vec<SearchResult>, VectorStoreError> {
264 let request = ChromaQueryRequest {
265 query_embeddings: vec![query_embedding.to_vec()],
266 n_results: k,
267 include: Some(vec![
268 "documents".to_string(),
269 "distances".to_string(),
270 "metadatas".to_string(),
271 ]),
272 };
273
274 let url = self.collection_url("query")?;
275 let response = self
276 .client
277 .post(&url)
278 .json(&request)
279 .send()
280 .await
281 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
282
283 if !response.status().is_success() {
284 let text = response.text().await.unwrap_or_default();
285 return Err(VectorStoreError::StorageError(format!(
286 "查询失败: {}",
287 text
288 )));
289 }
290
291 let query_result: ChromaQueryResponse = response
292 .json()
293 .await
294 .map_err(|e| VectorStoreError::StorageError(format!("解析查询结果失败: {}", e)))?;
295
296 let mut results = Vec::new();
297
298 if let Some(doc_list) = query_result.documents.into_iter().next() {
300 let dist_list = query_result
301 .distances
302 .into_iter()
303 .next()
304 .unwrap_or_default();
305 let meta_list = query_result
306 .metadatas
307 .into_iter()
308 .next()
309 .unwrap_or_default();
310 let id_list = query_result.ids.into_iter().next().unwrap_or_default();
311
312 for (i, content) in doc_list.into_iter().enumerate() {
313 let score = dist_list.get(i).copied().unwrap_or(0.0);
314 let similarity = 1.0 / (1.0 + score);
316 let metadata = meta_list
317 .get(i)
318 .unwrap_or(&None)
319 .clone()
320 .unwrap_or_default();
321 let doc_id = id_list.get(i).cloned();
322
323 results.push(SearchResult {
324 document: Document {
325 content,
326 metadata,
327 id: doc_id,
328 },
329 score: similarity as f32,
330 });
331 }
332 }
333
334 results.sort_by(|a, b| {
336 b.score
337 .partial_cmp(&a.score)
338 .unwrap_or(std::cmp::Ordering::Equal)
339 });
340 Ok(results)
341 }
342
343 async fn get_document(&self, id: &str) -> Result<Option<Document>, VectorStoreError> {
344 let url = self.collection_url("get")?;
345 let body = json!({
346 "ids": [id],
347 "include": ["documents", "metadatas"]
348 });
349
350 let response = self
351 .client
352 .post(&url)
353 .json(&body)
354 .send()
355 .await
356 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
357
358 if !response.status().is_success() {
359 return Ok(None);
360 }
361
362 let get_result: ChromaGetResponse = response
363 .json()
364 .await
365 .map_err(|e| VectorStoreError::StorageError(format!("解析文档失败: {}", e)))?;
366
367 if get_result.ids.is_empty() {
368 return Ok(None);
369 }
370
371 let content = get_result
372 .documents
373 .into_iter()
374 .next()
375 .flatten()
376 .unwrap_or_default();
377 let metadata = get_result
378 .metadatas
379 .into_iter()
380 .next()
381 .flatten()
382 .unwrap_or_default();
383
384 Ok(Some(Document {
385 content,
386 metadata,
387 id: Some(id.to_string()),
388 }))
389 }
390
391 async fn get_embedding(&self, id: &str) -> Result<Option<Vec<f32>>, VectorStoreError> {
392 let url = self.collection_url("get")?;
393 let body = json!({
394 "ids": [id],
395 "include": ["embeddings"]
396 });
397
398 let response = self
399 .client
400 .post(&url)
401 .json(&body)
402 .send()
403 .await
404 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
405
406 if !response.status().is_success() {
407 return Ok(None);
408 }
409
410 let get_result: ChromaGetResponse = response
411 .json()
412 .await
413 .map_err(|e| VectorStoreError::StorageError(format!("解析文档失败: {}", e)))?;
414
415 if let Some(embeddings) = get_result.embeddings {
416 Ok(embeddings.into_iter().next())
417 } else {
418 Ok(None)
419 }
420 }
421
422 async fn delete_document(&self, id: &str) -> Result<(), VectorStoreError> {
423 let url = self.collection_url("delete")?;
424 let body = json!({
425 "ids": [id]
426 });
427
428 let response = self
429 .client
430 .post(&url)
431 .json(&body)
432 .send()
433 .await
434 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
435
436 if !response.status().is_success() {
437 let text = response.text().await.unwrap_or_default();
438 return Err(VectorStoreError::StorageError(format!(
439 "删除文档失败: {}",
440 text
441 )));
442 }
443
444 Ok(())
445 }
446
447 async fn count(&self) -> usize {
448 let url = match self.collection_url("count") {
449 Ok(u) => u,
450 Err(e) => {
451 log::warn!("ChromaDB count() failed to build URL: {}", e);
452 return 0;
453 }
454 };
455
456 let response = self.client.post(&url).send().await;
457 match response {
458 Ok(resp) => {
459 if resp.status().is_success() {
460 match resp.json::<usize>().await {
461 Ok(count) => count,
462 Err(e) => {
463 log::warn!("ChromaDB count() failed to parse response: {}", e);
464 0
465 }
466 }
467 } else {
468 log::warn!("ChromaDB count() request failed with non-success status");
469 0
470 }
471 }
472 Err(e) => {
473 log::warn!("ChromaDB count() request error: {}", e);
474 0
475 }
476 }
477 }
478
479 async fn clear(&self) -> Result<(), VectorStoreError> {
480 let get_url = self.collection_url("get")?;
482 let body = json!({
483 "include": []
484 });
485
486 let response = self
487 .client
488 .post(&get_url)
489 .json(&body)
490 .send()
491 .await
492 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
493
494 if !response.status().is_success() {
495 let text = response.text().await.unwrap_or_default();
496 return Err(VectorStoreError::StorageError(format!(
497 "获取文档列表失败: {}",
498 text
499 )));
500 }
501
502 let get_result: ChromaGetResponse = response
503 .json()
504 .await
505 .map_err(|e| VectorStoreError::StorageError(format!("解析文档列表失败: {}", e)))?;
506
507 if get_result.ids.is_empty() {
508 return Ok(());
509 }
510
511 let del_url = self.collection_url("delete")?;
513 let del_body = json!({
514 "ids": get_result.ids
515 });
516
517 let response = self
518 .client
519 .post(&del_url)
520 .json(&del_body)
521 .send()
522 .await
523 .map_err(|e| VectorStoreError::ConnectionError(e.to_string()))?;
524
525 if !response.status().is_success() {
526 let text = response.text().await.unwrap_or_default();
527 return Err(VectorStoreError::StorageError(format!(
528 "清空集合失败: {}",
529 text
530 )));
531 }
532
533 Ok(())
534 }
535}