Search code examples
optaplanner

Best approach for many to many relationships in an optaplanner domain design


Hi i would like to ask what approach to take when designing the domain for the problem that i am trying to solve, as i see in the examples the design tends to be like the entity relationship model where for instance a many to many relationship is solved by putting an associative entity between the two entities. My question is why is this so does it help in performance and should I follow this design pattern?


Solution

  • Many to many relationships where both sides are a problem property (for example if both classes are a problem fact)

    The use of associate entity's is optional: it's your design call. OptaPlanner doesn't care, but Drools does, so it can affect performance of your score DRL's.

    For example, let's presume we have a many to many relationship between an Employee and a Skill. An employee has multiple skills and a skill is attained by multiple employees. I wrote most of the examples and I prefer using associate entities - so I 'd use a SkillAttained class here (although the new example, taskassigning will avoid those associate entities as a demonstration and to improve test coverage).

    As for the performance impact, it's all about how hashing and combination count. And how that affects incremental score calculation (see docs about that last concept). In any case, avoid unneeded accumulates and collect's in the DRL, as they don't fully work incrementally yet and therefore reduce the gain of incremental score calculation.

    With SkillAttained it's usually easier to get good performance and to design the rules.

    when
        ShiftAssignment($s : shift, $e : employee) // The when side must always contain a planning entity
        SkillRequired(shift == $s, $s : skill) // Small perf opportunity: ShiftAssigment.getEmployee().getRequiredSkills() would avoid a lookup
        not SkillAttained(employee == $e, skill == $s) // Good: the skill matching uses hashing for scalability if there are many skills
    then
        ...addHard(-1); // Fires once per 1 missing skill
    

    Without it, it's often harder to write - without using accumulates or collects. However, a method like Employee.hasSkill(Skill) (presuming the employee's skills are in a LinkedHashSet) is very efficient.

    when
        ShiftAssignment($c : countMissingSkills())
    then
        ...addHard(- $c); // Fires once per ShiftAssignment with at least 1 missing skill
    

    So in this example, ShiftAssignment.countMissingSkills() must be really efficient (e.g. use LinkedHashSet/Maps etc).

    Many to many relationships that are a @PlanningVariabe

    Currently OptaPlanner 6.4 doesn't support many to many relationships of planning variables yet. Vote for this jira. So an associate entity is needed to workaround that for now.