1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! BDD tests for specific bug fixes.
//!
//! Each test documents the original bug, provides a minimal reproduction,
//! and proves the fix holds.
use super::helpers::{
create_test_db, execute_sql, execute_sql_with_params, payload_str, vector_param,
};
use serde_json::json;
use velesdb_core::Point;
// =========================================================================
// Helpers
// =========================================================================
/// Creates a vector collection `docs` with duplicate categories for DISTINCT tests.
fn setup_docs_with_duplicate_categories(db: &velesdb_core::Database) {
execute_sql(
db,
"CREATE COLLECTION docs (dimension = 4, metric = 'cosine');",
)
.expect("test: CREATE docs");
let coll = db.get_vector_collection("docs").expect("test: get docs");
coll.upsert(vec![
Point::new(
1,
vec![0.9, 0.1, 0.0, 0.0],
Some(json!({"category": "tech", "content": "database systems"})),
),
Point::new(
2,
vec![0.8, 0.2, 0.0, 0.0],
Some(json!({"category": "tech", "content": "database indexing"})),
),
Point::new(
3,
vec![0.1, 0.9, 0.0, 0.0],
Some(json!({"category": "science", "content": "quantum physics"})),
),
Point::new(
4,
vec![0.0, 0.1, 0.9, 0.0],
Some(json!({"category": "tech", "content": "machine learning"})),
),
Point::new(
5,
vec![0.0, 0.0, 0.1, 0.9],
Some(json!({"category": "science", "content": "biology research"})),
),
])
.expect("test: upsert docs");
}
// =========================================================================
// Bug #475: early-return query paths skip DISTINCT
// =========================================================================
/// GIVEN a collection with duplicate category values
/// WHEN `SELECT DISTINCT category FROM docs WHERE NOT similarity(vector, $v) > 0.8 LIMIT 10`
/// THEN the result should not contain duplicate categories.
///
/// Bug: `execute_early_return_query` applied ORDER BY, OFFSET, LIMIT but NOT DISTINCT.
/// The `finalize_query_results` path correctly applied DISTINCT via `apply_select_postprocessing`.
#[test]
fn test_bug_475_early_return_applies_distinct() {
let (_dir, db) = create_test_db();
setup_docs_with_duplicate_categories(&db);
let params = vector_param(&[1.0, 0.0, 0.0, 0.0]);
let sql = "SELECT DISTINCT category FROM docs \
WHERE NOT similarity(vector, $v) > 0.8 \
LIMIT 10";
let results =
execute_sql_with_params(&db, sql, ¶ms).expect("DISTINCT + NOT similarity should work");
// Collect all category values from results
let categories: Vec<&str> = results
.iter()
.filter_map(|r| payload_str(r, "category"))
.collect();
// There should be no duplicate categories
let mut unique = categories.clone();
unique.sort_unstable();
unique.dedup();
assert_eq!(
categories.len(),
unique.len(),
"Bug #475: DISTINCT should remove duplicate categories in early-return path. \
Got duplicates: {categories:?}"
);
}
// =========================================================================
// Bug #474: NEAR + text MATCH + metadata filter silently drops metadata filter
// =========================================================================
/// GIVEN a collection with documents having different categories
/// WHEN `SELECT * FROM docs WHERE vector NEAR $v AND content MATCH 'database' AND category = 'tech' LIMIT 10`
/// THEN only results where category = 'tech' should be returned.
///
/// Bug: `dispatch_near_with_filter` called `hybrid_search` when it found a MATCH clause,
/// but silently dropped co-occurring metadata filters like `category = 'tech'`.
#[test]
fn test_bug_474_near_match_metadata_filter_not_dropped() {
let (_dir, db) = create_test_db();
setup_docs_with_duplicate_categories(&db);
let params = vector_param(&[0.5, 0.5, 0.0, 0.0]);
let sql = "SELECT * FROM docs \
WHERE vector NEAR $v AND content MATCH 'database' AND category = 'tech' \
LIMIT 10";
let results = execute_sql_with_params(&db, sql, ¶ms)
.expect("NEAR + MATCH + metadata filter should work");
// Every result must have category = 'tech'
for result in &results {
let cat = payload_str(result, "category");
assert_eq!(
cat,
Some("tech"),
"Bug #474: metadata filter 'category = tech' was silently dropped. \
Got category={cat:?} for point id={}",
result.point.id
);
}
}