Drools 7.12.0中如何按日期选择电表读数的直接前置记录
解决Drools中为电表读数匹配直接前置记录的问题
嘿,我来帮你搞定这个Drools的问题!你要实现的是给每个MeterReading找到同电表下按readDate排序的直接前置记录(也就是日期比当前记录早,但又是所有更早记录里最新的那个),先分析下你之前尝试的问题,再给你正确的实现方案。
你之前方法的问题分析
- 第一个
accumulate规则:只拿到了最大的前置日期,但没有关联到对应的MeterReading对象,所以无法获取该记录的value等关键信息,满足不了验证需求。 - 第二个自定义
accumulate规则:逻辑写反了——你是只要新的$p日期比prev小就替换,最后得到的是整个集合里最早的记录,而不是我们需要的“比当前记录日期小的最晚记录”。
正确的实现方案
方案一:改进自定义Accumulate(推荐,性能更优)
这个方案在收集过程中直接筛选出目标记录,不需要额外排序,适合数据量较大的场景。注意一定要加上meterId == $currentMR.meterId的条件,确保只匹配同一个电表的读数:
rule "Match Direct Previous Meter Reading" when // 匹配当前待验证的电表读数 $currentMR: MeterReading() // 通过accumulate找到同电表、日期更早的记录里,日期最新的那个 $previousMR: MeterReading() from accumulate( $p: MeterReading( meterId == $currentMR.meterId, readDate < $currentMR.readDate ), init( MeterReading latestPrev = null; ), action( // 只保留比当前latestPrev日期更新(但仍早于当前记录)的记录 if (latestPrev == null || $p.getReadDate().isAfter(latestPrev.getReadDate())) { latestPrev = $p; } ), result( latestPrev ) ) then // 这里可以添加你的验证逻辑,比如对比当前值和前置值的差值 System.out.println("当前读数ID: " + $currentMR.getId() + " | 前置读数ID: " + $previousMR.getId() + " | 前置日期: " + $previousMR.getReadDate() + " | 前置数值: " + $previousMR.getValue()); // 示例:如果差值在合理范围内,标记为有效 // int diff = $currentMR.getValue() - $previousMR.getValue(); // $currentMR.setValid(diff >= 0 && diff <= 100); // 假设合理差值是0-100 end
方案二:使用Collect+Stream排序(更直观)
如果数据量不大,也可以先收集所有符合条件的前置记录,再通过Stream倒序排序后取第一个元素:
rule "Match Previous Reading with Collect & Sort" when $currentMR: MeterReading() // 收集同电表、日期更早的所有读数 $prevReadings: List(size > 0) from collect( MeterReading( meterId == $currentMR.meterId, readDate < $currentMR.readDate ) ) // 按readDate倒序排序,取第一个(即日期最新的前置记录) $previousMR: MeterReading() from $prevReadings.stream() .sorted(Comparator.comparing(MeterReading::getReadDate).reversed()) .findFirst() then // 同样执行你的验证逻辑 System.out.println("当前读数ID: " + $currentMR.getId() + " | 前置读数ID: " + $previousMR.getId() + " | 前置数值: " + $previousMR.getValue()); end
额外注意事项
- 处理无前置记录的情况:如果某个电表的读数是第一条(没有更早的记录),上面的规则都不会触发。你可以单独写一个规则来处理这种场景,比如直接标记为有效:
rule "Handle First Meter Reading" when $currentMR: MeterReading() // 确认没有同电表的更早记录 not MeterReading( meterId == $currentMR.meterId, readDate < $currentMR.readDate ) then $currentMR.setValid(true); System.out.println("读数ID: " + $currentMR.getId() + " 是第一条记录,标记为有效"); end - 性能优化:如果你的数据集很大,方案一的
accumulate会比方案二更高效,因为它在收集过程中直接筛选出目标记录,不需要先收集所有符合条件的记录再排序。
内容的提问来源于stack exchange,提问作者user497087
相关产品推荐
相关产品推荐

