#java #quarkus #optaplanner
Вопрос:
У меня есть проблема AgentAvailability
, Agent
которая Agent
ссылается на объект теневого планирования ( не имеет переменной планирования, только обратная переменная, указывающая на свойство планирования Shift
)
Вот мои определения классов:
Shift.class
@PlanningEntity
public class Shift extends AbstractPersistable implements Comparable<Shift> {
@PlanningVariable(valueRangeProviderRefs = { "agentRange" }, nullable = true)
private Agent agent;
private Spot spot;
private LocalDateTime startDateTime;
private LocalDateTime endDateTime;
//Constructor, getters, setters and JPA annotations removed for clarity
Agent.class
@PlanningEntity
public class Agent implements Comparable<Agent> {
@PlanningId
private long registrationNumber;
private String username;
private boolean pro;
private SortedSet<Skill> skillSet = new TreeSet<>();
@InverseRelationShadowVariable(sourceVariableName = "agent")
private SortedSet<Shift> shiftSet = new TreeSet<>();
//Constructor, getters, setters and JPA annotations removed for clarity
AgentAvailability.class
public class AgentAvailability extends AbstractPersistable implements Comparable<AgentAvailability> {
private Agent agent;
private LocalDateTime startDateTime;
private LocalDateTime endDateTime;
//Constructor, getters, setters and JPA annotations removed for clarity
и вот мое определение решения:
Schedule.class
@PlanningSolution
public class Schedule extends AbstractPersistable {
// ...
@ProblemFactCollectionProperty
@ValueRangeProvider(id = "agentRange")
private List<Agent> agentList = new ArrayList<>();
@ProblemFactCollectionProperty
private List<AgentAvailability> agentAvailabilities = new ArrayList<>();
@PlanningEntityCollectionProperty
private List<Shift> shifts = new ArrayList<>();
// ...
//Constructor, getters, setters and JPA annotations removed for clarity
переменная inverseshadow работает так, как ожидалось: когда an Agent
назначается a Shift
, значение shiftSet
из назначенного Agent
обновляется с учетом этого сдвига.
Проблема в поле agent
от AgentAvailability
.
Насколько я понимаю, когда Optaplanner клонирует решение, согласно документации, он повторно использует экземпляры для проблемных объектов и использует клонирование для объектов планирования.
Поскольку AgentAvailability
это факт, я предполагаю, что он используется повторно (и в представлении отладчика это так, экземпляр одинаков между исходной проблемой и найденным лучшим решением)
Agent
от Schedule
правильно клонированы с их shiftSet
решением тоже.
Но проблема в том, что agent
поле in AgentAvailability
по-прежнему является Agent
экземпляром исходной проблемы.
Таким образом, поле shiftSet
agent
в AgentAvailability
экземплярах по-прежнему пусто.
Это имеет место в конце в найденном наилучшем решении, но также и во время вычислений.
Поэтому я не могу использовать подобное ограничение:
return constraintFactory.from(AgentAvailability.class)
.groupBy(AgentAvailability::getAgent, count())
.filter((agent, availabilities) -> availabilities - agent.getShiftSet().size() > 0)
.penalizeConfigurableLong(ShiftSchedulingConstraintConfiguration.PRO_HAS_SHIFT, (agent, availabilities) -> availabilities - agent.getShiftSet().size());
так agent.getShiftSet().size()
как всегда возвращается 0, даже если агент был назначен на смену.
Возможным обходным путем было бы использовать от(Agent.class) предложение и объединяйтесь с AgentAvailabilities и переключайтесь, чтобы «перестроить» набор сдвигов из Agent
Сущности. но есть ли какой-либо способ «клонировать» набор сдвигов из Agent
сущностей в поле « Agent
из», AgentAvailability
чтобы я мог использовать поле «агент AgentAvailability
» с обновлением shiftSet
?
Ответ №1:
Это проблема с вашей моделью. Вы не можете ожидать AgentAvailability
, что вы одновременно будете и не будете клонированы. Если AgentAvailability
это факт проблемы, он не будет клонирован, и, следовательно, значение agent
не будет изменено.
(Примечание сбоку: может быть, нам следует определить, есть ли ссылка на объект из факта планирования, и быстро потерпеть неудачу? Я не вижу, как такая ситуация может привести к чему-то, кроме неприятностей.)
Существует несколько решений этой проблемы. Я рекомендую вам сделать Agent
факт проблемы и создать новую сущность AgentAssignment
, сопоставив 1 к 1 Agent
. В этой модели вы можете свободно ссылаться Agent
на экземпляры фактов в других фактах.
Затем ограничение становится относительно простым в написании.
from(AgentAssignment.class)
.join(AgentAvailability.class,
Joiners.equal(
AgentAssignment::getAgent,
AgentAvailability::getAgent))
.groupBy(
(agentAssignment, agentAvailability) -> agentAssignment,
countBi())
.filter((agentAssignment, availabilities) -> availabilities - agentAssignment.getShiftSet().size() > 0)
...
Комментарии:
1. Это имеет смысл, спасибо за ваше предложение, я стараюсь
Ответ №2:
В качестве альтернативного решения добавьте @DeepPlanningClone
аннотацию на AgentAvailability
:
@DeepPlanningClone
public class AgentAvailability ... { ... }
Но ответ Лукаса лучше.
Комментарии:
1. Я думал, что уже пробовал это, но, должно быть, нет… так как это действительно работает с этой аннотацией. Я буду придерживаться ответа Лукаса, так как вы оба согласны с этим. Спасибо за вашу помощь и ваш продукт 😉