rag 0.1.5

A Rust library and CLI for Retrieval-Augmented Generation
Documentation
#![cfg(feature = "postgres")]

use rag::id::new_uuid_v4;
use rag::vector_store::{Document, MetadataFilter, VectorStore};
use rag::{DistanceMetric, PostgresVectorStore};

fn postgres_url() -> String {
    std::env::var("POSTGRES_TEST_URL")
        .or_else(|_| std::env::var("POSTGRES_URL"))
        .unwrap_or_default()
}

async fn connect_test_store(prefix: &str) -> Option<(PostgresVectorStore, String)> {
    let conn_str = postgres_url();
    let table = format!("{}_{}", prefix, new_uuid_v4().replace('-', "_"));
    match PostgresVectorStore::connect(&conn_str, &table).await {
        Ok(store) => Some((store, table)),
        Err(e) => {
            eprintln!("Skipping Postgres test ({}): {}", table, e);
            None
        }
    }
}

#[tokio::test]
async fn test_postgres_vector_store_basic_operations() {
    let Some((store, _table)) = connect_test_store("test_basic").await else {
        return;
    };

    let doc1 = Document::new("Rust is fast".to_string())
        .with_embedding(vec![1.0, 0.0, 0.0])
        .with_metadata("lang".to_string(), "rust".to_string());
    let doc2 = Document::new("Python is easy".to_string())
        .with_embedding(vec![0.0, 1.0, 0.0])
        .with_metadata("lang".to_string(), "python".to_string());

    store.add(doc1.clone()).await.unwrap();
    store.add(doc2.clone()).await.unwrap();

    assert_eq!(store.count().await.unwrap(), 2);

    let results = store.search(&[1.0, 0.0, 0.0], 2).await.unwrap();
    assert_eq!(results.len(), 2);
    assert_eq!(results[0].document.content, "Rust is fast");
    assert!(results[0].score > results[1].score);

    let filter = MetadataFilter::new().add("lang".to_string(), "python".to_string());
    let filtered = store
        .search_with_filter(&[1.0, 0.0, 0.0], 2, &filter)
        .await
        .unwrap();
    assert_eq!(filtered.len(), 1);
    assert_eq!(filtered[0].document.content, "Python is easy");

    store.drop_table().await.unwrap();
}

#[tokio::test]
async fn test_postgres_vector_store_metrics() {
    for metric in [
        DistanceMetric::Cosine,
        DistanceMetric::Euclidean,
        DistanceMetric::DotProduct,
        DistanceMetric::Manhattan,
    ] {
        let Some((store, _table)) = connect_test_store("test_metric").await else {
            return;
        };
        let store = store.with_metric(metric);

        let doc = Document::new("aligned".to_string()).with_embedding(vec![1.0, 0.0, 0.0]);
        store.add(doc).await.unwrap();

        let results = store.search(&[1.0, 0.0, 0.0], 1).await.unwrap();
        assert_eq!(results.len(), 1);
        assert!(
            (results[0].score - 1.0).abs() < 1e-5,
            "metric {:?} should return score ~1.0 for identical vectors",
            metric
        );

        store.drop_table().await.unwrap();
    }
}

#[tokio::test]
async fn test_postgres_vector_store_batch_and_clear() {
    let Some((store, _table)) = connect_test_store("test_batch").await else {
        return;
    };

    let docs = vec![
        Document::new("a".to_string()).with_embedding(vec![1.0, 0.0]),
        Document::new("b".to_string()).with_embedding(vec![0.0, 1.0]),
        Document::new("c".to_string()).with_embedding(vec![0.5, 0.5]),
    ];
    store.add_batch(docs).await.unwrap();
    assert_eq!(store.count().await.unwrap(), 3);

    store.clear().await.unwrap();
    assert_eq!(store.count().await.unwrap(), 0);

    store.drop_table().await.unwrap();
}