如何在Apex触发器中优化多规则字符串匹配性能?
Apex规则匹配性能优化方案
1. 按匹配类型分类预处理规则
把规则按Match Type拆分,针对不同类型设计高效匹配逻辑,避免全量遍历:
- Exact (Case Insensitive):将所有匹配条件转成小写,存入
Map<String, List<String>> exactRuleMap(key为小写的匹配条件,value为对应的Category列表)。匹配时只需将用户字符串转小写,直接查询Map是否存在对应key,时间复杂度O(1)。 - Contains (Case Insensitive):将所有匹配条件转义后合并为一个正则表达式(如
(Thing a|Thing b)),预编译成Pattern实例。通过一次正则匹配即可找出所有命中的子串,再反向映射到对应的Category(提前建立子串到Category的映射Map)。 - Regular Expression:预编译所有正则规则为
Pattern实例,存入列表,避免每次匹配重复编译。
2. 批量处理时复用预处理结果
在触发器事务内,先一次性加载所有规则并完成预处理(利用静态变量缓存,确保事务内仅处理一次规则),再批量处理所有记录:
public class RuleMatcher { // 静态缓存预处理后的规则,事务内仅初始化一次 private static Map<String, List<String>> exactRuleMap; private static Pattern containsPattern; private static Map<String, String> containsKeyToCategory; private static List<PatternWithCategory> regexPatterns; // 内部类存储正则表达式和对应的Category private class PatternWithCategory { public Pattern pattern; public String category; public PatternWithCategory(Pattern p, String c) { pattern = p; category = c; } } // 初始化规则缓存 private static void initRules() { if (exactRuleMap != null) return; // 已初始化则跳过 exactRuleMap = new Map<String, List<String>>(); containsKeyToCategory = new Map<String, String>(); regexPatterns = new List<PatternWithCategory>(); List<String> containsCriteria = new List<String>(); // 假设规则存储在自定义对象Rule__c中 for (Rule__c rule : [SELECT Match_Criteria__c, Match_Type__c, Category__c FROM Rule__c]) { String criteria = rule.Match_Criteria__c?.trim(); if (String.isBlank(criteria)) continue; switch on rule.Match_Type__c { when 'Exact (Case Insensitive)' { String lowerCriteria = criteria.toLowerCase(); if (!exactRuleMap.containsKey(lowerCriteria)) { exactRuleMap.put(lowerCriteria, new List<String>()); } exactRuleMap.get(lowerCriteria).add(rule.Category__c); } when 'Contains (Case Insensitive)' { // 转义正则特殊字符,避免匹配异常 String escapedCriteria = Pattern.quote(criteria); containsCriteria.add(escapedCriteria); containsKeyToCategory.put(criteria.toLowerCase(), rule.Category__c); } when 'Regular Expression' { regexPatterns.add(new PatternWithCategory(Pattern.compile(criteria), rule.Category__c)); } } } // 构建Contains类型的合并正则 if (!containsCriteria.isEmpty()) { String regexStr = '(' + String.join(containsCriteria, '|') + ')'; containsPattern = Pattern.compile(regexStr, Pattern.CASE_INSENSITIVE); } } // 单条字符串匹配方法 public static List<String> matchString(String input) { initRules(); List<String> results = new List<String>(); if (String.isBlank(input)) return results; String lowerInput = input.toLowerCase(); // 匹配Exact规则 if (exactRuleMap.containsKey(lowerInput)) { results.addAll(exactRuleMap.get(lowerInput)); } // 匹配Contains规则 if (containsPattern != null) { Matcher matcher = containsPattern.matcher(input); while (matcher.find()) { String matchedStr = matcher.group().toLowerCase(); if (containsKeyToCategory.containsKey(matchedStr)) { results.add(containsKeyToCategory.get(matchedStr)); } } } // 匹配正则规则 for (PatternWithCategory pwc : regexPatterns) { Matcher matcher = pwc.pattern.matcher(input); if (matcher.find()) { results.add(pwc.category); } } return results; } // 批量匹配方法 public static Map<Id, List<String>> matchRecords(Map<Id, SObject> records, String fieldApiName) { initRules(); Map<Id, List<String>> recordMatches = new Map<Id, List<String>>(); for (Id recordId : records.keySet()) { String input = (String)records.get(recordId).get(fieldApiName); recordMatches.put(recordId, matchString(input)); } return recordMatches; } }
3. 触发器中调用示例
trigger MyObjectTrigger on MyObject__c (before insert, before update) { Map<Id, List<String>> matches = RuleMatcher.matchRecords(Trigger.newMap, 'Input_String__c'); for (MyObject__c obj : Trigger.new) { List<String> categories = matches.get(obj.Id); if (!categories.isEmpty()) { // 将匹配结果赋值到对应字段,比如用分号分隔 obj.Matched_Categories__c = String.join(categories, '; '); } } }
关键优化点说明
- 避免嵌套循环:原方案是
记录数×规则数的O(n*m)复杂度,优化后变为规则预处理O(m) + 每条记录匹配O(k)(k为匹配类型的固定开销),性能随规则数量增长的衰减大幅降低。 - 静态缓存:规则预处理在事务内仅执行一次,避免重复查询和编译。
- 正则复用:Contains类型合并为单个正则,减少匹配次数;正则类型预编译,避免重复编译开销。
内容的提问来源于stack exchange,提问作者Trevor Gibson
相关产品推荐
相关产品推荐

