use super::*;
use crate::velesql::{CompareOp, Comparison, Value};
#[test]
fn test_metadata_filter_built_once_per_leaf() {
let cond = Condition::Comparison(Comparison {
column: "status".to_string(),
operator: CompareOp::Eq,
value: Value::String("active".to_string()),
});
let mut cache = GraphMatchEvalCache::default();
let active = serde_json::json!({"status": "active"});
let inactive = serde_json::json!({"status": "inactive"});
for _ in 0..100 {
let f = cache.metadata_filter(&cond);
assert!(f.matches(&active));
assert!(!f.matches(&inactive));
}
assert_eq!(
cache.filters_built(),
1,
"metadata Filter must be built exactly once, not per row"
);
}
#[test]
fn test_metadata_filter_distinct_leaves_cached_separately() {
let cond_a = Condition::Comparison(Comparison {
column: "a".to_string(),
operator: CompareOp::Eq,
value: Value::Integer(1),
});
let cond_b = Condition::Comparison(Comparison {
column: "b".to_string(),
operator: CompareOp::Eq,
value: Value::Integer(2),
});
let mut cache = GraphMatchEvalCache::default();
let _ = cache.metadata_filter(&cond_a);
let _ = cache.metadata_filter(&cond_b);
let _ = cache.metadata_filter(&cond_a);
assert_eq!(cache.filters_built(), 2);
}
#[test]
fn test_condition_contains_or_detects_nested_or() {
let cond = Condition::And(
Box::new(Condition::Comparison(Comparison {
column: "status".to_string(),
operator: CompareOp::Eq,
value: Value::String("active".to_string()),
})),
Box::new(Condition::Group(Box::new(Condition::Or(
Box::new(Condition::Comparison(Comparison {
column: "tier".to_string(),
operator: CompareOp::Eq,
value: Value::String("pro".to_string()),
})),
Box::new(Condition::Comparison(Comparison {
column: "tier".to_string(),
operator: CompareOp::Eq,
value: Value::String("enterprise".to_string()),
})),
)))),
);
assert!(Collection::condition_contains_or(&cond));
}
#[test]
fn test_condition_contains_or_false_without_or() {
let cond = Condition::And(
Box::new(Condition::Comparison(Comparison {
column: "status".to_string(),
operator: CompareOp::Eq,
value: Value::String("active".to_string()),
})),
Box::new(Condition::Not(Box::new(Condition::Comparison(
Comparison {
column: "deleted".to_string(),
operator: CompareOp::Eq,
value: Value::Boolean(true),
},
)))),
);
assert!(!Collection::condition_contains_or(&cond));
}
#[cfg(feature = "persistence")]
mod similarity_length_mismatch {
use crate::collection::Collection;
use crate::distance::DistanceMetric;
use crate::point::{Point, SearchResult};
use crate::velesql::{CompareOp, Condition, SimilarityCondition, VectorExpr};
use std::path::PathBuf;
#[test]
fn test_where_similarity_rejects_length_mismatched_query_vector() {
let dir = tempfile::tempdir().expect("test: tempdir");
let col = Collection::create(PathBuf::from(dir.path()), 2, DistanceMetric::Euclidean)
.expect("test: collection");
let results = vec![SearchResult::new(
Point {
id: 1,
vector: vec![3.0, 0.0],
payload: None,
sparse_vectors: None,
},
0.0,
)];
let condition = Condition::Similarity(SimilarityCondition {
field: "vector".to_string(),
vector: VectorExpr::Literal(vec![0.0, 0.0, 0.0]),
operator: CompareOp::Gt,
threshold: 0.9,
});
let filtered = col
.apply_where_condition_to_results(
results,
&condition,
&std::collections::HashMap::new(),
&[],
)
.expect("test: where evaluation");
assert!(
filtered.is_empty(),
"an unscorable vector pair must fail the predicate, got {filtered:?}"
);
}
#[test]
fn test_unknown_similarity_follows_three_valued_logic() {
let dir = tempfile::tempdir().expect("test: tempdir");
let col = Collection::create(PathBuf::from(dir.path()), 2, DistanceMetric::Euclidean)
.expect("test: collection");
let unknown = || {
Condition::Similarity(SimilarityCondition {
field: "vector".to_string(),
vector: VectorExpr::Literal(vec![0.0, 0.0, 0.0]),
operator: CompareOp::Gt,
threshold: 0.9,
})
};
let meta = |value: &str| {
Condition::Comparison(crate::velesql::Comparison {
column: "cat".to_string(),
operator: CompareOp::Eq,
value: crate::velesql::Value::String(value.to_string()),
})
};
let row = || {
vec![SearchResult::new(
Point {
id: 1,
vector: vec![3.0, 0.0],
payload: Some(serde_json::json!({"cat": "a"})),
sparse_vectors: None,
},
0.0,
)]
};
let admits = |condition: &Condition| -> bool {
!col.apply_where_condition_to_results(
row(),
condition,
&std::collections::HashMap::new(),
&[],
)
.expect("test: where evaluation")
.is_empty()
};
assert!(!admits(&unknown()), "UNKNOWN must not admit the row");
assert!(
!admits(&Condition::Not(Box::new(unknown()))),
"NOT UNKNOWN is UNKNOWN, not true"
);
assert!(
!admits(&Condition::And(Box::new(unknown()), Box::new(meta("a")))),
"UNKNOWN AND true is UNKNOWN"
);
assert!(
admits(&Condition::Not(Box::new(Condition::And(
Box::new(unknown()),
Box::new(meta("z"))
)))),
"NOT (UNKNOWN AND false) is true: the conjunction is false whatever \
the unscoreable leaf would have been"
);
assert!(
admits(&Condition::Or(Box::new(unknown()), Box::new(meta("a")))),
"UNKNOWN OR true is true"
);
assert!(
!admits(&Condition::Or(Box::new(unknown()), Box::new(meta("z")))),
"UNKNOWN OR false is UNKNOWN"
);
}
}