How to use drools to fit complex condition? - drools

Recently I am leaning drools,I think it is a great software.Drools‘s core idea is drl file.We should write the rule file,For example:
rule "name"
when
then
end
And the when section depend on the entity's property.For example:Now I have a User class which is ready to use in my rule.
public class User {
private int money;
private Date time;
//getter and setter.....
}
Now I need to know a user's money between 2012-09-11 and 2013-01-01,and if his money>100 then do my logic,How drools do?

That could be easily been achieved using the following rule:
rule 'Some Rule'
$u: User( time > '11-sep-2009', time < '01-jan-2013', money > 100)
then
//do your logic here. $u references the User object
end
Hope it helps,

You can write a function in DRL file which will return true if user's money between 2012-09-11 and 2013-01-01 is greater than 100. You can call this function from when part of your rule using eval and write your logic in then part.Writing functions is not good idea in DRL files though!

Related

Hibernate Search: Find in list of intervals

I am using Hibernate Search 6.x within my Spring Boot application. I got an indexed entity with a set of date intervals.
#Indexed
public class ProudParent {
...
#IndexedEmbedded(includePaths = {"start", "end"})
Set<DateInterval> parentalLeaves;
}
And the class DateInterval looks like
#Embeddable
public class DateInterval {
#GenericField
LocalDate start;
#GenericField
LocalDate end;
}
That is my range query to search for all proud parents which were in parental leave on a specific date:
bool.must(factory.range().field("parentalLeaves.start").lessThan(date));
bool.must(factory.range().field("parentalLeaves.end").greaterThan(date));
Now the problem is that it finds proud parents which first parental leave was before date and the last parental leave was after date. So it does not search in the "same" date interval.
Can someone give me a hint of what I'm doing wrong?
Regards, Rokko
#IndexedEmbedded will "flatten" the object structure by default, so all starts and ends are mixed together in the index, with no way to know which end matches which start.
You can read more about this in this section of the reference documentation. In short, this default is mainly for performance, because preserving the object structure can be (very) costly on large indexes, and is usually not necessary.
The solution, in your case, would be to use a NESTED structure:
#Indexed
public class ProudParent {
...
#IndexedEmbedded(includePaths = {"start", "end"}, structure = ObjectStructure.NESTED)
Set<DateInterval> parentalLeaves;
}
Then make sure to tell Hibernate Search you want the matches on start and end to be on the same parentalLeave, using a nested predicate:
bool.must(factory.nested().objectField("parentalLeaves")
.nest(factory.bool()
.must(factory.range()
.field("parentalLeaves.start")
.lessThan(date))
.must(factory.range()
.field("parentalLeaves.end")
.greaterThan(date)))
Don't forget to reindex your data before testing.
Side note: the syntax should be slightly more convenient with the upcoming Hibernate Search 6.2.0.Alpha2 (not yet released):
bool.must(factory.nested("parentalLeaves")
.add(factory.range()
.field("parentalLeaves.start")
.lessThan(date))
.add(factory.range()
.field("parentalLeaves.end")
.greaterThan(date)))
Other side note: hopefully one day we'll have date ranges as a primitive type in Hibernate Search (HSEARCH-4199) and this whole discussion will become irrelevant :)

Drools - Running a rule with an empty object

I'm trying to write a rule to calculate prices for an insurance product based on conditions. In the 'when' I'm using an object called AdditionalDriver, which contains the details for drivers other than the policy holder. From this, different prices can be calculated based on whether the additional driver is a parent, friend, spouse etc. See below:
when
AdditionalDriver($relToProp : relationToProposer)
then
String relToProp = $relToProp;
if(!relToProp.equals("P"))
{
//prices
}
end
"P" = parent.
This rule works when an additional driver has been added. However, if there is no additional driver, then the object is empty, and so the rule does not run. What do I need to do to get this rule to run, even when the object is empty?
Thanks in advance.
You should write one rule for each of the relative or acquaintance classes:
when
PolicyHolder( $phid: id )
AdditionalDriver( relationToProposer == "P", belongsTo == $phid )
then
//prices
end
For no additional driver being requested, write a rule
when
PolicyHolder( $phid: id )
not AdditionalDriver( belongsTo == $phid )
then
// cheaper prices
end
Don't use conditional statement in your consequences to further distinguish facts. This is a code smell.

Access 2013: I am trying to return today's date when a yes/no fields is marked as "Yes"

I am trying to return today's date in a date field when another field is marked as "Yes". I tried the following expression but to no avail. I have limited experience with expressions and greatly appreciate any guidance. If the field does not have a "Yes" the date field can be blank. I cannot set the default to "No" for the Approved field.
Approved Date: IIf([Approved]=True,Today(),Null)
If I set the date, it will work but the date is dynamic so this is not really helpful.
Approved Date: IIf([Approved]=True,5/1/2016,Null)
Thank you in advance for your help.
OK, so I spent a little time looking at how to do this with an expression. The answer is you can't, at least not without a helper function. So, my hybrid solution is:
1) Create a function in a standard module:
Public Function SetControlValue( _
ByVal ctlControl As Access.Control, _
ByVal varValue As Variant, _
Optional ByVal varTest As Variant = True)
If (varTest) Then
ctlControl.Value = varValue
End If
End Function
2) In the AfterUpdate event for the Approved checkbox, enter:
=SetControlValue([ApprovedDate],Date(),([Approved]=True) AND (IsNull([ApprovedDate])))
This approach saves you from making a class module under the form. And you can keep all such code in a common module, so you can build a library of such functions for other forms.
(old answer 2)
Based on your answers, what you can do is add an AfterUpdate event to the Approved control, which does something like:
Private Sub Approved_AfterUpdate()
If (Approved.Value = True) And IsNull(ApprovedDate.Value) Then
ApprovedDate.Value = Date()
End If
End Sub
This will set the approved date once, when the approved checkbox is first checked. If you need different behavior, this can easily be modified.
(old answer 1)
I think you have it backwards.
When [Approved] is set to True, set [ApprovedDate] = Today(). That way, it is saved to the table, and you have a permanent record of when it was approved.

Optaplanner passing a variable through planning solution

I have a planning entity Request and a planning variable as taxi.
I want to pass the Date(a particular day) to the drools file for cab allocation.
I tried adding Date to the planning solution but the rule always failed where i captured the Date.
Planning Solution
#PlanningSolution
public class NRequest extends AbstractPersistable implements Solution<HardMediumSoftScore> {
private Date date;
private List<Cabs> list_cabs;
#PlanningEntityCollectionProperty
private List<Requests> list_req;
.....
.....
}
Drools file
rule "Check overlap Shift1"
when
$date:Date()
then
scoreHolder.addHardConstraintMatch(kcontext, 3);
scoreHolder.addSoftConstraintMatch(kcontext, 2);
end
I'd suggest the NurseRosteringParametrization appoach.
The FooSolution class has a single FooParameterization class, which holds things like the date or the planning window starting date or the specific score weights etc. Then simply match on FooParameterization in your drools rules (you know there is only 1 instance) and that's it. Make sure that FooParameterization is part getProblemFacts() or #ProblemFactProperty

How to get available classes at runtime

I would like to poll a class and have it return all available subclasses in a way I can then address them. It can return an array, a dictionary, or something else. As long as I can then loop through the set of them and read properties or call functions from each.
Scenario:
I want to create a form where the user inputs the details of a series of events. Then I read the form and output a report. Each kind of event has a ".Name", but a different set of inputs (FormOptions) and methods (FormatOutput) to create an output. Right now this is implemented with a complex form and a script that runs after the user submits the form.
The trouble is that every time I add an option to the form, I have to change code in several different places. In the interest of making my code easier to maintain, I would like to contain all the code for each event type in a Class, then build the form based on the available Classes.
So as an example, I'm building an itinerary from a collection of Meeting and Travel objects:
Class Itinerary
Class Event
Public Property Get Name()
Name = "Meeting"
End Property
Public Function FormOptions(id)
Form Options = "<div id="& id &">form code for Meeting options</div>"
End Function
Public Sub FormatOutput(Time, Title, Location)
'Make a fancy meeting entry
End Sub
End Class
Class Travel
Public Property Get Name()
Name = "Travel"
End Property
Public Function FormOptions(id)
Form Options = "<div id="& id &">form code for Travel options</div>"
End Function
Public Sub FormatOutput(StartTime, EndTime, Origin, Destination)
'Make a fancy travel entry
End Sub
End Class
End Class
When the script runs it creates a form where the user can add a series of events. Each time the user chooses between "Meeting" and "Travel" then fills out the options for that event type. At the end, they push a button and the script makes a pretty document listing all the user's inputs.
At some point in the future, I will want to add a new kind of event: lodging.
Class Lodging
Public Property Get Name()
Name = "Lodging"
End Property
Public Function FormOptions(id)
Form Options = "<div id="& id &">form code for Lodging options</div>"
End Function
Public Sub FormatOutput(Date, Location)
'Make a fancy Lodging entry
End Sub
End Class
How do I setup my Itinerary class so that it automatically recognizes the new class and can return it as an available event type? I can think of several ways of doing this, but they all involve keeping an index of the available classes separate from the actual classes, and I'm trying to minimize the number of places I have to change code when I add new event types.
I am very purposely not including any details on how I build the form since at this point I'm open to anything. Also, please be gentle with references to "inheritance", "extensibility", or "polymorphism". I'm a just a scripter and these OOP concepts are still a bit foreign to me.
I don't think this is possible, but one way to come close to this would be to have a unique list of class names -- the simplest would be to have a global dictionary.
You can get a list of classes by reading the Keys collection of the dictionary.
I don't think VBScript supports references to classes, so when the user chooses one of the types use Eval to create an instance of the appropriate class.
Dim ClassList
Set ClassList = CreateObject("Scripting.Dictionary")
'on type selection:
Function GetItineraryInstance(className)
Set GetItineraryInstance = Eval("New " & className)
End Function
ClassList("Travel") = 1
Class Travel
'methods
End Class
At least the class registration can be kept together with the class definition.