Joiner on a list of Elements from a groupBy

114 views
Skip to first unread message

Marko Bilic

unread,
Jan 26, 2022, 10:59:00 AM1/26/22
to OptaPlanner development
Hey Guys,

So for my specific issue I'm having I have the below entities for my problem 
  • Employee
  • Visits
We have a constraint in place that if an employee does not meet the minimum amount of visit hours in a given 2 week pay period we penalize it but while that works great we realized that we need to have a constraint in place for employees that were not assigned to visits in a given pay period at all and this is where the problem kicks in 

this is what we have so far 
return constraintFactory
     .from(Visit.class)
     .groupBy(Visit::getPayPeriod, ConstraintCollectors.toList())
     .ifNotExists(Employee.class, Joiners.equal((payperiod, visits) -> { },  Function.identity())) 

ideally what we are trying to do is for each pay period if there is an employee that does not exist in the list of visits we want to penalize the score but from what im Aware of joiners only take functions so im quite sure how to do it with a list from a groupBy. So just posing the question to you guys if you know what a solution could be or maybe im looking at this problem in a wrong way and there is a better way to phrase the solution. Let me know what you guys think Thank you

Christopher Chianelli

unread,
Jan 26, 2022, 11:53:06 AM1/26/22
to OptaPlanner development
I am assuming Visit has a planning variable/shadow variable employee.

Is PayPeriod in a @ProblemFactCollection on the @PlanningSolution? If so, then I would write this constraint as:

```
constraintFactory
    .forEach(Employee.class)
    .join(PayPeriod.class)
    .ifNotExists(Visit.class,
                          Joiners.equal((employee, payPeriod) -> employee, Visit::getEmployee),
                          Joiners.equal((employee, payPeriod) -> payPeriod, Visit::getPayPeriod))
    .penalize("Employee has no visit for pay period", HardSoftScore.ONE_HARD);
```

If PayPeriod is not in a @ProblemFactCollection on the @PlanningSolution, it is trickier, since you would need to get the PayPeriod from the Visit, which leads to this constraint:

```
constraintFactory
    .forEach(Visit.class)
    .groupBy(Visit::getPayPeriod)
    .join(Employee.class)
    .ifNotExists(Visit.class,
                          Joiners.equal((payPeriod, employee) -> employee, Visit::getEmployee),
                          Joiners.equal((payPeriod, employee) -> payPeriod, Visit::getPayPeriod))
    .penalize("Employee has no visit for pay period", HardSoftScore.ONE_HARD);
```

Marko Bilic

unread,
Jan 26, 2022, 1:21:53 PM1/26/22
to OptaPlanner development
Thank you for the Response!!

as you stated in the latter the pay period is apart of the @PlanningEntity which is the Visit class so the second solution is more applicable but at the same time I'm so used to using Joiners.equal() that I didnt even think to use a normal join I made a unit test and made some sample scenarios so this looks like its working great for my Solution Thank you for opening my eyes 

Reply all
Reply to author
Forward
0 new messages