|
|
@@ -1,2521 +0,0 @@
|
|
|
-using System.ComponentModel;
|
|
|
-using System.Text.RegularExpressions;
|
|
|
-
|
|
|
-namespace OASystem.Infrastructure.Tools
|
|
|
-{
|
|
|
- /// <summary>
|
|
|
- /// 增强版高级搜索助手(支持智能拆分组合词)
|
|
|
- /// 支持组合查询:多词条/单词条 + 单字 混合搜索
|
|
|
- /// 提供智能搜索条件构建、相关性分析、分阶段搜索等功能
|
|
|
- /// </summary>
|
|
|
- public class AdvancedSearchHelper
|
|
|
- {
|
|
|
- #region 公共方法 - 主要搜索功能
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建增强搜索条件(支持组合查询和智能拆分)
|
|
|
- /// </summary>
|
|
|
- /// <param name="searchTerm">搜索关键词</param>
|
|
|
- /// <param name="searchFields">搜索字段列表</param>
|
|
|
- /// <param name="prefix">参数前缀,用于避免参数名冲突</param>
|
|
|
- /// <param name="searchMode">搜索模式,默认为自动检测</param>
|
|
|
- /// <param name="combinationMode">组合模式,默认智能混合</param>
|
|
|
- /// <param name="config">搜索配置选项</param>
|
|
|
- /// <returns>构建的SQL条件和参数</returns>
|
|
|
- /// <example>
|
|
|
- /// <code>
|
|
|
- /// var fields = new List<string> { "Name", "Description" };
|
|
|
- /// var config = new SearchConfig {
|
|
|
- /// EnableSmartSplitting = true, // 启用智能拆分
|
|
|
- /// RequireAllWordsForSplit = false
|
|
|
- /// };
|
|
|
- /// var (condition, parameters) = AdvancedSearchHelper.BuildEnhancedSearchCondition(
|
|
|
- /// "重庆科学",
|
|
|
- /// fields,
|
|
|
- /// "search_",
|
|
|
- /// SearchMode.AutoDetect,
|
|
|
- /// CombinationMode.SmartMix,
|
|
|
- /// config
|
|
|
- /// );
|
|
|
- /// </code>
|
|
|
- /// </example>
|
|
|
- public static (string Condition, List<SugarParameter> Parameters) BuildEnhancedSearchCondition(
|
|
|
- string searchTerm,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix = "",
|
|
|
- SearchMode searchMode = SearchMode.AutoDetect,
|
|
|
- CombinationMode combinationMode = CombinationMode.SmartMix,
|
|
|
- SearchConfig config = null)
|
|
|
- {
|
|
|
- // 参数验证
|
|
|
- if (searchFields == null || !searchFields.Any())
|
|
|
- throw new ArgumentException("搜索字段列表不能为空", nameof(searchFields));
|
|
|
-
|
|
|
- if (string.IsNullOrWhiteSpace(searchTerm))
|
|
|
- return (string.Empty, new List<SugarParameter>());
|
|
|
-
|
|
|
- searchTerm = searchTerm.Trim();
|
|
|
- config ??= new SearchConfig();
|
|
|
-
|
|
|
- // 检查搜索词长度
|
|
|
- if (searchTerm.Length > config.MaxSearchTermLength)
|
|
|
- searchTerm = searchTerm.Substring(0, config.MaxSearchTermLength);
|
|
|
-
|
|
|
- // 分析搜索词结构
|
|
|
- var structure = AnalyzeSearchStructure(searchTerm);
|
|
|
-
|
|
|
- // 根据组合模式构建条件
|
|
|
- return combinationMode switch
|
|
|
- {
|
|
|
- CombinationMode.SmartMix => BuildSmartMixCondition(structure, searchFields, prefix, searchMode, config),
|
|
|
- CombinationMode.WordsAndChars => BuildWordsAndCharsCondition(structure, searchFields, prefix, config),
|
|
|
- CombinationMode.WordsOnly => BuildWordsOnlyCondition(structure, searchFields, prefix, config),
|
|
|
- CombinationMode.CharsOnly => BuildCharsOnlyCondition(structure, searchFields, prefix, config),
|
|
|
- _ => BuildSmartMixCondition(structure, searchFields, prefix, searchMode, config)
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 为Sugar ORM构建增强搜索条件
|
|
|
- /// </summary>
|
|
|
- public static (string Condition, List<SugarParameter> Parameters) BuildSugarSearchCondition(
|
|
|
- string searchTerm,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix = "",
|
|
|
- SearchMode searchMode = SearchMode.AutoDetect,
|
|
|
- CombinationMode combinationMode = CombinationMode.SmartMix,
|
|
|
- SearchConfig config = null)
|
|
|
- {
|
|
|
- // 先使用原有方法构建条件
|
|
|
- var (condition, parameters) = BuildEnhancedSearchCondition(
|
|
|
- searchTerm, searchFields, prefix, searchMode, combinationMode, config);
|
|
|
-
|
|
|
- if (string.IsNullOrEmpty(condition))
|
|
|
- return (string.Empty, parameters);
|
|
|
-
|
|
|
- // 转换参数名为Sugar ORM兼容格式
|
|
|
- var sugarParameters = new List<SugarParameter>();
|
|
|
- var paramMapping = new Dictionary<string, string>();
|
|
|
- int paramIndex = 0;
|
|
|
-
|
|
|
- foreach (var param in parameters)
|
|
|
- {
|
|
|
- var oldName = param.ParameterName;
|
|
|
- var newName = $"@{prefix}p{paramIndex++}";
|
|
|
-
|
|
|
- paramMapping[oldName] = newName;
|
|
|
- sugarParameters.Add(new SugarParameter(newName, param.Value));
|
|
|
- }
|
|
|
-
|
|
|
- // 替换参数名
|
|
|
- foreach (var mapping in paramMapping)
|
|
|
- {
|
|
|
- condition = condition.Replace(mapping.Key, mapping.Value);
|
|
|
- }
|
|
|
-
|
|
|
- // 转换LIKE表达式为Sugar ORM格式
|
|
|
- condition = Regex.Replace(condition, @"LIKE @\w+", match =>
|
|
|
- {
|
|
|
- var paramName = match.Value.Substring(5); // 移除"LIKE "
|
|
|
- return $"LIKE '%' + {paramName} + '%'";
|
|
|
- });
|
|
|
-
|
|
|
- return (condition, sugarParameters);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建带相关性信息的搜索条件
|
|
|
- /// </summary>
|
|
|
- /// <param name="searchTerm">搜索关键词</param>
|
|
|
- /// <param name="searchFields">搜索字段列表</param>
|
|
|
- /// <param name="prefix">参数前缀</param>
|
|
|
- /// <param name="searchMode">搜索模式</param>
|
|
|
- /// <param name="config">搜索配置</param>
|
|
|
- /// <returns>包含条件、参数和相关性的完整结果</returns>
|
|
|
- public static (string Condition, List<SugarParameter> Parameters, RelevanceInfo Relevance)
|
|
|
- BuildRelevanceSearchCondition(
|
|
|
- string searchTerm,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix = "",
|
|
|
- SearchMode searchMode = SearchMode.AutoDetect,
|
|
|
- SearchConfig config = null)
|
|
|
- {
|
|
|
- config ??= new SearchConfig();
|
|
|
-
|
|
|
- var structure = AnalyzeSearchStructure(searchTerm);
|
|
|
- var (condition, parameters) = BuildEnhancedSearchCondition(
|
|
|
- searchTerm, searchFields, prefix, searchMode, CombinationMode.SmartMix, config);
|
|
|
-
|
|
|
- var relevance = new RelevanceInfo
|
|
|
- {
|
|
|
- SearchStructure = structure,
|
|
|
- ExpectedMatches = CalculateExpectedMatches(structure, searchFields.Count),
|
|
|
- BoostFields = GetBoostFields(structure, searchFields),
|
|
|
- ScoringRules = GetScoringRules(structure, config),
|
|
|
- MatchStrategy = GetMatchStrategy(structure, config),
|
|
|
- GeneratedAt = DateTime.Now
|
|
|
- };
|
|
|
-
|
|
|
- return (condition, parameters, relevance);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建分阶段搜索条件(用于分阶段匹配和降级搜索)
|
|
|
- /// </summary>
|
|
|
- /// <param name="searchTerm">搜索关键词</param>
|
|
|
- /// <param name="searchFields">搜索字段列表</param>
|
|
|
- /// <param name="prefix">参数前缀</param>
|
|
|
- /// <param name="config">搜索配置</param>
|
|
|
- /// <returns>按优先级排序的搜索阶段列表</returns>
|
|
|
- public static List<SearchStage> BuildStagedSearchConditions(
|
|
|
- string searchTerm,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix = "",
|
|
|
- SearchConfig config = null)
|
|
|
- {
|
|
|
- config ??= new SearchConfig();
|
|
|
- var structure = AnalyzeSearchStructure(searchTerm);
|
|
|
- var stages = new List<SearchStage>();
|
|
|
-
|
|
|
- // 阶段1:精确匹配(完全相等)
|
|
|
- if (structure.Words.Any() && config.EnableExactMatch)
|
|
|
- {
|
|
|
- foreach (var word in structure.Words.Where(w => w.Length >= config.MinExactMatchLength))
|
|
|
- {
|
|
|
- var (condition, parameters) = BuildExactCondition(word, searchFields, $"{prefix}exact_{stages.Count}");
|
|
|
- if (!string.IsNullOrEmpty(condition))
|
|
|
- {
|
|
|
- stages.Add(new SearchStage
|
|
|
- {
|
|
|
- Stage = 1,
|
|
|
- Name = $"精确匹配: {word}",
|
|
|
- Condition = condition,
|
|
|
- Parameters = parameters,
|
|
|
- Priority = 100,
|
|
|
- Description = $"完全匹配词语'{word}'",
|
|
|
- FallbackToNext = false
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 阶段2:开头匹配
|
|
|
- if (structure.Words.Any() && config.EnablePrefixMatch)
|
|
|
- {
|
|
|
- foreach (var word in structure.Words)
|
|
|
- {
|
|
|
- var (condition, parameters) = BuildPrefixCondition(word, searchFields, $"{prefix}prefix_{stages.Count}");
|
|
|
- if (!string.IsNullOrEmpty(condition))
|
|
|
- {
|
|
|
- stages.Add(new SearchStage
|
|
|
- {
|
|
|
- Stage = 2,
|
|
|
- Name = $"开头匹配: {word}",
|
|
|
- Condition = condition,
|
|
|
- Parameters = parameters,
|
|
|
- Priority = 80,
|
|
|
- Description = $"以'{word}'开头",
|
|
|
- FallbackToNext = true
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 阶段3:词语模糊匹配
|
|
|
- if (structure.Words.Any() && config.EnableFuzzySearch)
|
|
|
- {
|
|
|
- var wordsText = string.Join(" ", structure.Words);
|
|
|
- var (condition, parameters) = BuildMultiWordCondition(
|
|
|
- wordsText, searchFields, $"{prefix}fuzzy_{stages.Count}",
|
|
|
- config.RequireAllWords);
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(condition))
|
|
|
- {
|
|
|
- stages.Add(new SearchStage
|
|
|
- {
|
|
|
- Stage = 3,
|
|
|
- Name = "词语模糊匹配",
|
|
|
- Condition = condition,
|
|
|
- Parameters = parameters,
|
|
|
- Priority = 60,
|
|
|
- Description = $"包含词语: {wordsText}",
|
|
|
- FallbackToNext = true
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 阶段4:单字匹配
|
|
|
- if (structure.SingleChars.Any() && config.EnableCharSearch)
|
|
|
- {
|
|
|
- var charsText = string.Join("", structure.SingleChars);
|
|
|
- var (condition, parameters) = BuildSingleCharCondition(
|
|
|
- charsText, searchFields, $"{prefix}chars_{stages.Count}",
|
|
|
- config.RequireAllChars);
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(condition))
|
|
|
- {
|
|
|
- stages.Add(new SearchStage
|
|
|
- {
|
|
|
- Stage = 4,
|
|
|
- Name = "单字匹配",
|
|
|
- Condition = condition,
|
|
|
- Parameters = parameters,
|
|
|
- Priority = 40,
|
|
|
- Description = $"包含单字: {charsText}",
|
|
|
- FallbackToNext = true
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 阶段5:智能拆分匹配(新增阶段)
|
|
|
- if (config.EnableSmartSplitting && structure.Words.Any(w => w.Length >= config.MinSplitLength))
|
|
|
- {
|
|
|
- var longWords = structure.Words.Where(w => w.Length >= config.MinSplitLength).ToList();
|
|
|
- foreach (var longWord in longWords)
|
|
|
- {
|
|
|
- if (IsSplittableCombinationWord(longWord))
|
|
|
- {
|
|
|
- var (condition, parameters) = BuildSmartSplitConditionForWord(
|
|
|
- longWord, searchFields, $"{prefix}split_{stages.Count}", config);
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(condition))
|
|
|
- {
|
|
|
- stages.Add(new SearchStage
|
|
|
- {
|
|
|
- Stage = 5,
|
|
|
- Name = $"智能拆分: {longWord}",
|
|
|
- Condition = condition,
|
|
|
- Parameters = parameters,
|
|
|
- Priority = 50, // 优先级在词语模糊匹配和单字匹配之间
|
|
|
- Description = $"智能拆分'{longWord}'为子词或单字",
|
|
|
- FallbackToNext = true
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 阶段6:宽松混合匹配
|
|
|
- if (stages.Any(s => s.Stage >= 3 && s.Stage <= 5))
|
|
|
- {
|
|
|
- var mixedConditions = stages
|
|
|
- .Where(s => s.Stage >= 3 && s.Stage <= 5)
|
|
|
- .Select(s => s.Condition)
|
|
|
- .ToList();
|
|
|
-
|
|
|
- var mixedParameters = stages
|
|
|
- .Where(s => s.Stage >= 3 && s.Stage <= 5)
|
|
|
- .SelectMany(s => s.Parameters)
|
|
|
- .ToList();
|
|
|
-
|
|
|
- if (mixedConditions.Any())
|
|
|
- {
|
|
|
- stages.Add(new SearchStage
|
|
|
- {
|
|
|
- Stage = 6,
|
|
|
- Name = "宽松混合匹配",
|
|
|
- Condition = $"({string.Join(" OR ", mixedConditions)})",
|
|
|
- Parameters = mixedParameters,
|
|
|
- Priority = 20,
|
|
|
- Description = "词语或单字的任意匹配",
|
|
|
- FallbackToNext = false
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return stages.OrderByDescending(s => s.Priority).ToList();
|
|
|
- }
|
|
|
-
|
|
|
- #endregion
|
|
|
-
|
|
|
- #region 核心构建方法
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 智能混合构建条件(支持智能拆分组合词)
|
|
|
- /// </summary>
|
|
|
- private static (string, List<SugarParameter>) BuildSmartMixCondition(
|
|
|
- SearchStructure structure,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix,
|
|
|
- SearchMode searchMode,
|
|
|
- SearchConfig config)
|
|
|
- {
|
|
|
- var parameters = new List<SugarParameter>();
|
|
|
- var conditions = new List<string>();
|
|
|
-
|
|
|
- // 分析结构复杂度
|
|
|
- var complexity = CalculateComplexityScore(structure);
|
|
|
-
|
|
|
- // 检查是否有需要智能拆分的组合词
|
|
|
- var longWords = structure.Words.Where(w => w.Length >= config.MinSplitLength).ToList();
|
|
|
- var shouldSplitLongWords = longWords.Any() && config.EnableSmartSplitting;
|
|
|
-
|
|
|
- if (shouldSplitLongWords && longWords.Any(w => IsSplittableCombinationWord(w)))
|
|
|
- {
|
|
|
- // 使用智能拆分策略处理组合词
|
|
|
- return BuildSmartSplitCondition(structure, searchFields, prefix, config, longWords);
|
|
|
- }
|
|
|
-
|
|
|
- // 智能分析混合类型的特点
|
|
|
- var hasLongWords = structure.Words.Any(w => w.Length >= 2);
|
|
|
- var hasManyChars = structure.SingleChars.Count >= 3;
|
|
|
- var isCharDominant = structure.SingleChars.Count > structure.Words.Count;
|
|
|
- var hasMixedTypes = structure.HasWords && structure.HasSingleChars;
|
|
|
-
|
|
|
- // 根据混合特点选择更精细的策略
|
|
|
- string strategy;
|
|
|
- if (hasMixedTypes)
|
|
|
- {
|
|
|
- if (isCharDominant && hasManyChars)
|
|
|
- {
|
|
|
- strategy = "char_optimized"; // 单字主导,优化单字搜索
|
|
|
- }
|
|
|
- else if (hasLongWords && !hasManyChars)
|
|
|
- {
|
|
|
- strategy = "word_focused"; // 词语为主,单字为辅
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- strategy = complexity >= 6 ? "strict_mixed" :
|
|
|
- complexity >= 3 ? "balanced_mixed" : "loose_mixed";
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- strategy = complexity >= 7 ? "strict" :
|
|
|
- complexity >= 4 ? "balanced" : "loose";
|
|
|
- }
|
|
|
-
|
|
|
- // 处理词语部分
|
|
|
- if (structure.HasWords)
|
|
|
- {
|
|
|
- if (structure.Words.Count == 1)
|
|
|
- {
|
|
|
- // 单个词语
|
|
|
- var (wordCondition, wordParams) = BuildSingleWordCondition(
|
|
|
- structure.Words[0], searchFields, $"{prefix}word", config);
|
|
|
- if (!string.IsNullOrEmpty(wordCondition))
|
|
|
- {
|
|
|
- conditions.Add(wordCondition);
|
|
|
- parameters.AddRange(wordParams);
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- // 多个词语
|
|
|
- var wordsText = string.Join(" ", structure.Words);
|
|
|
-
|
|
|
- // 根据策略动态调整词语匹配要求
|
|
|
- bool requireAllWords;
|
|
|
- if (strategy == "char_optimized" && isCharDominant)
|
|
|
- {
|
|
|
- // 单字主导时,放宽词语要求
|
|
|
- requireAllWords = false;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- requireAllWords = strategy.Contains("strict") || config.RequireAllWords;
|
|
|
- }
|
|
|
-
|
|
|
- var (wordsCondition, wordsParams) = BuildMultiWordCondition(
|
|
|
- wordsText, searchFields, $"{prefix}words", requireAllWords);
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(wordsCondition))
|
|
|
- {
|
|
|
- conditions.Add(wordsCondition);
|
|
|
- parameters.AddRange(wordsParams);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 处理单字部分 - 优化策略
|
|
|
- if (structure.HasSingleChars)
|
|
|
- {
|
|
|
- var charsText = string.Join("", structure.SingleChars);
|
|
|
-
|
|
|
- // 根据策略动态调整单字匹配要求
|
|
|
- bool requireAllChars;
|
|
|
- string charStrategy;
|
|
|
-
|
|
|
- if (strategy == "char_optimized")
|
|
|
- {
|
|
|
- // 单字优化策略:多个单字时放宽要求
|
|
|
- requireAllChars = structure.SingleChars.Count <= 3; // 3个及以下单字要求全部匹配
|
|
|
- charStrategy = "optimized";
|
|
|
- }
|
|
|
- else if (strategy.Contains("loose"))
|
|
|
- {
|
|
|
- requireAllChars = false;
|
|
|
- charStrategy = "loose";
|
|
|
- }
|
|
|
- else if (strategy.Contains("strict"))
|
|
|
- {
|
|
|
- requireAllChars = true;
|
|
|
- charStrategy = "strict";
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- requireAllChars = strategy.Contains("balanced") && structure.SingleChars.Count <= 2;
|
|
|
- charStrategy = "balanced";
|
|
|
- }
|
|
|
-
|
|
|
- var (charsCondition, charsParams) = BuildEnhancedCharCondition(
|
|
|
- charsText, searchFields, $"{prefix}chars",
|
|
|
- requireAllChars, charStrategy);
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(charsCondition))
|
|
|
- {
|
|
|
- conditions.Add(charsCondition);
|
|
|
- parameters.AddRange(charsParams);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 构建最终条件
|
|
|
- if (!conditions.Any())
|
|
|
- return (string.Empty, parameters);
|
|
|
-
|
|
|
- string finalCondition = BuildMixedConditionByStrategy(
|
|
|
- conditions, structure, strategy);
|
|
|
-
|
|
|
- return (finalCondition, parameters);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建智能拆分条件(针对长词如"重庆科学")
|
|
|
- /// </summary>
|
|
|
- private static (string, List<SugarParameter>) BuildSmartSplitCondition(
|
|
|
- SearchStructure structure,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix,
|
|
|
- SearchConfig config,
|
|
|
- List<string> longWords)
|
|
|
- {
|
|
|
- var parameters = new List<SugarParameter>();
|
|
|
- var conditions = new List<string>();
|
|
|
-
|
|
|
- // 处理所有长词(需要拆分的词)
|
|
|
- foreach (var longWord in longWords)
|
|
|
- {
|
|
|
- if (IsSplittableCombinationWord(longWord))
|
|
|
- {
|
|
|
- // 为每个长词构建多层次条件
|
|
|
- var (wordConditions, wordParams) = BuildMultiLevelConditionForWord(
|
|
|
- longWord, searchFields, $"{prefix}word_{longWord.GetHashCode()}", config);
|
|
|
-
|
|
|
- conditions.AddRange(wordConditions);
|
|
|
- parameters.AddRange(wordParams);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- // 普通长词,直接搜索
|
|
|
- var (wordCondition, wordParams) = BuildSingleWordCondition(
|
|
|
- longWord, searchFields, $"{prefix}long_{parameters.Count}", config);
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(wordCondition))
|
|
|
- {
|
|
|
- conditions.Add(wordCondition);
|
|
|
- parameters.AddRange(wordParams);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 处理剩余的非长词
|
|
|
- var normalWords = structure.Words.Where(w => w.Length < config.MinSplitLength).ToList();
|
|
|
- if (normalWords.Any())
|
|
|
- {
|
|
|
- var wordsText = string.Join(" ", normalWords);
|
|
|
- var (normalCondition, normalParams) = BuildMultiWordCondition(
|
|
|
- wordsText, searchFields, $"{prefix}normal", config.RequireAllWords);
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(normalCondition))
|
|
|
- {
|
|
|
- conditions.Add(normalCondition);
|
|
|
- parameters.AddRange(normalParams);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 处理原有的单字
|
|
|
- if (structure.SingleChars.Any())
|
|
|
- {
|
|
|
- var charsText = string.Join("", structure.SingleChars);
|
|
|
- var (charCondition, charParams) = BuildSingleCharCondition(
|
|
|
- charsText, searchFields, $"{prefix}single", false);
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(charCondition))
|
|
|
- {
|
|
|
- conditions.Add(charCondition);
|
|
|
- parameters.AddRange(charParams);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 构建最终条件:使用层次化OR连接
|
|
|
- if (conditions.Count == 0)
|
|
|
- return (string.Empty, parameters);
|
|
|
-
|
|
|
- if (conditions.Count == 1)
|
|
|
- return (conditions[0], parameters);
|
|
|
-
|
|
|
- // 对条件进行分组和优先级排序
|
|
|
- var groupedConditions = GroupConditionsByPriority(conditions, longWords);
|
|
|
- return (BuildHierarchicalCondition(groupedConditions), parameters);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 为单个长词构建多层次条件
|
|
|
- /// </summary>
|
|
|
- private static (List<string> Conditions, List<SugarParameter> Parameters) BuildMultiLevelConditionForWord(
|
|
|
- string longWord,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix,
|
|
|
- SearchConfig config)
|
|
|
- {
|
|
|
- var conditions = new List<string>();
|
|
|
- var parameters = new List<SugarParameter>();
|
|
|
-
|
|
|
- // 第一层:完整词匹配(最高优先级)
|
|
|
- var (fullCondition, fullParams) = BuildSingleWordCondition(
|
|
|
- longWord, searchFields, $"{prefix}_full", config);
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(fullCondition))
|
|
|
- {
|
|
|
- conditions.Add(fullCondition);
|
|
|
- parameters.AddRange(fullParams);
|
|
|
- }
|
|
|
-
|
|
|
- // 第二层:智能拆分成子词
|
|
|
- var subWords = SplitCombinationWord(longWord);
|
|
|
- if (subWords.Count >= 2)
|
|
|
- {
|
|
|
- var (splitCondition, splitParams) = BuildMultiWordCondition(
|
|
|
- string.Join(" ", subWords),
|
|
|
- searchFields,
|
|
|
- $"{prefix}_split",
|
|
|
- config.RequireAllWordsForSplit);
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(splitCondition))
|
|
|
- {
|
|
|
- conditions.Add($"({splitCondition})"); // 用括号包裹
|
|
|
- parameters.AddRange(splitParams);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 第三层:拆分成单字作为备选
|
|
|
- var chars = longWord.ToCharArray().Select(c => c.ToString()).ToList();
|
|
|
- var (charCondition, charParams) = BuildSingleCharCondition(
|
|
|
- string.Join("", chars),
|
|
|
- searchFields,
|
|
|
- $"{prefix}_chars",
|
|
|
- config.RequireAllCharsForSplit);
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(charCondition))
|
|
|
- {
|
|
|
- conditions.Add($"({charCondition})");
|
|
|
- parameters.AddRange(charParams);
|
|
|
- }
|
|
|
-
|
|
|
- // 如果有多层条件,构建OR连接
|
|
|
- if (conditions.Count > 1)
|
|
|
- {
|
|
|
- var combinedCondition = $"({string.Join(" OR ", conditions)})";
|
|
|
- return (new List<string> { combinedCondition }, parameters);
|
|
|
- }
|
|
|
-
|
|
|
- return (conditions, parameters);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建智能拆分条件(用于分阶段搜索)
|
|
|
- /// </summary>
|
|
|
- private static (string, List<SugarParameter>) BuildSmartSplitConditionForWord(
|
|
|
- string longWord,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix,
|
|
|
- SearchConfig config)
|
|
|
- {
|
|
|
- var (conditions, parameters) = BuildMultiLevelConditionForWord(longWord, searchFields, prefix, config);
|
|
|
-
|
|
|
- if (conditions.Any())
|
|
|
- {
|
|
|
- return (conditions[0], parameters);
|
|
|
- }
|
|
|
-
|
|
|
- return (string.Empty, new List<SugarParameter>());
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 词语和单字组合条件
|
|
|
- /// </summary>
|
|
|
- private static (string, List<SugarParameter>) BuildWordsAndCharsCondition(
|
|
|
- SearchStructure structure,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix,
|
|
|
- SearchConfig config)
|
|
|
- {
|
|
|
- var parameters = new List<SugarParameter>();
|
|
|
- var conditions = new List<string>();
|
|
|
-
|
|
|
- // 词语部分(必须满足)
|
|
|
- if (structure.Words.Any())
|
|
|
- {
|
|
|
- var wordsText = string.Join(" ", structure.Words);
|
|
|
- var (wordsCondition, wordsParams) = BuildMultiWordCondition(
|
|
|
- wordsText, searchFields, $"{prefix}w", true);
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(wordsCondition))
|
|
|
- {
|
|
|
- conditions.Add(wordsCondition);
|
|
|
- parameters.AddRange(wordsParams);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 单字部分(作为增强条件)
|
|
|
- if (structure.SingleChars.Any())
|
|
|
- {
|
|
|
- var charsText = string.Join("", structure.SingleChars);
|
|
|
- var (charsCondition, charsParams) = BuildSingleCharCondition(
|
|
|
- charsText, searchFields, $"{prefix}c", false);
|
|
|
-
|
|
|
- if (!string.IsNullOrEmpty(charsCondition))
|
|
|
- {
|
|
|
- // 单字作为加分项,用OR连接
|
|
|
- conditions.Add($"({charsCondition})");
|
|
|
- parameters.AddRange(charsParams);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!conditions.Any())
|
|
|
- return (string.Empty, parameters);
|
|
|
-
|
|
|
- // 构建条件:词语必须满足,单字作为增强
|
|
|
- string finalCondition = conditions.Count > 1
|
|
|
- ? $"({conditions[0]} AND ({string.Join(" OR ", conditions.Skip(1))}))"
|
|
|
- : conditions[0];
|
|
|
-
|
|
|
- return (finalCondition, parameters);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 仅词语条件
|
|
|
- /// </summary>
|
|
|
- private static (string, List<SugarParameter>) BuildWordsOnlyCondition(
|
|
|
- SearchStructure structure,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix,
|
|
|
- SearchConfig config)
|
|
|
- {
|
|
|
- if (!structure.Words.Any())
|
|
|
- return (string.Empty, new List<SugarParameter>());
|
|
|
-
|
|
|
- var wordsText = string.Join(" ", structure.Words);
|
|
|
-
|
|
|
- if (structure.Words.Count == 1)
|
|
|
- {
|
|
|
- return BuildSingleWordCondition(wordsText, searchFields, prefix, config);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- return BuildMultiWordCondition(wordsText, searchFields, prefix, config.RequireAllWords);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 仅单字条件
|
|
|
- /// </summary>
|
|
|
- private static (string, List<SugarParameter>) BuildCharsOnlyCondition(
|
|
|
- SearchStructure structure,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix,
|
|
|
- SearchConfig config)
|
|
|
- {
|
|
|
- if (!structure.SingleChars.Any())
|
|
|
- return (string.Empty, new List<SugarParameter>());
|
|
|
-
|
|
|
- var charsText = string.Join("", structure.SingleChars);
|
|
|
- return BuildSingleCharCondition(charsText, searchFields, prefix, config.RequireAllChars);
|
|
|
- }
|
|
|
-
|
|
|
- #endregion
|
|
|
-
|
|
|
- #region 基础条件构建方法
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建单词条查询条件
|
|
|
- /// </summary>
|
|
|
- public static (string Condition, List<SugarParameter> Parameters) BuildSingleWordCondition(
|
|
|
- string searchTerm,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix,
|
|
|
- SearchConfig config = null)
|
|
|
- {
|
|
|
- config ??= new SearchConfig();
|
|
|
- var parameters = new List<SugarParameter>();
|
|
|
- var fieldConditions = new List<string>();
|
|
|
- var paramName = $"{prefix}word";
|
|
|
-
|
|
|
- foreach (var field in searchFields)
|
|
|
- {
|
|
|
- fieldConditions.Add($"{field} LIKE @{paramName}");
|
|
|
- }
|
|
|
-
|
|
|
- string condition = $"({string.Join(" OR ", fieldConditions)})";
|
|
|
- parameters.Add(new SugarParameter($"@{paramName}", $"%{searchTerm}%"));
|
|
|
-
|
|
|
- return (condition, parameters);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建多词条查询条件
|
|
|
- /// </summary>
|
|
|
- public static (string Condition, List<SugarParameter> Parameters) BuildMultiWordCondition(
|
|
|
- string searchTerm,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix,
|
|
|
- bool requireAll = true)
|
|
|
- {
|
|
|
- var parameters = new List<SugarParameter>();
|
|
|
- var separators = new[] { ' ', ',', '、', ',', ';', ';' };
|
|
|
- var words = searchTerm.Split(separators, StringSplitOptions.RemoveEmptyEntries)
|
|
|
- .Select(w => w.Trim())
|
|
|
- .Where(w => !string.IsNullOrEmpty(w))
|
|
|
- .Distinct()
|
|
|
- .ToList();
|
|
|
-
|
|
|
- if (!words.Any())
|
|
|
- return (string.Empty, parameters);
|
|
|
-
|
|
|
- var wordConditions = new List<string>();
|
|
|
-
|
|
|
- foreach (var word in words)
|
|
|
- {
|
|
|
- var fieldConditions = new List<string>();
|
|
|
- var paramName = $"{prefix}word_{parameters.Count}";
|
|
|
-
|
|
|
- foreach (var field in searchFields)
|
|
|
- {
|
|
|
- fieldConditions.Add($"{field} LIKE @{paramName}");
|
|
|
- }
|
|
|
-
|
|
|
- wordConditions.Add($"({string.Join(" OR ", fieldConditions)})");
|
|
|
- parameters.Add(new SugarParameter($"@{paramName}", $"%{word}%"));
|
|
|
- }
|
|
|
-
|
|
|
- // 构建条件
|
|
|
- string condition;
|
|
|
- if (requireAll && words.Count > 1)
|
|
|
- {
|
|
|
- condition = $"({string.Join(" AND ", wordConditions)})";
|
|
|
- }
|
|
|
- else if (!requireAll && words.Count > 1)
|
|
|
- {
|
|
|
- condition = $"({string.Join(" OR ", wordConditions)})";
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- condition = wordConditions.FirstOrDefault() ?? "";
|
|
|
- }
|
|
|
-
|
|
|
- return (condition, parameters);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建单字查询条件
|
|
|
- /// </summary>
|
|
|
- public static (string Condition, List<SugarParameter> Parameters) BuildSingleCharCondition(
|
|
|
- string searchTerm,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix,
|
|
|
- bool requireAll = true)
|
|
|
- {
|
|
|
- var parameters = new List<SugarParameter>();
|
|
|
- var cleanTerm = RemoveSeparators(searchTerm);
|
|
|
- var chars = cleanTerm.ToCharArray().Distinct().ToArray();
|
|
|
-
|
|
|
- if (chars.Length == 0)
|
|
|
- return (string.Empty, parameters);
|
|
|
-
|
|
|
- var charConditions = new List<string>();
|
|
|
-
|
|
|
- foreach (char c in chars)
|
|
|
- {
|
|
|
- var fieldConditions = new List<string>();
|
|
|
- var paramName = $"{prefix}char_{parameters.Count}";
|
|
|
-
|
|
|
- foreach (var field in searchFields)
|
|
|
- {
|
|
|
- fieldConditions.Add($"{field} LIKE @{paramName}");
|
|
|
- }
|
|
|
-
|
|
|
- charConditions.Add($"({string.Join(" OR ", fieldConditions)})");
|
|
|
- parameters.Add(new SugarParameter($"@{paramName}", $"%{c}%"));
|
|
|
- }
|
|
|
-
|
|
|
- // 构建条件
|
|
|
- string condition;
|
|
|
- if (requireAll && chars.Length > 1)
|
|
|
- {
|
|
|
- condition = $"({string.Join(" AND ", charConditions)})";
|
|
|
- }
|
|
|
- else if (!requireAll && chars.Length > 1)
|
|
|
- {
|
|
|
- condition = $"({string.Join(" OR ", charConditions)})";
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- condition = charConditions.FirstOrDefault() ?? "";
|
|
|
- }
|
|
|
-
|
|
|
- return (condition, parameters);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建增强的单字搜索条件(支持不同策略)
|
|
|
- /// </summary>
|
|
|
- private static (string, List<SugarParameter>) BuildEnhancedCharCondition(
|
|
|
- string searchTerm,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix,
|
|
|
- bool requireAll,
|
|
|
- string strategy)
|
|
|
- {
|
|
|
- var parameters = new List<SugarParameter>();
|
|
|
- var cleanTerm = RemoveSeparators(searchTerm);
|
|
|
- var chars = cleanTerm.ToCharArray().Distinct().ToArray();
|
|
|
-
|
|
|
- if (chars.Length == 0)
|
|
|
- return (string.Empty, parameters);
|
|
|
-
|
|
|
- // 根据策略调整处理方式
|
|
|
- var charConditions = new List<string>();
|
|
|
-
|
|
|
- foreach (char c in chars)
|
|
|
- {
|
|
|
- var fieldConditions = new List<string>();
|
|
|
- var paramName = $"{prefix}char_{parameters.Count}";
|
|
|
-
|
|
|
- foreach (var field in searchFields)
|
|
|
- {
|
|
|
- // 根据不同策略使用不同的匹配方式
|
|
|
- if (strategy == "optimized" && IsChineseCharacter(c))
|
|
|
- {
|
|
|
- // 优化策略:中文单字更宽松
|
|
|
- fieldConditions.Add($"({field} LIKE @{paramName} OR {field} LIKE @{paramName}_prefix)");
|
|
|
- parameters.Add(new SugarParameter($"@{paramName}", $"%{c}%"));
|
|
|
- parameters.Add(new SugarParameter($"@{paramName}_prefix", $"{c}%"));
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- fieldConditions.Add($"{field} LIKE @{paramName}");
|
|
|
- parameters.Add(new SugarParameter($"@{paramName}", $"%{c}%"));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- charConditions.Add($"({string.Join(" OR ", fieldConditions)})");
|
|
|
- }
|
|
|
-
|
|
|
- // 根据策略构建条件
|
|
|
- string condition;
|
|
|
- if (requireAll && chars.Length > 1)
|
|
|
- {
|
|
|
- // 必须包含所有单字
|
|
|
- condition = $"({string.Join(" AND ", charConditions)})";
|
|
|
- }
|
|
|
- else if (!requireAll && chars.Length > 1)
|
|
|
- {
|
|
|
- // 任意一个单字即可
|
|
|
- condition = $"({string.Join(" OR ", charConditions)})";
|
|
|
- }
|
|
|
- else if (strategy == "optimized" && chars.Length > 2)
|
|
|
- {
|
|
|
- // 优化策略:3个以上单字时,至少匹配一半
|
|
|
- int requiredCount = (int)Math.Ceiling(chars.Length / 2.0);
|
|
|
- var orConditions = new List<string>();
|
|
|
-
|
|
|
- // 生成所有可能的组合(限制数量避免爆炸)
|
|
|
- var combinations = GetCharCombinations(charConditions, chars.Length, requiredCount, 5);
|
|
|
-
|
|
|
- foreach (var combination in combinations)
|
|
|
- {
|
|
|
- orConditions.Add($"({string.Join(" AND ", combination)})");
|
|
|
- }
|
|
|
-
|
|
|
- condition = orConditions.Any() ? $"({string.Join(" OR ", orConditions)})" : charConditions.FirstOrDefault() ?? "";
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- condition = charConditions.FirstOrDefault() ?? "";
|
|
|
- }
|
|
|
-
|
|
|
- return (condition, parameters);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建精确匹配条件
|
|
|
- /// </summary>
|
|
|
- public static (string Condition, List<SugarParameter> Parameters) BuildExactCondition(
|
|
|
- string searchTerm,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix)
|
|
|
- {
|
|
|
- var parameters = new List<SugarParameter>();
|
|
|
- var fieldConditions = new List<string>();
|
|
|
- var paramName = $"{prefix}exact";
|
|
|
-
|
|
|
- foreach (var field in searchFields)
|
|
|
- {
|
|
|
- fieldConditions.Add($"{field} = @{paramName}");
|
|
|
- }
|
|
|
-
|
|
|
- string condition = $"({string.Join(" OR ", fieldConditions)})";
|
|
|
- parameters.Add(new SugarParameter($"@{paramName}", searchTerm.Trim()));
|
|
|
-
|
|
|
- return (condition, parameters);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建开头匹配条件
|
|
|
- /// </summary>
|
|
|
- public static (string Condition, List<SugarParameter> Parameters) BuildPrefixCondition(
|
|
|
- string searchTerm,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix)
|
|
|
- {
|
|
|
- var parameters = new List<SugarParameter>();
|
|
|
- var fieldConditions = new List<string>();
|
|
|
- var paramName = $"{prefix}prefix";
|
|
|
-
|
|
|
- foreach (var field in searchFields)
|
|
|
- {
|
|
|
- fieldConditions.Add($"{field} LIKE @{paramName}");
|
|
|
- }
|
|
|
-
|
|
|
- string condition = $"({string.Join(" OR ", fieldConditions)})";
|
|
|
- parameters.Add(new SugarParameter($"@{paramName}", $"{searchTerm}%"));
|
|
|
-
|
|
|
- return (condition, parameters);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建结尾匹配条件
|
|
|
- /// </summary>
|
|
|
- public static (string Condition, List<SugarParameter> Parameters) BuildSuffixCondition(
|
|
|
- string searchTerm,
|
|
|
- List<string> searchFields,
|
|
|
- string prefix)
|
|
|
- {
|
|
|
- var parameters = new List<SugarParameter>();
|
|
|
- var fieldConditions = new List<string>();
|
|
|
- var paramName = $"{prefix}suffix";
|
|
|
-
|
|
|
- foreach (var field in searchFields)
|
|
|
- {
|
|
|
- fieldConditions.Add($"{field} LIKE @{paramName}");
|
|
|
- }
|
|
|
-
|
|
|
- string condition = $"({string.Join(" OR ", fieldConditions)})";
|
|
|
- parameters.Add(new SugarParameter($"@{paramName}", $"%{searchTerm}"));
|
|
|
-
|
|
|
- return (condition, parameters);
|
|
|
- }
|
|
|
-
|
|
|
- #endregion
|
|
|
-
|
|
|
- #region 智能拆分方法
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 判断是否为可拆分的组合词
|
|
|
- /// </summary>
|
|
|
- private static bool IsSplittableCombinationWord(string word)
|
|
|
- {
|
|
|
- if (word.Length < 4)
|
|
|
- return false;
|
|
|
-
|
|
|
- // 检查是否由常见的地名+名词等组成
|
|
|
- var commonPatterns = new HashSet<string>
|
|
|
- {
|
|
|
- "重庆", "北京", "上海", "广州", "深圳", "天津", "武汉", "成都", "杭州", "南京",
|
|
|
- "科学", "技术", "研究", "发展", "教育", "培训", "管理", "系统", "工程", "设计",
|
|
|
- "大学", "学院", "学校", "公司", "集团", "中心", "研究所", "实验室", "医院", "银行"
|
|
|
- };
|
|
|
-
|
|
|
- // 尝试找到明显的组合边界
|
|
|
- for (int i = 2; i <= word.Length - 2; i++)
|
|
|
- {
|
|
|
- var part1 = word.Substring(0, i);
|
|
|
- var part2 = word.Substring(i);
|
|
|
-
|
|
|
- if (commonPatterns.Contains(part1) || commonPatterns.Contains(part2))
|
|
|
- return true;
|
|
|
-
|
|
|
- // 检查是否都是常见词汇
|
|
|
- if (IsCommonWord(part1) && IsCommonWord(part2))
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 拆分组合词
|
|
|
- /// </summary>
|
|
|
- private static List<string> SplitCombinationWord(string word)
|
|
|
- {
|
|
|
- var result = new List<string>();
|
|
|
-
|
|
|
- if (word.Length <= 2)
|
|
|
- {
|
|
|
- result.Add(word);
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- // 优先按2+2模式拆分(如"重庆科学" -> "重庆", "科学")
|
|
|
- if (word.Length == 4)
|
|
|
- {
|
|
|
- result.Add(word.Substring(0, 2));
|
|
|
- result.Add(word.Substring(2, 2));
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- // 尝试按常见模式拆分
|
|
|
- var commonTwoCharWords = new HashSet<string>
|
|
|
- {
|
|
|
- "重庆", "北京", "上海", "广州", "深圳", "天津", "武汉", "成都", "杭州", "南京",
|
|
|
- "科学", "技术", "研究", "发展", "教育", "培训", "管理", "系统", "工程", "设计",
|
|
|
- "大学", "学院", "学校", "公司", "集团", "中心", "医院", "银行", "保险", "证券",
|
|
|
- "软件", "硬件", "网络", "数据", "信息", "智能", "数字", "电子", "通信", "互联网"
|
|
|
- };
|
|
|
-
|
|
|
- // 检查前缀
|
|
|
- for (int i = 2; i <= Math.Min(4, word.Length - 2); i++)
|
|
|
- {
|
|
|
- var prefix = word.Substring(0, i);
|
|
|
- var suffix = word.Substring(i);
|
|
|
-
|
|
|
- if (commonTwoCharWords.Contains(prefix) ||
|
|
|
- (i == 2 && IsLikelyWord(prefix)))
|
|
|
- {
|
|
|
- result.Add(prefix);
|
|
|
-
|
|
|
- // 递归拆分剩余部分
|
|
|
- if (suffix.Length >= 2)
|
|
|
- {
|
|
|
- var subSplits = SplitCombinationWord(suffix);
|
|
|
- result.AddRange(subSplits);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- result.Add(suffix);
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 无法智能拆分,按固定长度拆分
|
|
|
- int splitLength = word.Length >= 6 ? 3 : 2;
|
|
|
- for (int i = 0; i < word.Length; i += splitLength)
|
|
|
- {
|
|
|
- var part = word.Substring(i, Math.Min(splitLength, word.Length - i));
|
|
|
- result.Add(part);
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 检查是否可能是常见词汇
|
|
|
- /// </summary>
|
|
|
- private static bool IsLikelyWord(string word)
|
|
|
- {
|
|
|
- if (word.Length != 2) return false;
|
|
|
-
|
|
|
- // 检查是否为常见的中文二字词
|
|
|
- // 这里可以扩展更复杂的判断逻辑
|
|
|
- return IsChineseCharacter(word[0]) && IsChineseCharacter(word[1]);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 检查是否是常见词汇
|
|
|
- /// </summary>
|
|
|
- private static bool IsCommonWord(string word)
|
|
|
- {
|
|
|
- // 这里可以扩展更复杂的常见词判断逻辑
|
|
|
- // 例如从词典加载、使用统计等
|
|
|
- var commonWords = new HashSet<string>
|
|
|
- {
|
|
|
- "中国", "国家", "人民", "社会", "经济", "文化", "教育", "科技", "发展",
|
|
|
- "企业", "公司", "市场", "产品", "服务", "技术", "管理", "工作", "生活"
|
|
|
- };
|
|
|
-
|
|
|
- return commonWords.Contains(word) ||
|
|
|
- (word.Length == 2 && IsChineseCharacter(word[0]) && IsChineseCharacter(word[1]));
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 按优先级分组条件
|
|
|
- /// </summary>
|
|
|
- private static Dictionary<string, List<string>> GroupConditionsByPriority(
|
|
|
- List<string> conditions, List<string> longWords)
|
|
|
- {
|
|
|
- var groups = new Dictionary<string, List<string>>
|
|
|
- {
|
|
|
- ["original"] = new List<string>(), // 原词条件
|
|
|
- ["split"] = new List<string>(), // 拆分子词条件
|
|
|
- ["chars"] = new List<string>(), // 单字条件
|
|
|
- ["normal"] = new List<string>() // 普通词条件
|
|
|
- };
|
|
|
-
|
|
|
- // 简单分组逻辑,实际使用中可能需要更复杂的判断
|
|
|
- int longWordCount = longWords.Count;
|
|
|
- int currentIndex = 0;
|
|
|
-
|
|
|
- // 原词条件(每个长词一个)
|
|
|
- for (int i = 0; i < longWordCount && currentIndex < conditions.Count; i++)
|
|
|
- {
|
|
|
- groups["original"].Add(conditions[currentIndex]);
|
|
|
- currentIndex++;
|
|
|
- }
|
|
|
-
|
|
|
- // 拆分子词条件
|
|
|
- for (int i = 0; i < longWordCount && currentIndex < conditions.Count; i++)
|
|
|
- {
|
|
|
- groups["split"].Add(conditions[currentIndex]);
|
|
|
- currentIndex++;
|
|
|
- }
|
|
|
-
|
|
|
- // 单字条件
|
|
|
- for (int i = 0; i < longWordCount && currentIndex < conditions.Count; i++)
|
|
|
- {
|
|
|
- groups["chars"].Add(conditions[currentIndex]);
|
|
|
- currentIndex++;
|
|
|
- }
|
|
|
-
|
|
|
- // 剩余的都是普通词条件
|
|
|
- while (currentIndex < conditions.Count)
|
|
|
- {
|
|
|
- groups["normal"].Add(conditions[currentIndex]);
|
|
|
- currentIndex++;
|
|
|
- }
|
|
|
-
|
|
|
- return groups;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建层次化条件
|
|
|
- /// </summary>
|
|
|
- private static string BuildHierarchicalCondition(Dictionary<string, List<string>> groups)
|
|
|
- {
|
|
|
- var parts = new List<string>();
|
|
|
-
|
|
|
- // 第一层:原词匹配(最高优先级)
|
|
|
- if (groups["original"].Any())
|
|
|
- {
|
|
|
- parts.Add($"({string.Join(" OR ", groups["original"])})");
|
|
|
- }
|
|
|
-
|
|
|
- // 第二层:拆分子词匹配
|
|
|
- if (groups["split"].Any())
|
|
|
- {
|
|
|
- var splitCondition = groups["split"].Count == 1
|
|
|
- ? groups["split"][0]
|
|
|
- : $"({string.Join(" AND ", groups["split"])})";
|
|
|
- parts.Add($"({splitCondition})");
|
|
|
- }
|
|
|
-
|
|
|
- // 普通词条件(插入到合适位置)
|
|
|
- if (groups["normal"].Any())
|
|
|
- {
|
|
|
- var normalCondition = groups["normal"].Count == 1
|
|
|
- ? groups["normal"][0]
|
|
|
- : $"({string.Join(" AND ", groups["normal"])})";
|
|
|
-
|
|
|
- // 如果已经有原词条件,放在第二层;否则放在第一层
|
|
|
- if (parts.Count >= 1)
|
|
|
- parts.Insert(1, normalCondition);
|
|
|
- else
|
|
|
- parts.Add(normalCondition);
|
|
|
- }
|
|
|
-
|
|
|
- // 第三层:单字匹配(最低优先级)
|
|
|
- if (groups["chars"].Any())
|
|
|
- {
|
|
|
- var charCondition = groups["chars"].Count == 1
|
|
|
- ? groups["chars"][0]
|
|
|
- : $"({string.Join(" OR ", groups["chars"])})";
|
|
|
- parts.Add($"({charCondition})");
|
|
|
- }
|
|
|
-
|
|
|
- if (parts.Count == 0)
|
|
|
- return string.Empty;
|
|
|
-
|
|
|
- if (parts.Count == 1)
|
|
|
- return parts[0];
|
|
|
-
|
|
|
- // 构建层次化OR条件
|
|
|
- return $"({string.Join(" OR ", parts)})";
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 根据策略构建混合条件
|
|
|
- /// </summary>
|
|
|
- private static string BuildMixedConditionByStrategy(
|
|
|
- List<string> conditions,
|
|
|
- SearchStructure structure,
|
|
|
- string strategy)
|
|
|
- {
|
|
|
- if (conditions.Count == 1)
|
|
|
- return conditions[0];
|
|
|
-
|
|
|
- switch (strategy)
|
|
|
- {
|
|
|
- case "char_optimized":
|
|
|
- // 单字优化策略:词语作为主要条件,单字作为增强
|
|
|
- return BuildCharOptimizedCondition(conditions, structure);
|
|
|
-
|
|
|
- case "word_focused":
|
|
|
- // 词语聚焦策略:词语必须,单字作为加分
|
|
|
- return BuildWordFocusedCondition(conditions, structure);
|
|
|
-
|
|
|
- case "strict_mixed":
|
|
|
- // 严格混合:所有条件必须满足
|
|
|
- return BuildStrictCondition(conditions, structure);
|
|
|
-
|
|
|
- case "balanced_mixed":
|
|
|
- // 平衡混合:词语必须,单字推荐
|
|
|
- return BuildBalancedCondition(conditions, structure);
|
|
|
-
|
|
|
- case "loose_mixed":
|
|
|
- // 宽松混合:任意条件满足即可
|
|
|
- return BuildLooseCondition(conditions, structure);
|
|
|
-
|
|
|
- default:
|
|
|
- return BuildBalancedCondition(conditions, structure);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建单字优化条件
|
|
|
- /// </summary>
|
|
|
- private static string BuildCharOptimizedCondition(List<string> conditions, SearchStructure structure)
|
|
|
- {
|
|
|
- var wordConditions = conditions
|
|
|
- .Where((c, i) => i < structure.Words.Count)
|
|
|
- .ToList();
|
|
|
-
|
|
|
- var charConditions = conditions
|
|
|
- .Skip(structure.Words.Count)
|
|
|
- .ToList();
|
|
|
-
|
|
|
- if (wordConditions.Any() && charConditions.Any())
|
|
|
- {
|
|
|
- // 词语必须,单字作为重要补充(至少匹配一定比例)
|
|
|
- if (charConditions.Count > 2)
|
|
|
- {
|
|
|
- // 多个单字时,要求至少匹配一半
|
|
|
- int requiredChars = (int)Math.Ceiling(charConditions.Count / 2.0);
|
|
|
- var charCombinations = GetConditionCombinations(charConditions, requiredChars, 3);
|
|
|
-
|
|
|
- if (charCombinations.Any())
|
|
|
- {
|
|
|
- var charCombinationStrings = charCombinations
|
|
|
- .Select(comb => $"({string.Join(" AND ", comb)})")
|
|
|
- .ToList();
|
|
|
-
|
|
|
- return $"({string.Join(" AND ", wordConditions)} AND ({string.Join(" OR ", charCombinationStrings)}))";
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return $"({string.Join(" AND ", wordConditions)} AND ({string.Join(" OR ", charConditions)}))";
|
|
|
- }
|
|
|
- else if (charConditions.Any())
|
|
|
- {
|
|
|
- // 只有单字时,至少匹配一半
|
|
|
- if (charConditions.Count > 1)
|
|
|
- {
|
|
|
- int requiredChars = (int)Math.Ceiling(charConditions.Count / 2.0);
|
|
|
- var combinations = GetConditionCombinations(charConditions, requiredChars, 3);
|
|
|
-
|
|
|
- if (combinations.Any())
|
|
|
- {
|
|
|
- var combinationStrings = combinations
|
|
|
- .Select(comb => $"({string.Join(" AND ", comb)})")
|
|
|
- .ToList();
|
|
|
-
|
|
|
- return $"({string.Join(" OR ", combinationStrings)})";
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return $"({string.Join(" OR ", charConditions)})";
|
|
|
- }
|
|
|
-
|
|
|
- return conditions.FirstOrDefault() ?? "";
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建词语聚焦条件
|
|
|
- /// </summary>
|
|
|
- private static string BuildWordFocusedCondition(List<string> conditions, SearchStructure structure)
|
|
|
- {
|
|
|
- var wordConditions = conditions
|
|
|
- .Where((c, i) => i < structure.Words.Count)
|
|
|
- .ToList();
|
|
|
-
|
|
|
- var charConditions = conditions
|
|
|
- .Skip(structure.Words.Count)
|
|
|
- .ToList();
|
|
|
-
|
|
|
- if (wordConditions.Any() && charConditions.Any())
|
|
|
- {
|
|
|
- // 词语必须满足,单字作为加分项(OR连接)
|
|
|
- return $"({string.Join(" AND ", wordConditions)} AND ({string.Join(" OR ", charConditions)}))";
|
|
|
- }
|
|
|
-
|
|
|
- return $"({string.Join(" AND ", conditions)})";
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 获取条件组合(限制数量避免爆炸)
|
|
|
- /// </summary>
|
|
|
- private static List<List<string>> GetConditionCombinations(
|
|
|
- List<string> conditions,
|
|
|
- int requiredCount,
|
|
|
- int maxCombinations = 10)
|
|
|
- {
|
|
|
- var result = new List<List<string>>();
|
|
|
-
|
|
|
- if (conditions.Count <= requiredCount || conditions.Count <= 1)
|
|
|
- {
|
|
|
- result.Add(conditions);
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- // 生成C(n, requiredCount)组合,但限制数量
|
|
|
- var indices = Enumerable.Range(0, conditions.Count).ToArray();
|
|
|
- var combinations = GetCombinations(indices, requiredCount, maxCombinations);
|
|
|
-
|
|
|
- foreach (var combination in combinations)
|
|
|
- {
|
|
|
- var selectedConditions = combination
|
|
|
- .Select(idx => conditions[idx])
|
|
|
- .ToList();
|
|
|
-
|
|
|
- result.Add(selectedConditions);
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 获取字符条件组合
|
|
|
- /// </summary>
|
|
|
- private static List<List<string>> GetCharCombinations(
|
|
|
- List<string> charConditions,
|
|
|
- int totalChars,
|
|
|
- int requiredCount,
|
|
|
- int maxCombinations)
|
|
|
- {
|
|
|
- return GetConditionCombinations(charConditions, requiredCount, maxCombinations);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 获取索引组合(算法)
|
|
|
- /// </summary>
|
|
|
- private static List<List<int>> GetCombinations(int[] indices, int k, int maxCombinations)
|
|
|
- {
|
|
|
- var result = new List<List<int>>();
|
|
|
- var combination = new List<int>();
|
|
|
-
|
|
|
- void Backtrack(int start)
|
|
|
- {
|
|
|
- if (result.Count >= maxCombinations)
|
|
|
- return;
|
|
|
-
|
|
|
- if (combination.Count == k)
|
|
|
- {
|
|
|
- result.Add(new List<int>(combination));
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- for (int i = start; i < indices.Length; i++)
|
|
|
- {
|
|
|
- combination.Add(indices[i]);
|
|
|
- Backtrack(i + 1);
|
|
|
- combination.RemoveAt(combination.Count - 1);
|
|
|
-
|
|
|
- if (result.Count >= maxCombinations)
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- Backtrack(0);
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- #endregion
|
|
|
-
|
|
|
- #region 分析工具方法
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 分析搜索词结构
|
|
|
- /// </summary>
|
|
|
- public static SearchStructure AnalyzeSearchStructure(string searchTerm)
|
|
|
- {
|
|
|
- var structure = new SearchStructure
|
|
|
- {
|
|
|
- OriginalTerm = searchTerm,
|
|
|
- CleanTerm = searchTerm.Trim(),
|
|
|
- AnalyzedAt = DateTime.Now
|
|
|
- };
|
|
|
-
|
|
|
- // 移除分隔符获取所有字符
|
|
|
- var cleanTerm = RemoveSeparators(searchTerm);
|
|
|
- structure.AllCharacters = cleanTerm.ToCharArray().ToList();
|
|
|
-
|
|
|
- // 分割成词条(保持原始分割)
|
|
|
- var separators = new[] { ' ', ',', '、', ',', ';', ';', '|', '/', '\\' };
|
|
|
- var rawSegments = searchTerm.Split(separators, StringSplitOptions.RemoveEmptyEntries)
|
|
|
- .Select(s => s.Trim())
|
|
|
- .Where(s => !string.IsNullOrEmpty(s))
|
|
|
- .ToList();
|
|
|
-
|
|
|
- structure.Segments = rawSegments;
|
|
|
-
|
|
|
- // 分析每个段落的类型
|
|
|
- foreach (var segment in rawSegments)
|
|
|
- {
|
|
|
- if (segment.Length == 1 && IsChineseCharacter(segment[0]))
|
|
|
- {
|
|
|
- structure.SingleChars.Add(segment);
|
|
|
- structure.HasSingleChars = true;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- structure.Words.Add(segment);
|
|
|
- structure.HasWords = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 设置组合类型
|
|
|
- if (structure.HasWords && structure.HasSingleChars)
|
|
|
- {
|
|
|
- structure.CombinationType = CombinationType.WordsAndChars;
|
|
|
- structure.Notes = "混合查询:包含词语和单字";
|
|
|
- }
|
|
|
- else if (structure.HasWords && !structure.HasSingleChars)
|
|
|
- {
|
|
|
- structure.CombinationType = CombinationType.WordsOnly;
|
|
|
- structure.Notes = structure.Words.Count > 1 ? "多词查询" : "单词查询";
|
|
|
- }
|
|
|
- else if (!structure.HasWords && structure.HasSingleChars)
|
|
|
- {
|
|
|
- structure.CombinationType = CombinationType.CharsOnly;
|
|
|
- structure.Notes = structure.SingleChars.Count > 1 ? "多字查询" : "单字查询";
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- structure.CombinationType = CombinationType.Unknown;
|
|
|
- structure.Notes = "无法识别的查询类型";
|
|
|
- }
|
|
|
-
|
|
|
- // 计算复杂度评分
|
|
|
- structure.ComplexityScore = CalculateComplexityScore(structure);
|
|
|
-
|
|
|
- return structure;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 计算搜索复杂度评分
|
|
|
- /// </summary>
|
|
|
- private static int CalculateComplexityScore(SearchStructure structure)
|
|
|
- {
|
|
|
- int score = 0;
|
|
|
-
|
|
|
- // 词条数量(1-5分)
|
|
|
- int segmentCount = structure.Words.Count + structure.SingleChars.Count;
|
|
|
- score += Math.Min(segmentCount, 5);
|
|
|
-
|
|
|
- // 是否混合类型(加3分)
|
|
|
- if (structure.HasWords && structure.HasSingleChars)
|
|
|
- score += 3;
|
|
|
-
|
|
|
- // 单字数量(每3个加1分,最多2分)
|
|
|
- if (structure.SingleChars.Count >= 3)
|
|
|
- score += Math.Min(structure.SingleChars.Count / 3, 2);
|
|
|
-
|
|
|
- // 词语平均长度(长词加2分)
|
|
|
- if (structure.Words.Any())
|
|
|
- {
|
|
|
- double avgLength = structure.Words.Average(w => w.Length);
|
|
|
- if (avgLength > 4) score += 2;
|
|
|
- }
|
|
|
-
|
|
|
- // 总字符数(10个以上加1分)
|
|
|
- if (structure.AllCharacters.Count > 10)
|
|
|
- score += 1;
|
|
|
-
|
|
|
- return Math.Min(score, 10); // 最高10分
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 获取搜索策略
|
|
|
- /// </summary>
|
|
|
- private static MatchStrategy GetMatchStrategy(SearchStructure structure, SearchConfig config)
|
|
|
- {
|
|
|
- var complexity = CalculateComplexityScore(structure);
|
|
|
-
|
|
|
- return new MatchStrategy
|
|
|
- {
|
|
|
- StrategyName = complexity >= 7 ? "strict" :
|
|
|
- complexity >= 4 ? "balanced" : "loose",
|
|
|
- ComplexityScore = complexity,
|
|
|
- UseExactMatch = config.EnableExactMatch && structure.Words.Any(w => w.Length >= 3),
|
|
|
- UsePrefixMatch = config.EnablePrefixMatch,
|
|
|
- UseFuzzySearch = config.EnableFuzzySearch,
|
|
|
- UseCharSearch = config.EnableCharSearch && structure.SingleChars.Any(),
|
|
|
- UseSmartSplitting = config.EnableSmartSplitting && structure.Words.Any(w => w.Length >= config.MinSplitLength),
|
|
|
- RecommendedPageSize = complexity >= 7 ? 10 : 20
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 获取计分规则
|
|
|
- /// </summary>
|
|
|
- private static List<ScoringRule> GetScoringRules(SearchStructure structure, SearchConfig config)
|
|
|
- {
|
|
|
- var rules = new List<ScoringRule>();
|
|
|
-
|
|
|
- // 词语匹配规则
|
|
|
- foreach (var word in structure.Words)
|
|
|
- {
|
|
|
- rules.Add(new ScoringRule
|
|
|
- {
|
|
|
- Type = "word",
|
|
|
- Value = word,
|
|
|
- BaseScore = 10,
|
|
|
- Bonus = word.Length >= 3 ? 5 : 2,
|
|
|
- Multiplier = 1.0,
|
|
|
- Condition = $"包含词语'{word}'"
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // 单字匹配规则
|
|
|
- foreach (var ch in structure.SingleChars)
|
|
|
- {
|
|
|
- rules.Add(new ScoringRule
|
|
|
- {
|
|
|
- Type = "char",
|
|
|
- Value = ch,
|
|
|
- BaseScore = 3,
|
|
|
- Bonus = 1,
|
|
|
- Multiplier = 1.0,
|
|
|
- Condition = $"包含单字'{ch}'"
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // 混合匹配奖励
|
|
|
- if (structure.HasWords && structure.HasSingleChars)
|
|
|
- {
|
|
|
- rules.Add(new ScoringRule
|
|
|
- {
|
|
|
- Type = "combo",
|
|
|
- Value = "words_and_chars",
|
|
|
- BaseScore = 0,
|
|
|
- Bonus = 8,
|
|
|
- Multiplier = 1.2,
|
|
|
- Condition = "同时匹配词语和单字"
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // 智能拆分奖励
|
|
|
- if (config.EnableSmartSplitting && structure.Words.Any(w => w.Length >= config.MinSplitLength))
|
|
|
- {
|
|
|
- rules.Add(new ScoringRule
|
|
|
- {
|
|
|
- Type = "smart_split",
|
|
|
- Value = "smart_split_match",
|
|
|
- BaseScore = 0,
|
|
|
- Bonus = 5,
|
|
|
- Multiplier = 1.1,
|
|
|
- Condition = "智能拆分匹配"
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // 完全匹配奖励
|
|
|
- rules.Add(new ScoringRule
|
|
|
- {
|
|
|
- Type = "exact",
|
|
|
- Value = "exact_match",
|
|
|
- BaseScore = 0,
|
|
|
- Bonus = 15,
|
|
|
- Multiplier = 1.5,
|
|
|
- Condition = "完全匹配搜索词"
|
|
|
- });
|
|
|
-
|
|
|
- return rules;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 获取需要提升权重的字段
|
|
|
- /// </summary>
|
|
|
- private static List<string> GetBoostFields(SearchStructure structure, List<string> searchFields)
|
|
|
- {
|
|
|
- var boostFields = new List<string>();
|
|
|
-
|
|
|
- if (structure.HasWords && structure.Words.Any(w => w.Length >= 2))
|
|
|
- {
|
|
|
- // 长词语在名称字段中效果更好
|
|
|
- boostFields.AddRange(searchFields.Where(f =>
|
|
|
- f.Contains("name", StringComparison.OrdinalIgnoreCase) ||
|
|
|
- f.Contains("title", StringComparison.OrdinalIgnoreCase) ||
|
|
|
- f.Contains("subject", StringComparison.OrdinalIgnoreCase)));
|
|
|
- }
|
|
|
-
|
|
|
- if (structure.HasSingleChars)
|
|
|
- {
|
|
|
- // 单字在描述、内容字段中效果更好
|
|
|
- boostFields.AddRange(searchFields.Where(f =>
|
|
|
- f.Contains("desc", StringComparison.OrdinalIgnoreCase) ||
|
|
|
- f.Contains("content", StringComparison.OrdinalIgnoreCase) ||
|
|
|
- f.Contains("remark", StringComparison.OrdinalIgnoreCase) ||
|
|
|
- f.Contains("note", StringComparison.OrdinalIgnoreCase)));
|
|
|
- }
|
|
|
-
|
|
|
- return boostFields.Distinct().ToList();
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 计算预期匹配数
|
|
|
- /// </summary>
|
|
|
- private static int CalculateExpectedMatches(SearchStructure structure, int fieldCount)
|
|
|
- {
|
|
|
- int baseMatches = structure.Words.Count + structure.SingleChars.Count;
|
|
|
- int expected = baseMatches * fieldCount;
|
|
|
-
|
|
|
- // 调整预期值
|
|
|
- if (structure.CombinationType == CombinationType.WordsAndChars)
|
|
|
- expected = (int)(expected * 1.5);
|
|
|
- else if (structure.CombinationType == CombinationType.CharsOnly)
|
|
|
- expected = (int)(expected * 0.7);
|
|
|
-
|
|
|
- return Math.Max(expected, 1);
|
|
|
- }
|
|
|
-
|
|
|
- #endregion
|
|
|
-
|
|
|
- #region 条件构建策略
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建严格条件(所有条件必须满足)
|
|
|
- /// </summary>
|
|
|
- private static string BuildStrictCondition(List<string> conditions, SearchStructure structure)
|
|
|
- {
|
|
|
- if (conditions.Count == 1)
|
|
|
- return conditions[0];
|
|
|
-
|
|
|
- // 词语条件在前,单字条件在后
|
|
|
- var wordConditions = conditions
|
|
|
- .Where((c, i) => i < structure.Words.Count)
|
|
|
- .ToList();
|
|
|
-
|
|
|
- var charConditions = conditions
|
|
|
- .Skip(structure.Words.Count)
|
|
|
- .ToList();
|
|
|
-
|
|
|
- if (wordConditions.Any() && charConditions.Any())
|
|
|
- {
|
|
|
- return $"({string.Join(" AND ", wordConditions)} AND ({string.Join(" OR ", charConditions)}))";
|
|
|
- }
|
|
|
-
|
|
|
- return $"({string.Join(" AND ", conditions)})";
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建平衡条件
|
|
|
- /// </summary>
|
|
|
- private static string BuildBalancedCondition(List<string> conditions, SearchStructure structure)
|
|
|
- {
|
|
|
- if (conditions.Count == 1)
|
|
|
- return conditions[0];
|
|
|
-
|
|
|
- // 词语优先,单字作为增强
|
|
|
- if (structure.HasWords && structure.HasSingleChars)
|
|
|
- {
|
|
|
- var wordCondition = conditions.First();
|
|
|
- var charConditions = conditions.Skip(1).ToList();
|
|
|
-
|
|
|
- return $"({wordCondition} AND ({string.Join(" OR ", charConditions)}))";
|
|
|
- }
|
|
|
-
|
|
|
- return $"({string.Join(" AND ", conditions)})";
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 构建宽松条件(提高召回率)
|
|
|
- /// </summary>
|
|
|
- private static string BuildLooseCondition(List<string> conditions, SearchStructure structure)
|
|
|
- {
|
|
|
- if (conditions.Count == 1)
|
|
|
- return conditions[0];
|
|
|
-
|
|
|
- // 所有条件OR连接
|
|
|
- return $"({string.Join(" OR ", conditions)})";
|
|
|
- }
|
|
|
-
|
|
|
- #endregion
|
|
|
-
|
|
|
- #region 实用工具方法
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 检查字符是否为中文字符
|
|
|
- /// </summary>
|
|
|
- public static bool IsChineseCharacter(char c)
|
|
|
- {
|
|
|
- // 基本汉字(0x4E00-0x9FFF)
|
|
|
- if (c >= 0x4E00 && c <= 0x9FFF)
|
|
|
- return true;
|
|
|
-
|
|
|
- // 扩展A区(0x3400-0x4DBF)
|
|
|
- if (c >= 0x3400 && c <= 0x4DBF)
|
|
|
- return true;
|
|
|
-
|
|
|
- // 基本汉字补充(0x9FA6-0x9FFF)
|
|
|
- if (c >= 0x9FA6 && c <= 0x9FFF)
|
|
|
- return true;
|
|
|
-
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 移除字符串中的分隔符
|
|
|
- /// </summary>
|
|
|
- private static string RemoveSeparators(string text)
|
|
|
- {
|
|
|
- if (string.IsNullOrEmpty(text))
|
|
|
- return text;
|
|
|
-
|
|
|
- var separators = new[] { ' ', ',', '、', ',', ';', ';', '-', '_', '.', '。', '·', '|', '/', '\\' };
|
|
|
- return new string(text.Where(c => !separators.Contains(c)).ToArray());
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 验证搜索字段列表
|
|
|
- /// </summary>
|
|
|
- public static (bool IsValid, string Message, List<string> ValidFields) ValidateSearchFields(
|
|
|
- List<string> searchFields, List<string> allFields)
|
|
|
- {
|
|
|
- if (searchFields == null || !searchFields.Any())
|
|
|
- return (false, "搜索字段列表不能为空", new List<string>());
|
|
|
-
|
|
|
- if (allFields == null || !allFields.Any())
|
|
|
- return (true, "无验证字段列表,接受所有输入", searchFields);
|
|
|
-
|
|
|
- var validFields = searchFields.Intersect(allFields).ToList();
|
|
|
- var invalidFields = searchFields.Except(allFields).ToList();
|
|
|
-
|
|
|
- if (invalidFields.Any())
|
|
|
- {
|
|
|
- return (false,
|
|
|
- $"发现无效字段: {string.Join(", ", invalidFields)}",
|
|
|
- validFields);
|
|
|
- }
|
|
|
-
|
|
|
- return (true, "所有字段有效", validFields);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 生成搜索统计信息
|
|
|
- /// </summary>
|
|
|
- public static SearchStats GenerateSearchStats(SearchStructure structure, int resultCount)
|
|
|
- {
|
|
|
- return new SearchStats
|
|
|
- {
|
|
|
- SearchTerm = structure.OriginalTerm,
|
|
|
- CombinationType = structure.CombinationType,
|
|
|
- WordCount = structure.Words.Count,
|
|
|
- CharCount = structure.SingleChars.Count,
|
|
|
- TotalSegments = structure.Segments.Count,
|
|
|
- ComplexityScore = structure.ComplexityScore,
|
|
|
- ResultCount = resultCount,
|
|
|
- GeneratedAt = DateTime.Now
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- #endregion
|
|
|
- }
|
|
|
-
|
|
|
- #region 数据模型和枚举
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 搜索模式枚举
|
|
|
- /// </summary>
|
|
|
- public enum SearchMode
|
|
|
- {
|
|
|
- /// <summary>
|
|
|
- /// 自动检测搜索类型
|
|
|
- /// </summary>
|
|
|
- [Description("自动检测")]
|
|
|
- AutoDetect = 0,
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 单词条搜索
|
|
|
- /// </summary>
|
|
|
- [Description("单词条搜索")]
|
|
|
- SingleWord = 1,
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 多词条搜索
|
|
|
- /// </summary>
|
|
|
- [Description("多词条搜索")]
|
|
|
- MultiWord = 2,
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 单字搜索
|
|
|
- /// </summary>
|
|
|
- [Description("单字搜索")]
|
|
|
- SingleChar = 3,
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 精确匹配
|
|
|
- /// </summary>
|
|
|
- [Description("精确匹配")]
|
|
|
- Exact = 4,
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 高级查询
|
|
|
- /// </summary>
|
|
|
- [Description("高级查询")]
|
|
|
- Advanced = 5
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 组合模式枚举
|
|
|
- /// </summary>
|
|
|
- public enum CombinationMode
|
|
|
- {
|
|
|
- /// <summary>
|
|
|
- /// 智能混合(自动调整权重和连接方式)
|
|
|
- /// </summary>
|
|
|
- [Description("智能混合")]
|
|
|
- SmartMix = 0,
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 词语和单字组合(词语必须,单字增强)
|
|
|
- /// </summary>
|
|
|
- [Description("词语+单字")]
|
|
|
- WordsAndChars = 1,
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 仅词语
|
|
|
- /// </summary>
|
|
|
- [Description("仅词语")]
|
|
|
- WordsOnly = 2,
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 仅单字
|
|
|
- /// </summary>
|
|
|
- [Description("仅单字")]
|
|
|
- CharsOnly = 3
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 组合类型
|
|
|
- /// </summary>
|
|
|
- public enum CombinationType
|
|
|
- {
|
|
|
- [Description("未知")]
|
|
|
- Unknown,
|
|
|
-
|
|
|
- [Description("仅词语")]
|
|
|
- WordsOnly,
|
|
|
-
|
|
|
- [Description("仅单字")]
|
|
|
- CharsOnly,
|
|
|
-
|
|
|
- [Description("词语和单字混合")]
|
|
|
- WordsAndChars
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 搜索结构分析结果
|
|
|
- /// </summary>
|
|
|
- public class SearchStructure
|
|
|
- {
|
|
|
- public string OriginalTerm { get; set; } = "";
|
|
|
- public string CleanTerm { get; set; } = "";
|
|
|
- public DateTime AnalyzedAt { get; set; }
|
|
|
- public List<string> Segments { get; set; } = new List<string>();
|
|
|
- public List<string> Words { get; set; } = new List<string>();
|
|
|
- public List<string> SingleChars { get; set; } = new List<string>();
|
|
|
- public List<char> AllCharacters { get; set; } = new List<char>();
|
|
|
- public bool HasWords { get; set; }
|
|
|
- public bool HasSingleChars { get; set; }
|
|
|
- public CombinationType CombinationType { get; set; }
|
|
|
- public string Notes { get; set; } = "";
|
|
|
- public int ComplexityScore { get; set; }
|
|
|
-
|
|
|
- // 统计信息
|
|
|
- public int TotalWordLength => Words.Sum(w => w.Length);
|
|
|
- public int MaxWordLength => Words.Any() ? Words.Max(w => w.Length) : 0;
|
|
|
- public double AvgWordLength => Words.Any() ? Words.Average(w => w.Length) : 0;
|
|
|
- public int TotalCharacterCount => AllCharacters.Count;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 搜索配置类(增强版)
|
|
|
- /// 控制搜索行为、匹配策略和性能优化的参数集合
|
|
|
- /// </summary>
|
|
|
- public class SearchConfig
|
|
|
- {
|
|
|
- /// <summary>
|
|
|
- /// 是否启用精确匹配
|
|
|
- /// 默认值: true
|
|
|
- /// 说明: 当设置为true时,系统会尝试使用等号(=)进行完全匹配
|
|
|
- /// 适用场景: 搜索ID、编码、精确名称等唯一标识
|
|
|
- /// 示例: 搜索"1001"时,只匹配完全等于"1001"的记录
|
|
|
- /// 性能影响: 精确匹配通常使用索引,性能最好
|
|
|
- /// </summary>
|
|
|
- public bool EnableExactMatch { get; set; } = true;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否启用前缀匹配
|
|
|
- /// 默认值: true
|
|
|
- /// 说明: 当设置为true时,系统会尝试匹配以搜索词开头的记录
|
|
|
- /// 适用场景: 自动补全、拼音首字母搜索、编码前缀搜索
|
|
|
- /// 示例: 搜索"北京"时,会匹配"北京市"、"北京旅游"等
|
|
|
- /// 性能影响: 前缀匹配可以使用索引,性能良好
|
|
|
- /// </summary>
|
|
|
- public bool EnablePrefixMatch { get; set; } = true;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否启用模糊搜索
|
|
|
- /// 默认值: true
|
|
|
- /// 说明: 当设置为true时,系统会使用LIKE '%关键词%'进行模糊匹配
|
|
|
- /// 适用场景: 通用全文搜索、包含关系搜索
|
|
|
- /// 示例: 搜索"长城"时,会匹配"长城旅游"、"八达岭长城"等
|
|
|
- /// 性能影响: 模糊搜索不能使用普通索引,建议配合全文索引使用
|
|
|
- /// 注意: 大数据量时可能会影响性能,建议合理设置搜索字段
|
|
|
- /// </summary>
|
|
|
- public bool EnableFuzzySearch { get; set; } = true;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否启用单字搜索
|
|
|
- /// 默认值: true
|
|
|
- /// 说明: 当设置为true时,系统会将搜索词拆分成单个字符进行匹配
|
|
|
- /// 适用场景: 中文单字搜索、记忆不完整的模糊搜索
|
|
|
- /// 示例: 搜索"北上广"时,会分别匹配包含"北"、"上"、"广"的记录
|
|
|
- /// 注意: 启用后会生成多个LIKE条件,可能影响性能
|
|
|
- /// 建议: 单字数量较多时,考虑使用RequireAllChars控制匹配逻辑
|
|
|
- /// </summary>
|
|
|
- public bool EnableCharSearch { get; set; } = true;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否要求所有词语都必须匹配
|
|
|
- /// 默认值: true
|
|
|
- /// 说明: 当设置为true时,多词搜索中所有词语都必须在记录中出现
|
|
|
- /// 当设置为false时,任意一个词语出现即可匹配
|
|
|
- /// 适用场景:
|
|
|
- /// true - 精确搜索,如"北京 长城"要求同时包含"北京"和"长城"
|
|
|
- /// false - 宽松搜索,如"北京 长城"匹配包含"北京"或"长城"的记录
|
|
|
- /// 性能影响: true时可能返回更少结果,但条件更复杂
|
|
|
- /// 用户体验: true时结果更精确,false时召回率更高
|
|
|
- /// </summary>
|
|
|
- public bool RequireAllWords { get; set; } = true;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否要求所有单字都必须匹配
|
|
|
- /// 默认值: true
|
|
|
- /// 说明: 当设置为true时,单字搜索中所有字符都必须在记录中出现
|
|
|
- /// 当设置为false时,任意一个字符出现即可匹配
|
|
|
- /// 适用场景:
|
|
|
- /// true - 如"北上广"要求同时包含"北"、"上"、"广"
|
|
|
- /// false - 如"北上广"匹配包含"北"或"上"或"广"的记录
|
|
|
- /// 注意: 单字数量较多时,建议设为false以提高召回率
|
|
|
- /// </summary>
|
|
|
- public bool RequireAllChars { get; set; } = true;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 精确匹配的最小长度要求
|
|
|
- /// 默认值: 2
|
|
|
- /// 说明: 只有当搜索词长度大于等于此值时,才会尝试精确匹配
|
|
|
- /// 适用场景: 避免短词(如"a"、"的")使用精确匹配
|
|
|
- /// 示例: 设置为3时,"北京"不会使用精确匹配,"北京市"会使用
|
|
|
- /// 单位: 字符数
|
|
|
- /// 建议值: 2-5之间,根据业务需求调整
|
|
|
- /// </summary>
|
|
|
- public int MinExactMatchLength { get; set; } = 2;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 搜索词的最大长度限制
|
|
|
- /// 默认值: 200
|
|
|
- /// 说明: 超过此长度的搜索词会被自动截断
|
|
|
- /// 目的:
|
|
|
- /// 1. 防止恶意输入导致性能问题
|
|
|
- /// 2. 避免过长的SQL语句
|
|
|
- /// 3. 提高系统安全性
|
|
|
- /// 注意: 实际截断时保留前面部分,不会影响搜索意图
|
|
|
- /// 单位: 字符数
|
|
|
- /// 建议值: 50-500之间,根据业务场景调整
|
|
|
- /// </summary>
|
|
|
- public int MaxSearchTermLength { get; set; } = 200;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 匹配度的最低分数要求
|
|
|
- /// 默认值: 0
|
|
|
- /// 说明: 只有匹配度分数大于等于此值的记录才会被返回
|
|
|
- /// 适用场景: 质量控制,过滤掉相关性太低的结果
|
|
|
- /// 分数范围: 通常为0-100,具体取决于评分算法
|
|
|
- /// 示例: 设置为10时,匹配度低于10分的记录会被过滤
|
|
|
- /// 注意: 需要EnableRelevanceScoring=true才会生效
|
|
|
- /// </summary>
|
|
|
- public double MinScore { get; set; } = 0;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否启用相关性评分
|
|
|
- /// 默认值: true
|
|
|
- /// 说明: 当设置为true时,系统会计算每条记录的匹配度分数
|
|
|
- /// 功能:
|
|
|
- /// 1. 按匹配度排序(分数高的在前)
|
|
|
- /// 2. 可以设置MinScore过滤低分记录
|
|
|
- /// 3. 提供MatchScore字段供前端显示
|
|
|
- /// 性能影响: 会增加计算开销,但用户体验更好
|
|
|
- /// 建议: 搜索结果需要排序时启用,简单列表查询可关闭
|
|
|
- /// </summary>
|
|
|
- public bool EnableRelevanceScoring { get; set; } = true;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否启用分阶段搜索
|
|
|
- /// 默认值: false
|
|
|
- /// 说明: 当设置为true时,系统会按优先级分阶段执行搜索
|
|
|
- /// 搜索阶段:
|
|
|
- /// 1. 精确匹配(最高优先级)
|
|
|
- /// 2. 前缀匹配
|
|
|
- /// 3. 模糊匹配
|
|
|
- /// 4. 单字匹配(最低优先级)
|
|
|
- /// 优点:
|
|
|
- /// 1. 优先返回最相关的结果
|
|
|
- /// 2. 可以设置降级策略
|
|
|
- /// 3. 提高搜索成功率
|
|
|
- /// 缺点:
|
|
|
- /// 1. 可能执行多次查询
|
|
|
- /// 2. 逻辑更复杂
|
|
|
- /// 适用场景: 对搜索结果质量要求较高的场景
|
|
|
- /// </summary>
|
|
|
- public bool EnableStagedSearch { get; set; } = false;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 字段权重配置
|
|
|
- /// 默认值: 预定义的权重字典
|
|
|
- /// 说明: 控制不同字段在匹配度计算中的重要性
|
|
|
- /// 权重范围: 建议0.5-2.0之间
|
|
|
- /// 示例配置:
|
|
|
- /// "name": 1.5 // 名称字段权重高
|
|
|
- /// "title": 1.5 // 标题字段权重高
|
|
|
- /// "code": 1.3 // 编码字段权重中
|
|
|
- /// "desc": 1.0 // 描述字段权重正常
|
|
|
- /// "remark": 0.7 // 备注字段权重低
|
|
|
- /// 使用方式:
|
|
|
- /// 1. 匹配度分数 = 基础分 × 字段权重
|
|
|
- /// 2. 权重高的字段对总分影响更大
|
|
|
- /// 建议: 根据业务重要性调整权重
|
|
|
- /// </summary>
|
|
|
- public Dictionary<string, double> FieldWeights { get; set; } = new Dictionary<string, double>
|
|
|
- {
|
|
|
- { "name", 1.5 },
|
|
|
- { "title", 1.5 },
|
|
|
- { "code", 1.3 },
|
|
|
- { "description", 1.0 },
|
|
|
- { "content", 0.8 },
|
|
|
- { "remark", 0.7 }
|
|
|
- };
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否启用同义词扩展
|
|
|
- /// 默认值: false
|
|
|
- /// 说明: 当设置为true时,系统会自动扩展搜索词的同义词
|
|
|
- /// 示例: 搜索"电脑"时,也会搜索"计算机"、"微机"等
|
|
|
- /// 实现方式: 需要配合同义词词典使用
|
|
|
- /// 优点: 提高召回率,改善搜索体验
|
|
|
- /// 缺点: 可能引入不相关结果
|
|
|
- /// 适用场景: 专业术语搜索、方言词汇搜索
|
|
|
- /// </summary>
|
|
|
- public bool EnableSynonymExpansion { get; set; } = false;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否启用拼音搜索
|
|
|
- /// 默认值: false
|
|
|
- /// 说明: 当设置为true时,系统会将中文转换为拼音进行搜索
|
|
|
- /// 示例: 搜索"beijing"可以匹配"北京"
|
|
|
- /// 实现方式: 需要拼音转换库
|
|
|
- /// 适用场景:
|
|
|
- /// 1. 输入拼音搜索中文
|
|
|
- /// 2. 首字母搜索(如"bj"匹配"北京")
|
|
|
- /// 注意: 会增加搜索复杂度和索引大小
|
|
|
- /// </summary>
|
|
|
- public bool EnablePinyinSearch { get; set; } = false;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 搜索结果的最大数量限制
|
|
|
- /// 默认值: 1000
|
|
|
- /// 说明: 限制单次搜索返回的最大记录数
|
|
|
- /// 目的:
|
|
|
- /// 1. 防止返回过多数据影响性能
|
|
|
- /// 2. 避免内存溢出
|
|
|
- /// 3. 提高响应速度
|
|
|
- /// 注意: 分页查询时,此限制针对总记录数
|
|
|
- /// 建议值: 500-5000之间,根据系统承载能力调整
|
|
|
- /// </summary>
|
|
|
- public int MaxResultCount { get; set; } = 1000;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否启用搜索缓存
|
|
|
- /// 默认值: false
|
|
|
- /// 说明: 当设置为true时,相同的搜索条件会使用缓存结果
|
|
|
- /// 缓存策略:
|
|
|
- /// 1. 基于搜索词和参数的哈希值
|
|
|
- /// 2. 可设置缓存时间
|
|
|
- /// 3. 支持滑动过期
|
|
|
- /// 适用场景:
|
|
|
- /// 1. 热门搜索词
|
|
|
- /// 2. 数据更新频率低的场景
|
|
|
- /// 3. 性能要求高的搜索
|
|
|
- /// 注意: 数据频繁更新时,需要合理设置缓存时间
|
|
|
- /// </summary>
|
|
|
- public bool EnableSearchCache { get; set; } = false;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 缓存时间(分钟)
|
|
|
- /// 默认值: 5
|
|
|
- /// 说明: 搜索结果的缓存保留时间
|
|
|
- /// 单位: 分钟
|
|
|
- /// 使用条件: 需要EnableSearchCache=true
|
|
|
- /// 建议值:
|
|
|
- /// - 高频搜索: 1-5分钟
|
|
|
- /// - 低频搜索: 10-30分钟
|
|
|
- /// - 静态数据: 60分钟以上
|
|
|
- /// </summary>
|
|
|
- public int CacheDurationMinutes { get; set; } = 5;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否启用搜索词分析
|
|
|
- /// 默认值: true
|
|
|
- /// 说明: 当设置为true时,系统会对搜索词进行智能分析
|
|
|
- /// 分析内容:
|
|
|
- /// 1. 去除停用词(的、了、在等)
|
|
|
- /// 2. 提取关键词
|
|
|
- /// 3. 识别搜索意图
|
|
|
- /// 4. 分析词性
|
|
|
- /// 优点: 提高搜索准确性和智能性
|
|
|
- /// 性能影响: 轻微,主要消耗在分析算法上
|
|
|
- /// 建议: 一般保持启用状态
|
|
|
- /// </summary>
|
|
|
- public bool EnableQueryAnalysis { get; set; } = true;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否记录搜索日志
|
|
|
- /// 默认值: false
|
|
|
- /// 说明: 当设置为true时,系统会记录搜索行为日志
|
|
|
- /// 记录内容:
|
|
|
- /// 1. 搜索词
|
|
|
- /// 2. 搜索时间
|
|
|
- /// 3. 返回结果数
|
|
|
- /// 4. 响应时间
|
|
|
- /// 5. 用户信息(可选)
|
|
|
- /// 用途:
|
|
|
- /// 1. 分析用户搜索习惯
|
|
|
- /// 2. 优化搜索算法
|
|
|
- /// 3. 监控搜索性能
|
|
|
- /// 4. 安全审计
|
|
|
- /// 注意: 涉及隐私时需谨慎处理
|
|
|
- /// </summary>
|
|
|
- public bool EnableSearchLogging { get; set; } = false;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 搜索超时时间(秒)
|
|
|
- /// 默认值: 30
|
|
|
- /// 说明: 搜索操作的最大执行时间
|
|
|
- /// 目的:
|
|
|
- /// 1. 防止长时间运行的查询
|
|
|
- /// 2. 提高系统稳定性
|
|
|
- /// 3. 改善用户体验
|
|
|
- /// 单位: 秒
|
|
|
- /// 建议值:
|
|
|
- /// - 实时搜索: 5-10秒
|
|
|
- /// - 后台搜索: 30-60秒
|
|
|
- /// - 大数据搜索: 120秒以上
|
|
|
- /// 注意: 超时后会取消查询,返回已有结果或错误
|
|
|
- /// </summary>
|
|
|
- public int SearchTimeoutSeconds { get; set; } = 30;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否启用异步搜索
|
|
|
- /// 默认值: true
|
|
|
- /// 说明: 当设置为true时,搜索操作会异步执行
|
|
|
- /// 优点:
|
|
|
- /// 1. 不阻塞主线程
|
|
|
- /// 2. 提高并发处理能力
|
|
|
- /// 3. 更好的用户体验
|
|
|
- /// 适用场景:
|
|
|
- /// 1. 复杂搜索条件
|
|
|
- /// 2. 大数据量搜索
|
|
|
- /// 3. 高并发场景
|
|
|
- /// 注意: 需要合理的线程管理和资源控制
|
|
|
- /// </summary>
|
|
|
- public bool EnableAsyncSearch { get; set; } = true;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否启用智能提示
|
|
|
- /// 默认值: false
|
|
|
- /// 说明: 当设置为true时,系统会根据输入提供搜索建议
|
|
|
- /// 功能:
|
|
|
- /// 1. 自动补全
|
|
|
- /// 2. 热门搜索建议
|
|
|
- /// 3. 相关搜索推荐
|
|
|
- /// 4. 拼写纠正
|
|
|
- /// 实现方式: 基于搜索历史、词典、算法模型
|
|
|
- /// 适用场景: 搜索框智能提示功能
|
|
|
- /// 注意: 需要额外的数据处理和存储
|
|
|
- /// </summary>
|
|
|
- public bool EnableSmartSuggestions { get; set; } = false;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否启用搜索结果高亮
|
|
|
- /// 默认值: false
|
|
|
- /// 说明: 当设置为true时,搜索结果中的匹配部分会被高亮显示
|
|
|
- /// 高亮方式:
|
|
|
- /// 1. HTML标记(如<em>关键词</em>)
|
|
|
- /// 2. 特殊字符包裹
|
|
|
- /// 3. 前端处理
|
|
|
- /// 适用场景: 需要突出显示匹配内容的搜索
|
|
|
- /// 注意: 会增加数据传输量和前端处理复杂度
|
|
|
- /// </summary>
|
|
|
- public bool EnableResultHighlighting { get; set; } = false;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否启用搜索结果分组
|
|
|
- /// 默认值: false
|
|
|
- /// 说明: 当设置为true时,搜索结果会按指定字段进行分组
|
|
|
- /// 分组方式:
|
|
|
- /// 1. 按类型分组
|
|
|
- /// 2. 按时间分组
|
|
|
- /// 3. 按相关度分组
|
|
|
- /// 适用场景:
|
|
|
- /// 1. 多类型混合搜索
|
|
|
- /// 2. 时间线展示
|
|
|
- /// 3. 分类浏览
|
|
|
- /// 注意: 会增加查询复杂度和处理时间
|
|
|
- /// </summary>
|
|
|
- public bool EnableResultGrouping { get; set; } = false;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 是否启用智能拆分
|
|
|
- /// 默认值: true
|
|
|
- /// 说明: 当设置为true时,系统会自动将长词拆分成合理的子词组合
|
|
|
- /// 适用场景: 搜索"重庆科学"这类组合词时,会自动尝试"重庆"+"科学"的组合
|
|
|
- /// 工作方式:
|
|
|
- /// 1. 检测长度为MinSplitLength及以上的词语
|
|
|
- /// 2. 使用智能算法判断是否为可拆分组合词
|
|
|
- /// 3. 如果可拆分,生成多层次搜索条件
|
|
|
- /// 多层次条件示例: "重庆科学" →
|
|
|
- /// - 完整词匹配(最高优先级)
|
|
|
- /// - 拆分子词匹配("重庆" AND "科学")
|
|
|
- /// - 单字匹配("重" OR "庆" OR "科" OR "学")
|
|
|
- /// 优点:
|
|
|
- /// 1. 提高组合词的搜索成功率
|
|
|
- /// 2. 保持搜索结果的准确性
|
|
|
- /// 3. 提升用户体验
|
|
|
- /// 性能影响: 会增加搜索条件复杂度,但能显著提高召回率
|
|
|
- /// 建议: 中文搜索场景建议启用
|
|
|
- /// </summary>
|
|
|
- public bool EnableSmartSplitting { get; set; } = true;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 智能拆分时是否要求所有子词都匹配
|
|
|
- /// 默认值: false
|
|
|
- /// 说明: 控制智能拆分时对拆分子词的匹配严格度
|
|
|
- /// 示例:
|
|
|
- /// - true: "重庆科学"拆分为"重庆"和"科学",要求同时包含这两个词
|
|
|
- /// - false: "重庆科学"拆分为"重庆"和"科学",包含任意一个即可匹配
|
|
|
- /// 适用场景:
|
|
|
- /// true: 需要精确拆分匹配的场景
|
|
|
- /// false: 需要提高召回率的场景
|
|
|
- /// 建议: 通常设置为false以获得更好的搜索结果覆盖率
|
|
|
- /// 注意: 此设置仅影响智能拆分的子词匹配,不影响原始词语匹配
|
|
|
- /// </summary>
|
|
|
- public bool RequireAllWordsForSplit { get; set; } = false;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 智能拆分时是否要求所有单字都匹配
|
|
|
- /// 默认值: false
|
|
|
- /// 说明: 控制智能拆分成单字时的匹配严格度
|
|
|
- /// 示例:
|
|
|
- /// - true: "重庆科学"拆分为单字时,要求同时包含"重"、"庆"、"科"、"学"
|
|
|
- /// - false: "重庆科学"拆分为单字时,包含任意一个单字即可匹配
|
|
|
- /// 适用场景:
|
|
|
- /// true: 需要完全匹配所有单字的严格场景
|
|
|
- /// false: 需要宽松匹配以提高召回率的场景
|
|
|
- /// 建议: 通常设置为false,因为单字完全匹配要求较高,容易漏掉相关结果
|
|
|
- /// 注意: 此设置仅影响智能拆分的单字匹配阶段
|
|
|
- /// </summary>
|
|
|
- public bool RequireAllCharsForSplit { get; set; } = false;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 最小拆分长度
|
|
|
- /// 默认值: 4
|
|
|
- /// 说明: 只有长度大于等于此值的词才会被考虑进行智能拆分
|
|
|
- /// 单位: 字符数
|
|
|
- /// 示例:
|
|
|
- /// - 设置为4时,"重庆科学"(4字)会被考虑拆分
|
|
|
- /// - 设置为5时,"北京长城游"(5字)会被考虑拆分
|
|
|
- /// 适用场景:
|
|
|
- /// - 较低值(3-4):适用于短词较多的场景
|
|
|
- /// - 较高值(5-6):适用于长词较多的专业场景
|
|
|
- /// 建议值:
|
|
|
- /// - 中文搜索: 4
|
|
|
- /// - 英文搜索: 8(对应4个英文单词)
|
|
|
- /// - 混合搜索: 根据主要语言调整
|
|
|
- /// 注意: 设置过低可能导致过多词语被拆分,影响性能;设置过高可能错过可拆分的组合词
|
|
|
- /// </summary>
|
|
|
- public int MinSplitLength { get; set; } = 4;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 最大拆分组合数
|
|
|
- /// 默认值: 3
|
|
|
- /// 说明: 限制一个词的最大拆分方式数量,防止组合爆炸
|
|
|
- /// 示例:
|
|
|
- /// - 设置为3时,一个6字词最多尝试3种拆分方式
|
|
|
- /// - 设置为1时,只使用最优的拆分方式
|
|
|
- /// 适用场景:
|
|
|
- /// - 性能敏感场景: 1-2
|
|
|
- /// - 平衡场景: 3
|
|
|
- /// - 准确性优先场景: 5-10
|
|
|
- /// 性能影响: 每增加一个拆分组合,会增加一个搜索条件分支
|
|
|
- /// 建议:
|
|
|
- /// - 实时搜索: 1-3
|
|
|
- /// - 后台搜索: 3-5
|
|
|
- /// - 复杂分析: 5-10
|
|
|
- /// 注意: 过高的值可能导致SQL条件过于复杂,影响数据库性能
|
|
|
- /// </summary>
|
|
|
- public int MaxSplitCombinations { get; set; } = 3;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 搜索阶段
|
|
|
- /// </summary>
|
|
|
- public class SearchStage
|
|
|
- {
|
|
|
- public int Stage { get; set; }
|
|
|
- public string Name { get; set; } = "";
|
|
|
- public string Condition { get; set; } = "";
|
|
|
- public List<SugarParameter> Parameters { get; set; } = new List<SugarParameter>();
|
|
|
- public int Priority { get; set; }
|
|
|
- public string Description { get; set; } = "";
|
|
|
- public bool FallbackToNext { get; set; } = true;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 相关性信息(增强版)
|
|
|
- /// </summary>
|
|
|
- public class RelevanceInfo
|
|
|
- {
|
|
|
- public SearchStructure SearchStructure { get; set; }
|
|
|
- public int ExpectedMatches { get; set; }
|
|
|
- public List<string> BoostFields { get; set; } = new List<string>();
|
|
|
- public List<ScoringRule> ScoringRules { get; set; } = new List<ScoringRule>();
|
|
|
- public MatchStrategy MatchStrategy { get; set; }
|
|
|
- public DateTime GeneratedAt { get; set; }
|
|
|
- public bool HasSmartSplitting { get; set; }
|
|
|
- public List<string> SplittableWords { get; set; } = new List<string>();
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 匹配策略(增强版)
|
|
|
- /// </summary>
|
|
|
- public class MatchStrategy
|
|
|
- {
|
|
|
- public string StrategyName { get; set; } = "";
|
|
|
- public int ComplexityScore { get; set; }
|
|
|
- public bool UseExactMatch { get; set; }
|
|
|
- public bool UsePrefixMatch { get; set; }
|
|
|
- public bool UseFuzzySearch { get; set; }
|
|
|
- public bool UseCharSearch { get; set; }
|
|
|
- public bool UseSmartSplitting { get; set; }
|
|
|
- public int RecommendedPageSize { get; set; } = 20;
|
|
|
- public string Description { get; set; } = "";
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 计分规则
|
|
|
- /// </summary>
|
|
|
- public class ScoringRule
|
|
|
- {
|
|
|
- public string Type { get; set; } = ""; // word, char, combo, exact, smart_split
|
|
|
- public string Value { get; set; } = "";
|
|
|
- public int BaseScore { get; set; }
|
|
|
- public int Bonus { get; set; }
|
|
|
- public double Multiplier { get; set; } = 1.0;
|
|
|
- public string Condition { get; set; } = "";
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 搜索统计信息
|
|
|
- /// </summary>
|
|
|
- public class SearchStats
|
|
|
- {
|
|
|
- public string SearchTerm { get; set; } = "";
|
|
|
- public CombinationType CombinationType { get; set; }
|
|
|
- public int WordCount { get; set; }
|
|
|
- public int CharCount { get; set; }
|
|
|
- public int TotalSegments { get; set; }
|
|
|
- public int ComplexityScore { get; set; }
|
|
|
- public int ResultCount { get; set; }
|
|
|
- public DateTime GeneratedAt { get; set; }
|
|
|
-
|
|
|
- public double MatchRatio => TotalSegments > 0 ? (double)ResultCount / TotalSegments : 0;
|
|
|
- public string ComplexityLevel => ComplexityScore >= 7 ? "高" :
|
|
|
- ComplexityScore >= 4 ? "中" : "低";
|
|
|
- }
|
|
|
-
|
|
|
- #endregion
|
|
|
-
|
|
|
- #region 扩展方法
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// AdvancedSearchHelper 扩展方法
|
|
|
- /// </summary>
|
|
|
- public static class EnhancedSearchExtensions
|
|
|
- {
|
|
|
- /// <summary>
|
|
|
- /// 获取组合类型的详细说明
|
|
|
- /// </summary>
|
|
|
- public static string GetDetail(this CombinationType type)
|
|
|
- {
|
|
|
- return type switch
|
|
|
- {
|
|
|
- CombinationType.WordsOnly => "仅使用词语进行搜索,适合精确的关键词匹配",
|
|
|
- CombinationType.CharsOnly => "仅使用单字进行搜索,适合模糊或记忆不完整的搜索",
|
|
|
- CombinationType.WordsAndChars => "混合使用词语和单字,提供更灵活的搜索体验",
|
|
|
- _ => "未知的搜索类型"
|
|
|
- };
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 获取搜索模式的推荐使用场景
|
|
|
- /// </summary>
|
|
|
- public static string GetUsageScenario(this SearchMode mode)
|
|
|
- {
|
|
|
- return mode switch
|
|
|
- {
|
|
|
- SearchMode.AutoDetect => "适用于通用搜索场景,系统自动识别最佳搜索方式",
|
|
|
- SearchMode.SingleWord => "适用于已知确切名称或关键词的搜索",
|
|
|
- SearchMode.MultiWord => "适用于多个相关关键词的组合搜索",
|
|
|
- SearchMode.SingleChar => "适用于模糊搜索或拼音首字母搜索",
|
|
|
- SearchMode.Exact => "适用于需要完全匹配的精确搜索",
|
|
|
- SearchMode.Advanced => "适用于专业用户的高级搜索需求",
|
|
|
- _ => "通用搜索场景"
|
|
|
- };
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- #endregion
|
|
|
-}
|