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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//! Vector database builder methods for Qdrant.
use crate::ast::{Action, Qail};
impl Qail {
/// Create a vector similarity search command.
///
/// # Example
/// ```ignore
/// use qail_core::prelude::*;
///
/// let cmd = Qail::search("products")
/// .vector(vec![0.1, 0.2, 0.3])
/// .limit(10);
/// ```
pub fn search(collection: &str) -> Self {
Self {
action: Action::Search,
table: collection.to_string(),
..Default::default()
}
}
/// Create a vector upsert command (insert or update points).
///
/// # Example
/// ```ignore
/// let cmd = Qail::upsert("products");
/// ```
pub fn upsert(collection: &str) -> Self {
Self {
action: Action::Upsert,
table: collection.to_string(),
..Default::default()
}
}
/// Create a scroll command for paginated iteration.
///
/// # Example
/// ```ignore
/// let cmd = Qail::scroll("products").limit(100);
/// ```
pub fn scroll(collection: &str) -> Self {
Self {
action: Action::Scroll,
table: collection.to_string(),
..Default::default()
}
}
/// Set the query vector for similarity search.
///
/// # Example
/// ```
/// use qail_core::prelude::*;
///
/// let embedding = vec![0.1, 0.2, 0.3, 0.4];
/// let cmd = Qail::search("products").vector(embedding);
/// assert!(cmd.vector.is_some());
/// ```
pub fn vector(mut self, embedding: Vec<f32>) -> Self {
self.vector = Some(embedding);
self
}
/// Set minimum similarity score threshold.
///
/// Points with similarity below this threshold will be filtered out.
///
/// # Example
/// ```
/// use qail_core::prelude::*;
///
/// let cmd = Qail::search("products")
/// .vector(vec![0.1, 0.2])
/// .score_threshold(0.8);
/// assert_eq!(cmd.score_threshold, Some(0.8));
/// ```
pub fn score_threshold(mut self, threshold: f32) -> Self {
self.score_threshold = Some(threshold);
self
}
/// Specify which named vector to search (for multi-vector collections).
///
/// # Example
/// ```
/// use qail_core::prelude::*;
///
/// // Collection with separate "title" and "content" vectors
/// let title_embedding = vec![0.1, 0.2, 0.3];
/// let cmd = Qail::search("articles")
/// .vector_name("title")
/// .vector(title_embedding);
/// ```
pub fn vector_name(mut self, name: &str) -> Self {
self.vector_name = Some(name.to_string());
self
}
/// Include vectors in search results.
///
/// # Example
/// ```
/// use qail_core::prelude::*;
///
/// let embedding = vec![0.1, 0.2, 0.3];
/// let cmd = Qail::search("products")
/// .vector(embedding)
/// .with_vectors();
/// assert!(cmd.with_vector);
/// ```
pub fn with_vectors(mut self) -> Self {
self.with_vector = true;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_search_builder() {
let cmd = Qail::search("products")
.vector(vec![0.1, 0.2, 0.3])
.score_threshold(0.8)
.limit(10);
assert_eq!(cmd.action, Action::Search);
assert_eq!(cmd.table, "products");
assert_eq!(cmd.vector, Some(vec![0.1, 0.2, 0.3]));
assert_eq!(cmd.score_threshold, Some(0.8));
}
#[test]
fn test_vector_name() {
let cmd = Qail::search("articles")
.vector_name("title")
.vector(vec![0.5, 0.5]);
assert_eq!(cmd.vector_name, Some("title".to_string()));
}
#[test]
fn test_with_vectors() {
let cmd = Qail::search("products").vector(vec![0.1]).with_vectors();
assert!(cmd.with_vector);
}
}