use crate::experience::{Experience, ExperienceType};
use crate::types::Timestamp;
#[derive(Clone, Debug)]
pub struct SearchFilter {
pub domains: Option<Vec<String>>,
pub experience_types: Option<Vec<ExperienceType>>,
pub min_importance: Option<f32>,
pub min_confidence: Option<f32>,
pub since: Option<Timestamp>,
pub exclude_archived: bool,
}
impl Default for SearchFilter {
fn default() -> Self {
Self {
domains: None,
experience_types: None,
min_importance: None,
min_confidence: None,
since: None,
exclude_archived: true,
}
}
}
impl SearchFilter {
pub fn matches(&self, experience: &Experience) -> bool {
if self.exclude_archived && experience.archived {
return false;
}
if let Some(ref domains) = self.domains {
let has_match = experience
.domain
.iter()
.any(|d| domains.iter().any(|f| f == d));
if !has_match {
return false;
}
}
if let Some(ref types) = self.experience_types {
let exp_tag = experience.experience_type.type_tag();
let has_match = types.iter().any(|t| t.type_tag() == exp_tag);
if !has_match {
return false;
}
}
if let Some(min) = self.min_importance {
if experience.importance < min {
return false;
}
}
if let Some(min) = self.min_confidence {
if experience.confidence < min {
return false;
}
}
if let Some(since) = self.since {
if experience.timestamp < since {
return false;
}
}
true
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{AgentId, CollectiveId, ExperienceId};
fn test_experience() -> Experience {
let timestamp = Timestamp::now();
Experience {
id: ExperienceId::new(),
collective_id: CollectiveId::new(),
content: "test content".to_string(),
embedding: vec![0.1; 384],
experience_type: ExperienceType::Fact {
statement: "test".to_string(),
source: String::new(),
},
importance: 0.5,
confidence: 0.8,
applications: std::collections::BTreeMap::new(),
domain: vec!["rust".to_string(), "testing".to_string()],
related_files: vec![],
source_agent: AgentId::new("agent-1"),
source_task: None,
timestamp,
last_reinforced: timestamp,
archived: false,
}
}
#[test]
fn test_default_filter_excludes_archived() {
let filter = SearchFilter::default();
assert!(filter.exclude_archived);
let mut exp = test_experience();
assert!(filter.matches(&exp));
exp.archived = true;
assert!(!filter.matches(&exp));
}
#[test]
fn test_default_filter_matches_all_non_archived() {
let filter = SearchFilter::default();
let exp = test_experience();
assert!(filter.matches(&exp));
}
#[test]
fn test_domain_filter() {
let filter = SearchFilter {
domains: Some(vec!["rust".to_string()]),
..SearchFilter::default()
};
let exp = test_experience(); assert!(filter.matches(&exp));
let filter_no_match = SearchFilter {
domains: Some(vec!["python".to_string()]),
..SearchFilter::default()
};
assert!(!filter_no_match.matches(&exp));
}
#[test]
fn test_experience_type_filter() {
let filter = SearchFilter {
experience_types: Some(vec![ExperienceType::Fact {
statement: String::new(),
source: String::new(),
}]),
..SearchFilter::default()
};
let exp = test_experience(); assert!(filter.matches(&exp));
let filter_no_match = SearchFilter {
experience_types: Some(vec![ExperienceType::Generic { category: None }]),
..SearchFilter::default()
};
assert!(!filter_no_match.matches(&exp));
}
#[test]
fn test_importance_filter() {
let filter = SearchFilter {
min_importance: Some(0.7),
..SearchFilter::default()
};
let mut exp = test_experience(); assert!(!filter.matches(&exp));
exp.importance = 0.8;
assert!(filter.matches(&exp));
}
#[test]
fn test_confidence_filter() {
let filter = SearchFilter {
min_confidence: Some(0.9),
..SearchFilter::default()
};
let exp = test_experience(); assert!(!filter.matches(&exp));
}
#[test]
fn test_since_filter() {
let before = Timestamp::now();
let exp = test_experience();
let filter = SearchFilter {
since: Some(before),
..SearchFilter::default()
};
assert!(filter.matches(&exp));
}
#[test]
fn test_combined_filters() {
let filter = SearchFilter {
domains: Some(vec!["rust".to_string()]),
min_importance: Some(0.3),
min_confidence: Some(0.5),
exclude_archived: true,
..SearchFilter::default()
};
let exp = test_experience(); assert!(filter.matches(&exp));
}
}