use super::{FulltextQuery, StructuredQuery, VectorQuery};
pub struct FilterPushdown;
impl FilterPushdown {
pub fn pushdown_to_vector(filter: &StructuredQuery, vector_query: &mut VectorQuery) {
if filter.where_clauses.is_empty() {
return;
}
let combined = filter.where_clauses.join(" AND ");
match &mut vector_query.filter {
Some(existing) => {
*existing = format!("{existing} AND ({combined})");
}
None => {
vector_query.filter = Some(combined);
}
}
}
pub fn pushdown_to_fulltext(filter: &StructuredQuery, fulltext_query: &mut FulltextQuery) {
if filter.where_clauses.is_empty() {
return;
}
let combined = filter.where_clauses.join(" AND ");
fulltext_query.fields.push(format!("__filter__:{combined}"));
}
}
#[cfg(test)]
mod tests {
use super::super::VectorMetric;
use super::*;
#[test]
fn test_pushdown_to_vector_new_filter() {
let filter = StructuredQuery {
table: "products".to_string(),
where_clauses: vec!["price < 1000".to_string()],
order_by: None,
};
let mut vector_query = VectorQuery {
collection: "docs".to_string(),
query_vector: vec![1.0, 0.0],
metric: VectorMetric::Cosine,
filter: None,
};
FilterPushdown::pushdown_to_vector(&filter, &mut vector_query);
assert_eq!(vector_query.filter.as_deref(), Some("price < 1000"));
}
#[test]
fn test_pushdown_to_vector_existing_filter() {
let filter = StructuredQuery {
table: "products".to_string(),
where_clauses: vec!["price < 1000".to_string()],
order_by: None,
};
let mut vector_query = VectorQuery {
collection: "docs".to_string(),
query_vector: vec![1.0, 0.0],
metric: VectorMetric::Cosine,
filter: Some("category = 'electronics'".to_string()),
};
FilterPushdown::pushdown_to_vector(&filter, &mut vector_query);
assert!(vector_query.filter.as_ref().unwrap().contains("category"));
assert!(vector_query.filter.as_ref().unwrap().contains("price"));
}
#[test]
fn test_pushdown_to_fulltext() {
let filter = StructuredQuery {
table: "products".to_string(),
where_clauses: vec!["price < 1000".to_string()],
order_by: None,
};
let mut fulltext_query = FulltextQuery {
index: "docs_idx".to_string(),
query_text: "laptop".to_string(),
fields: vec!["title".to_string()],
};
FilterPushdown::pushdown_to_fulltext(&filter, &mut fulltext_query);
assert!(fulltext_query
.fields
.iter()
.any(|f| f.contains("__filter__")));
}
#[test]
fn test_pushdown_empty_clauses() {
let filter = StructuredQuery {
table: "products".to_string(),
where_clauses: vec![],
order_by: None,
};
let mut vector_query = VectorQuery {
collection: "docs".to_string(),
query_vector: vec![1.0],
metric: VectorMetric::Cosine,
filter: None,
};
FilterPushdown::pushdown_to_vector(&filter, &mut vector_query);
assert!(vector_query.filter.is_none());
}
}