use crate::config::QdrantConfig;
use crate::error::{Error, Result};
use qdrant_client::client::QdrantClient;
use qdrant_client::qdrant::HealthCheckReply;
use std::sync::Arc;
use std::time::Duration;
#[derive(Clone)]
pub struct VectorDatabase {
client: Arc<QdrantClient>,
config: QdrantConfig,
}
impl std::fmt::Debug for VectorDatabase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VectorDatabase")
.field("config", &self.config)
.finish()
}
}
impl VectorDatabase {
pub async fn from_config(config: QdrantConfig) -> Result<Self> {
if !config.enabled {
return Err(Error::Database("Qdrant 向量数据库未启用".to_string()));
}
let mut client_builder = QdrantClient::from_url(&config.url)
.with_timeout(Duration::from_secs(config.timeout));
if let Some(api_key) = &config.api_key {
if !api_key.is_empty() {
client_builder = client_builder.with_api_key(api_key.clone());
}
}
let client = client_builder
.build()
.map_err(|e| Error::Database(format!("Qdrant 连接失败: {}", e)))?;
tracing::info!(
"Qdrant 向量数据库连接成功: {}, 默认集合: {}",
config.url,
config.default_collection
);
Ok(Self {
client: Arc::new(client),
config,
})
}
pub fn client(&self) -> &QdrantClient {
&self.client
}
pub fn config(&self) -> &QdrantConfig {
&self.config
}
pub fn default_collection(&self) -> &str {
&self.config.default_collection
}
pub async fn ping(&self) -> Result<HealthCheckReply> {
self.client
.health_check()
.await
.map_err(|e| Error::Database(format!("Qdrant 连接测试失败: {}", e)))
}
pub async fn collection_exists(&self, collection_name: &str) -> Result<bool> {
self.client
.collection_exists(collection_name)
.await
.map_err(|e| Error::Database(format!("检查集合失败: {}", e)))
}
pub async fn list_collections(&self) -> Result<Vec<String>> {
let response = self
.client
.list_collections()
.await
.map_err(|e| Error::Database(format!("列出集合失败: {}", e)))?;
Ok(response
.collections
.iter()
.map(|c| c.name.clone())
.collect())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
#[ignore] async fn test_vector_database_connection() {
let config = QdrantConfig {
enabled: true,
url: "http://localhost:6334".to_string(),
api_key: None,
timeout: 30,
default_collection: "test".to_string(),
};
let db = VectorDatabase::from_config(config).await;
assert!(db.is_ok());
if let Ok(db) = db {
assert!(db.ping().await.is_ok());
}
}
}