ruvector_filter/lib.rs
1#![recursion_limit = "2048"]
2
3//! # rUvector Filter
4//!
5//! Advanced payload indexing and filtering for rUvector.
6//!
7//! This crate provides:
8//! - Flexible filter expressions (equality, range, geo, text, logical operators)
9//! - Efficient payload indexing (integer, float, keyword, boolean, geo, text)
10//! - Fast filter evaluation using indices
11//! - Support for complex queries with AND/OR/NOT
12//!
13//! ## Examples
14//!
15//! ### Creating and Using Filters
16//!
17//! ```rust
18//! use ruvector_filter::{FilterExpression, PayloadIndexManager, FilterEvaluator, IndexType};
19//! use serde_json::json;
20//!
21//! // Create index manager
22//! let mut manager = PayloadIndexManager::new();
23//! manager.create_index("status", IndexType::Keyword).unwrap();
24//! manager.create_index("age", IndexType::Integer).unwrap();
25//!
26//! // Index some payloads
27//! manager.index_payload("v1", &json!({"status": "active", "age": 25})).unwrap();
28//! manager.index_payload("v2", &json!({"status": "active", "age": 30})).unwrap();
29//! manager.index_payload("v3", &json!({"status": "inactive", "age": 25})).unwrap();
30//!
31//! // Create filter
32//! let filter = FilterExpression::and(vec![
33//! FilterExpression::eq("status", json!("active")),
34//! FilterExpression::gte("age", json!(25)),
35//! ]);
36//!
37//! // Evaluate filter
38//! let evaluator = FilterEvaluator::new(&manager);
39//! let results = evaluator.evaluate(&filter).unwrap();
40//! assert_eq!(results.len(), 2);
41//! ```
42//!
43//! ### Geo Filtering
44//!
45//! ```rust
46//! use ruvector_filter::{FilterExpression, PayloadIndexManager, FilterEvaluator, IndexType};
47//! use serde_json::json;
48//!
49//! let mut manager = PayloadIndexManager::new();
50//! manager.create_index("location", IndexType::Geo).unwrap();
51//!
52//! manager.index_payload("v1", &json!({
53//! "location": {"lat": 40.7128, "lon": -74.0060}
54//! })).unwrap();
55//!
56//! // Find all points within 1000m of a location
57//! let filter = FilterExpression::geo_radius("location", 40.7128, -74.0060, 1000.0);
58//! let evaluator = FilterEvaluator::new(&manager);
59//! let results = evaluator.evaluate(&filter).unwrap();
60//! ```
61
62pub mod error;
63pub mod evaluator;
64pub mod expression;
65pub mod index;
66
67// Re-export main types
68pub use error::{FilterError, Result};
69pub use evaluator::FilterEvaluator;
70pub use expression::FilterExpression;
71pub use index::{IndexType, PayloadIndex, PayloadIndexManager};
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use serde_json::json;
77
78 #[test]
79 fn test_full_workflow() {
80 // Create index manager
81 let mut manager = PayloadIndexManager::new();
82 manager.create_index("status", IndexType::Keyword).unwrap();
83 manager.create_index("age", IndexType::Integer).unwrap();
84 manager.create_index("score", IndexType::Float).unwrap();
85
86 // Index payloads
87 manager
88 .index_payload(
89 "v1",
90 &json!({
91 "status": "active",
92 "age": 25,
93 "score": 0.9
94 }),
95 )
96 .unwrap();
97
98 manager
99 .index_payload(
100 "v2",
101 &json!({
102 "status": "active",
103 "age": 30,
104 "score": 0.85
105 }),
106 )
107 .unwrap();
108
109 manager
110 .index_payload(
111 "v3",
112 &json!({
113 "status": "inactive",
114 "age": 25,
115 "score": 0.7
116 }),
117 )
118 .unwrap();
119
120 // Create complex filter
121 let filter = FilterExpression::and(vec![
122 FilterExpression::eq("status", json!("active")),
123 FilterExpression::or(vec![
124 FilterExpression::gte("age", json!(30)),
125 FilterExpression::gte("score", json!(0.9)),
126 ]),
127 ]);
128
129 // Evaluate
130 let evaluator = FilterEvaluator::new(&manager);
131 let results = evaluator.evaluate(&filter).unwrap();
132
133 // Should match v1 (age=25, score=0.9) and v2 (age=30, score=0.85)
134 assert_eq!(results.len(), 2);
135 assert!(results.contains("v1"));
136 assert!(results.contains("v2"));
137 }
138
139 #[test]
140 fn test_text_matching() {
141 let mut manager = PayloadIndexManager::new();
142 manager
143 .create_index("description", IndexType::Text)
144 .unwrap();
145
146 manager
147 .index_payload(
148 "v1",
149 &json!({
150 "description": "The quick brown fox"
151 }),
152 )
153 .unwrap();
154
155 manager
156 .index_payload(
157 "v2",
158 &json!({
159 "description": "The lazy dog"
160 }),
161 )
162 .unwrap();
163
164 let evaluator = FilterEvaluator::new(&manager);
165 let filter = FilterExpression::match_text("description", "quick");
166 let results = evaluator.evaluate(&filter).unwrap();
167
168 assert_eq!(results.len(), 1);
169 assert!(results.contains("v1"));
170 }
171
172 #[test]
173 fn test_not_filter() {
174 let mut manager = PayloadIndexManager::new();
175 manager.create_index("status", IndexType::Keyword).unwrap();
176
177 manager
178 .index_payload("v1", &json!({"status": "active"}))
179 .unwrap();
180 manager
181 .index_payload("v2", &json!({"status": "inactive"}))
182 .unwrap();
183
184 let evaluator = FilterEvaluator::new(&manager);
185 let filter = FilterExpression::not(FilterExpression::eq("status", json!("active")));
186 let results = evaluator.evaluate(&filter).unwrap();
187
188 assert_eq!(results.len(), 1);
189 assert!(results.contains("v2"));
190 }
191
192 #[test]
193 fn test_in_filter() {
194 let mut manager = PayloadIndexManager::new();
195 manager.create_index("status", IndexType::Keyword).unwrap();
196
197 manager
198 .index_payload("v1", &json!({"status": "active"}))
199 .unwrap();
200 manager
201 .index_payload("v2", &json!({"status": "pending"}))
202 .unwrap();
203 manager
204 .index_payload("v3", &json!({"status": "inactive"}))
205 .unwrap();
206
207 let evaluator = FilterEvaluator::new(&manager);
208 let filter = FilterExpression::in_values("status", vec![json!("active"), json!("pending")]);
209 let results = evaluator.evaluate(&filter).unwrap();
210
211 assert_eq!(results.len(), 2);
212 assert!(results.contains("v1"));
213 assert!(results.contains("v2"));
214 }
215}