AdvancedSearchHelper.cs 94 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521
  1. using System.ComponentModel;
  2. using System.Text.RegularExpressions;
  3. namespace OASystem.Infrastructure.Tools
  4. {
  5. /// <summary>
  6. /// 增强版高级搜索助手(支持智能拆分组合词)
  7. /// 支持组合查询:多词条/单词条 + 单字 混合搜索
  8. /// 提供智能搜索条件构建、相关性分析、分阶段搜索等功能
  9. /// </summary>
  10. public class AdvancedSearchHelper
  11. {
  12. #region 公共方法 - 主要搜索功能
  13. /// <summary>
  14. /// 构建增强搜索条件(支持组合查询和智能拆分)
  15. /// </summary>
  16. /// <param name="searchTerm">搜索关键词</param>
  17. /// <param name="searchFields">搜索字段列表</param>
  18. /// <param name="prefix">参数前缀,用于避免参数名冲突</param>
  19. /// <param name="searchMode">搜索模式,默认为自动检测</param>
  20. /// <param name="combinationMode">组合模式,默认智能混合</param>
  21. /// <param name="config">搜索配置选项</param>
  22. /// <returns>构建的SQL条件和参数</returns>
  23. /// <example>
  24. /// <code>
  25. /// var fields = new List&lt;string&gt; { "Name", "Description" };
  26. /// var config = new SearchConfig {
  27. /// EnableSmartSplitting = true, // 启用智能拆分
  28. /// RequireAllWordsForSplit = false
  29. /// };
  30. /// var (condition, parameters) = AdvancedSearchHelper.BuildEnhancedSearchCondition(
  31. /// "重庆科学",
  32. /// fields,
  33. /// "search_",
  34. /// SearchMode.AutoDetect,
  35. /// CombinationMode.SmartMix,
  36. /// config
  37. /// );
  38. /// </code>
  39. /// </example>
  40. public static (string Condition, List<SugarParameter> Parameters) BuildEnhancedSearchCondition(
  41. string searchTerm,
  42. List<string> searchFields,
  43. string prefix = "",
  44. SearchMode searchMode = SearchMode.AutoDetect,
  45. CombinationMode combinationMode = CombinationMode.SmartMix,
  46. SearchConfig config = null)
  47. {
  48. // 参数验证
  49. if (searchFields == null || !searchFields.Any())
  50. throw new ArgumentException("搜索字段列表不能为空", nameof(searchFields));
  51. if (string.IsNullOrWhiteSpace(searchTerm))
  52. return (string.Empty, new List<SugarParameter>());
  53. searchTerm = searchTerm.Trim();
  54. config ??= new SearchConfig();
  55. // 检查搜索词长度
  56. if (searchTerm.Length > config.MaxSearchTermLength)
  57. searchTerm = searchTerm.Substring(0, config.MaxSearchTermLength);
  58. // 分析搜索词结构
  59. var structure = AnalyzeSearchStructure(searchTerm);
  60. // 根据组合模式构建条件
  61. return combinationMode switch
  62. {
  63. CombinationMode.SmartMix => BuildSmartMixCondition(structure, searchFields, prefix, searchMode, config),
  64. CombinationMode.WordsAndChars => BuildWordsAndCharsCondition(structure, searchFields, prefix, config),
  65. CombinationMode.WordsOnly => BuildWordsOnlyCondition(structure, searchFields, prefix, config),
  66. CombinationMode.CharsOnly => BuildCharsOnlyCondition(structure, searchFields, prefix, config),
  67. _ => BuildSmartMixCondition(structure, searchFields, prefix, searchMode, config)
  68. };
  69. }
  70. /// <summary>
  71. /// 为Sugar ORM构建增强搜索条件
  72. /// </summary>
  73. public static (string Condition, List<SugarParameter> Parameters) BuildSugarSearchCondition(
  74. string searchTerm,
  75. List<string> searchFields,
  76. string prefix = "",
  77. SearchMode searchMode = SearchMode.AutoDetect,
  78. CombinationMode combinationMode = CombinationMode.SmartMix,
  79. SearchConfig config = null)
  80. {
  81. // 先使用原有方法构建条件
  82. var (condition, parameters) = BuildEnhancedSearchCondition(
  83. searchTerm, searchFields, prefix, searchMode, combinationMode, config);
  84. if (string.IsNullOrEmpty(condition))
  85. return (string.Empty, parameters);
  86. // 转换参数名为Sugar ORM兼容格式
  87. var sugarParameters = new List<SugarParameter>();
  88. var paramMapping = new Dictionary<string, string>();
  89. int paramIndex = 0;
  90. foreach (var param in parameters)
  91. {
  92. var oldName = param.ParameterName;
  93. var newName = $"@{prefix}p{paramIndex++}";
  94. paramMapping[oldName] = newName;
  95. sugarParameters.Add(new SugarParameter(newName, param.Value));
  96. }
  97. // 替换参数名
  98. foreach (var mapping in paramMapping)
  99. {
  100. condition = condition.Replace(mapping.Key, mapping.Value);
  101. }
  102. // 转换LIKE表达式为Sugar ORM格式
  103. condition = Regex.Replace(condition, @"LIKE @\w+", match =>
  104. {
  105. var paramName = match.Value.Substring(5); // 移除"LIKE "
  106. return $"LIKE '%' + {paramName} + '%'";
  107. });
  108. return (condition, sugarParameters);
  109. }
  110. /// <summary>
  111. /// 构建带相关性信息的搜索条件
  112. /// </summary>
  113. /// <param name="searchTerm">搜索关键词</param>
  114. /// <param name="searchFields">搜索字段列表</param>
  115. /// <param name="prefix">参数前缀</param>
  116. /// <param name="searchMode">搜索模式</param>
  117. /// <param name="config">搜索配置</param>
  118. /// <returns>包含条件、参数和相关性的完整结果</returns>
  119. public static (string Condition, List<SugarParameter> Parameters, RelevanceInfo Relevance)
  120. BuildRelevanceSearchCondition(
  121. string searchTerm,
  122. List<string> searchFields,
  123. string prefix = "",
  124. SearchMode searchMode = SearchMode.AutoDetect,
  125. SearchConfig config = null)
  126. {
  127. config ??= new SearchConfig();
  128. var structure = AnalyzeSearchStructure(searchTerm);
  129. var (condition, parameters) = BuildEnhancedSearchCondition(
  130. searchTerm, searchFields, prefix, searchMode, CombinationMode.SmartMix, config);
  131. var relevance = new RelevanceInfo
  132. {
  133. SearchStructure = structure,
  134. ExpectedMatches = CalculateExpectedMatches(structure, searchFields.Count),
  135. BoostFields = GetBoostFields(structure, searchFields),
  136. ScoringRules = GetScoringRules(structure, config),
  137. MatchStrategy = GetMatchStrategy(structure, config),
  138. GeneratedAt = DateTime.Now
  139. };
  140. return (condition, parameters, relevance);
  141. }
  142. /// <summary>
  143. /// 构建分阶段搜索条件(用于分阶段匹配和降级搜索)
  144. /// </summary>
  145. /// <param name="searchTerm">搜索关键词</param>
  146. /// <param name="searchFields">搜索字段列表</param>
  147. /// <param name="prefix">参数前缀</param>
  148. /// <param name="config">搜索配置</param>
  149. /// <returns>按优先级排序的搜索阶段列表</returns>
  150. public static List<SearchStage> BuildStagedSearchConditions(
  151. string searchTerm,
  152. List<string> searchFields,
  153. string prefix = "",
  154. SearchConfig config = null)
  155. {
  156. config ??= new SearchConfig();
  157. var structure = AnalyzeSearchStructure(searchTerm);
  158. var stages = new List<SearchStage>();
  159. // 阶段1:精确匹配(完全相等)
  160. if (structure.Words.Any() && config.EnableExactMatch)
  161. {
  162. foreach (var word in structure.Words.Where(w => w.Length >= config.MinExactMatchLength))
  163. {
  164. var (condition, parameters) = BuildExactCondition(word, searchFields, $"{prefix}exact_{stages.Count}");
  165. if (!string.IsNullOrEmpty(condition))
  166. {
  167. stages.Add(new SearchStage
  168. {
  169. Stage = 1,
  170. Name = $"精确匹配: {word}",
  171. Condition = condition,
  172. Parameters = parameters,
  173. Priority = 100,
  174. Description = $"完全匹配词语'{word}'",
  175. FallbackToNext = false
  176. });
  177. }
  178. }
  179. }
  180. // 阶段2:开头匹配
  181. if (structure.Words.Any() && config.EnablePrefixMatch)
  182. {
  183. foreach (var word in structure.Words)
  184. {
  185. var (condition, parameters) = BuildPrefixCondition(word, searchFields, $"{prefix}prefix_{stages.Count}");
  186. if (!string.IsNullOrEmpty(condition))
  187. {
  188. stages.Add(new SearchStage
  189. {
  190. Stage = 2,
  191. Name = $"开头匹配: {word}",
  192. Condition = condition,
  193. Parameters = parameters,
  194. Priority = 80,
  195. Description = $"以'{word}'开头",
  196. FallbackToNext = true
  197. });
  198. }
  199. }
  200. }
  201. // 阶段3:词语模糊匹配
  202. if (structure.Words.Any() && config.EnableFuzzySearch)
  203. {
  204. var wordsText = string.Join(" ", structure.Words);
  205. var (condition, parameters) = BuildMultiWordCondition(
  206. wordsText, searchFields, $"{prefix}fuzzy_{stages.Count}",
  207. config.RequireAllWords);
  208. if (!string.IsNullOrEmpty(condition))
  209. {
  210. stages.Add(new SearchStage
  211. {
  212. Stage = 3,
  213. Name = "词语模糊匹配",
  214. Condition = condition,
  215. Parameters = parameters,
  216. Priority = 60,
  217. Description = $"包含词语: {wordsText}",
  218. FallbackToNext = true
  219. });
  220. }
  221. }
  222. // 阶段4:单字匹配
  223. if (structure.SingleChars.Any() && config.EnableCharSearch)
  224. {
  225. var charsText = string.Join("", structure.SingleChars);
  226. var (condition, parameters) = BuildSingleCharCondition(
  227. charsText, searchFields, $"{prefix}chars_{stages.Count}",
  228. config.RequireAllChars);
  229. if (!string.IsNullOrEmpty(condition))
  230. {
  231. stages.Add(new SearchStage
  232. {
  233. Stage = 4,
  234. Name = "单字匹配",
  235. Condition = condition,
  236. Parameters = parameters,
  237. Priority = 40,
  238. Description = $"包含单字: {charsText}",
  239. FallbackToNext = true
  240. });
  241. }
  242. }
  243. // 阶段5:智能拆分匹配(新增阶段)
  244. if (config.EnableSmartSplitting && structure.Words.Any(w => w.Length >= config.MinSplitLength))
  245. {
  246. var longWords = structure.Words.Where(w => w.Length >= config.MinSplitLength).ToList();
  247. foreach (var longWord in longWords)
  248. {
  249. if (IsSplittableCombinationWord(longWord))
  250. {
  251. var (condition, parameters) = BuildSmartSplitConditionForWord(
  252. longWord, searchFields, $"{prefix}split_{stages.Count}", config);
  253. if (!string.IsNullOrEmpty(condition))
  254. {
  255. stages.Add(new SearchStage
  256. {
  257. Stage = 5,
  258. Name = $"智能拆分: {longWord}",
  259. Condition = condition,
  260. Parameters = parameters,
  261. Priority = 50, // 优先级在词语模糊匹配和单字匹配之间
  262. Description = $"智能拆分'{longWord}'为子词或单字",
  263. FallbackToNext = true
  264. });
  265. }
  266. }
  267. }
  268. }
  269. // 阶段6:宽松混合匹配
  270. if (stages.Any(s => s.Stage >= 3 && s.Stage <= 5))
  271. {
  272. var mixedConditions = stages
  273. .Where(s => s.Stage >= 3 && s.Stage <= 5)
  274. .Select(s => s.Condition)
  275. .ToList();
  276. var mixedParameters = stages
  277. .Where(s => s.Stage >= 3 && s.Stage <= 5)
  278. .SelectMany(s => s.Parameters)
  279. .ToList();
  280. if (mixedConditions.Any())
  281. {
  282. stages.Add(new SearchStage
  283. {
  284. Stage = 6,
  285. Name = "宽松混合匹配",
  286. Condition = $"({string.Join(" OR ", mixedConditions)})",
  287. Parameters = mixedParameters,
  288. Priority = 20,
  289. Description = "词语或单字的任意匹配",
  290. FallbackToNext = false
  291. });
  292. }
  293. }
  294. return stages.OrderByDescending(s => s.Priority).ToList();
  295. }
  296. #endregion
  297. #region 核心构建方法
  298. /// <summary>
  299. /// 智能混合构建条件(支持智能拆分组合词)
  300. /// </summary>
  301. private static (string, List<SugarParameter>) BuildSmartMixCondition(
  302. SearchStructure structure,
  303. List<string> searchFields,
  304. string prefix,
  305. SearchMode searchMode,
  306. SearchConfig config)
  307. {
  308. var parameters = new List<SugarParameter>();
  309. var conditions = new List<string>();
  310. // 分析结构复杂度
  311. var complexity = CalculateComplexityScore(structure);
  312. // 检查是否有需要智能拆分的组合词
  313. var longWords = structure.Words.Where(w => w.Length >= config.MinSplitLength).ToList();
  314. var shouldSplitLongWords = longWords.Any() && config.EnableSmartSplitting;
  315. if (shouldSplitLongWords && longWords.Any(w => IsSplittableCombinationWord(w)))
  316. {
  317. // 使用智能拆分策略处理组合词
  318. return BuildSmartSplitCondition(structure, searchFields, prefix, config, longWords);
  319. }
  320. // 智能分析混合类型的特点
  321. var hasLongWords = structure.Words.Any(w => w.Length >= 2);
  322. var hasManyChars = structure.SingleChars.Count >= 3;
  323. var isCharDominant = structure.SingleChars.Count > structure.Words.Count;
  324. var hasMixedTypes = structure.HasWords && structure.HasSingleChars;
  325. // 根据混合特点选择更精细的策略
  326. string strategy;
  327. if (hasMixedTypes)
  328. {
  329. if (isCharDominant && hasManyChars)
  330. {
  331. strategy = "char_optimized"; // 单字主导,优化单字搜索
  332. }
  333. else if (hasLongWords && !hasManyChars)
  334. {
  335. strategy = "word_focused"; // 词语为主,单字为辅
  336. }
  337. else
  338. {
  339. strategy = complexity >= 6 ? "strict_mixed" :
  340. complexity >= 3 ? "balanced_mixed" : "loose_mixed";
  341. }
  342. }
  343. else
  344. {
  345. strategy = complexity >= 7 ? "strict" :
  346. complexity >= 4 ? "balanced" : "loose";
  347. }
  348. // 处理词语部分
  349. if (structure.HasWords)
  350. {
  351. if (structure.Words.Count == 1)
  352. {
  353. // 单个词语
  354. var (wordCondition, wordParams) = BuildSingleWordCondition(
  355. structure.Words[0], searchFields, $"{prefix}word", config);
  356. if (!string.IsNullOrEmpty(wordCondition))
  357. {
  358. conditions.Add(wordCondition);
  359. parameters.AddRange(wordParams);
  360. }
  361. }
  362. else
  363. {
  364. // 多个词语
  365. var wordsText = string.Join(" ", structure.Words);
  366. // 根据策略动态调整词语匹配要求
  367. bool requireAllWords;
  368. if (strategy == "char_optimized" && isCharDominant)
  369. {
  370. // 单字主导时,放宽词语要求
  371. requireAllWords = false;
  372. }
  373. else
  374. {
  375. requireAllWords = strategy.Contains("strict") || config.RequireAllWords;
  376. }
  377. var (wordsCondition, wordsParams) = BuildMultiWordCondition(
  378. wordsText, searchFields, $"{prefix}words", requireAllWords);
  379. if (!string.IsNullOrEmpty(wordsCondition))
  380. {
  381. conditions.Add(wordsCondition);
  382. parameters.AddRange(wordsParams);
  383. }
  384. }
  385. }
  386. // 处理单字部分 - 优化策略
  387. if (structure.HasSingleChars)
  388. {
  389. var charsText = string.Join("", structure.SingleChars);
  390. // 根据策略动态调整单字匹配要求
  391. bool requireAllChars;
  392. string charStrategy;
  393. if (strategy == "char_optimized")
  394. {
  395. // 单字优化策略:多个单字时放宽要求
  396. requireAllChars = structure.SingleChars.Count <= 3; // 3个及以下单字要求全部匹配
  397. charStrategy = "optimized";
  398. }
  399. else if (strategy.Contains("loose"))
  400. {
  401. requireAllChars = false;
  402. charStrategy = "loose";
  403. }
  404. else if (strategy.Contains("strict"))
  405. {
  406. requireAllChars = true;
  407. charStrategy = "strict";
  408. }
  409. else
  410. {
  411. requireAllChars = strategy.Contains("balanced") && structure.SingleChars.Count <= 2;
  412. charStrategy = "balanced";
  413. }
  414. var (charsCondition, charsParams) = BuildEnhancedCharCondition(
  415. charsText, searchFields, $"{prefix}chars",
  416. requireAllChars, charStrategy);
  417. if (!string.IsNullOrEmpty(charsCondition))
  418. {
  419. conditions.Add(charsCondition);
  420. parameters.AddRange(charsParams);
  421. }
  422. }
  423. // 构建最终条件
  424. if (!conditions.Any())
  425. return (string.Empty, parameters);
  426. string finalCondition = BuildMixedConditionByStrategy(
  427. conditions, structure, strategy);
  428. return (finalCondition, parameters);
  429. }
  430. /// <summary>
  431. /// 构建智能拆分条件(针对长词如"重庆科学")
  432. /// </summary>
  433. private static (string, List<SugarParameter>) BuildSmartSplitCondition(
  434. SearchStructure structure,
  435. List<string> searchFields,
  436. string prefix,
  437. SearchConfig config,
  438. List<string> longWords)
  439. {
  440. var parameters = new List<SugarParameter>();
  441. var conditions = new List<string>();
  442. // 处理所有长词(需要拆分的词)
  443. foreach (var longWord in longWords)
  444. {
  445. if (IsSplittableCombinationWord(longWord))
  446. {
  447. // 为每个长词构建多层次条件
  448. var (wordConditions, wordParams) = BuildMultiLevelConditionForWord(
  449. longWord, searchFields, $"{prefix}word_{longWord.GetHashCode()}", config);
  450. conditions.AddRange(wordConditions);
  451. parameters.AddRange(wordParams);
  452. }
  453. else
  454. {
  455. // 普通长词,直接搜索
  456. var (wordCondition, wordParams) = BuildSingleWordCondition(
  457. longWord, searchFields, $"{prefix}long_{parameters.Count}", config);
  458. if (!string.IsNullOrEmpty(wordCondition))
  459. {
  460. conditions.Add(wordCondition);
  461. parameters.AddRange(wordParams);
  462. }
  463. }
  464. }
  465. // 处理剩余的非长词
  466. var normalWords = structure.Words.Where(w => w.Length < config.MinSplitLength).ToList();
  467. if (normalWords.Any())
  468. {
  469. var wordsText = string.Join(" ", normalWords);
  470. var (normalCondition, normalParams) = BuildMultiWordCondition(
  471. wordsText, searchFields, $"{prefix}normal", config.RequireAllWords);
  472. if (!string.IsNullOrEmpty(normalCondition))
  473. {
  474. conditions.Add(normalCondition);
  475. parameters.AddRange(normalParams);
  476. }
  477. }
  478. // 处理原有的单字
  479. if (structure.SingleChars.Any())
  480. {
  481. var charsText = string.Join("", structure.SingleChars);
  482. var (charCondition, charParams) = BuildSingleCharCondition(
  483. charsText, searchFields, $"{prefix}single", false);
  484. if (!string.IsNullOrEmpty(charCondition))
  485. {
  486. conditions.Add(charCondition);
  487. parameters.AddRange(charParams);
  488. }
  489. }
  490. // 构建最终条件:使用层次化OR连接
  491. if (conditions.Count == 0)
  492. return (string.Empty, parameters);
  493. if (conditions.Count == 1)
  494. return (conditions[0], parameters);
  495. // 对条件进行分组和优先级排序
  496. var groupedConditions = GroupConditionsByPriority(conditions, longWords);
  497. return (BuildHierarchicalCondition(groupedConditions), parameters);
  498. }
  499. /// <summary>
  500. /// 为单个长词构建多层次条件
  501. /// </summary>
  502. private static (List<string> Conditions, List<SugarParameter> Parameters) BuildMultiLevelConditionForWord(
  503. string longWord,
  504. List<string> searchFields,
  505. string prefix,
  506. SearchConfig config)
  507. {
  508. var conditions = new List<string>();
  509. var parameters = new List<SugarParameter>();
  510. // 第一层:完整词匹配(最高优先级)
  511. var (fullCondition, fullParams) = BuildSingleWordCondition(
  512. longWord, searchFields, $"{prefix}_full", config);
  513. if (!string.IsNullOrEmpty(fullCondition))
  514. {
  515. conditions.Add(fullCondition);
  516. parameters.AddRange(fullParams);
  517. }
  518. // 第二层:智能拆分成子词
  519. var subWords = SplitCombinationWord(longWord);
  520. if (subWords.Count >= 2)
  521. {
  522. var (splitCondition, splitParams) = BuildMultiWordCondition(
  523. string.Join(" ", subWords),
  524. searchFields,
  525. $"{prefix}_split",
  526. config.RequireAllWordsForSplit);
  527. if (!string.IsNullOrEmpty(splitCondition))
  528. {
  529. conditions.Add($"({splitCondition})"); // 用括号包裹
  530. parameters.AddRange(splitParams);
  531. }
  532. }
  533. // 第三层:拆分成单字作为备选
  534. var chars = longWord.ToCharArray().Select(c => c.ToString()).ToList();
  535. var (charCondition, charParams) = BuildSingleCharCondition(
  536. string.Join("", chars),
  537. searchFields,
  538. $"{prefix}_chars",
  539. config.RequireAllCharsForSplit);
  540. if (!string.IsNullOrEmpty(charCondition))
  541. {
  542. conditions.Add($"({charCondition})");
  543. parameters.AddRange(charParams);
  544. }
  545. // 如果有多层条件,构建OR连接
  546. if (conditions.Count > 1)
  547. {
  548. var combinedCondition = $"({string.Join(" OR ", conditions)})";
  549. return (new List<string> { combinedCondition }, parameters);
  550. }
  551. return (conditions, parameters);
  552. }
  553. /// <summary>
  554. /// 构建智能拆分条件(用于分阶段搜索)
  555. /// </summary>
  556. private static (string, List<SugarParameter>) BuildSmartSplitConditionForWord(
  557. string longWord,
  558. List<string> searchFields,
  559. string prefix,
  560. SearchConfig config)
  561. {
  562. var (conditions, parameters) = BuildMultiLevelConditionForWord(longWord, searchFields, prefix, config);
  563. if (conditions.Any())
  564. {
  565. return (conditions[0], parameters);
  566. }
  567. return (string.Empty, new List<SugarParameter>());
  568. }
  569. /// <summary>
  570. /// 词语和单字组合条件
  571. /// </summary>
  572. private static (string, List<SugarParameter>) BuildWordsAndCharsCondition(
  573. SearchStructure structure,
  574. List<string> searchFields,
  575. string prefix,
  576. SearchConfig config)
  577. {
  578. var parameters = new List<SugarParameter>();
  579. var conditions = new List<string>();
  580. // 词语部分(必须满足)
  581. if (structure.Words.Any())
  582. {
  583. var wordsText = string.Join(" ", structure.Words);
  584. var (wordsCondition, wordsParams) = BuildMultiWordCondition(
  585. wordsText, searchFields, $"{prefix}w", true);
  586. if (!string.IsNullOrEmpty(wordsCondition))
  587. {
  588. conditions.Add(wordsCondition);
  589. parameters.AddRange(wordsParams);
  590. }
  591. }
  592. // 单字部分(作为增强条件)
  593. if (structure.SingleChars.Any())
  594. {
  595. var charsText = string.Join("", structure.SingleChars);
  596. var (charsCondition, charsParams) = BuildSingleCharCondition(
  597. charsText, searchFields, $"{prefix}c", false);
  598. if (!string.IsNullOrEmpty(charsCondition))
  599. {
  600. // 单字作为加分项,用OR连接
  601. conditions.Add($"({charsCondition})");
  602. parameters.AddRange(charsParams);
  603. }
  604. }
  605. if (!conditions.Any())
  606. return (string.Empty, parameters);
  607. // 构建条件:词语必须满足,单字作为增强
  608. string finalCondition = conditions.Count > 1
  609. ? $"({conditions[0]} AND ({string.Join(" OR ", conditions.Skip(1))}))"
  610. : conditions[0];
  611. return (finalCondition, parameters);
  612. }
  613. /// <summary>
  614. /// 仅词语条件
  615. /// </summary>
  616. private static (string, List<SugarParameter>) BuildWordsOnlyCondition(
  617. SearchStructure structure,
  618. List<string> searchFields,
  619. string prefix,
  620. SearchConfig config)
  621. {
  622. if (!structure.Words.Any())
  623. return (string.Empty, new List<SugarParameter>());
  624. var wordsText = string.Join(" ", structure.Words);
  625. if (structure.Words.Count == 1)
  626. {
  627. return BuildSingleWordCondition(wordsText, searchFields, prefix, config);
  628. }
  629. else
  630. {
  631. return BuildMultiWordCondition(wordsText, searchFields, prefix, config.RequireAllWords);
  632. }
  633. }
  634. /// <summary>
  635. /// 仅单字条件
  636. /// </summary>
  637. private static (string, List<SugarParameter>) BuildCharsOnlyCondition(
  638. SearchStructure structure,
  639. List<string> searchFields,
  640. string prefix,
  641. SearchConfig config)
  642. {
  643. if (!structure.SingleChars.Any())
  644. return (string.Empty, new List<SugarParameter>());
  645. var charsText = string.Join("", structure.SingleChars);
  646. return BuildSingleCharCondition(charsText, searchFields, prefix, config.RequireAllChars);
  647. }
  648. #endregion
  649. #region 基础条件构建方法
  650. /// <summary>
  651. /// 构建单词条查询条件
  652. /// </summary>
  653. public static (string Condition, List<SugarParameter> Parameters) BuildSingleWordCondition(
  654. string searchTerm,
  655. List<string> searchFields,
  656. string prefix,
  657. SearchConfig config = null)
  658. {
  659. config ??= new SearchConfig();
  660. var parameters = new List<SugarParameter>();
  661. var fieldConditions = new List<string>();
  662. var paramName = $"{prefix}word";
  663. foreach (var field in searchFields)
  664. {
  665. fieldConditions.Add($"{field} LIKE @{paramName}");
  666. }
  667. string condition = $"({string.Join(" OR ", fieldConditions)})";
  668. parameters.Add(new SugarParameter($"@{paramName}", $"%{searchTerm}%"));
  669. return (condition, parameters);
  670. }
  671. /// <summary>
  672. /// 构建多词条查询条件
  673. /// </summary>
  674. public static (string Condition, List<SugarParameter> Parameters) BuildMultiWordCondition(
  675. string searchTerm,
  676. List<string> searchFields,
  677. string prefix,
  678. bool requireAll = true)
  679. {
  680. var parameters = new List<SugarParameter>();
  681. var separators = new[] { ' ', ',', '、', ',', ';', ';' };
  682. var words = searchTerm.Split(separators, StringSplitOptions.RemoveEmptyEntries)
  683. .Select(w => w.Trim())
  684. .Where(w => !string.IsNullOrEmpty(w))
  685. .Distinct()
  686. .ToList();
  687. if (!words.Any())
  688. return (string.Empty, parameters);
  689. var wordConditions = new List<string>();
  690. foreach (var word in words)
  691. {
  692. var fieldConditions = new List<string>();
  693. var paramName = $"{prefix}word_{parameters.Count}";
  694. foreach (var field in searchFields)
  695. {
  696. fieldConditions.Add($"{field} LIKE @{paramName}");
  697. }
  698. wordConditions.Add($"({string.Join(" OR ", fieldConditions)})");
  699. parameters.Add(new SugarParameter($"@{paramName}", $"%{word}%"));
  700. }
  701. // 构建条件
  702. string condition;
  703. if (requireAll && words.Count > 1)
  704. {
  705. condition = $"({string.Join(" AND ", wordConditions)})";
  706. }
  707. else if (!requireAll && words.Count > 1)
  708. {
  709. condition = $"({string.Join(" OR ", wordConditions)})";
  710. }
  711. else
  712. {
  713. condition = wordConditions.FirstOrDefault() ?? "";
  714. }
  715. return (condition, parameters);
  716. }
  717. /// <summary>
  718. /// 构建单字查询条件
  719. /// </summary>
  720. public static (string Condition, List<SugarParameter> Parameters) BuildSingleCharCondition(
  721. string searchTerm,
  722. List<string> searchFields,
  723. string prefix,
  724. bool requireAll = true)
  725. {
  726. var parameters = new List<SugarParameter>();
  727. var cleanTerm = RemoveSeparators(searchTerm);
  728. var chars = cleanTerm.ToCharArray().Distinct().ToArray();
  729. if (chars.Length == 0)
  730. return (string.Empty, parameters);
  731. var charConditions = new List<string>();
  732. foreach (char c in chars)
  733. {
  734. var fieldConditions = new List<string>();
  735. var paramName = $"{prefix}char_{parameters.Count}";
  736. foreach (var field in searchFields)
  737. {
  738. fieldConditions.Add($"{field} LIKE @{paramName}");
  739. }
  740. charConditions.Add($"({string.Join(" OR ", fieldConditions)})");
  741. parameters.Add(new SugarParameter($"@{paramName}", $"%{c}%"));
  742. }
  743. // 构建条件
  744. string condition;
  745. if (requireAll && chars.Length > 1)
  746. {
  747. condition = $"({string.Join(" AND ", charConditions)})";
  748. }
  749. else if (!requireAll && chars.Length > 1)
  750. {
  751. condition = $"({string.Join(" OR ", charConditions)})";
  752. }
  753. else
  754. {
  755. condition = charConditions.FirstOrDefault() ?? "";
  756. }
  757. return (condition, parameters);
  758. }
  759. /// <summary>
  760. /// 构建增强的单字搜索条件(支持不同策略)
  761. /// </summary>
  762. private static (string, List<SugarParameter>) BuildEnhancedCharCondition(
  763. string searchTerm,
  764. List<string> searchFields,
  765. string prefix,
  766. bool requireAll,
  767. string strategy)
  768. {
  769. var parameters = new List<SugarParameter>();
  770. var cleanTerm = RemoveSeparators(searchTerm);
  771. var chars = cleanTerm.ToCharArray().Distinct().ToArray();
  772. if (chars.Length == 0)
  773. return (string.Empty, parameters);
  774. // 根据策略调整处理方式
  775. var charConditions = new List<string>();
  776. foreach (char c in chars)
  777. {
  778. var fieldConditions = new List<string>();
  779. var paramName = $"{prefix}char_{parameters.Count}";
  780. foreach (var field in searchFields)
  781. {
  782. // 根据不同策略使用不同的匹配方式
  783. if (strategy == "optimized" && IsChineseCharacter(c))
  784. {
  785. // 优化策略:中文单字更宽松
  786. fieldConditions.Add($"({field} LIKE @{paramName} OR {field} LIKE @{paramName}_prefix)");
  787. parameters.Add(new SugarParameter($"@{paramName}", $"%{c}%"));
  788. parameters.Add(new SugarParameter($"@{paramName}_prefix", $"{c}%"));
  789. }
  790. else
  791. {
  792. fieldConditions.Add($"{field} LIKE @{paramName}");
  793. parameters.Add(new SugarParameter($"@{paramName}", $"%{c}%"));
  794. }
  795. }
  796. charConditions.Add($"({string.Join(" OR ", fieldConditions)})");
  797. }
  798. // 根据策略构建条件
  799. string condition;
  800. if (requireAll && chars.Length > 1)
  801. {
  802. // 必须包含所有单字
  803. condition = $"({string.Join(" AND ", charConditions)})";
  804. }
  805. else if (!requireAll && chars.Length > 1)
  806. {
  807. // 任意一个单字即可
  808. condition = $"({string.Join(" OR ", charConditions)})";
  809. }
  810. else if (strategy == "optimized" && chars.Length > 2)
  811. {
  812. // 优化策略:3个以上单字时,至少匹配一半
  813. int requiredCount = (int)Math.Ceiling(chars.Length / 2.0);
  814. var orConditions = new List<string>();
  815. // 生成所有可能的组合(限制数量避免爆炸)
  816. var combinations = GetCharCombinations(charConditions, chars.Length, requiredCount, 5);
  817. foreach (var combination in combinations)
  818. {
  819. orConditions.Add($"({string.Join(" AND ", combination)})");
  820. }
  821. condition = orConditions.Any() ? $"({string.Join(" OR ", orConditions)})" : charConditions.FirstOrDefault() ?? "";
  822. }
  823. else
  824. {
  825. condition = charConditions.FirstOrDefault() ?? "";
  826. }
  827. return (condition, parameters);
  828. }
  829. /// <summary>
  830. /// 构建精确匹配条件
  831. /// </summary>
  832. public static (string Condition, List<SugarParameter> Parameters) BuildExactCondition(
  833. string searchTerm,
  834. List<string> searchFields,
  835. string prefix)
  836. {
  837. var parameters = new List<SugarParameter>();
  838. var fieldConditions = new List<string>();
  839. var paramName = $"{prefix}exact";
  840. foreach (var field in searchFields)
  841. {
  842. fieldConditions.Add($"{field} = @{paramName}");
  843. }
  844. string condition = $"({string.Join(" OR ", fieldConditions)})";
  845. parameters.Add(new SugarParameter($"@{paramName}", searchTerm.Trim()));
  846. return (condition, parameters);
  847. }
  848. /// <summary>
  849. /// 构建开头匹配条件
  850. /// </summary>
  851. public static (string Condition, List<SugarParameter> Parameters) BuildPrefixCondition(
  852. string searchTerm,
  853. List<string> searchFields,
  854. string prefix)
  855. {
  856. var parameters = new List<SugarParameter>();
  857. var fieldConditions = new List<string>();
  858. var paramName = $"{prefix}prefix";
  859. foreach (var field in searchFields)
  860. {
  861. fieldConditions.Add($"{field} LIKE @{paramName}");
  862. }
  863. string condition = $"({string.Join(" OR ", fieldConditions)})";
  864. parameters.Add(new SugarParameter($"@{paramName}", $"{searchTerm}%"));
  865. return (condition, parameters);
  866. }
  867. /// <summary>
  868. /// 构建结尾匹配条件
  869. /// </summary>
  870. public static (string Condition, List<SugarParameter> Parameters) BuildSuffixCondition(
  871. string searchTerm,
  872. List<string> searchFields,
  873. string prefix)
  874. {
  875. var parameters = new List<SugarParameter>();
  876. var fieldConditions = new List<string>();
  877. var paramName = $"{prefix}suffix";
  878. foreach (var field in searchFields)
  879. {
  880. fieldConditions.Add($"{field} LIKE @{paramName}");
  881. }
  882. string condition = $"({string.Join(" OR ", fieldConditions)})";
  883. parameters.Add(new SugarParameter($"@{paramName}", $"%{searchTerm}"));
  884. return (condition, parameters);
  885. }
  886. #endregion
  887. #region 智能拆分方法
  888. /// <summary>
  889. /// 判断是否为可拆分的组合词
  890. /// </summary>
  891. private static bool IsSplittableCombinationWord(string word)
  892. {
  893. if (word.Length < 4)
  894. return false;
  895. // 检查是否由常见的地名+名词等组成
  896. var commonPatterns = new HashSet<string>
  897. {
  898. "重庆", "北京", "上海", "广州", "深圳", "天津", "武汉", "成都", "杭州", "南京",
  899. "科学", "技术", "研究", "发展", "教育", "培训", "管理", "系统", "工程", "设计",
  900. "大学", "学院", "学校", "公司", "集团", "中心", "研究所", "实验室", "医院", "银行"
  901. };
  902. // 尝试找到明显的组合边界
  903. for (int i = 2; i <= word.Length - 2; i++)
  904. {
  905. var part1 = word.Substring(0, i);
  906. var part2 = word.Substring(i);
  907. if (commonPatterns.Contains(part1) || commonPatterns.Contains(part2))
  908. return true;
  909. // 检查是否都是常见词汇
  910. if (IsCommonWord(part1) && IsCommonWord(part2))
  911. return true;
  912. }
  913. return false;
  914. }
  915. /// <summary>
  916. /// 拆分组合词
  917. /// </summary>
  918. private static List<string> SplitCombinationWord(string word)
  919. {
  920. var result = new List<string>();
  921. if (word.Length <= 2)
  922. {
  923. result.Add(word);
  924. return result;
  925. }
  926. // 优先按2+2模式拆分(如"重庆科学" -> "重庆", "科学")
  927. if (word.Length == 4)
  928. {
  929. result.Add(word.Substring(0, 2));
  930. result.Add(word.Substring(2, 2));
  931. return result;
  932. }
  933. // 尝试按常见模式拆分
  934. var commonTwoCharWords = new HashSet<string>
  935. {
  936. "重庆", "北京", "上海", "广州", "深圳", "天津", "武汉", "成都", "杭州", "南京",
  937. "科学", "技术", "研究", "发展", "教育", "培训", "管理", "系统", "工程", "设计",
  938. "大学", "学院", "学校", "公司", "集团", "中心", "医院", "银行", "保险", "证券",
  939. "软件", "硬件", "网络", "数据", "信息", "智能", "数字", "电子", "通信", "互联网"
  940. };
  941. // 检查前缀
  942. for (int i = 2; i <= Math.Min(4, word.Length - 2); i++)
  943. {
  944. var prefix = word.Substring(0, i);
  945. var suffix = word.Substring(i);
  946. if (commonTwoCharWords.Contains(prefix) ||
  947. (i == 2 && IsLikelyWord(prefix)))
  948. {
  949. result.Add(prefix);
  950. // 递归拆分剩余部分
  951. if (suffix.Length >= 2)
  952. {
  953. var subSplits = SplitCombinationWord(suffix);
  954. result.AddRange(subSplits);
  955. }
  956. else
  957. {
  958. result.Add(suffix);
  959. }
  960. return result;
  961. }
  962. }
  963. // 无法智能拆分,按固定长度拆分
  964. int splitLength = word.Length >= 6 ? 3 : 2;
  965. for (int i = 0; i < word.Length; i += splitLength)
  966. {
  967. var part = word.Substring(i, Math.Min(splitLength, word.Length - i));
  968. result.Add(part);
  969. }
  970. return result;
  971. }
  972. /// <summary>
  973. /// 检查是否可能是常见词汇
  974. /// </summary>
  975. private static bool IsLikelyWord(string word)
  976. {
  977. if (word.Length != 2) return false;
  978. // 检查是否为常见的中文二字词
  979. // 这里可以扩展更复杂的判断逻辑
  980. return IsChineseCharacter(word[0]) && IsChineseCharacter(word[1]);
  981. }
  982. /// <summary>
  983. /// 检查是否是常见词汇
  984. /// </summary>
  985. private static bool IsCommonWord(string word)
  986. {
  987. // 这里可以扩展更复杂的常见词判断逻辑
  988. // 例如从词典加载、使用统计等
  989. var commonWords = new HashSet<string>
  990. {
  991. "中国", "国家", "人民", "社会", "经济", "文化", "教育", "科技", "发展",
  992. "企业", "公司", "市场", "产品", "服务", "技术", "管理", "工作", "生活"
  993. };
  994. return commonWords.Contains(word) ||
  995. (word.Length == 2 && IsChineseCharacter(word[0]) && IsChineseCharacter(word[1]));
  996. }
  997. /// <summary>
  998. /// 按优先级分组条件
  999. /// </summary>
  1000. private static Dictionary<string, List<string>> GroupConditionsByPriority(
  1001. List<string> conditions, List<string> longWords)
  1002. {
  1003. var groups = new Dictionary<string, List<string>>
  1004. {
  1005. ["original"] = new List<string>(), // 原词条件
  1006. ["split"] = new List<string>(), // 拆分子词条件
  1007. ["chars"] = new List<string>(), // 单字条件
  1008. ["normal"] = new List<string>() // 普通词条件
  1009. };
  1010. // 简单分组逻辑,实际使用中可能需要更复杂的判断
  1011. int longWordCount = longWords.Count;
  1012. int currentIndex = 0;
  1013. // 原词条件(每个长词一个)
  1014. for (int i = 0; i < longWordCount && currentIndex < conditions.Count; i++)
  1015. {
  1016. groups["original"].Add(conditions[currentIndex]);
  1017. currentIndex++;
  1018. }
  1019. // 拆分子词条件
  1020. for (int i = 0; i < longWordCount && currentIndex < conditions.Count; i++)
  1021. {
  1022. groups["split"].Add(conditions[currentIndex]);
  1023. currentIndex++;
  1024. }
  1025. // 单字条件
  1026. for (int i = 0; i < longWordCount && currentIndex < conditions.Count; i++)
  1027. {
  1028. groups["chars"].Add(conditions[currentIndex]);
  1029. currentIndex++;
  1030. }
  1031. // 剩余的都是普通词条件
  1032. while (currentIndex < conditions.Count)
  1033. {
  1034. groups["normal"].Add(conditions[currentIndex]);
  1035. currentIndex++;
  1036. }
  1037. return groups;
  1038. }
  1039. /// <summary>
  1040. /// 构建层次化条件
  1041. /// </summary>
  1042. private static string BuildHierarchicalCondition(Dictionary<string, List<string>> groups)
  1043. {
  1044. var parts = new List<string>();
  1045. // 第一层:原词匹配(最高优先级)
  1046. if (groups["original"].Any())
  1047. {
  1048. parts.Add($"({string.Join(" OR ", groups["original"])})");
  1049. }
  1050. // 第二层:拆分子词匹配
  1051. if (groups["split"].Any())
  1052. {
  1053. var splitCondition = groups["split"].Count == 1
  1054. ? groups["split"][0]
  1055. : $"({string.Join(" AND ", groups["split"])})";
  1056. parts.Add($"({splitCondition})");
  1057. }
  1058. // 普通词条件(插入到合适位置)
  1059. if (groups["normal"].Any())
  1060. {
  1061. var normalCondition = groups["normal"].Count == 1
  1062. ? groups["normal"][0]
  1063. : $"({string.Join(" AND ", groups["normal"])})";
  1064. // 如果已经有原词条件,放在第二层;否则放在第一层
  1065. if (parts.Count >= 1)
  1066. parts.Insert(1, normalCondition);
  1067. else
  1068. parts.Add(normalCondition);
  1069. }
  1070. // 第三层:单字匹配(最低优先级)
  1071. if (groups["chars"].Any())
  1072. {
  1073. var charCondition = groups["chars"].Count == 1
  1074. ? groups["chars"][0]
  1075. : $"({string.Join(" OR ", groups["chars"])})";
  1076. parts.Add($"({charCondition})");
  1077. }
  1078. if (parts.Count == 0)
  1079. return string.Empty;
  1080. if (parts.Count == 1)
  1081. return parts[0];
  1082. // 构建层次化OR条件
  1083. return $"({string.Join(" OR ", parts)})";
  1084. }
  1085. /// <summary>
  1086. /// 根据策略构建混合条件
  1087. /// </summary>
  1088. private static string BuildMixedConditionByStrategy(
  1089. List<string> conditions,
  1090. SearchStructure structure,
  1091. string strategy)
  1092. {
  1093. if (conditions.Count == 1)
  1094. return conditions[0];
  1095. switch (strategy)
  1096. {
  1097. case "char_optimized":
  1098. // 单字优化策略:词语作为主要条件,单字作为增强
  1099. return BuildCharOptimizedCondition(conditions, structure);
  1100. case "word_focused":
  1101. // 词语聚焦策略:词语必须,单字作为加分
  1102. return BuildWordFocusedCondition(conditions, structure);
  1103. case "strict_mixed":
  1104. // 严格混合:所有条件必须满足
  1105. return BuildStrictCondition(conditions, structure);
  1106. case "balanced_mixed":
  1107. // 平衡混合:词语必须,单字推荐
  1108. return BuildBalancedCondition(conditions, structure);
  1109. case "loose_mixed":
  1110. // 宽松混合:任意条件满足即可
  1111. return BuildLooseCondition(conditions, structure);
  1112. default:
  1113. return BuildBalancedCondition(conditions, structure);
  1114. }
  1115. }
  1116. /// <summary>
  1117. /// 构建单字优化条件
  1118. /// </summary>
  1119. private static string BuildCharOptimizedCondition(List<string> conditions, SearchStructure structure)
  1120. {
  1121. var wordConditions = conditions
  1122. .Where((c, i) => i < structure.Words.Count)
  1123. .ToList();
  1124. var charConditions = conditions
  1125. .Skip(structure.Words.Count)
  1126. .ToList();
  1127. if (wordConditions.Any() && charConditions.Any())
  1128. {
  1129. // 词语必须,单字作为重要补充(至少匹配一定比例)
  1130. if (charConditions.Count > 2)
  1131. {
  1132. // 多个单字时,要求至少匹配一半
  1133. int requiredChars = (int)Math.Ceiling(charConditions.Count / 2.0);
  1134. var charCombinations = GetConditionCombinations(charConditions, requiredChars, 3);
  1135. if (charCombinations.Any())
  1136. {
  1137. var charCombinationStrings = charCombinations
  1138. .Select(comb => $"({string.Join(" AND ", comb)})")
  1139. .ToList();
  1140. return $"({string.Join(" AND ", wordConditions)} AND ({string.Join(" OR ", charCombinationStrings)}))";
  1141. }
  1142. }
  1143. return $"({string.Join(" AND ", wordConditions)} AND ({string.Join(" OR ", charConditions)}))";
  1144. }
  1145. else if (charConditions.Any())
  1146. {
  1147. // 只有单字时,至少匹配一半
  1148. if (charConditions.Count > 1)
  1149. {
  1150. int requiredChars = (int)Math.Ceiling(charConditions.Count / 2.0);
  1151. var combinations = GetConditionCombinations(charConditions, requiredChars, 3);
  1152. if (combinations.Any())
  1153. {
  1154. var combinationStrings = combinations
  1155. .Select(comb => $"({string.Join(" AND ", comb)})")
  1156. .ToList();
  1157. return $"({string.Join(" OR ", combinationStrings)})";
  1158. }
  1159. }
  1160. return $"({string.Join(" OR ", charConditions)})";
  1161. }
  1162. return conditions.FirstOrDefault() ?? "";
  1163. }
  1164. /// <summary>
  1165. /// 构建词语聚焦条件
  1166. /// </summary>
  1167. private static string BuildWordFocusedCondition(List<string> conditions, SearchStructure structure)
  1168. {
  1169. var wordConditions = conditions
  1170. .Where((c, i) => i < structure.Words.Count)
  1171. .ToList();
  1172. var charConditions = conditions
  1173. .Skip(structure.Words.Count)
  1174. .ToList();
  1175. if (wordConditions.Any() && charConditions.Any())
  1176. {
  1177. // 词语必须满足,单字作为加分项(OR连接)
  1178. return $"({string.Join(" AND ", wordConditions)} AND ({string.Join(" OR ", charConditions)}))";
  1179. }
  1180. return $"({string.Join(" AND ", conditions)})";
  1181. }
  1182. /// <summary>
  1183. /// 获取条件组合(限制数量避免爆炸)
  1184. /// </summary>
  1185. private static List<List<string>> GetConditionCombinations(
  1186. List<string> conditions,
  1187. int requiredCount,
  1188. int maxCombinations = 10)
  1189. {
  1190. var result = new List<List<string>>();
  1191. if (conditions.Count <= requiredCount || conditions.Count <= 1)
  1192. {
  1193. result.Add(conditions);
  1194. return result;
  1195. }
  1196. // 生成C(n, requiredCount)组合,但限制数量
  1197. var indices = Enumerable.Range(0, conditions.Count).ToArray();
  1198. var combinations = GetCombinations(indices, requiredCount, maxCombinations);
  1199. foreach (var combination in combinations)
  1200. {
  1201. var selectedConditions = combination
  1202. .Select(idx => conditions[idx])
  1203. .ToList();
  1204. result.Add(selectedConditions);
  1205. }
  1206. return result;
  1207. }
  1208. /// <summary>
  1209. /// 获取字符条件组合
  1210. /// </summary>
  1211. private static List<List<string>> GetCharCombinations(
  1212. List<string> charConditions,
  1213. int totalChars,
  1214. int requiredCount,
  1215. int maxCombinations)
  1216. {
  1217. return GetConditionCombinations(charConditions, requiredCount, maxCombinations);
  1218. }
  1219. /// <summary>
  1220. /// 获取索引组合(算法)
  1221. /// </summary>
  1222. private static List<List<int>> GetCombinations(int[] indices, int k, int maxCombinations)
  1223. {
  1224. var result = new List<List<int>>();
  1225. var combination = new List<int>();
  1226. void Backtrack(int start)
  1227. {
  1228. if (result.Count >= maxCombinations)
  1229. return;
  1230. if (combination.Count == k)
  1231. {
  1232. result.Add(new List<int>(combination));
  1233. return;
  1234. }
  1235. for (int i = start; i < indices.Length; i++)
  1236. {
  1237. combination.Add(indices[i]);
  1238. Backtrack(i + 1);
  1239. combination.RemoveAt(combination.Count - 1);
  1240. if (result.Count >= maxCombinations)
  1241. break;
  1242. }
  1243. }
  1244. Backtrack(0);
  1245. return result;
  1246. }
  1247. #endregion
  1248. #region 分析工具方法
  1249. /// <summary>
  1250. /// 分析搜索词结构
  1251. /// </summary>
  1252. public static SearchStructure AnalyzeSearchStructure(string searchTerm)
  1253. {
  1254. var structure = new SearchStructure
  1255. {
  1256. OriginalTerm = searchTerm,
  1257. CleanTerm = searchTerm.Trim(),
  1258. AnalyzedAt = DateTime.Now
  1259. };
  1260. // 移除分隔符获取所有字符
  1261. var cleanTerm = RemoveSeparators(searchTerm);
  1262. structure.AllCharacters = cleanTerm.ToCharArray().ToList();
  1263. // 分割成词条(保持原始分割)
  1264. var separators = new[] { ' ', ',', '、', ',', ';', ';', '|', '/', '\\' };
  1265. var rawSegments = searchTerm.Split(separators, StringSplitOptions.RemoveEmptyEntries)
  1266. .Select(s => s.Trim())
  1267. .Where(s => !string.IsNullOrEmpty(s))
  1268. .ToList();
  1269. structure.Segments = rawSegments;
  1270. // 分析每个段落的类型
  1271. foreach (var segment in rawSegments)
  1272. {
  1273. if (segment.Length == 1 && IsChineseCharacter(segment[0]))
  1274. {
  1275. structure.SingleChars.Add(segment);
  1276. structure.HasSingleChars = true;
  1277. }
  1278. else
  1279. {
  1280. structure.Words.Add(segment);
  1281. structure.HasWords = true;
  1282. }
  1283. }
  1284. // 设置组合类型
  1285. if (structure.HasWords && structure.HasSingleChars)
  1286. {
  1287. structure.CombinationType = CombinationType.WordsAndChars;
  1288. structure.Notes = "混合查询:包含词语和单字";
  1289. }
  1290. else if (structure.HasWords && !structure.HasSingleChars)
  1291. {
  1292. structure.CombinationType = CombinationType.WordsOnly;
  1293. structure.Notes = structure.Words.Count > 1 ? "多词查询" : "单词查询";
  1294. }
  1295. else if (!structure.HasWords && structure.HasSingleChars)
  1296. {
  1297. structure.CombinationType = CombinationType.CharsOnly;
  1298. structure.Notes = structure.SingleChars.Count > 1 ? "多字查询" : "单字查询";
  1299. }
  1300. else
  1301. {
  1302. structure.CombinationType = CombinationType.Unknown;
  1303. structure.Notes = "无法识别的查询类型";
  1304. }
  1305. // 计算复杂度评分
  1306. structure.ComplexityScore = CalculateComplexityScore(structure);
  1307. return structure;
  1308. }
  1309. /// <summary>
  1310. /// 计算搜索复杂度评分
  1311. /// </summary>
  1312. private static int CalculateComplexityScore(SearchStructure structure)
  1313. {
  1314. int score = 0;
  1315. // 词条数量(1-5分)
  1316. int segmentCount = structure.Words.Count + structure.SingleChars.Count;
  1317. score += Math.Min(segmentCount, 5);
  1318. // 是否混合类型(加3分)
  1319. if (structure.HasWords && structure.HasSingleChars)
  1320. score += 3;
  1321. // 单字数量(每3个加1分,最多2分)
  1322. if (structure.SingleChars.Count >= 3)
  1323. score += Math.Min(structure.SingleChars.Count / 3, 2);
  1324. // 词语平均长度(长词加2分)
  1325. if (structure.Words.Any())
  1326. {
  1327. double avgLength = structure.Words.Average(w => w.Length);
  1328. if (avgLength > 4) score += 2;
  1329. }
  1330. // 总字符数(10个以上加1分)
  1331. if (structure.AllCharacters.Count > 10)
  1332. score += 1;
  1333. return Math.Min(score, 10); // 最高10分
  1334. }
  1335. /// <summary>
  1336. /// 获取搜索策略
  1337. /// </summary>
  1338. private static MatchStrategy GetMatchStrategy(SearchStructure structure, SearchConfig config)
  1339. {
  1340. var complexity = CalculateComplexityScore(structure);
  1341. return new MatchStrategy
  1342. {
  1343. StrategyName = complexity >= 7 ? "strict" :
  1344. complexity >= 4 ? "balanced" : "loose",
  1345. ComplexityScore = complexity,
  1346. UseExactMatch = config.EnableExactMatch && structure.Words.Any(w => w.Length >= 3),
  1347. UsePrefixMatch = config.EnablePrefixMatch,
  1348. UseFuzzySearch = config.EnableFuzzySearch,
  1349. UseCharSearch = config.EnableCharSearch && structure.SingleChars.Any(),
  1350. UseSmartSplitting = config.EnableSmartSplitting && structure.Words.Any(w => w.Length >= config.MinSplitLength),
  1351. RecommendedPageSize = complexity >= 7 ? 10 : 20
  1352. };
  1353. }
  1354. /// <summary>
  1355. /// 获取计分规则
  1356. /// </summary>
  1357. private static List<ScoringRule> GetScoringRules(SearchStructure structure, SearchConfig config)
  1358. {
  1359. var rules = new List<ScoringRule>();
  1360. // 词语匹配规则
  1361. foreach (var word in structure.Words)
  1362. {
  1363. rules.Add(new ScoringRule
  1364. {
  1365. Type = "word",
  1366. Value = word,
  1367. BaseScore = 10,
  1368. Bonus = word.Length >= 3 ? 5 : 2,
  1369. Multiplier = 1.0,
  1370. Condition = $"包含词语'{word}'"
  1371. });
  1372. }
  1373. // 单字匹配规则
  1374. foreach (var ch in structure.SingleChars)
  1375. {
  1376. rules.Add(new ScoringRule
  1377. {
  1378. Type = "char",
  1379. Value = ch,
  1380. BaseScore = 3,
  1381. Bonus = 1,
  1382. Multiplier = 1.0,
  1383. Condition = $"包含单字'{ch}'"
  1384. });
  1385. }
  1386. // 混合匹配奖励
  1387. if (structure.HasWords && structure.HasSingleChars)
  1388. {
  1389. rules.Add(new ScoringRule
  1390. {
  1391. Type = "combo",
  1392. Value = "words_and_chars",
  1393. BaseScore = 0,
  1394. Bonus = 8,
  1395. Multiplier = 1.2,
  1396. Condition = "同时匹配词语和单字"
  1397. });
  1398. }
  1399. // 智能拆分奖励
  1400. if (config.EnableSmartSplitting && structure.Words.Any(w => w.Length >= config.MinSplitLength))
  1401. {
  1402. rules.Add(new ScoringRule
  1403. {
  1404. Type = "smart_split",
  1405. Value = "smart_split_match",
  1406. BaseScore = 0,
  1407. Bonus = 5,
  1408. Multiplier = 1.1,
  1409. Condition = "智能拆分匹配"
  1410. });
  1411. }
  1412. // 完全匹配奖励
  1413. rules.Add(new ScoringRule
  1414. {
  1415. Type = "exact",
  1416. Value = "exact_match",
  1417. BaseScore = 0,
  1418. Bonus = 15,
  1419. Multiplier = 1.5,
  1420. Condition = "完全匹配搜索词"
  1421. });
  1422. return rules;
  1423. }
  1424. /// <summary>
  1425. /// 获取需要提升权重的字段
  1426. /// </summary>
  1427. private static List<string> GetBoostFields(SearchStructure structure, List<string> searchFields)
  1428. {
  1429. var boostFields = new List<string>();
  1430. if (structure.HasWords && structure.Words.Any(w => w.Length >= 2))
  1431. {
  1432. // 长词语在名称字段中效果更好
  1433. boostFields.AddRange(searchFields.Where(f =>
  1434. f.Contains("name", StringComparison.OrdinalIgnoreCase) ||
  1435. f.Contains("title", StringComparison.OrdinalIgnoreCase) ||
  1436. f.Contains("subject", StringComparison.OrdinalIgnoreCase)));
  1437. }
  1438. if (structure.HasSingleChars)
  1439. {
  1440. // 单字在描述、内容字段中效果更好
  1441. boostFields.AddRange(searchFields.Where(f =>
  1442. f.Contains("desc", StringComparison.OrdinalIgnoreCase) ||
  1443. f.Contains("content", StringComparison.OrdinalIgnoreCase) ||
  1444. f.Contains("remark", StringComparison.OrdinalIgnoreCase) ||
  1445. f.Contains("note", StringComparison.OrdinalIgnoreCase)));
  1446. }
  1447. return boostFields.Distinct().ToList();
  1448. }
  1449. /// <summary>
  1450. /// 计算预期匹配数
  1451. /// </summary>
  1452. private static int CalculateExpectedMatches(SearchStructure structure, int fieldCount)
  1453. {
  1454. int baseMatches = structure.Words.Count + structure.SingleChars.Count;
  1455. int expected = baseMatches * fieldCount;
  1456. // 调整预期值
  1457. if (structure.CombinationType == CombinationType.WordsAndChars)
  1458. expected = (int)(expected * 1.5);
  1459. else if (structure.CombinationType == CombinationType.CharsOnly)
  1460. expected = (int)(expected * 0.7);
  1461. return Math.Max(expected, 1);
  1462. }
  1463. #endregion
  1464. #region 条件构建策略
  1465. /// <summary>
  1466. /// 构建严格条件(所有条件必须满足)
  1467. /// </summary>
  1468. private static string BuildStrictCondition(List<string> conditions, SearchStructure structure)
  1469. {
  1470. if (conditions.Count == 1)
  1471. return conditions[0];
  1472. // 词语条件在前,单字条件在后
  1473. var wordConditions = conditions
  1474. .Where((c, i) => i < structure.Words.Count)
  1475. .ToList();
  1476. var charConditions = conditions
  1477. .Skip(structure.Words.Count)
  1478. .ToList();
  1479. if (wordConditions.Any() && charConditions.Any())
  1480. {
  1481. return $"({string.Join(" AND ", wordConditions)} AND ({string.Join(" OR ", charConditions)}))";
  1482. }
  1483. return $"({string.Join(" AND ", conditions)})";
  1484. }
  1485. /// <summary>
  1486. /// 构建平衡条件
  1487. /// </summary>
  1488. private static string BuildBalancedCondition(List<string> conditions, SearchStructure structure)
  1489. {
  1490. if (conditions.Count == 1)
  1491. return conditions[0];
  1492. // 词语优先,单字作为增强
  1493. if (structure.HasWords && structure.HasSingleChars)
  1494. {
  1495. var wordCondition = conditions.First();
  1496. var charConditions = conditions.Skip(1).ToList();
  1497. return $"({wordCondition} AND ({string.Join(" OR ", charConditions)}))";
  1498. }
  1499. return $"({string.Join(" AND ", conditions)})";
  1500. }
  1501. /// <summary>
  1502. /// 构建宽松条件(提高召回率)
  1503. /// </summary>
  1504. private static string BuildLooseCondition(List<string> conditions, SearchStructure structure)
  1505. {
  1506. if (conditions.Count == 1)
  1507. return conditions[0];
  1508. // 所有条件OR连接
  1509. return $"({string.Join(" OR ", conditions)})";
  1510. }
  1511. #endregion
  1512. #region 实用工具方法
  1513. /// <summary>
  1514. /// 检查字符是否为中文字符
  1515. /// </summary>
  1516. public static bool IsChineseCharacter(char c)
  1517. {
  1518. // 基本汉字(0x4E00-0x9FFF)
  1519. if (c >= 0x4E00 && c <= 0x9FFF)
  1520. return true;
  1521. // 扩展A区(0x3400-0x4DBF)
  1522. if (c >= 0x3400 && c <= 0x4DBF)
  1523. return true;
  1524. // 基本汉字补充(0x9FA6-0x9FFF)
  1525. if (c >= 0x9FA6 && c <= 0x9FFF)
  1526. return true;
  1527. return false;
  1528. }
  1529. /// <summary>
  1530. /// 移除字符串中的分隔符
  1531. /// </summary>
  1532. private static string RemoveSeparators(string text)
  1533. {
  1534. if (string.IsNullOrEmpty(text))
  1535. return text;
  1536. var separators = new[] { ' ', ',', '、', ',', ';', ';', '-', '_', '.', '。', '·', '|', '/', '\\' };
  1537. return new string(text.Where(c => !separators.Contains(c)).ToArray());
  1538. }
  1539. /// <summary>
  1540. /// 验证搜索字段列表
  1541. /// </summary>
  1542. public static (bool IsValid, string Message, List<string> ValidFields) ValidateSearchFields(
  1543. List<string> searchFields, List<string> allFields)
  1544. {
  1545. if (searchFields == null || !searchFields.Any())
  1546. return (false, "搜索字段列表不能为空", new List<string>());
  1547. if (allFields == null || !allFields.Any())
  1548. return (true, "无验证字段列表,接受所有输入", searchFields);
  1549. var validFields = searchFields.Intersect(allFields).ToList();
  1550. var invalidFields = searchFields.Except(allFields).ToList();
  1551. if (invalidFields.Any())
  1552. {
  1553. return (false,
  1554. $"发现无效字段: {string.Join(", ", invalidFields)}",
  1555. validFields);
  1556. }
  1557. return (true, "所有字段有效", validFields);
  1558. }
  1559. /// <summary>
  1560. /// 生成搜索统计信息
  1561. /// </summary>
  1562. public static SearchStats GenerateSearchStats(SearchStructure structure, int resultCount)
  1563. {
  1564. return new SearchStats
  1565. {
  1566. SearchTerm = structure.OriginalTerm,
  1567. CombinationType = structure.CombinationType,
  1568. WordCount = structure.Words.Count,
  1569. CharCount = structure.SingleChars.Count,
  1570. TotalSegments = structure.Segments.Count,
  1571. ComplexityScore = structure.ComplexityScore,
  1572. ResultCount = resultCount,
  1573. GeneratedAt = DateTime.Now
  1574. };
  1575. }
  1576. #endregion
  1577. }
  1578. #region 数据模型和枚举
  1579. /// <summary>
  1580. /// 搜索模式枚举
  1581. /// </summary>
  1582. public enum SearchMode
  1583. {
  1584. /// <summary>
  1585. /// 自动检测搜索类型
  1586. /// </summary>
  1587. [Description("自动检测")]
  1588. AutoDetect = 0,
  1589. /// <summary>
  1590. /// 单词条搜索
  1591. /// </summary>
  1592. [Description("单词条搜索")]
  1593. SingleWord = 1,
  1594. /// <summary>
  1595. /// 多词条搜索
  1596. /// </summary>
  1597. [Description("多词条搜索")]
  1598. MultiWord = 2,
  1599. /// <summary>
  1600. /// 单字搜索
  1601. /// </summary>
  1602. [Description("单字搜索")]
  1603. SingleChar = 3,
  1604. /// <summary>
  1605. /// 精确匹配
  1606. /// </summary>
  1607. [Description("精确匹配")]
  1608. Exact = 4,
  1609. /// <summary>
  1610. /// 高级查询
  1611. /// </summary>
  1612. [Description("高级查询")]
  1613. Advanced = 5
  1614. }
  1615. /// <summary>
  1616. /// 组合模式枚举
  1617. /// </summary>
  1618. public enum CombinationMode
  1619. {
  1620. /// <summary>
  1621. /// 智能混合(自动调整权重和连接方式)
  1622. /// </summary>
  1623. [Description("智能混合")]
  1624. SmartMix = 0,
  1625. /// <summary>
  1626. /// 词语和单字组合(词语必须,单字增强)
  1627. /// </summary>
  1628. [Description("词语+单字")]
  1629. WordsAndChars = 1,
  1630. /// <summary>
  1631. /// 仅词语
  1632. /// </summary>
  1633. [Description("仅词语")]
  1634. WordsOnly = 2,
  1635. /// <summary>
  1636. /// 仅单字
  1637. /// </summary>
  1638. [Description("仅单字")]
  1639. CharsOnly = 3
  1640. }
  1641. /// <summary>
  1642. /// 组合类型
  1643. /// </summary>
  1644. public enum CombinationType
  1645. {
  1646. [Description("未知")]
  1647. Unknown,
  1648. [Description("仅词语")]
  1649. WordsOnly,
  1650. [Description("仅单字")]
  1651. CharsOnly,
  1652. [Description("词语和单字混合")]
  1653. WordsAndChars
  1654. }
  1655. /// <summary>
  1656. /// 搜索结构分析结果
  1657. /// </summary>
  1658. public class SearchStructure
  1659. {
  1660. public string OriginalTerm { get; set; } = "";
  1661. public string CleanTerm { get; set; } = "";
  1662. public DateTime AnalyzedAt { get; set; }
  1663. public List<string> Segments { get; set; } = new List<string>();
  1664. public List<string> Words { get; set; } = new List<string>();
  1665. public List<string> SingleChars { get; set; } = new List<string>();
  1666. public List<char> AllCharacters { get; set; } = new List<char>();
  1667. public bool HasWords { get; set; }
  1668. public bool HasSingleChars { get; set; }
  1669. public CombinationType CombinationType { get; set; }
  1670. public string Notes { get; set; } = "";
  1671. public int ComplexityScore { get; set; }
  1672. // 统计信息
  1673. public int TotalWordLength => Words.Sum(w => w.Length);
  1674. public int MaxWordLength => Words.Any() ? Words.Max(w => w.Length) : 0;
  1675. public double AvgWordLength => Words.Any() ? Words.Average(w => w.Length) : 0;
  1676. public int TotalCharacterCount => AllCharacters.Count;
  1677. }
  1678. /// <summary>
  1679. /// 搜索配置类(增强版)
  1680. /// 控制搜索行为、匹配策略和性能优化的参数集合
  1681. /// </summary>
  1682. public class SearchConfig
  1683. {
  1684. /// <summary>
  1685. /// 是否启用精确匹配
  1686. /// 默认值: true
  1687. /// 说明: 当设置为true时,系统会尝试使用等号(=)进行完全匹配
  1688. /// 适用场景: 搜索ID、编码、精确名称等唯一标识
  1689. /// 示例: 搜索"1001"时,只匹配完全等于"1001"的记录
  1690. /// 性能影响: 精确匹配通常使用索引,性能最好
  1691. /// </summary>
  1692. public bool EnableExactMatch { get; set; } = true;
  1693. /// <summary>
  1694. /// 是否启用前缀匹配
  1695. /// 默认值: true
  1696. /// 说明: 当设置为true时,系统会尝试匹配以搜索词开头的记录
  1697. /// 适用场景: 自动补全、拼音首字母搜索、编码前缀搜索
  1698. /// 示例: 搜索"北京"时,会匹配"北京市"、"北京旅游"等
  1699. /// 性能影响: 前缀匹配可以使用索引,性能良好
  1700. /// </summary>
  1701. public bool EnablePrefixMatch { get; set; } = true;
  1702. /// <summary>
  1703. /// 是否启用模糊搜索
  1704. /// 默认值: true
  1705. /// 说明: 当设置为true时,系统会使用LIKE '%关键词%'进行模糊匹配
  1706. /// 适用场景: 通用全文搜索、包含关系搜索
  1707. /// 示例: 搜索"长城"时,会匹配"长城旅游"、"八达岭长城"等
  1708. /// 性能影响: 模糊搜索不能使用普通索引,建议配合全文索引使用
  1709. /// 注意: 大数据量时可能会影响性能,建议合理设置搜索字段
  1710. /// </summary>
  1711. public bool EnableFuzzySearch { get; set; } = true;
  1712. /// <summary>
  1713. /// 是否启用单字搜索
  1714. /// 默认值: true
  1715. /// 说明: 当设置为true时,系统会将搜索词拆分成单个字符进行匹配
  1716. /// 适用场景: 中文单字搜索、记忆不完整的模糊搜索
  1717. /// 示例: 搜索"北上广"时,会分别匹配包含"北"、"上"、"广"的记录
  1718. /// 注意: 启用后会生成多个LIKE条件,可能影响性能
  1719. /// 建议: 单字数量较多时,考虑使用RequireAllChars控制匹配逻辑
  1720. /// </summary>
  1721. public bool EnableCharSearch { get; set; } = true;
  1722. /// <summary>
  1723. /// 是否要求所有词语都必须匹配
  1724. /// 默认值: true
  1725. /// 说明: 当设置为true时,多词搜索中所有词语都必须在记录中出现
  1726. /// 当设置为false时,任意一个词语出现即可匹配
  1727. /// 适用场景:
  1728. /// true - 精确搜索,如"北京 长城"要求同时包含"北京"和"长城"
  1729. /// false - 宽松搜索,如"北京 长城"匹配包含"北京"或"长城"的记录
  1730. /// 性能影响: true时可能返回更少结果,但条件更复杂
  1731. /// 用户体验: true时结果更精确,false时召回率更高
  1732. /// </summary>
  1733. public bool RequireAllWords { get; set; } = true;
  1734. /// <summary>
  1735. /// 是否要求所有单字都必须匹配
  1736. /// 默认值: true
  1737. /// 说明: 当设置为true时,单字搜索中所有字符都必须在记录中出现
  1738. /// 当设置为false时,任意一个字符出现即可匹配
  1739. /// 适用场景:
  1740. /// true - 如"北上广"要求同时包含"北"、"上"、"广"
  1741. /// false - 如"北上广"匹配包含"北"或"上"或"广"的记录
  1742. /// 注意: 单字数量较多时,建议设为false以提高召回率
  1743. /// </summary>
  1744. public bool RequireAllChars { get; set; } = true;
  1745. /// <summary>
  1746. /// 精确匹配的最小长度要求
  1747. /// 默认值: 2
  1748. /// 说明: 只有当搜索词长度大于等于此值时,才会尝试精确匹配
  1749. /// 适用场景: 避免短词(如"a"、"的")使用精确匹配
  1750. /// 示例: 设置为3时,"北京"不会使用精确匹配,"北京市"会使用
  1751. /// 单位: 字符数
  1752. /// 建议值: 2-5之间,根据业务需求调整
  1753. /// </summary>
  1754. public int MinExactMatchLength { get; set; } = 2;
  1755. /// <summary>
  1756. /// 搜索词的最大长度限制
  1757. /// 默认值: 200
  1758. /// 说明: 超过此长度的搜索词会被自动截断
  1759. /// 目的:
  1760. /// 1. 防止恶意输入导致性能问题
  1761. /// 2. 避免过长的SQL语句
  1762. /// 3. 提高系统安全性
  1763. /// 注意: 实际截断时保留前面部分,不会影响搜索意图
  1764. /// 单位: 字符数
  1765. /// 建议值: 50-500之间,根据业务场景调整
  1766. /// </summary>
  1767. public int MaxSearchTermLength { get; set; } = 200;
  1768. /// <summary>
  1769. /// 匹配度的最低分数要求
  1770. /// 默认值: 0
  1771. /// 说明: 只有匹配度分数大于等于此值的记录才会被返回
  1772. /// 适用场景: 质量控制,过滤掉相关性太低的结果
  1773. /// 分数范围: 通常为0-100,具体取决于评分算法
  1774. /// 示例: 设置为10时,匹配度低于10分的记录会被过滤
  1775. /// 注意: 需要EnableRelevanceScoring=true才会生效
  1776. /// </summary>
  1777. public double MinScore { get; set; } = 0;
  1778. /// <summary>
  1779. /// 是否启用相关性评分
  1780. /// 默认值: true
  1781. /// 说明: 当设置为true时,系统会计算每条记录的匹配度分数
  1782. /// 功能:
  1783. /// 1. 按匹配度排序(分数高的在前)
  1784. /// 2. 可以设置MinScore过滤低分记录
  1785. /// 3. 提供MatchScore字段供前端显示
  1786. /// 性能影响: 会增加计算开销,但用户体验更好
  1787. /// 建议: 搜索结果需要排序时启用,简单列表查询可关闭
  1788. /// </summary>
  1789. public bool EnableRelevanceScoring { get; set; } = true;
  1790. /// <summary>
  1791. /// 是否启用分阶段搜索
  1792. /// 默认值: false
  1793. /// 说明: 当设置为true时,系统会按优先级分阶段执行搜索
  1794. /// 搜索阶段:
  1795. /// 1. 精确匹配(最高优先级)
  1796. /// 2. 前缀匹配
  1797. /// 3. 模糊匹配
  1798. /// 4. 单字匹配(最低优先级)
  1799. /// 优点:
  1800. /// 1. 优先返回最相关的结果
  1801. /// 2. 可以设置降级策略
  1802. /// 3. 提高搜索成功率
  1803. /// 缺点:
  1804. /// 1. 可能执行多次查询
  1805. /// 2. 逻辑更复杂
  1806. /// 适用场景: 对搜索结果质量要求较高的场景
  1807. /// </summary>
  1808. public bool EnableStagedSearch { get; set; } = false;
  1809. /// <summary>
  1810. /// 字段权重配置
  1811. /// 默认值: 预定义的权重字典
  1812. /// 说明: 控制不同字段在匹配度计算中的重要性
  1813. /// 权重范围: 建议0.5-2.0之间
  1814. /// 示例配置:
  1815. /// "name": 1.5 // 名称字段权重高
  1816. /// "title": 1.5 // 标题字段权重高
  1817. /// "code": 1.3 // 编码字段权重中
  1818. /// "desc": 1.0 // 描述字段权重正常
  1819. /// "remark": 0.7 // 备注字段权重低
  1820. /// 使用方式:
  1821. /// 1. 匹配度分数 = 基础分 × 字段权重
  1822. /// 2. 权重高的字段对总分影响更大
  1823. /// 建议: 根据业务重要性调整权重
  1824. /// </summary>
  1825. public Dictionary<string, double> FieldWeights { get; set; } = new Dictionary<string, double>
  1826. {
  1827. { "name", 1.5 },
  1828. { "title", 1.5 },
  1829. { "code", 1.3 },
  1830. { "description", 1.0 },
  1831. { "content", 0.8 },
  1832. { "remark", 0.7 }
  1833. };
  1834. /// <summary>
  1835. /// 是否启用同义词扩展
  1836. /// 默认值: false
  1837. /// 说明: 当设置为true时,系统会自动扩展搜索词的同义词
  1838. /// 示例: 搜索"电脑"时,也会搜索"计算机"、"微机"等
  1839. /// 实现方式: 需要配合同义词词典使用
  1840. /// 优点: 提高召回率,改善搜索体验
  1841. /// 缺点: 可能引入不相关结果
  1842. /// 适用场景: 专业术语搜索、方言词汇搜索
  1843. /// </summary>
  1844. public bool EnableSynonymExpansion { get; set; } = false;
  1845. /// <summary>
  1846. /// 是否启用拼音搜索
  1847. /// 默认值: false
  1848. /// 说明: 当设置为true时,系统会将中文转换为拼音进行搜索
  1849. /// 示例: 搜索"beijing"可以匹配"北京"
  1850. /// 实现方式: 需要拼音转换库
  1851. /// 适用场景:
  1852. /// 1. 输入拼音搜索中文
  1853. /// 2. 首字母搜索(如"bj"匹配"北京")
  1854. /// 注意: 会增加搜索复杂度和索引大小
  1855. /// </summary>
  1856. public bool EnablePinyinSearch { get; set; } = false;
  1857. /// <summary>
  1858. /// 搜索结果的最大数量限制
  1859. /// 默认值: 1000
  1860. /// 说明: 限制单次搜索返回的最大记录数
  1861. /// 目的:
  1862. /// 1. 防止返回过多数据影响性能
  1863. /// 2. 避免内存溢出
  1864. /// 3. 提高响应速度
  1865. /// 注意: 分页查询时,此限制针对总记录数
  1866. /// 建议值: 500-5000之间,根据系统承载能力调整
  1867. /// </summary>
  1868. public int MaxResultCount { get; set; } = 1000;
  1869. /// <summary>
  1870. /// 是否启用搜索缓存
  1871. /// 默认值: false
  1872. /// 说明: 当设置为true时,相同的搜索条件会使用缓存结果
  1873. /// 缓存策略:
  1874. /// 1. 基于搜索词和参数的哈希值
  1875. /// 2. 可设置缓存时间
  1876. /// 3. 支持滑动过期
  1877. /// 适用场景:
  1878. /// 1. 热门搜索词
  1879. /// 2. 数据更新频率低的场景
  1880. /// 3. 性能要求高的搜索
  1881. /// 注意: 数据频繁更新时,需要合理设置缓存时间
  1882. /// </summary>
  1883. public bool EnableSearchCache { get; set; } = false;
  1884. /// <summary>
  1885. /// 缓存时间(分钟)
  1886. /// 默认值: 5
  1887. /// 说明: 搜索结果的缓存保留时间
  1888. /// 单位: 分钟
  1889. /// 使用条件: 需要EnableSearchCache=true
  1890. /// 建议值:
  1891. /// - 高频搜索: 1-5分钟
  1892. /// - 低频搜索: 10-30分钟
  1893. /// - 静态数据: 60分钟以上
  1894. /// </summary>
  1895. public int CacheDurationMinutes { get; set; } = 5;
  1896. /// <summary>
  1897. /// 是否启用搜索词分析
  1898. /// 默认值: true
  1899. /// 说明: 当设置为true时,系统会对搜索词进行智能分析
  1900. /// 分析内容:
  1901. /// 1. 去除停用词(的、了、在等)
  1902. /// 2. 提取关键词
  1903. /// 3. 识别搜索意图
  1904. /// 4. 分析词性
  1905. /// 优点: 提高搜索准确性和智能性
  1906. /// 性能影响: 轻微,主要消耗在分析算法上
  1907. /// 建议: 一般保持启用状态
  1908. /// </summary>
  1909. public bool EnableQueryAnalysis { get; set; } = true;
  1910. /// <summary>
  1911. /// 是否记录搜索日志
  1912. /// 默认值: false
  1913. /// 说明: 当设置为true时,系统会记录搜索行为日志
  1914. /// 记录内容:
  1915. /// 1. 搜索词
  1916. /// 2. 搜索时间
  1917. /// 3. 返回结果数
  1918. /// 4. 响应时间
  1919. /// 5. 用户信息(可选)
  1920. /// 用途:
  1921. /// 1. 分析用户搜索习惯
  1922. /// 2. 优化搜索算法
  1923. /// 3. 监控搜索性能
  1924. /// 4. 安全审计
  1925. /// 注意: 涉及隐私时需谨慎处理
  1926. /// </summary>
  1927. public bool EnableSearchLogging { get; set; } = false;
  1928. /// <summary>
  1929. /// 搜索超时时间(秒)
  1930. /// 默认值: 30
  1931. /// 说明: 搜索操作的最大执行时间
  1932. /// 目的:
  1933. /// 1. 防止长时间运行的查询
  1934. /// 2. 提高系统稳定性
  1935. /// 3. 改善用户体验
  1936. /// 单位: 秒
  1937. /// 建议值:
  1938. /// - 实时搜索: 5-10秒
  1939. /// - 后台搜索: 30-60秒
  1940. /// - 大数据搜索: 120秒以上
  1941. /// 注意: 超时后会取消查询,返回已有结果或错误
  1942. /// </summary>
  1943. public int SearchTimeoutSeconds { get; set; } = 30;
  1944. /// <summary>
  1945. /// 是否启用异步搜索
  1946. /// 默认值: true
  1947. /// 说明: 当设置为true时,搜索操作会异步执行
  1948. /// 优点:
  1949. /// 1. 不阻塞主线程
  1950. /// 2. 提高并发处理能力
  1951. /// 3. 更好的用户体验
  1952. /// 适用场景:
  1953. /// 1. 复杂搜索条件
  1954. /// 2. 大数据量搜索
  1955. /// 3. 高并发场景
  1956. /// 注意: 需要合理的线程管理和资源控制
  1957. /// </summary>
  1958. public bool EnableAsyncSearch { get; set; } = true;
  1959. /// <summary>
  1960. /// 是否启用智能提示
  1961. /// 默认值: false
  1962. /// 说明: 当设置为true时,系统会根据输入提供搜索建议
  1963. /// 功能:
  1964. /// 1. 自动补全
  1965. /// 2. 热门搜索建议
  1966. /// 3. 相关搜索推荐
  1967. /// 4. 拼写纠正
  1968. /// 实现方式: 基于搜索历史、词典、算法模型
  1969. /// 适用场景: 搜索框智能提示功能
  1970. /// 注意: 需要额外的数据处理和存储
  1971. /// </summary>
  1972. public bool EnableSmartSuggestions { get; set; } = false;
  1973. /// <summary>
  1974. /// 是否启用搜索结果高亮
  1975. /// 默认值: false
  1976. /// 说明: 当设置为true时,搜索结果中的匹配部分会被高亮显示
  1977. /// 高亮方式:
  1978. /// 1. HTML标记(如<em>关键词</em>)
  1979. /// 2. 特殊字符包裹
  1980. /// 3. 前端处理
  1981. /// 适用场景: 需要突出显示匹配内容的搜索
  1982. /// 注意: 会增加数据传输量和前端处理复杂度
  1983. /// </summary>
  1984. public bool EnableResultHighlighting { get; set; } = false;
  1985. /// <summary>
  1986. /// 是否启用搜索结果分组
  1987. /// 默认值: false
  1988. /// 说明: 当设置为true时,搜索结果会按指定字段进行分组
  1989. /// 分组方式:
  1990. /// 1. 按类型分组
  1991. /// 2. 按时间分组
  1992. /// 3. 按相关度分组
  1993. /// 适用场景:
  1994. /// 1. 多类型混合搜索
  1995. /// 2. 时间线展示
  1996. /// 3. 分类浏览
  1997. /// 注意: 会增加查询复杂度和处理时间
  1998. /// </summary>
  1999. public bool EnableResultGrouping { get; set; } = false;
  2000. /// <summary>
  2001. /// 是否启用智能拆分
  2002. /// 默认值: true
  2003. /// 说明: 当设置为true时,系统会自动将长词拆分成合理的子词组合
  2004. /// 适用场景: 搜索"重庆科学"这类组合词时,会自动尝试"重庆"+"科学"的组合
  2005. /// 工作方式:
  2006. /// 1. 检测长度为MinSplitLength及以上的词语
  2007. /// 2. 使用智能算法判断是否为可拆分组合词
  2008. /// 3. 如果可拆分,生成多层次搜索条件
  2009. /// 多层次条件示例: "重庆科学" →
  2010. /// - 完整词匹配(最高优先级)
  2011. /// - 拆分子词匹配("重庆" AND "科学")
  2012. /// - 单字匹配("重" OR "庆" OR "科" OR "学")
  2013. /// 优点:
  2014. /// 1. 提高组合词的搜索成功率
  2015. /// 2. 保持搜索结果的准确性
  2016. /// 3. 提升用户体验
  2017. /// 性能影响: 会增加搜索条件复杂度,但能显著提高召回率
  2018. /// 建议: 中文搜索场景建议启用
  2019. /// </summary>
  2020. public bool EnableSmartSplitting { get; set; } = true;
  2021. /// <summary>
  2022. /// 智能拆分时是否要求所有子词都匹配
  2023. /// 默认值: false
  2024. /// 说明: 控制智能拆分时对拆分子词的匹配严格度
  2025. /// 示例:
  2026. /// - true: "重庆科学"拆分为"重庆"和"科学",要求同时包含这两个词
  2027. /// - false: "重庆科学"拆分为"重庆"和"科学",包含任意一个即可匹配
  2028. /// 适用场景:
  2029. /// true: 需要精确拆分匹配的场景
  2030. /// false: 需要提高召回率的场景
  2031. /// 建议: 通常设置为false以获得更好的搜索结果覆盖率
  2032. /// 注意: 此设置仅影响智能拆分的子词匹配,不影响原始词语匹配
  2033. /// </summary>
  2034. public bool RequireAllWordsForSplit { get; set; } = false;
  2035. /// <summary>
  2036. /// 智能拆分时是否要求所有单字都匹配
  2037. /// 默认值: false
  2038. /// 说明: 控制智能拆分成单字时的匹配严格度
  2039. /// 示例:
  2040. /// - true: "重庆科学"拆分为单字时,要求同时包含"重"、"庆"、"科"、"学"
  2041. /// - false: "重庆科学"拆分为单字时,包含任意一个单字即可匹配
  2042. /// 适用场景:
  2043. /// true: 需要完全匹配所有单字的严格场景
  2044. /// false: 需要宽松匹配以提高召回率的场景
  2045. /// 建议: 通常设置为false,因为单字完全匹配要求较高,容易漏掉相关结果
  2046. /// 注意: 此设置仅影响智能拆分的单字匹配阶段
  2047. /// </summary>
  2048. public bool RequireAllCharsForSplit { get; set; } = false;
  2049. /// <summary>
  2050. /// 最小拆分长度
  2051. /// 默认值: 4
  2052. /// 说明: 只有长度大于等于此值的词才会被考虑进行智能拆分
  2053. /// 单位: 字符数
  2054. /// 示例:
  2055. /// - 设置为4时,"重庆科学"(4字)会被考虑拆分
  2056. /// - 设置为5时,"北京长城游"(5字)会被考虑拆分
  2057. /// 适用场景:
  2058. /// - 较低值(3-4):适用于短词较多的场景
  2059. /// - 较高值(5-6):适用于长词较多的专业场景
  2060. /// 建议值:
  2061. /// - 中文搜索: 4
  2062. /// - 英文搜索: 8(对应4个英文单词)
  2063. /// - 混合搜索: 根据主要语言调整
  2064. /// 注意: 设置过低可能导致过多词语被拆分,影响性能;设置过高可能错过可拆分的组合词
  2065. /// </summary>
  2066. public int MinSplitLength { get; set; } = 4;
  2067. /// <summary>
  2068. /// 最大拆分组合数
  2069. /// 默认值: 3
  2070. /// 说明: 限制一个词的最大拆分方式数量,防止组合爆炸
  2071. /// 示例:
  2072. /// - 设置为3时,一个6字词最多尝试3种拆分方式
  2073. /// - 设置为1时,只使用最优的拆分方式
  2074. /// 适用场景:
  2075. /// - 性能敏感场景: 1-2
  2076. /// - 平衡场景: 3
  2077. /// - 准确性优先场景: 5-10
  2078. /// 性能影响: 每增加一个拆分组合,会增加一个搜索条件分支
  2079. /// 建议:
  2080. /// - 实时搜索: 1-3
  2081. /// - 后台搜索: 3-5
  2082. /// - 复杂分析: 5-10
  2083. /// 注意: 过高的值可能导致SQL条件过于复杂,影响数据库性能
  2084. /// </summary>
  2085. public int MaxSplitCombinations { get; set; } = 3;
  2086. }
  2087. /// <summary>
  2088. /// 搜索阶段
  2089. /// </summary>
  2090. public class SearchStage
  2091. {
  2092. public int Stage { get; set; }
  2093. public string Name { get; set; } = "";
  2094. public string Condition { get; set; } = "";
  2095. public List<SugarParameter> Parameters { get; set; } = new List<SugarParameter>();
  2096. public int Priority { get; set; }
  2097. public string Description { get; set; } = "";
  2098. public bool FallbackToNext { get; set; } = true;
  2099. }
  2100. /// <summary>
  2101. /// 相关性信息(增强版)
  2102. /// </summary>
  2103. public class RelevanceInfo
  2104. {
  2105. public SearchStructure SearchStructure { get; set; }
  2106. public int ExpectedMatches { get; set; }
  2107. public List<string> BoostFields { get; set; } = new List<string>();
  2108. public List<ScoringRule> ScoringRules { get; set; } = new List<ScoringRule>();
  2109. public MatchStrategy MatchStrategy { get; set; }
  2110. public DateTime GeneratedAt { get; set; }
  2111. public bool HasSmartSplitting { get; set; }
  2112. public List<string> SplittableWords { get; set; } = new List<string>();
  2113. }
  2114. /// <summary>
  2115. /// 匹配策略(增强版)
  2116. /// </summary>
  2117. public class MatchStrategy
  2118. {
  2119. public string StrategyName { get; set; } = "";
  2120. public int ComplexityScore { get; set; }
  2121. public bool UseExactMatch { get; set; }
  2122. public bool UsePrefixMatch { get; set; }
  2123. public bool UseFuzzySearch { get; set; }
  2124. public bool UseCharSearch { get; set; }
  2125. public bool UseSmartSplitting { get; set; }
  2126. public int RecommendedPageSize { get; set; } = 20;
  2127. public string Description { get; set; } = "";
  2128. }
  2129. /// <summary>
  2130. /// 计分规则
  2131. /// </summary>
  2132. public class ScoringRule
  2133. {
  2134. public string Type { get; set; } = ""; // word, char, combo, exact, smart_split
  2135. public string Value { get; set; } = "";
  2136. public int BaseScore { get; set; }
  2137. public int Bonus { get; set; }
  2138. public double Multiplier { get; set; } = 1.0;
  2139. public string Condition { get; set; } = "";
  2140. }
  2141. /// <summary>
  2142. /// 搜索统计信息
  2143. /// </summary>
  2144. public class SearchStats
  2145. {
  2146. public string SearchTerm { get; set; } = "";
  2147. public CombinationType CombinationType { get; set; }
  2148. public int WordCount { get; set; }
  2149. public int CharCount { get; set; }
  2150. public int TotalSegments { get; set; }
  2151. public int ComplexityScore { get; set; }
  2152. public int ResultCount { get; set; }
  2153. public DateTime GeneratedAt { get; set; }
  2154. public double MatchRatio => TotalSegments > 0 ? (double)ResultCount / TotalSegments : 0;
  2155. public string ComplexityLevel => ComplexityScore >= 7 ? "高" :
  2156. ComplexityScore >= 4 ? "中" : "低";
  2157. }
  2158. #endregion
  2159. #region 扩展方法
  2160. /// <summary>
  2161. /// AdvancedSearchHelper 扩展方法
  2162. /// </summary>
  2163. public static class EnhancedSearchExtensions
  2164. {
  2165. /// <summary>
  2166. /// 获取组合类型的详细说明
  2167. /// </summary>
  2168. public static string GetDetail(this CombinationType type)
  2169. {
  2170. return type switch
  2171. {
  2172. CombinationType.WordsOnly => "仅使用词语进行搜索,适合精确的关键词匹配",
  2173. CombinationType.CharsOnly => "仅使用单字进行搜索,适合模糊或记忆不完整的搜索",
  2174. CombinationType.WordsAndChars => "混合使用词语和单字,提供更灵活的搜索体验",
  2175. _ => "未知的搜索类型"
  2176. };
  2177. }
  2178. /// <summary>
  2179. /// 获取搜索模式的推荐使用场景
  2180. /// </summary>
  2181. public static string GetUsageScenario(this SearchMode mode)
  2182. {
  2183. return mode switch
  2184. {
  2185. SearchMode.AutoDetect => "适用于通用搜索场景,系统自动识别最佳搜索方式",
  2186. SearchMode.SingleWord => "适用于已知确切名称或关键词的搜索",
  2187. SearchMode.MultiWord => "适用于多个相关关键词的组合搜索",
  2188. SearchMode.SingleChar => "适用于模糊搜索或拼音首字母搜索",
  2189. SearchMode.Exact => "适用于需要完全匹配的精确搜索",
  2190. SearchMode.Advanced => "适用于专业用户的高级搜索需求",
  2191. _ => "通用搜索场景"
  2192. };
  2193. }
  2194. }
  2195. #endregion
  2196. }