feat: implement enhanced search filter system based on Python reference
- Enhanced SearchFilterBuilder with complex nested filter logic - Added support for category-specific filtering with proper AND/OR logic - Implemented enhanced query building with keyword integration - Added comprehensive configuration validation and debugging - Extended SearchConfig with debug mode, custom filters, and query enhancement - Updated frontend components with debug options and advanced settings - Added comprehensive unit tests for all new functionality - Fixed compilation issues and ensured all tests pass Follows promptx/tauri-desktop-app-expert development specifications
This commit is contained in:
parent
722b141d22
commit
c6e02e0b36
|
|
@ -62,6 +62,7 @@ impl Default for ColorFilter {
|
|||
}
|
||||
|
||||
/// 搜索配置
|
||||
/// 扩展配置以支持更复杂的搜索过滤逻辑
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SearchConfig {
|
||||
/// 相关性阈值
|
||||
|
|
@ -76,6 +77,36 @@ pub struct SearchConfig {
|
|||
pub design_styles: HashMap<String, Vec<String>>,
|
||||
/// 最大关键词数量
|
||||
pub max_keywords: usize,
|
||||
/// 是否启用调试模式
|
||||
pub debug_mode: bool,
|
||||
/// 自定义过滤器字符串
|
||||
pub custom_filters: Vec<String>,
|
||||
/// 查询增强模式
|
||||
pub query_enhancement_enabled: bool,
|
||||
/// 颜色阈值配置
|
||||
pub color_thresholds: ColorThresholds,
|
||||
}
|
||||
|
||||
/// 颜色阈值全局配置
|
||||
/// 参考 Python 实现中的阈值设置
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ColorThresholds {
|
||||
/// 默认色相阈值
|
||||
pub default_hue_threshold: f64,
|
||||
/// 默认饱和度阈值
|
||||
pub default_saturation_threshold: f64,
|
||||
/// 默认明度阈值
|
||||
pub default_value_threshold: f64,
|
||||
}
|
||||
|
||||
impl Default for ColorThresholds {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
default_hue_threshold: 0.05,
|
||||
default_saturation_threshold: 0.05,
|
||||
default_value_threshold: 0.20,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SearchConfig {
|
||||
|
|
@ -87,6 +118,10 @@ impl Default for SearchConfig {
|
|||
color_filters: HashMap::new(),
|
||||
design_styles: HashMap::new(),
|
||||
max_keywords: 10,
|
||||
debug_mode: false,
|
||||
custom_filters: Vec::new(),
|
||||
query_enhancement_enabled: true,
|
||||
color_thresholds: ColorThresholds::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -230,95 +265,251 @@ impl Default for OutfitSearchGlobalConfig {
|
|||
}
|
||||
|
||||
/// 搜索过滤器构建器
|
||||
/// 基于 Google Cloud Search API 规范实现复杂过滤器构建
|
||||
pub struct SearchFilterBuilder;
|
||||
|
||||
impl SearchFilterBuilder {
|
||||
/// 构建搜索过滤器字符串
|
||||
/// 参考 Python 实现,支持复杂的嵌套过滤逻辑
|
||||
pub fn build_filters(config: &SearchConfig) -> String {
|
||||
let mut filters = Vec::new();
|
||||
|
||||
// 类别过滤
|
||||
|
||||
// 类别过滤 - 每个类别创建独立的过滤器组
|
||||
if !config.categories.is_empty() {
|
||||
for category in &config.categories {
|
||||
let mut inner_filters = vec![
|
||||
format!("products.category: ANY(\"{}\")", category)
|
||||
];
|
||||
|
||||
// 颜色过滤
|
||||
if let Some(color_filter) = config.color_filters.get(category) {
|
||||
if color_filter.enabled {
|
||||
inner_filters.extend(Self::build_color_filters(color_filter));
|
||||
}
|
||||
let category_filter = Self::build_category_filter(category, config);
|
||||
if !category_filter.is_empty() {
|
||||
filters.push(category_filter);
|
||||
}
|
||||
|
||||
// 设计风格过滤
|
||||
if let Some(styles) = config.design_styles.get(category) {
|
||||
if !styles.is_empty() {
|
||||
let styles_str = styles.iter()
|
||||
.map(|s| format!("\"{}\"", s))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
inner_filters.push(format!("products.design_styles: ANY({})", styles_str));
|
||||
}
|
||||
}
|
||||
|
||||
filters.push(format!("({})", inner_filters.join(" AND ")));
|
||||
}
|
||||
}
|
||||
|
||||
// 环境标签过滤
|
||||
|
||||
// 环境标签过滤 - 独立的环境过滤器
|
||||
if !config.environments.is_empty() {
|
||||
let env_str = config.environments.iter()
|
||||
.map(|e| format!("\"{}\"", e))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
filters.push(format!("environment_tags: ANY({})", env_str));
|
||||
let env_filter = Self::build_environment_filter(&config.environments);
|
||||
filters.push(env_filter);
|
||||
}
|
||||
|
||||
filters.join(" AND ")
|
||||
|
||||
// 使用 AND 连接所有过滤器组
|
||||
let result = filters.join(" AND ");
|
||||
|
||||
// 调试日志
|
||||
if !result.is_empty() {
|
||||
eprintln!("构建的过滤器字符串: {}", result);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
/// 为单个类别构建过滤器组
|
||||
/// 类似 Python 中的 inner_filters 逻辑
|
||||
fn build_category_filter(category: &str, config: &SearchConfig) -> String {
|
||||
let mut inner_filters = vec![
|
||||
format!("products.category: ANY(\"{}\")", category)
|
||||
];
|
||||
|
||||
// 颜色检测过滤
|
||||
if let Some(color_filter) = config.color_filters.get(category) {
|
||||
if color_filter.enabled {
|
||||
let color_filters = Self::build_color_filters(color_filter);
|
||||
inner_filters.extend(color_filters);
|
||||
}
|
||||
}
|
||||
|
||||
// 设计风格过滤
|
||||
if let Some(styles) = config.design_styles.get(category) {
|
||||
if !styles.is_empty() {
|
||||
let styles_filter = Self::build_design_styles_filter(styles);
|
||||
inner_filters.push(styles_filter);
|
||||
}
|
||||
}
|
||||
|
||||
// 返回括号包围的 AND 组合
|
||||
if inner_filters.len() > 1 {
|
||||
format!("({})", inner_filters.join(" AND "))
|
||||
} else {
|
||||
inner_filters.into_iter().next().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
/// 构建环境标签过滤器
|
||||
fn build_environment_filter(environments: &[String]) -> String {
|
||||
let env_str = environments.iter()
|
||||
.map(|e| format!("\"{}\"", e))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
format!("environment_tags: ANY({})", env_str)
|
||||
}
|
||||
|
||||
/// 构建设计风格过滤器
|
||||
fn build_design_styles_filter(styles: &[String]) -> String {
|
||||
let styles_str = styles.iter()
|
||||
.map(|s| format!("\"{}\"", s))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
format!("products.design_styles: ANY({})", styles_str)
|
||||
}
|
||||
|
||||
/// 构建颜色过滤器
|
||||
/// 参考 Python 实现的 HSV 颜色范围过滤
|
||||
fn build_color_filters(color_filter: &ColorFilter) -> Vec<String> {
|
||||
let hsv = &color_filter.color;
|
||||
|
||||
// 计算颜色范围,确保在 [0, 1] 区间内
|
||||
let hue_min = (hsv.hue - color_filter.hue_threshold).max(0.0);
|
||||
let hue_max = (hsv.hue + color_filter.hue_threshold).min(1.0);
|
||||
let sat_min = (hsv.saturation - color_filter.saturation_threshold).max(0.0);
|
||||
let sat_max = (hsv.saturation + color_filter.saturation_threshold).min(1.0);
|
||||
let val_min = (hsv.value - color_filter.value_threshold).max(0.0);
|
||||
let val_max = (hsv.value + color_filter.value_threshold).min(1.0);
|
||||
|
||||
vec![
|
||||
format!(
|
||||
"products.color_pattern.Hue: IN({}, {})",
|
||||
(hsv.hue - color_filter.hue_threshold).max(0.0),
|
||||
(hsv.hue + color_filter.hue_threshold).min(1.0)
|
||||
),
|
||||
format!(
|
||||
"products.color_pattern.Saturation: IN({}, {})",
|
||||
(hsv.saturation - color_filter.saturation_threshold).max(0.0),
|
||||
(hsv.saturation + color_filter.saturation_threshold).min(1.0)
|
||||
),
|
||||
format!(
|
||||
"products.color_pattern.Value: IN({}, {})",
|
||||
(hsv.value - color_filter.value_threshold).max(0.0),
|
||||
(hsv.value + color_filter.value_threshold).min(1.0)
|
||||
),
|
||||
format!("products.color_pattern.Hue: IN({}, {})", hue_min, hue_max),
|
||||
format!("products.color_pattern.Saturation: IN({}, {})", sat_min, sat_max),
|
||||
format!("products.color_pattern.Value: IN({}, {})", val_min, val_max),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
/// 构建查询关键词
|
||||
/// 参考 Python 实现,支持关键词优先级和数量限制
|
||||
pub fn build_query_keywords(config: &SearchConfig) -> Vec<String> {
|
||||
let mut keywords = Vec::new();
|
||||
|
||||
// 添加设计风格关键词
|
||||
|
||||
// 优先添加环境关键词(高优先级)
|
||||
keywords.extend(config.environments.clone());
|
||||
|
||||
// 添加设计风格关键词(按类别添加)
|
||||
for styles in config.design_styles.values() {
|
||||
keywords.extend(styles.clone());
|
||||
}
|
||||
|
||||
// 添加环境关键词
|
||||
keywords.extend(config.environments.clone());
|
||||
|
||||
// 限制关键词数量
|
||||
|
||||
// 限制关键词数量,参考 Python 中的 max_keywords 逻辑
|
||||
if keywords.len() > config.max_keywords {
|
||||
keywords.truncate(config.max_keywords);
|
||||
}
|
||||
|
||||
|
||||
// 调试日志
|
||||
if !keywords.is_empty() {
|
||||
eprintln!("构建的查询关键词: {:?}", keywords);
|
||||
}
|
||||
|
||||
keywords
|
||||
}
|
||||
|
||||
/// 构建增强的查询字符串
|
||||
/// 参考 Python 实现,将基础查询与关键词组合
|
||||
pub fn build_enhanced_query(base_query: &str, config: &SearchConfig) -> String {
|
||||
// 如果查询增强被禁用,直接返回原始查询
|
||||
if !config.query_enhancement_enabled {
|
||||
return base_query.to_string();
|
||||
}
|
||||
|
||||
let keywords = Self::build_query_keywords(config);
|
||||
|
||||
let result = if keywords.is_empty() {
|
||||
base_query.to_string()
|
||||
} else {
|
||||
let keywords_str = keywords.join(" ");
|
||||
if base_query.trim().is_empty() {
|
||||
format!("model {}", keywords_str)
|
||||
} else {
|
||||
format!("{} {}", base_query.trim(), keywords_str)
|
||||
}
|
||||
};
|
||||
|
||||
// 调试日志
|
||||
if config.debug_mode {
|
||||
eprintln!("查询构建详情:");
|
||||
eprintln!(" 原始查询: '{}'", base_query);
|
||||
eprintln!(" 关键词: {:?}", keywords);
|
||||
eprintln!(" 查询增强: {}", config.query_enhancement_enabled);
|
||||
eprintln!(" 最终查询: '{}'", result);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// 验证搜索配置
|
||||
/// 检查配置的有效性并提供调试信息
|
||||
pub fn validate_config(config: &SearchConfig) -> Result<(), String> {
|
||||
// 检查类别配置
|
||||
if config.categories.is_empty() && config.environments.is_empty() {
|
||||
if config.debug_mode {
|
||||
eprintln!("警告: 没有设置任何类别或环境过滤器");
|
||||
}
|
||||
}
|
||||
|
||||
// 检查颜色过滤器配置
|
||||
for (category, color_filter) in &config.color_filters {
|
||||
if color_filter.enabled {
|
||||
if !config.categories.contains(category) {
|
||||
return Err(format!("颜色过滤器类别 '{}' 不在选定类别中", category));
|
||||
}
|
||||
|
||||
// 验证颜色值范围
|
||||
let hsv = &color_filter.color;
|
||||
if hsv.hue < 0.0 || hsv.hue > 1.0 ||
|
||||
hsv.saturation < 0.0 || hsv.saturation > 1.0 ||
|
||||
hsv.value < 0.0 || hsv.value > 1.0 {
|
||||
return Err(format!("类别 '{}' 的颜色值超出有效范围 [0, 1]", category));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查设计风格配置
|
||||
for (category, styles) in &config.design_styles {
|
||||
if !styles.is_empty() && !config.categories.contains(category) {
|
||||
return Err(format!("设计风格类别 '{}' 不在选定类别中", category));
|
||||
}
|
||||
}
|
||||
|
||||
if config.debug_mode {
|
||||
eprintln!("搜索配置验证通过");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 生成搜索配置摘要
|
||||
/// 用于调试和日志记录
|
||||
pub fn generate_config_summary(config: &SearchConfig) -> String {
|
||||
let mut summary = Vec::new();
|
||||
|
||||
summary.push(format!("相关性阈值: {:?}", config.relevance_threshold));
|
||||
|
||||
if !config.categories.is_empty() {
|
||||
summary.push(format!("类别: {:?}", config.categories));
|
||||
}
|
||||
|
||||
if !config.environments.is_empty() {
|
||||
summary.push(format!("环境: {:?}", config.environments));
|
||||
}
|
||||
|
||||
let active_color_filters: Vec<_> = config.color_filters.iter()
|
||||
.filter(|(_, filter)| filter.enabled)
|
||||
.map(|(category, _)| category.clone())
|
||||
.collect();
|
||||
if !active_color_filters.is_empty() {
|
||||
summary.push(format!("颜色过滤: {:?}", active_color_filters));
|
||||
}
|
||||
|
||||
let active_style_filters: Vec<_> = config.design_styles.iter()
|
||||
.filter(|(_, styles)| !styles.is_empty())
|
||||
.map(|(category, styles)| format!("{}:{:?}", category, styles))
|
||||
.collect();
|
||||
if !active_style_filters.is_empty() {
|
||||
summary.push(format!("设计风格: [{}]", active_style_filters.join(", ")));
|
||||
}
|
||||
|
||||
if !config.custom_filters.is_empty() {
|
||||
summary.push(format!("自定义过滤器: {:?}", config.custom_filters));
|
||||
}
|
||||
|
||||
summary.push(format!("最大关键词: {}", config.max_keywords));
|
||||
summary.push(format!("查询增强: {}", config.query_enhancement_enabled));
|
||||
|
||||
summary.join("; ")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -352,6 +543,9 @@ mod tests {
|
|||
assert!(config.color_filters.is_empty());
|
||||
assert!(config.design_styles.is_empty());
|
||||
assert_eq!(config.max_keywords, 10);
|
||||
assert!(!config.debug_mode);
|
||||
assert!(config.custom_filters.is_empty());
|
||||
assert!(config.query_enhancement_enabled);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -497,6 +691,160 @@ mod tests {
|
|||
assert_eq!(request.session_id, Some("session-123".to_string()));
|
||||
}
|
||||
|
||||
// 增强过滤器功能测试
|
||||
#[test]
|
||||
fn test_enhanced_filter_builder_complex_category() {
|
||||
let mut config = SearchConfig::default();
|
||||
config.categories = vec!["上装".to_string()];
|
||||
|
||||
// 添加颜色过滤器
|
||||
let color_filter = ColorFilter {
|
||||
enabled: true,
|
||||
color: ColorHSV::new(0.5, 0.8, 0.9),
|
||||
hue_threshold: 0.05,
|
||||
saturation_threshold: 0.05,
|
||||
value_threshold: 0.20,
|
||||
};
|
||||
config.color_filters.insert("上装".to_string(), color_filter);
|
||||
|
||||
// 添加设计风格
|
||||
config.design_styles.insert("上装".to_string(), vec!["休闲".to_string(), "正式".to_string()]);
|
||||
|
||||
let filters = SearchFilterBuilder::build_filters(&config);
|
||||
|
||||
// 验证包含所有过滤条件
|
||||
assert!(filters.contains("products.category: ANY(\"上装\")"));
|
||||
assert!(filters.contains("products.color_pattern.Hue: IN("));
|
||||
assert!(filters.contains("products.color_pattern.Saturation: IN("));
|
||||
assert!(filters.contains("products.color_pattern.Value: IN("));
|
||||
assert!(filters.contains("products.design_styles: ANY(\"休闲\",\"正式\")"));
|
||||
|
||||
// 验证括号结构
|
||||
assert!(filters.contains("(") && filters.contains(")"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enhanced_query_builder() {
|
||||
let mut config = SearchConfig::default();
|
||||
config.environments = vec!["Outdoor".to_string()];
|
||||
config.design_styles.insert("上装".to_string(), vec!["休闲".to_string()]);
|
||||
config.max_keywords = 5;
|
||||
|
||||
let enhanced_query = SearchFilterBuilder::build_enhanced_query("牛仔裤", &config);
|
||||
|
||||
assert!(enhanced_query.contains("牛仔裤"));
|
||||
assert!(enhanced_query.contains("Outdoor"));
|
||||
assert!(enhanced_query.contains("休闲"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enhanced_query_builder_empty_base() {
|
||||
let mut config = SearchConfig::default();
|
||||
config.environments = vec!["Outdoor".to_string()];
|
||||
|
||||
let enhanced_query = SearchFilterBuilder::build_enhanced_query("", &config);
|
||||
|
||||
assert!(enhanced_query.starts_with("model"));
|
||||
assert!(enhanced_query.contains("Outdoor"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enhanced_query_builder_disabled() {
|
||||
let mut config = SearchConfig::default();
|
||||
config.query_enhancement_enabled = false;
|
||||
config.environments = vec!["Outdoor".to_string()];
|
||||
|
||||
let enhanced_query = SearchFilterBuilder::build_enhanced_query("牛仔裤", &config);
|
||||
|
||||
assert_eq!(enhanced_query, "牛仔裤");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_validation_success() {
|
||||
let mut config = SearchConfig::default();
|
||||
config.categories = vec!["上装".to_string()];
|
||||
|
||||
let color_filter = ColorFilter {
|
||||
enabled: true,
|
||||
color: ColorHSV::new(0.5, 0.8, 0.9),
|
||||
hue_threshold: 0.05,
|
||||
saturation_threshold: 0.05,
|
||||
value_threshold: 0.20,
|
||||
};
|
||||
config.color_filters.insert("上装".to_string(), color_filter);
|
||||
|
||||
let result = SearchFilterBuilder::validate_config(&config);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_validation_invalid_color_category() {
|
||||
let mut config = SearchConfig::default();
|
||||
config.categories = vec!["上装".to_string()];
|
||||
|
||||
let color_filter = ColorFilter {
|
||||
enabled: true,
|
||||
color: ColorHSV::new(0.5, 0.8, 0.9),
|
||||
hue_threshold: 0.05,
|
||||
saturation_threshold: 0.05,
|
||||
value_threshold: 0.20,
|
||||
};
|
||||
config.color_filters.insert("下装".to_string(), color_filter); // 不在categories中
|
||||
|
||||
let result = SearchFilterBuilder::validate_config(&config);
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("不在选定类别中"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_validation_invalid_color_values() {
|
||||
let mut config = SearchConfig::default();
|
||||
config.categories = vec!["上装".to_string()];
|
||||
|
||||
// 直接创建带有无效值的ColorHSV,绕过new函数的clamp
|
||||
let invalid_color = ColorHSV {
|
||||
hue: 1.5, // 超出范围
|
||||
saturation: 0.8,
|
||||
value: 0.9,
|
||||
};
|
||||
|
||||
let color_filter = ColorFilter {
|
||||
enabled: true,
|
||||
color: invalid_color,
|
||||
hue_threshold: 0.05,
|
||||
saturation_threshold: 0.05,
|
||||
value_threshold: 0.20,
|
||||
};
|
||||
config.color_filters.insert("上装".to_string(), color_filter);
|
||||
|
||||
let result = SearchFilterBuilder::validate_config(&config);
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("超出有效范围"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_summary_generation() {
|
||||
let mut config = SearchConfig::default();
|
||||
config.categories = vec!["上装".to_string()];
|
||||
config.environments = vec!["Outdoor".to_string()];
|
||||
config.max_keywords = 15;
|
||||
config.debug_mode = true;
|
||||
|
||||
let summary = SearchFilterBuilder::generate_config_summary(&config);
|
||||
|
||||
assert!(summary.contains("类别"));
|
||||
assert!(summary.contains("环境"));
|
||||
assert!(summary.contains("最大关键词: 15"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_color_thresholds_default() {
|
||||
let thresholds = ColorThresholds::default();
|
||||
assert_eq!(thresholds.default_hue_threshold, 0.05);
|
||||
assert_eq!(thresholds.default_saturation_threshold, 0.05);
|
||||
assert_eq!(thresholds.default_value_threshold, 0.20);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_outfit_search_global_config_default() {
|
||||
let config = OutfitSearchGlobalConfig::default();
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ pub async fn generate_search_config_from_analysis(
|
|||
) -> Result<crate::data::models::outfit_search::SearchConfig, String> {
|
||||
// TODO: 实现基于分析结果生成搜索配置的逻辑
|
||||
// 暂时返回默认配置
|
||||
use crate::data::models::outfit_search::{RelevanceThreshold, SearchConfig};
|
||||
use crate::data::models::outfit_search::{RelevanceThreshold, SearchConfig, ColorThresholds};
|
||||
use std::collections::HashMap;
|
||||
|
||||
Ok(SearchConfig {
|
||||
|
|
@ -135,6 +135,10 @@ pub async fn generate_search_config_from_analysis(
|
|||
color_filters: HashMap::new(),
|
||||
design_styles: HashMap::new(),
|
||||
max_keywords: 10,
|
||||
debug_mode: false,
|
||||
custom_filters: Vec::new(),
|
||||
query_enhancement_enabled: true,
|
||||
color_thresholds: ColorThresholds::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -252,7 +256,9 @@ fn generate_search_suggestions(query: &str) -> Vec<String> {
|
|||
];
|
||||
|
||||
if query.is_empty() {
|
||||
return base_suggestions;
|
||||
let mut suggestions = base_suggestions;
|
||||
suggestions.truncate(10);
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
// 基于查询过滤和排序建议
|
||||
|
|
@ -310,7 +316,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_search_request_creation() {
|
||||
use crate::data::models::outfit_search::{RelevanceThreshold, SearchConfig};
|
||||
use crate::data::models::outfit_search::{RelevanceThreshold, SearchConfig, ColorThresholds};
|
||||
|
||||
let request = SearchRequest {
|
||||
query: "牛仔裤搭配".to_string(),
|
||||
|
|
@ -321,6 +327,10 @@ mod tests {
|
|||
color_filters: std::collections::HashMap::new(),
|
||||
design_styles: std::collections::HashMap::new(),
|
||||
max_keywords: 10,
|
||||
debug_mode: false,
|
||||
custom_filters: Vec::new(),
|
||||
query_enhancement_enabled: true,
|
||||
color_thresholds: ColorThresholds::default(),
|
||||
},
|
||||
page_size: 9,
|
||||
page_offset: 0,
|
||||
|
|
@ -440,23 +450,39 @@ async fn execute_vertex_ai_search(
|
|||
.await
|
||||
.map_err(|e| anyhow::anyhow!("获取访问令牌失败: {}。请检查网络连接或API配置。", e))?;
|
||||
|
||||
// 2. 获取全局配置
|
||||
// 2. 验证搜索配置
|
||||
if let Err(validation_error) = SearchFilterBuilder::validate_config(&request.config) {
|
||||
eprintln!("搜索配置验证失败: {}", validation_error);
|
||||
return Err(anyhow::anyhow!("搜索配置无效: {}", validation_error));
|
||||
}
|
||||
|
||||
// 3. 获取全局配置
|
||||
let global_config = OutfitSearchGlobalConfig::default();
|
||||
|
||||
// 3. 构建搜索过滤器
|
||||
// 4. 记录搜索配置摘要
|
||||
if request.config.debug_mode {
|
||||
let config_summary = SearchFilterBuilder::generate_config_summary(&request.config);
|
||||
eprintln!("搜索配置摘要: {}", config_summary);
|
||||
}
|
||||
|
||||
// 5. 构建搜索过滤器 - 使用增强的过滤器构建器
|
||||
let search_filter = SearchFilterBuilder::build_filters(&request.config);
|
||||
|
||||
// 4. 构建查询关键词
|
||||
let query_keywords = SearchFilterBuilder::build_query_keywords(&request.config);
|
||||
|
||||
// 5. 组合查询字符串
|
||||
let enhanced_query = if query_keywords.is_empty() {
|
||||
request.query.clone()
|
||||
// 6. 构建增强查询字符串 - 参考 Python 实现
|
||||
let enhanced_query = if request.config.query_enhancement_enabled {
|
||||
SearchFilterBuilder::build_enhanced_query(&request.query, &request.config)
|
||||
} else {
|
||||
format!("{} {}", request.query, query_keywords.join(" "))
|
||||
request.query.clone()
|
||||
};
|
||||
|
||||
// 6. 构建请求负载
|
||||
// 调试输出
|
||||
if request.config.debug_mode {
|
||||
eprintln!("原始查询: {}", request.query);
|
||||
eprintln!("增强查询: {}", enhanced_query);
|
||||
eprintln!("过滤器: {}", search_filter);
|
||||
}
|
||||
|
||||
// 5. 构建请求负载
|
||||
let mut payload = serde_json::json!({
|
||||
"query": enhanced_query,
|
||||
"pageSize": request.page_size,
|
||||
|
|
@ -468,25 +494,44 @@ async fn execute_vertex_ai_search(
|
|||
"returnRelevanceScore": true
|
||||
});
|
||||
|
||||
// 7. 添加过滤器(如果有)
|
||||
// 6. 添加过滤器(如果有)
|
||||
let mut all_filters = Vec::new();
|
||||
|
||||
// 添加主要过滤器
|
||||
if !search_filter.is_empty() {
|
||||
payload["filter"] = serde_json::Value::String(search_filter);
|
||||
all_filters.push(search_filter);
|
||||
}
|
||||
|
||||
// 8. 构建请求URL
|
||||
// 添加自定义过滤器
|
||||
if !request.config.custom_filters.is_empty() {
|
||||
all_filters.extend(request.config.custom_filters.clone());
|
||||
}
|
||||
|
||||
// 组合所有过滤器
|
||||
if !all_filters.is_empty() {
|
||||
let combined_filter = all_filters.join(" AND ");
|
||||
|
||||
if request.config.debug_mode {
|
||||
eprintln!("最终过滤器: {}", combined_filter);
|
||||
}
|
||||
|
||||
payload["filter"] = serde_json::Value::String(combined_filter);
|
||||
}
|
||||
|
||||
// 7. 构建请求URL
|
||||
let search_url = format!(
|
||||
"https://discoveryengine.googleapis.com/v1beta/projects/{}/locations/global/collections/default_collection/engines/{}/servingConfigs/default_search:search",
|
||||
global_config.google_project_id,
|
||||
global_config.vertex_ai_app_id
|
||||
);
|
||||
|
||||
// 9. 创建带有超时配置的客户端
|
||||
// 8. 创建带有超时配置的客户端
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(std::time::Duration::from_secs(60))
|
||||
.connect_timeout(std::time::Duration::from_secs(15))
|
||||
.build()?;
|
||||
|
||||
// 10. 发送HTTP请求(带重试机制)
|
||||
// 9. 发送HTTP请求(带重试机制)
|
||||
let mut last_error = None;
|
||||
for attempt in 0..3 {
|
||||
match client
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use tauri::{command, State};
|
||||
use crate::app_state::AppState;
|
||||
use crate::data::models::outfit_search::{SearchRequest, SearchResponse, SearchConfig, RelevanceThreshold};
|
||||
use crate::data::models::outfit_search::{SearchRequest, SearchResponse, SearchConfig, RelevanceThreshold, ColorThresholds};
|
||||
use crate::presentation::commands::outfit_search_commands::search_similar_outfits;
|
||||
|
||||
/// 相似度检索工具命令
|
||||
|
|
@ -33,6 +33,10 @@ pub async fn quick_similarity_search(
|
|||
color_filters: std::collections::HashMap::new(),
|
||||
design_styles: std::collections::HashMap::new(),
|
||||
max_keywords: 10,
|
||||
debug_mode: false,
|
||||
custom_filters: Vec::new(),
|
||||
query_enhancement_enabled: true,
|
||||
color_thresholds: ColorThresholds::default(),
|
||||
};
|
||||
|
||||
let request = SearchRequest {
|
||||
|
|
|
|||
Loading…
Reference in New Issue