ruvector_server/routes/
collections.rs

1//! Collection management endpoints
2
3use crate::{error::Error, state::AppState, Result};
4use axum::{
5    extract::{Path, State},
6    http::StatusCode,
7    response::IntoResponse,
8    routing::{get, post},
9    Json, Router,
10};
11use ruvector_core::{types::DbOptions, DistanceMetric, VectorDB};
12use serde::{Deserialize, Serialize};
13use std::sync::Arc;
14
15/// Collection creation request
16#[derive(Debug, Deserialize)]
17pub struct CreateCollectionRequest {
18    /// Collection name
19    pub name: String,
20    /// Vector dimension
21    pub dimension: usize,
22    /// Distance metric (optional, defaults to Cosine)
23    pub metric: Option<DistanceMetric>,
24}
25
26/// Collection info response
27#[derive(Debug, Serialize)]
28pub struct CollectionInfo {
29    /// Collection name
30    pub name: String,
31    /// Vector dimension
32    pub dimension: usize,
33    /// Distance metric
34    pub metric: DistanceMetric,
35}
36
37/// List of collections response
38#[derive(Debug, Serialize)]
39pub struct CollectionsList {
40    /// Collection names
41    pub collections: Vec<String>,
42}
43
44/// Create collection routes
45pub fn routes() -> Router<AppState> {
46    Router::new()
47        .route("/", post(create_collection).get(list_collections))
48        .route("/:name", get(get_collection).delete(delete_collection))
49}
50
51/// Create a new collection
52///
53/// POST /collections
54async fn create_collection(
55    State(state): State<AppState>,
56    Json(req): Json<CreateCollectionRequest>,
57) -> Result<impl IntoResponse> {
58    if state.contains_collection(&req.name) {
59        return Err(Error::CollectionExists(req.name));
60    }
61
62    let mut options = DbOptions::default();
63    options.dimensions = req.dimension;
64    options.distance_metric = req.metric.unwrap_or(DistanceMetric::Cosine);
65    // Use in-memory storage for server (storage path will be ignored for memory storage)
66    options.storage_path = format!("memory://{}", req.name);
67
68    let db = VectorDB::new(options.clone()).map_err(Error::Core)?;
69    state.insert_collection(req.name.clone(), Arc::new(db));
70
71    let info = CollectionInfo {
72        name: req.name,
73        dimension: req.dimension,
74        metric: options.distance_metric,
75    };
76
77    Ok((StatusCode::CREATED, Json(info)))
78}
79
80/// List all collections
81///
82/// GET /collections
83async fn list_collections(State(state): State<AppState>) -> Result<impl IntoResponse> {
84    let collections = state.collection_names();
85    Ok(Json(CollectionsList { collections }))
86}
87
88/// Get collection information
89///
90/// GET /collections/:name
91async fn get_collection(
92    State(state): State<AppState>,
93    Path(name): Path<String>,
94) -> Result<impl IntoResponse> {
95    let _db = state
96        .get_collection(&name)
97        .ok_or_else(|| Error::CollectionNotFound(name.clone()))?;
98
99    // Note: VectorDB doesn't expose config directly, so we return basic info
100    let info = CollectionInfo {
101        name,
102        dimension: 0, // Would need to be stored separately or queried from DB
103        metric: DistanceMetric::Cosine, // Default assumption
104    };
105
106    Ok(Json(info))
107}
108
109/// Delete a collection
110///
111/// DELETE /collections/:name
112async fn delete_collection(
113    State(state): State<AppState>,
114    Path(name): Path<String>,
115) -> Result<impl IntoResponse> {
116    state
117        .remove_collection(&name)
118        .ok_or_else(|| Error::CollectionNotFound(name))?;
119
120    Ok(StatusCode::NO_CONTENT)
121}