I'm trying to have the following working:
rule "userCount"
when
$user : User()
$minutes : Integer()
from accumulate(
MinutesPerUser( user == $user, $time : time)
and Time(this == $time),
sum(1)
)
then
System.out.println( $minutes );
end
but it seems the and Time(this == $user) part is never true. If I remove that part I get some println output.
What's wrong with the above code?
Not knowing the relation of the Java classes, it's a little difficult to state definite facts. But from
$user : User()
//...
and Time(this == $user),
it is quite evident that Time must be a superclass of User or vice versa: otherwise there's no way this constraint can be fulfilled.
I don't know what you should write instead, as there's no specification of what should be done.
BTW, sum(1) looks rather fishy, because that would be better expressed by count(1), producing the same result.
Related
How can I generate something like this in my rule using PackageDescr ?
$var: Number (doubleValue > 100 ) from myPredefinedFunction()
I tried the following :
PatternDescr pt = new PatternDescr("Number","$var");
RelationalExprDescr ex = new RelationalExprDescr(">", false, null, new ExprConstraintDescr("myPredefinedFunction()"), new ExprConstraintDescr("100"));
pt.addConstraint(ex);
but this is what I get :
$var : Number( myPredefinedFunction() > 100 )
You're trying to set the myPredefinedFuntion() as a constraint. Constraints are the part of the drools declaration between the parentheses, eg. MyObject( foo == "bar" ) ... the foo == "bar" is a constraint.
Instead you need to set the source using the setSource method. This is the 'from' part of the declaration. This method takes a instance of a PatternSourceDescr subclass -- likely a FromDescr for this particular scenario.
(Alternatively, you might need setResource instead of setSource. The problem with using internal-only APIs is that they're not documented and subject to change without notice. I strongly suggest not going down this route.)
I want use the 'in' keyword in 'eval' function in which I am getting error that 'in' is not recognized by drools. So I have multiple values which I want check against a particular fact's attribute
when
$person : Person(PIN in ("123","456","789"))
then
//do something
end
//Like this I want use it in eval
when
$person : Person()
eval($person.PIN in ("123","456","789"))
then
//do something
end
But it is showing compile time error.
is there any other way to do it.
Edited
So I have some conditions in Decision Table where I want to use eval because other ways are not helpful in my scenario, below snapshot will Explain
SnapShot 1: Decision Table without eval()
SnapShot 2: Decision Table with eval()
Issue in first snapshot:
When compiling the spreadsheet the condition goes to the second lines object like below code : this is how it gets interpreted
when
personMap : PersonMap ()
basicEligiblePerson : Person( personalAddress.PIN in ($param) ) from
personMap.AddressesList
addresses : Address() from basicEligiblePerson.AddressesList
personalAddress : PersonalAddress() from addresses.PersonalAddress
then
basicEligiblePerson.setEligibility(true);
end
Issue in second snapshot :
When compiling this spreadsheet the condition goes to eval() function but 'in' keyword does not work in eval().
when
personMap : PersonMap ()
basicEligiblePerson : Person( personalAddress.PIN in ($param) ) from
personMap.AddressesList
addresses : Address() from basicEligiblePerson.AddressesList
personalAddress : PersonalAddress() from addresses.PersonalAddress
eval( personalAddress.PIN in ($param) )
then
basicEligiblePerson.setEligibility(true);
end
what should I do?
First sample given in your question is sufficient for the validation. You don't need to use eval.
when
$person : Person(PIN in ("123","456","789"))
then
//do something
end
If your requirement is to set eligibility to true for a given set of PINs, then you don't really need a decision table. I don't completely understand your POJO structure, so if Person class has a member variable addressList and AddressList class has a member personalAddress which has the member variable pin, you can achieve the results using the following rule. Please note that the nested fields are referred using the member variable names, not the class names. Also when you access the nested elements, if any of the elements can be null, please add the null check as well to avoid null pointer exceptions.
when
$basicEligiblePerson : Person( addressesList.personalAddress.pin in ("1234", "4567") )
then
$basicEligiblePerson.setEligibility(true);
end
I'm new to drools and java and I'm trying to understand how this rule from the nurse rostering example works, especially the first part about $pattern.
rule "unwantedPatternShiftType3DaysPattern"
when
$pattern : ShiftType3DaysPattern(
$dayIndex0ShiftType : dayIndex0ShiftType,
$dayIndex1ShiftType : dayIndex1ShiftType,
$dayIndex2ShiftType : dayIndex2ShiftType
)
PatternContractLine(
pattern == $pattern, $contract : contract
)
ShiftAssignment(
shiftType == $dayIndex0ShiftType,
contract == $contract,
$employee : employee, $firstDayIndex : shiftDateDayIndex
)
ShiftAssignment(
shiftType == $dayIndex1ShiftType,
employee == $employee,
shiftDateDayIndex == ($firstDayIndex + 1)
)
ShiftAssignment(
shiftType == $dayIndex2ShiftType,
employee == $employee,
shiftDateDayIndex == ($firstDayIndex + 2)
)
then
scoreHolder.addSoftConstraintMatch(kcontext, - $pattern.getWeight());
end
Specifically, how does drools know what value is in: dayIndex0ShiftType, dayIndex1ShiftType, dayIndex2ShiftType? It calls the ShiftType3DaysPattern class with these values, but how are these values determined?
In addition, when it makes this call:
ShiftType3DaysPattern (dayIndex0ShiftType, dayIndex1ShiftType, dayIndex2ShiftType)
which refers to the following:
#XStreamAlias("ShiftType3DaysPattern")
public class ShiftType3DaysPattern extends Pattern {
private ShiftType dayIndex0ShiftType;
private ShiftType dayIndex1ShiftType;
private ShiftType dayIndex2ShiftType;
public ShiftType getDayIndex0ShiftType() {
return dayIndex0ShiftType;
}
public void setDayIndex0ShiftType(ShiftType dayIndex0ShiftType) {
this.dayIndex0ShiftType = dayIndex0ShiftType;
}
public ShiftType getDayIndex1ShiftType() {
return dayIndex1ShiftType;
}
public void setDayIndex1ShiftType(ShiftType dayIndex1ShiftType) {
this.dayIndex1ShiftType = dayIndex1ShiftType;
}
public ShiftType getDayIndex2ShiftType() {
return dayIndex2ShiftType;
}
public void setDayIndex2ShiftType(ShiftType dayIndex2ShiftType) {
this.dayIndex2ShiftType = dayIndex2ShiftType;
}
#Override
public String toString() {
return "Work pattern: " + dayIndex0ShiftType + ", " + dayIndex1ShiftType + ", " + dayIndex2ShiftType;
}
}
Is this shorthand for ShiftType3DaysPattern.getDayIndex0ShiftType, ShiftType3DaysPattern.getDayIndex1ShiftType, and ShiftType3DaysPattern.getDayIndex2ShiftType?
And if this is the case, how does ShiftType3DaysPattern know which pattern to return if there are more than one "3 day patterns" in the xml source files? What am I missing?
Furthermore, if there are more than one "3 day pattern" then, how does drool automatically apply this rule to all of these "3 day patterns"?
Specifically, how does drools know what value is in: dayIndex0ShiftType, dayIndex1ShiftType, dayIndex2ShiftType? It calls the ShiftType3DaysPattern class with these values, but how are these values determined?
Every ShiftType3DaysPattern in your problem fact collection will be evaluated against this rule in combination with the other conditions in the when clause. So every combination of a ShiftType3DaysPattern, PatternContractLine, and three ShiftAssignments available as problem fact in the Kie session will be evaluated. If all the conditions match (so the PatternContractLine matches the ShiftType3DayPattern and all three ShiftAssignment types are assigned in the order of the unwanted ShiftType3DayPattern, the rule will be fired and the score will be impacted negatively. So the value of dayIndex0ShiftType, dayIndex1ShiftType and dayIndex2ShiftType will be whatever is set in the ShiftType3DaysPattern in the problem fact collection.
Is this shorthand for ShiftType3DaysPattern.getDayIndex0ShiftType,
ShiftType3DaysPattern.getDayIndex1ShiftType, and
ShiftType3DaysPattern.getDayIndex2ShiftType?
I'm not sure what you are referring to exactly, but if you are referring to this:
$pattern : ShiftType3DaysPattern(
$dayIndex0ShiftType : dayIndex0ShiftType,
$dayIndex1ShiftType : dayIndex1ShiftType,
$dayIndex2ShiftType : dayIndex2ShiftType
)
This is just syntax for assigning dayIndex0ShiftType to the variable $dayIndex0ShiftType so it can be referred to in the rule later. The pattern itself is also assigned to a variable $pattern. The dollar sign itself is just a convention.
And if this is the case, how does ShiftType3DaysPattern know which pattern to return if there are more than one "3 day patterns" in the xml source files? What am I missing?
Furthermore, if there are more than one "3 day pattern" then, how does drool automatically apply this rule to all of these "3 day patterns"?
As I said before, the rule will be evaluated for every problem fact combination available, so every ShiftType3DaysPattern will be evaluated against the other facts stated in the when clause of the rule.
I recommend you to read the Drools documentation, it will help you to improve your basic understanding of Drools. It is a long read, but will be worth it. At least read the User guide chapter.
I have 5 fact types: BaseFact, AFact, BFact, CFact and DFact.
AFact, BFact, CFact and DFact all inherit from BaseFact.
I have some rules that run on BaseFacts, that I can no longer run on CFacts or DFacts.
What is the best way to modify my BaseFact rules so that they only run on BaseFacts, AFacts and BFacts.
Is there some sort of instanceOf function I can check, like the following?
rule "BaseRule"
when
fact : BaseFact(this instanceOf AFact || this instanceOf BFact)
...
then
...
end
Or do I need to split this rule into 2 new rules, for AFact and BFact?
Even if there is no instanceOf operator, there are several ways to achieve what you are looking for.
These are some ideas:
rule "BaseRule"
when
fact : BaseFact(class == AFact.class || == BFact.class)
...
then
//note that the variable fact is still of type BaseFact
...
end
A nastier version:
rule "BaseRule"
when
fact : BaseFact()
not CFact(this == fact)
not DFact(this == fact)
...
then
//note that the variable fact is still of type BaseFact
...
end
Or:
rule "BaseRule"
when
AFact() OR
BFact()
...
then
//note you can't bind a variable to AFact or BFact
...
end
If you only have 2 concrete types you want to match, then having 2 individual rules doesn't sound like a bad idea either.
Hope it helps,
Quick update: I was also able to use:
rule "MyRow"
dialect "mvel"
when
f1 : Wrapper( eval( nestedProperty#MyNestedClass ), ... )
then
...
end
Which is handy for testing classes of (nested) properties.
I've had to create pairs of rules to retract my events. It seems they don't expire. I had wanted one-and-done events. You can see below, they use the default duration, zero.
So for example, if I exclude the retraction rules and then insert the RemoveConnectionEvent first and then insert the CreateConnectionEvent, the RemoveConnection rule will still fire. (Using an agenda listener in my unit tests)
My expectation of an event was that RemoveConnectionEvent would be ignored, it would not do anything if its conditions were not met immediately. I did not expect it to hang around and trigger the RemoveConnection rule once that rules conditions were met when the NewConnection rule responded to the CreateConnectionEvent.
To get my rules to behave as I expected, I created RetractedCreation, RetractedRemoval, and RetractedUpdate. This seems to be a hack. I am imagining a declared my events wrong.
Any ideas?
ps This was a pretty good Q&A but I am not using windows. It might infer that perhaps my hack is an 'explicit expiration policy'.
Test Event expiration in Drools Fusion CEPTest Event Expiration
Here is my rule.
package com.xxx
import com.xxx.ConnectedDevice
import com.xxx.RemoveConnectionEvent
import com.xxx.CreateConnectionEvent
import com.xxx.UpdateConnectionEvent
declare CreateConnectionEvent #role( event ) end
declare UpdateConnectionEvent #role( event ) end
declare RemoveConnectionEvent #role( event ) end
rule NewConnection
when
$connection : CreateConnectionEvent($newChannel : streamId)
not ConnectedDevice( streamId == $newChannel )
then
insert( new ConnectedDevice($newChannel) );
end
rule RetractedCreation
when
$creationEvent : CreateConnectionEvent($newChannel : streamId)
exists ConnectedDevice(streamId == $newChannel)
then
retract($creationEvent)
end
rule RemoveConnection
when
$remove : RemoveConnectionEvent($newChannel : streamId)
$connection : ConnectedDevice( streamId == $newChannel )
then
retract( $connection );
end
rule RetractedRemoval
when
$removalEvent : RemoveConnectionEvent($newChannel : streamId)
not ConnectedDevice(streamId == $newChannel)
then
retract($removalEvent)
end
rule UpdateConnection
when
$connectionUpdate : UpdateConnectionEvent($newChannel : streamId)
$connection : ConnectedDevice( streamId == $newChannel )
then
$connection.setLastMessage();
end
rule RetractedUpdate
when
$removalEvent : UpdateConnectionEvent($newChannel : streamId)
not ConnectedDevice(streamId == $newChannel)
then
retract($removalEvent)
end
This automatic expiry is a rather elusive feature. There's no concise definition when it'll work, and what needs to be done to make it work.
In your apparently simple case where you don't use temporal operators and expect that events are to be retracted after they have matched one rule I'd adopt the following strategy without wasting another thought on "inferred expiration" and "managed lifecycle".
Maybe you have a common (abstract) base class for your events; otherwise create a marker interface and attach it to all events. Let's call this type Event. Then, a single rule
rule "retract event"
salience -999999
when
$e: Event()
then
retract( $e );
end
will take care for all (Create, Update, Remove) events.
Edit You may also use the explicit setting for event expiry.
declare CreateConnectionEvent
#role( event )
#expires(0ms)
end
Make sure to use
KieBaseConfiguration config = ks.newKieBaseConfiguration();
config.setOption( EventProcessingOption.STREAM );
KieBase kieBase = kieContainer.newKieBase( config );
when creating the KieBase. I also recommend to "let the time pass", i.e., advance a pseudo clock or let the thread running a fireUntilHalt for a jiffy or two after fact insertion.