原文学习链接:
docs.optaplanner.org/7.45.0.Fina…stream流说白了,和SQL的基本操作类似,熟练掌握SQL,流式处理数据基本意思相同。
约束流得分计算
6.1 约束流可以实现增量流计算
普通的SQL或者Java的Stream流不支持增量计算,流中的一个元素发生改变需要全体重新计算一遍;
在计算过程中constraint stream支持自动检测发生的变化,并且可以只重新计算只发生变化的部分。
6.2 创建一个 constraint stream
public class MyConstraintProvider implements ConstraintProvider {
@Override
public Constraint[] defineConstraints(ConstraintFactory factory) {
return new Constraint[] {
penalizeEveryShift(factory)
};
}
// 在这里实现constraint stream
private Constraint penalizeEveryShift(ConstraintFactory factory) {
return factory.from(Shift.class)
.penalize("Penalize a shift", HardSoftScore.ONE_SOFT);
}
}
6.3. Constraint stream cardinality (约束流基数)
支持四级基数:
| Cardinality | Prefix | Defining interface |
|---|---|---|
| 1 | Uni | UniConstraintStream<A> |
| 2 | Bi | BiConstraintStream<A, B> |
| 3 | Tri | TriConstraintStream<A, B, C> |
| 4 | Quad | QuadConstraintStream<A, B, C, D> |
| 代码示例: |
// 示例1
private Constraint doNotAssignAnn(ConstraintFactory factory) {
return factory.from(Shift.class) // Returns Uni<Shift>.
.join(Employee.class) // Returns Bi<Shift, Employee>.
.join(DayOff.class) // Returns Tri<Shift, Employee, DayOff>.
.join(Country.class) // Returns Quad<Shift, Employee, DayOff, Country>.
...
}
// 示例2
private Constraint doNotAssignAnn(ConstraintFactory factory) {
return factory.from(Shift.class) // Returns UniStream<Shift>.
.join(Employee.class) // Returns BiStream<Shift, Employee>.
.groupBy((shift, employee) -> employee) // Returns UniStream<Employee>.
...
}
6.4 Building blocks (构建块)
简单示例:
private Constraint penalizeInitializedShifts(ConstraintFactory factory) {
return factory.from(Shift.class)
.penalize("Initialized shift", HardSoftScore.ONE_SOFT);
}
6.4.1. Penalties and rewards (惩罚和奖励)
每个 constraint stream流都需要用a penalize() or a reward()来构建基本块。 无论是惩罚还是奖励都有以下的特点:
- 实现ConstraintProvider接口
- Constraint name 可以随意自定义,为了可读性而已
- Constraint weight:约束的权重类别;支持
SimpleScore.ONE, HardSoftScore.ONE_HARD and HardMediumSoftScore.of(1, 2, 3)等类别. - Constraint match weigher:默认是1,多次匹配多次累乘(multiplied)这个系数
constraint stream支持不同类别的约束项,API的例子如下:
- 简单类别:
penalize("Constraint name", SimpleScore.ONE) - 动态类别:
penalize("Constraint name", SimpleScore.ONE, Shift::getHours); Shift::getHours相当于是一个动态的约束权重. (目前代码演示不通) - 支持配置化的简单和动态类别.
6.4.2 Filtering
同Java8的流处理:
private Constraint penalizeShiftsOnOffDays(ConstraintFactory factory) {
return factory.from(Shift.class)
.join(DayOff.class)
.filter((shift, dayOff) -> shift.date == dayOff.date && shift.employee == dayOff.employee)
.penalize("Shift on an off-day", SimpleScore.ONE);
}
6.4.3. Joining内连接 (Joiner)
- 和sql的inner join...on...相似
private Constraint shiftOnDayOff(ConstraintFactory constraintFactory) {
return constraintFactory.from(Shift.class)
.join(DayOff.class,
Joiners.equal(Shift::getDate, DayOff::getDate),
Joiners.equal(Shift::getEmployee, DayOff::getEmployee))
.penalize("Shift on an off-day",
HardSoftScore.ONE_HARD);
}
Joiner 还支持以下语法:
- equal for joining constraint matches where they equals() one another.
- greaterThan, greaterThanOrEqual, lessThan and lessThanOrEqual for joining Comparable constraint matches per the prescribed ordering.
6.4.4. Grouping and collectors
分组:同jdk的Stream的功能 collectors:
- 聚合作用,支持sum()、count()、countDistinct()、sumLong()等
- 还支持ConstraintCollectors.min(…) and ConstraintCollectors.max(…)
6.4.5 条件传播 (Conditional propagation)
- ifExist()
private Constraint runningComputer(ConstraintFactory constraintFactory) {
return constraintFactory.from(CloudComputer.class)
.ifExists(CloudProcess.class, Joiners.equal(Function.identity(), CloudProcess::getComputer))
.penalize("runningComputer",
HardSoftScore.ONE_SOFT,
computer -> ...);
}
- ifNotExists() 略
6.5 测试一个约束流 (单测)
用Opta自己的测试框架来测试约束流,在求解问题之前进行检验约束
- 构建一个简单的N皇后的约束
protected Constraint horizontalConflict(ConstraintFactory factory) {
return factory
.fromUniquePair(Queen.class, equal(Queen::getRowIndex))
.penalize("Horizontal conflict", SimpleScore.ONE);
}
- 使用约束的 Verifier API 来创建一个单测
private ConstraintVerifier<NQueensConstraintProvider, NQueens> constraintVerifier
= ConstraintVerifier.build(new NQueensConstraintProvider(), NQueens.class, Queen.class);
@Test
public void horizontalConflictWithTwoQueens() {
Row row1 = new Row(0);
Column column1 = new Column(0);
Column column2 = new Column(1);
Queen queen1 = new Queen(0, row1, column1);
Queen queen2 = new Queen(1, row1, column2);
constraintVerifier.verifyThat(NQueensConstraintProvider::horizontalConflict)
.given(queen1, queen2)
.penalizesBy(1);
}
6.7 约束流还支持Drools的实现方式
原文翻译:
约束流有两种风格,一种默认使用的是Drools的默认实现,另一种是名为Bavet的纯Java实现。基于Drools的实现功能更完整。这两个变量都实现了相同的ConstraintProvider API。在这两者之间切换不需要Java代码更改。
Bavet是一个实验性的实现,它关注原始速度并提供优异的性能。但是,它缺少特性,因此许多示例不受支持。
综上,个人觉得还是Stream流的方式更容易上手。 第七章全章节都在讲述Drools score calculation的一种类似脚本的实现方式,这章直接跳过,快进到第8章。
2020年11月3日10:06:48