#[cfg(test)]
mod tests {
use crate::collection::graph::GraphEdge;
use crate::collection::types::Collection;
use crate::distance::DistanceMetric;
use crate::velesql::Parser;
use std::collections::HashMap;
use std::path::PathBuf;
fn create_test_collection() -> (Collection, tempfile::TempDir) {
let temp_dir = tempfile::tempdir().unwrap();
let path = PathBuf::from(temp_dir.path());
let collection = Collection::create(path, 4, DistanceMetric::Cosine).unwrap();
(collection, temp_dir)
}
#[test]
fn test_not_similarity_is_supported() {
let (collection, _temp) = create_test_collection();
let points = vec![
crate::Point {
id: 1,
vector: vec![1.0, 0.0, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "tech"})),
sparse_vectors: None,
},
crate::Point {
id: 2,
vector: vec![0.0, 1.0, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "science"})),
sparse_vectors: None,
},
];
collection.upsert(points).unwrap();
let query = "SELECT * FROM test WHERE NOT similarity(vector, $v) > 0.8 LIMIT 10";
let parsed = Parser::parse(query).unwrap();
let mut params = HashMap::new();
params.insert("v".to_string(), serde_json::json!([1.0, 0.0, 0.0, 0.0]));
let result = collection.execute_query(&parsed, ¶ms);
assert!(
result.is_ok(),
"NOT similarity() should be supported: {:?}",
result.err()
);
let results = result.unwrap();
assert!(
results.iter().all(|r| r.point.id != 1),
"Vector too close to query should be excluded by NOT similarity()"
);
}
#[test]
fn test_near_plus_similarity_is_supported() {
let (collection, _temp) = create_test_collection();
let points = vec![
crate::Point {
id: 1,
vector: vec![1.0, 0.0, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "tech"})),
sparse_vectors: None,
},
crate::Point {
id: 2,
vector: vec![0.9, 0.1, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "tech"})),
sparse_vectors: None,
},
crate::Point {
id: 3,
vector: vec![0.0, 1.0, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "other"})),
sparse_vectors: None,
},
];
collection.upsert(points).unwrap();
let query =
"SELECT * FROM test WHERE vector NEAR $v AND similarity(vector, $v) > 0.5 LIMIT 10";
let parsed = Parser::parse(query).unwrap();
let mut params = HashMap::new();
params.insert("v".to_string(), serde_json::json!([1.0, 0.0, 0.0, 0.0]));
let result = collection.execute_query(&parsed, ¶ms);
assert!(
result.is_ok(),
"NEAR + similarity() should be supported: {:?}",
result.err()
);
let results = result.unwrap();
assert!(!results.is_empty(), "Should return some results");
}
#[test]
fn test_near_plus_similarity_filters_by_threshold() {
let (collection, _temp) = create_test_collection();
let points = vec![
crate::Point {
id: 1,
vector: vec![1.0, 0.0, 0.0, 0.0], payload: None,
sparse_vectors: None,
},
crate::Point {
id: 2,
vector: vec![0.7, 0.7, 0.0, 0.0], payload: None,
sparse_vectors: None,
},
crate::Point {
id: 3,
vector: vec![0.0, 1.0, 0.0, 0.0], payload: None,
sparse_vectors: None,
},
];
collection.upsert(points).unwrap();
let query =
"SELECT * FROM test WHERE vector NEAR $v AND similarity(vector, $v) > 0.9 LIMIT 10";
let parsed = Parser::parse(query).unwrap();
let mut params = HashMap::new();
params.insert("v".to_string(), serde_json::json!([1.0, 0.0, 0.0, 0.0]));
let result = collection.execute_query(&parsed, ¶ms);
assert!(result.is_ok());
let results = result.unwrap();
assert!(
results.len() <= 2,
"High threshold should filter results: got {}",
results.len()
);
}
#[test]
fn test_not_metadata_equality_is_supported() {
let (collection, _temp) = create_test_collection();
let points = vec![
crate::Point {
id: 1,
vector: vec![1.0, 0.0, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "tech"})),
sparse_vectors: None,
},
crate::Point {
id: 2,
vector: vec![0.9, 0.1, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "science"})),
sparse_vectors: None,
},
];
collection.upsert(points).unwrap();
let query = "SELECT * FROM test WHERE NOT category = 'tech' LIMIT 10";
let parsed = Parser::parse(query).unwrap();
let result = collection.execute_query(&parsed, &HashMap::new());
assert!(
result.is_ok(),
"NOT metadata equality should be supported: {:?}",
result.err()
);
}
#[test]
fn test_not_equal_metadata_is_supported() {
let (collection, _temp) = create_test_collection();
let points = vec![
crate::Point {
id: 1,
vector: vec![1.0, 0.0, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "tech"})),
sparse_vectors: None,
},
crate::Point {
id: 2,
vector: vec![0.9, 0.1, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "science"})),
sparse_vectors: None,
},
];
collection.upsert(points).unwrap();
let query = "SELECT * FROM test WHERE category != 'tech' LIMIT 10";
let parsed = Parser::parse(query).unwrap();
let result = collection.execute_query(&parsed, &HashMap::new());
assert!(
result.is_ok(),
"!= metadata should be supported: {:?}",
result.err()
);
}
#[test]
fn test_similarity_and_not_equal_metadata_is_supported() {
let (collection, _temp) = create_test_collection();
let points = vec![
crate::Point {
id: 1,
vector: vec![1.0, 0.0, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "tech"})),
sparse_vectors: None,
},
crate::Point {
id: 2,
vector: vec![0.9, 0.1, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "science"})),
sparse_vectors: None,
},
];
collection.upsert(points).unwrap();
let query =
"SELECT * FROM test WHERE similarity(vector, $v) > 0.5 AND category != 'tech' LIMIT 10";
let parsed = Parser::parse(query).unwrap();
let mut params = HashMap::new();
params.insert("v".to_string(), serde_json::json!([1.0, 0.0, 0.0, 0.0]));
let result = collection.execute_query(&parsed, ¶ms);
assert!(
result.is_ok(),
"similarity() AND != metadata should be supported: {:?}",
result.err()
);
}
#[test]
fn test_order_by_two_columns_asc_desc() {
let (collection, _temp) = create_test_collection();
let points = vec![
crate::Point {
id: 1,
vector: vec![1.0, 0.0, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "A", "priority": 3})),
sparse_vectors: None,
},
crate::Point {
id: 2,
vector: vec![0.9, 0.1, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "A", "priority": 1})),
sparse_vectors: None,
},
crate::Point {
id: 3,
vector: vec![0.8, 0.2, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "B", "priority": 2})),
sparse_vectors: None,
},
crate::Point {
id: 4,
vector: vec![0.7, 0.3, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "A", "priority": 2})),
sparse_vectors: None,
},
];
collection.upsert(points).unwrap();
let query = "SELECT * FROM test ORDER BY category ASC, priority DESC LIMIT 10";
let parsed = Parser::parse(query).unwrap();
let result = collection.execute_query(&parsed, &HashMap::new());
assert!(
result.is_ok(),
"Multi-column ORDER BY should work: {:?}",
result.err()
);
let results = result.unwrap();
assert_eq!(results.len(), 4);
let categories: Vec<&str> = results
.iter()
.map(|r| {
r.point
.payload
.as_ref()
.unwrap()
.get("category")
.unwrap()
.as_str()
.unwrap()
})
.collect();
let priorities: Vec<i64> = results
.iter()
.map(|r| {
r.point
.payload
.as_ref()
.unwrap()
.get("priority")
.unwrap()
.as_i64()
.unwrap()
})
.collect();
assert_eq!(&categories[0..3], &["A", "A", "A"]);
assert_eq!(categories[3], "B");
assert_eq!(&priorities[0..3], &[3, 2, 1]);
}
#[test]
fn test_order_by_three_columns() {
let (collection, _temp) = create_test_collection();
let points = vec![
crate::Point {
id: 1,
vector: vec![1.0, 0.0, 0.0, 0.0],
payload: Some(serde_json::json!({"a": 1, "b": 2, "c": "x"})),
sparse_vectors: None,
},
crate::Point {
id: 2,
vector: vec![0.9, 0.1, 0.0, 0.0],
payload: Some(serde_json::json!({"a": 1, "b": 2, "c": "y"})),
sparse_vectors: None,
},
crate::Point {
id: 3,
vector: vec![0.8, 0.2, 0.0, 0.0],
payload: Some(serde_json::json!({"a": 1, "b": 1, "c": "z"})),
sparse_vectors: None,
},
crate::Point {
id: 4,
vector: vec![0.7, 0.3, 0.0, 0.0],
payload: Some(serde_json::json!({"a": 2, "b": 1, "c": "w"})),
sparse_vectors: None,
},
];
collection.upsert(points).unwrap();
let query = "SELECT * FROM test ORDER BY a ASC, b DESC, c ASC LIMIT 10";
let parsed = Parser::parse(query).unwrap();
let result = collection.execute_query(&parsed, &HashMap::new());
assert!(
result.is_ok(),
"Three-column ORDER BY should work: {:?}",
result.err()
);
let results = result.unwrap();
assert_eq!(results.len(), 4);
let ids: Vec<u64> = results.iter().map(|r| r.point.id).collect();
assert_eq!(ids, vec![1, 2, 3, 4]);
}
#[test]
fn test_order_by_stable_equal_values() {
let (collection, _temp) = create_test_collection();
let points = vec![
crate::Point {
id: 10,
vector: vec![1.0, 0.0, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "same", "seq": 1})),
sparse_vectors: None,
},
crate::Point {
id: 20,
vector: vec![0.9, 0.1, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "same", "seq": 2})),
sparse_vectors: None,
},
crate::Point {
id: 30,
vector: vec![0.8, 0.2, 0.0, 0.0],
payload: Some(serde_json::json!({"category": "same", "seq": 3})),
sparse_vectors: None,
},
];
collection.upsert(points).unwrap();
let query = "SELECT * FROM test ORDER BY category ASC LIMIT 10";
let parsed = Parser::parse(query).unwrap();
let result = collection.execute_query(&parsed, &HashMap::new());
assert!(result.is_ok());
let results = result.unwrap();
assert_eq!(results.len(), 3);
}
#[test]
fn test_select_where_or_graph_match_preserves_boolean_semantics() {
let (collection, _temp) = create_test_collection();
let points = vec![
crate::Point {
id: 1,
vector: vec![1.0, 0.0, 0.0, 0.0],
payload: Some(serde_json::json!({"_labels":["Doc"], "category":"tech"})),
sparse_vectors: None,
},
crate::Point {
id: 2,
vector: vec![0.9, 0.1, 0.0, 0.0],
payload: Some(serde_json::json!({"_labels":["Doc"], "category":"science"})),
sparse_vectors: None,
},
crate::Point {
id: 3,
vector: vec![0.0, 1.0, 0.0, 0.0],
payload: Some(serde_json::json!({"_labels":["Doc"], "category":"other"})),
sparse_vectors: None,
},
];
collection.upsert(points).unwrap();
collection
.add_edge(GraphEdge::new(10, 1, 2, "REL").unwrap())
.unwrap();
let query = "SELECT * FROM test AS d WHERE category = 'science' OR MATCH (d:Doc)-[:REL]->(x:Doc) LIMIT 10";
let parsed = Parser::parse(query).unwrap();
let results = collection.execute_query(&parsed, &HashMap::new()).unwrap();
let ids: std::collections::HashSet<u64> = results.iter().map(|r| r.point.id).collect();
assert!(ids.contains(&1), "id=1 should match graph predicate");
assert!(ids.contains(&2), "id=2 should match metadata predicate");
assert!(!ids.contains(&3), "id=3 should match neither predicate");
}
#[test]
fn test_select_where_not_graph_match_preserves_boolean_semantics() {
let (collection, _temp) = create_test_collection();
let points = vec![
crate::Point {
id: 1,
vector: vec![1.0, 0.0, 0.0, 0.0],
payload: Some(serde_json::json!({"_labels":["Doc"], "category":"tech"})),
sparse_vectors: None,
},
crate::Point {
id: 2,
vector: vec![0.9, 0.1, 0.0, 0.0],
payload: Some(serde_json::json!({"_labels":["Doc"], "category":"science"})),
sparse_vectors: None,
},
crate::Point {
id: 3,
vector: vec![0.0, 1.0, 0.0, 0.0],
payload: Some(serde_json::json!({"_labels":["Doc"], "category":"other"})),
sparse_vectors: None,
},
];
collection.upsert(points).unwrap();
collection
.add_edge(GraphEdge::new(11, 1, 2, "REL").unwrap())
.unwrap();
let query = "SELECT * FROM test AS d WHERE NOT (MATCH (d:Doc)-[:REL]->(x:Doc)) LIMIT 10";
let parsed = Parser::parse(query).unwrap();
let results = collection.execute_query(&parsed, &HashMap::new()).unwrap();
let ids: std::collections::HashSet<u64> = results.iter().map(|r| r.point.id).collect();
assert!(!ids.contains(&1), "id=1 should be excluded by NOT MATCH");
assert!(ids.contains(&2), "id=2 should remain");
assert!(ids.contains(&3), "id=3 should remain");
}
#[test]
fn test_execute_query_dispatches_top_level_match_query() {
let (collection, _temp) = create_test_collection();
let points = vec![
crate::Point {
id: 1,
vector: vec![1.0, 0.0, 0.0, 0.0],
payload: Some(serde_json::json!({"_labels":["Doc"], "category":"tech"})),
sparse_vectors: None,
},
crate::Point {
id: 2,
vector: vec![0.9, 0.1, 0.0, 0.0],
payload: Some(serde_json::json!({"_labels":["Doc"], "category":"science"})),
sparse_vectors: None,
},
crate::Point {
id: 3,
vector: vec![0.0, 1.0, 0.0, 0.0],
payload: Some(serde_json::json!({"_labels":["Doc"], "category":"other"})),
sparse_vectors: None,
},
];
collection.upsert(points).unwrap();
collection
.add_edge(GraphEdge::new(12, 1, 2, "REL").unwrap())
.unwrap();
let query = "MATCH (d:Doc)-[:REL]->(x:Doc) RETURN x LIMIT 10";
let parsed = Parser::parse(query).unwrap();
let results = collection.execute_query(&parsed, &HashMap::new()).unwrap();
let ids: std::collections::HashSet<u64> = results.iter().map(|r| r.point.id).collect();
assert_eq!(ids.len(), 1);
assert!(
ids.contains(&2),
"MATCH should return graph traversal target"
);
}
#[test]
fn test_execute_query_dispatches_top_level_match_with_where_filter() {
let (collection, _temp) = create_test_collection();
let points = vec![
crate::Point {
id: 1,
vector: vec![1.0, 0.0, 0.0, 0.0],
payload: Some(serde_json::json!({"_labels":["Doc"], "category":"tech"})),
sparse_vectors: None,
},
crate::Point {
id: 2,
vector: vec![0.9, 0.1, 0.0, 0.0],
payload: Some(serde_json::json!({"_labels":["Doc"], "category":"science"})),
sparse_vectors: None,
},
crate::Point {
id: 3,
vector: vec![0.0, 1.0, 0.0, 0.0],
payload: Some(serde_json::json!({"_labels":["Doc"], "category":"other"})),
sparse_vectors: None,
},
];
collection.upsert(points).unwrap();
collection
.add_edge(GraphEdge::new(13, 1, 2, "REL").unwrap())
.unwrap();
collection
.add_edge(GraphEdge::new(14, 3, 2, "REL").unwrap())
.unwrap();
let query = "MATCH (d:Doc)-[:REL]->(x:Doc) WHERE d.category = 'tech' RETURN x LIMIT 10";
let parsed = Parser::parse(query).unwrap();
let results = collection.execute_query(&parsed, &HashMap::new()).unwrap();
let ids: std::collections::HashSet<u64> = results.iter().map(|r| r.point.id).collect();
assert_eq!(ids.len(), 1);
assert!(
ids.contains(&2),
"Only traversal from d.category='tech' should match"
);
}
}