1#![warn(missing_docs)]
2pub mod chromadb;
8pub mod chunked_vector_store;
9pub mod document_store;
10mod file_store;
11pub mod filter;
12pub mod lancedb;
13mod memory;
14pub mod neo4j;
15mod provider;
16
17#[cfg(feature = "mongodb-persistence")]
18mod mongo_document_store;
19
20#[cfg(feature = "qdrant-integration")]
21mod qdrant;
22
23#[cfg(feature = "qdrant-integration")]
24pub mod hybrid_native;
25
26#[cfg(feature = "redis-storage")]
27pub mod redis_store;
28
29#[cfg(feature = "sqlite-storage")]
30pub mod sqlite_store;
31
32#[cfg(feature = "pgvector-storage")]
33pub mod pgvector;
34
35pub mod pinecone;
36
37pub use chunked_vector_store::ChunkedVectorStore;
38pub use document_store::{
39 ChunkedDocumentStore, ChunkedDocumentStoreTrait, DocumentStore, InMemoryChunkedDocumentStore,
40 InMemoryDocumentStore,
41};
42pub use file_store::FileVectorStore;
43pub use filter::{FilterOp, MetadataFilter};
44pub use lancedb::{LanceDBConfig, LanceDBVectorStore};
45pub use memory::InMemoryVectorStore;
46pub use neo4j::{Neo4jConfig, Neo4jVectorStore};
47pub use pinecone::PineconeStore;
48pub use provider::{VectorStoreBuilder, VectorStoreProvider, VectorStoreType};
49
50#[cfg(feature = "mongodb-persistence")]
51pub use mongo_document_store::{MongoChunkedDocumentStore, MongoStoreConfig};
52
53#[cfg(feature = "qdrant-integration")]
54pub use qdrant::{QdrantConfig, QdrantDistance, QdrantVectorStore};
55
56#[cfg(feature = "qdrant-integration")]
57pub use hybrid_native::{FusionMethod, NativeHybridQuery, NativeHybridSearch};
58
59pub use chromadb::{ChromaDBConfig, ChromaDBVectorStore};
60
61#[cfg(feature = "redis-storage")]
62pub use redis_store::{RedisDocumentStore, RedisStoreConfig};
63
64#[cfg(feature = "sqlite-storage")]
65pub use sqlite_store::{SQLiteDocumentStore, SQLiteStoreConfig};
66
67#[cfg(feature = "pgvector-storage")]
68pub use pgvector::{build_filter_sql, FilterBinding, FilterSql, PGVectorConfig, PGVectorStore};
69
70use async_trait::async_trait;
71
72pub use lc_shared::document::{ChunkDocument, Document, SearchResult, VectorDocument};
74
75pub use lc_core::math::cosine_similarity;
77
78#[derive(Debug, thiserror::Error)]
80#[non_exhaustive]
81pub enum VectorStoreError {
82 #[error("Document not found: {0}")]
84 DocumentNotFound(String),
85
86 #[error("Embedding error: {0}")]
88 EmbeddingError(String),
89
90 #[error("Storage error: {0}")]
92 StorageError(String),
93
94 #[error("Connection error: {0}")]
96 ConnectionError(String),
97
98 #[error("Configuration error: {0}")]
100 ConfigError(String),
101
102 #[error("Metadata filter not supported: {0}")]
104 UnsupportedFilter(String),
105}
106
107#[async_trait]
109pub trait VectorStore: Send + Sync {
110 async fn add_documents(
119 &self,
120 documents: Vec<Document>,
121 embeddings: Vec<Vec<f32>>,
122 ) -> Result<Vec<String>, VectorStoreError>;
123
124 async fn similarity_search(
133 &self,
134 query_embedding: &[f32],
135 k: usize,
136 ) -> Result<Vec<SearchResult>, VectorStoreError>;
137
138 fn embed_query(&self) -> Option<&dyn Embeddings> {
146 None
147 }
148
149 async fn similarity_search_text(
155 &self,
156 query: &str,
157 k: usize,
158 ) -> Result<Vec<SearchResult>, VectorStoreError> {
159 let Some(embeddings) = self.embed_query() else {
160 return Err(VectorStoreError::EmbeddingError(
161 "this vector store has no embedder configured; cannot auto-vectorize the query \
162 text; call similarity_search with a query vector instead"
163 .to_string(),
164 ));
165 };
166 let query_embedding = embeddings
167 .embed_query(query)
168 .await
169 .map_err(|e| VectorStoreError::EmbeddingError(e.to_string()))?;
170 self.similarity_search(&query_embedding, k).await
171 }
172
173 async fn similarity_search_with_filter(
184 &self,
185 query_embedding: &[f32],
186 k: usize,
187 filter: Option<&MetadataFilter>,
188 ) -> Result<Vec<SearchResult>, VectorStoreError> {
189 match filter {
190 None => self.similarity_search(query_embedding, k).await,
191 Some(_) => Err(VectorStoreError::UnsupportedFilter(
192 "this vector store does not support metadata filtering; pass filter: None or \
193 switch to a store that implements similarity_search_with_filter"
194 .to_string(),
195 )),
196 }
197 }
198
199 async fn similarity_search_with_min_score(
210 &self,
211 query_embedding: &[f32],
212 k: usize,
213 min_score: Option<f32>,
214 ) -> Result<Vec<SearchResult>, VectorStoreError> {
215 let results = self.similarity_search(query_embedding, k).await?;
216 match min_score {
217 Some(threshold) => Ok(results
218 .into_iter()
219 .filter(|r| r.score >= threshold)
220 .collect()),
221 None => Ok(results),
222 }
223 }
224
225 async fn get_document(&self, id: &str) -> Result<Option<Document>, VectorStoreError>;
227
228 async fn get_embedding(&self, id: &str) -> Result<Option<Vec<f32>>, VectorStoreError>;
230
231 async fn delete_document(&self, id: &str) -> Result<(), VectorStoreError>;
233
234 async fn count(&self) -> usize;
236
237 async fn clear(&self) -> Result<(), VectorStoreError>;
239}
240
241pub use lc_embeddings::{EmbeddingError, Embeddings};
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 #[test]
252 fn test_document_creation() {
253 let doc = Document::new("Hello, world!")
254 .with_metadata("source", "test")
255 .with_id("doc-1");
256
257 assert_eq!(doc.content, "Hello, world!");
258 assert_eq!(
259 doc.metadata.get("source"),
260 Some(&serde_json::Value::String("test".to_string()))
261 );
262 assert_eq!(doc.id, Some("doc-1".to_string()));
263 }
264
265 #[test]
266 fn test_document_page_content() {
267 let doc = Document::new("Test content");
268 assert_eq!(doc.page_content(), "Test content");
269 }
270}