#![allow(deprecated)]
use crate::collection::CollectionType;
use crate::error::Error;
use crate::point::Point;
use crate::Database;
use serde_json::json;
use tempfile::tempdir;
#[test]
fn test_collection_type_metadata_only_exists() {
let ct = CollectionType::MetadataOnly;
assert!(ct.is_metadata_only());
}
#[test]
fn test_collection_type_vector_exists() {
use crate::distance::DistanceMetric;
use crate::quantization::StorageMode;
let ct = CollectionType::Vector {
dimension: 768,
metric: DistanceMetric::Cosine,
storage_mode: StorageMode::Full,
};
match ct {
CollectionType::Vector {
dimension,
metric,
storage_mode,
} => {
assert_eq!(dimension, 768);
assert_eq!(metric, DistanceMetric::Cosine);
assert_eq!(storage_mode, StorageMode::Full);
}
CollectionType::MetadataOnly | CollectionType::Graph { .. } => {
panic!("Expected Vector variant")
}
}
}
#[test]
fn test_create_metadata_only_collection() {
let dir = tempdir().unwrap();
let db = Database::open(dir.path()).unwrap();
db.create_collection_typed("products", &CollectionType::MetadataOnly)
.unwrap();
let collections = db.list_collections();
assert!(collections.contains(&"products".to_string()));
let coll = db.get_collection("products").unwrap();
assert!(coll.is_metadata_only());
}
#[test]
fn test_create_vector_collection_typed() {
use crate::distance::DistanceMetric;
use crate::quantization::StorageMode;
let dir = tempdir().unwrap();
let db = Database::open(dir.path()).unwrap();
db.create_collection_typed(
"embeddings",
&CollectionType::Vector {
dimension: 768,
metric: DistanceMetric::Cosine,
storage_mode: StorageMode::Full,
},
)
.unwrap();
let coll = db.get_collection("embeddings").unwrap();
assert!(!coll.is_metadata_only());
assert_eq!(coll.config().dimension, 768);
}
#[test]
fn test_metadata_only_upsert_without_vector() {
let dir = tempdir().unwrap();
let db = Database::open(dir.path()).unwrap();
db.create_collection_typed("products", &CollectionType::MetadataOnly)
.unwrap();
let coll = db.get_collection("products").unwrap();
let result = coll.upsert_metadata(vec![
Point::metadata_only(
1,
json!({
"code_produit": "PROD001",
"pays": "France",
"nom_produit": "Séjour Paris",
"prix": 1500.0
}),
),
Point::metadata_only(
2,
json!({
"code_produit": "PROD002",
"pays": "Espagne",
"nom_produit": "Circuit Andalousie",
"prix": 2000.0
}),
),
]);
assert!(result.is_ok());
assert_eq!(coll.len(), 2);
}
#[test]
fn test_metadata_only_rejects_vector() {
let dir = tempdir().unwrap();
let db = Database::open(dir.path()).unwrap();
db.create_collection_typed("products", &CollectionType::MetadataOnly)
.unwrap();
let coll = db.get_collection("products").unwrap();
let result = coll.upsert(vec![Point::new(
1,
vec![0.1; 768],
Some(json!({"title": "Test"})),
)]);
assert!(result.is_err());
match result.unwrap_err() {
Error::VectorNotAllowed(collection_name) => {
assert_eq!(collection_name, "products");
}
e => panic!("Expected VectorNotAllowed error, got: {e:?}"),
}
}
#[test]
fn test_metadata_only_get_by_id() {
let dir = tempdir().unwrap();
let db = Database::open(dir.path()).unwrap();
db.create_collection_typed("products", &CollectionType::MetadataOnly)
.unwrap();
let coll = db.get_collection("products").unwrap();
coll.upsert_metadata(vec![Point::metadata_only(
42,
json!({"name": "Test Product", "price": 99.99}),
)])
.unwrap();
let results = coll.get(&[42]);
assert_eq!(results.len(), 1);
assert!(results[0].is_some());
let point = results[0].as_ref().unwrap();
assert_eq!(point.id, 42);
assert!(point.vector.is_empty()); assert!(point.payload.is_some());
}
#[test]
fn test_metadata_only_delete() {
let dir = tempdir().unwrap();
let db = Database::open(dir.path()).unwrap();
db.create_collection_typed("products", &CollectionType::MetadataOnly)
.unwrap();
let coll = db.get_collection("products").unwrap();
coll.upsert_metadata(vec![
Point::metadata_only(1, json!({"name": "Product 1"})),
Point::metadata_only(2, json!({"name": "Product 2"})),
])
.unwrap();
assert_eq!(coll.len(), 2);
coll.delete(&[1]).unwrap();
assert_eq!(coll.len(), 1);
}
#[test]
fn test_metadata_only_count() {
let dir = tempdir().unwrap();
let db = Database::open(dir.path()).unwrap();
db.create_collection_typed("products", &CollectionType::MetadataOnly)
.unwrap();
let coll = db.get_collection("products").unwrap();
assert_eq!(coll.len(), 0);
assert!(coll.is_empty());
coll.upsert_metadata(vec![
Point::metadata_only(1, json!({"name": "A"})),
Point::metadata_only(2, json!({"name": "B"})),
Point::metadata_only(3, json!({"name": "C"})),
])
.unwrap();
assert_eq!(coll.len(), 3);
assert!(!coll.is_empty());
}
#[test]
fn test_metadata_only_search_returns_error() {
let dir = tempdir().unwrap();
let db = Database::open(dir.path()).unwrap();
db.create_collection_typed("products", &CollectionType::MetadataOnly)
.unwrap();
let coll = db.get_collection("products").unwrap();
coll.upsert_metadata(vec![Point::metadata_only(1, json!({"name": "Test"}))])
.unwrap();
let query_vector = vec![0.1; 768];
let result = coll.search(&query_vector, 10);
assert!(result.is_err());
match result.unwrap_err() {
Error::SearchNotSupported(collection_name) => {
assert_eq!(collection_name, "products");
}
e => panic!("Expected SearchNotSupported error, got: {e:?}"),
}
}
#[test]
fn test_metadata_only_no_hnsw_file() {
let dir = tempdir().unwrap();
let db = Database::open(dir.path()).unwrap();
db.create_collection_typed("products", &CollectionType::MetadataOnly)
.unwrap();
let coll = db.get_collection("products").unwrap();
coll.upsert_metadata(vec![Point::metadata_only(1, json!({"name": "Test"}))])
.unwrap();
coll.flush().unwrap();
let hnsw_path = dir.path().join("products").join("hnsw.bin");
assert!(
!hnsw_path.exists(),
"HNSW index should not be created for metadata-only collections"
);
}
#[test]
#[allow(clippy::cast_precision_loss)]
fn test_metadata_only_memory_efficient() {
let dir = tempdir().unwrap();
let db = Database::open(dir.path()).unwrap();
db.create_collection_typed("products", &CollectionType::MetadataOnly)
.unwrap();
let coll = db.get_collection("products").unwrap();
let points: Vec<_> = (0..100_u64)
.map(|i| {
let price = (i as f64) * 10.0; Point::metadata_only(
i,
json!({
"id": i,
"name": format!("Product {i}"),
"price": price
}),
)
})
.collect();
coll.upsert_metadata(points).unwrap();
let vectors_path = dir.path().join("products").join("vectors.bin");
if vectors_path.exists() {
let size = std::fs::metadata(&vectors_path).unwrap().len();
assert!(
size < 1000,
"Vector storage should be minimal for metadata-only, got {size} bytes"
);
}
}
#[test]
fn test_metadata_only_persistence() {
let dir = tempdir().unwrap();
{
let db = Database::open(dir.path()).unwrap();
db.create_collection_typed("products", &CollectionType::MetadataOnly)
.unwrap();
let coll = db.get_collection("products").unwrap();
coll.upsert_metadata(vec![
Point::metadata_only(1, json!({"name": "Product 1"})),
Point::metadata_only(2, json!({"name": "Product 2"})),
])
.unwrap();
coll.flush().unwrap();
}
{
let db = Database::open(dir.path()).unwrap();
db.load_collections().unwrap();
let coll = db.get_collection("products").unwrap();
assert!(coll.is_metadata_only());
assert_eq!(coll.len(), 2);
let results = coll.get(&[1, 2]);
assert!(results[0].is_some());
assert!(results[1].is_some());
}
}
#[test]
fn test_execute_query_on_metadata_collection() {
use crate::velesql::Parser;
let dir = tempdir().unwrap();
let db = Database::open(dir.path()).unwrap();
db.create_metadata_collection("meta_items").unwrap();
let coll = db.get_metadata_collection("meta_items").unwrap();
let points: Vec<Point> = (1u64..=5)
.map(|i| Point::metadata_only(i, json!({"name": format!("item_{}", i)})))
.collect();
coll.upsert(points).unwrap();
drop(coll);
let query_str = "SELECT * FROM meta_items LIMIT 5";
let parsed = Parser::parse(query_str).unwrap();
let results = db
.execute_query(&parsed, &std::collections::HashMap::new())
.unwrap();
assert_eq!(results.len(), 5, "execute_query should return all 5 items");
}