Brian Blonski
Brian Blonski

Reputation: 1802

Optaplanner constraint streams join on List property

I'm rewriting some constraints from drools syntax to the new Constraint Streams API. I prefer the Constraint Streams syntax, but I'm not sure how to replicate a certain technique I've used in Drools, which involves matching on each element of a list provided from a property of a PlanningEntity.

Like this simplified example:

rule "My Rule"
when
  $e = MyEntity(selected)
  $s = Map.Entry($k:key, $v:value) from $e.scores
then
  scoreHolder.addSoftConstraintMatch(kcontext, (int)$k, (int)$v);
end

Is there a way to do this sort of matching with the ConstraintStreams API? I can do something similar with groupBy, but I have to score by the sum off all scores for an entity rather than a separate match for each score. I'd prefer to match each score separately for a cleaner score explanation.

Edit for more details.

Essentially I just want a flatMap() function in the constraintStreams API. My challenge is that I have a additional optional criteria that can be applied to each planningEntity by the user. These criteria come with a level, weight, and scoring function. I want to match each optional criteria separately, calculate the score function based on the selected entity and weight, then apply it to the given level (using a BendableScore).

Using groupBy, I can achieve a match of a planning entity with a list of it's optional criteria which can then evaluated and summed together. But I'd like a separate match for each optional criteria in the score explanation. e.g. instead of one match of a planning entity and it's 10 sub criteria, I'd want a flatmap of 10 matches with the planning entity and a single sub criteria.

I found that constraintStreams are unsuitable for my purpose anyway since I cannot control the Score level based one the matched entities when using the reward or penalize functions. I will continue to use the drools syntax.

Upvotes: 1

Views: 968

Answers (2)

Andreas Röhl
Andreas Röhl

Reputation: 33

One trick that you can use is to just collect the entire Constraint Stream and throw into .penelizeConfig(..., collection) because now you have left the boundaries of ConstraintStream and you can use Java Stream API to return the matchweight. Not sure if this helped you as I have never used BendableScore. More support in the ConstraintStream API is even better though.

Upvotes: 0

Geoffrey De Smet
Geoffrey De Smet

Reputation: 27312

The examples is a bit abstract to recognize what fits bests, and I hope scores doesn't have instances of OptaPlanner's Score class, but maybe ifExists() fulfills your needs.

f.from(MyEntity.class)
.filter(MyEntity::isSelected) // Obsolete if that is a null check on your planning var
ifExists(MyFact.class, equal(MyEntity::this, MyFact::getMyEntity))

Although it seems to a natural fit for groupBy():

f.from(MyFact.class)
.groupBy(MyFact::getMyEntity, count())
.filter((myFact, count) -> count > 5)
.penalize(...)

ConstraintMatches wise for justification, please elaborate on your challenges.

Upvotes: 1

Related Questions