4

My rule file as below,

import com.springapp.mvc.model.Person; 
dialect "java"
rule "4" 
    when
        $person:Person(((date > "20-Jan-2015") && (date < "20-Dec-2015")) && (call_count >= "299"))
    then
        System.out.println("Beep");
end

I added following person object and fire rules as below,

Person person = new Person();
person.date = "20-Feb-2015";
person.call_count = 400;
kSession.insert(person);
int fires = kSession.fireAllRules();

But it didn't print "Beep". I think conditions are not matched but I can't understand why this happen. How can I compare dates in drools?

My actual rule set,

package Customer_Loyalty_Categorization;
import com.springapp.mvc.model.Person; 
dialect "java"

rule "4" 
    when
        $person:Person(((date > "10-Nov-2015") && (date < "10-Dec-2015")) && (call_count >= "299"))
        $person:Person(((date > "10-Nov-2015")&&(date < "30-Dec-2015")) && (call_count >= "299"))
    then
        System.out.println("Point rule runs.");
        $person.points = ($person.call_count)*0.2;
end

rule "6" 
    when
        $person:Person(call_count >= "599")
    then
        System.out.println("Category rule runs.");
        $person.setCategory('PLATINUM');
end

And after changing type of the date variable of the person I got following exception,

java.lang.RuntimeException: Unable to Analyse Expression date > "20-Nov-2015":
[Error: Comparison operation requires compatible types. Found class java.util.Date and class java.lang.String]
[Near : {... date > "20-Nov-2015" ....}]
                    ^
[Line: 8, Column: 8] : [Rule name='4']

Unable to Analyse Expression date < "20-Dec-2015":
[Error: Comparison operation requires compatible types. Found class java.util.Date and class java.lang.String]
[Near : {... date < "20-Dec-2015" ....}]
                    ^
[Line: 8, Column: 8] : [Rule name='4']

Unable to Analyse Expression date > "01-Jan-2015":
[Error: Comparison operation requires compatible types. Found class java.util.Date and class java.lang.String]
[Near : {... date > "01-Jan-2015" ....}]
                    ^
[Line: 40, Column: 8] : [Rule name='1']

Unable to Analyse Expression date < "07-Jan-2015":
[Error: Comparison operation requires compatible types. Found class java.util.Date and class java.lang.String]
[Near : {... date < "07-Jan-2015" ....}]
                    ^
[Line: 40, Column: 8] : [Rule name='1']

Unable to Analyse Expression date > "01-Jan-2015":
[Error: Comparison operation requires compatible types. Found class java.util.Date and class java.lang.String]
[Near : {... date > "01-Jan-2015" ....}]
                    ^
[Line: 48, Column: 8] : [Rule name='2']

Unable to Analyse Expression date < "07-Jan-2015":
[Error: Comparison operation requires compatible types. Found class java.util.Date and class java.lang.String]
[Near : {... date < "07-Jan-2015" ....}]
                    ^
[Line: 48, Column: 8] : [Rule name='2']

Unable to Analyse Expression date > "05-Jan-2015":
[Error: Comparison operation requires compatible types. Found class java.util.Date and class java.lang.String]
[Near : {... date > "05-Jan-2015" ....}]
                    ^
[Line: 48, Column: 8] : [Rule name='2']

Unable to Analyse Expression date < "10-Jan-2015":
[Error: Comparison operation requires compatible types. Found class java.util.Date and class java.lang.String]
[Near : {... date < "10-Jan-2015" ....}]
                    ^
[Line: 48, Column: 8] : [Rule name='2']

I'm generating rules as set of strings and convert them into knowledgebase using following function,

public void createKnowledgeBase(){
        String ruleSet = loadRuleSet();//generate rules as strings.
        try {
            System.out.println(ruleSet);
            long start = System.currentTimeMillis();
            if(ruleSet!=null){
                KnowledgeBuilder knowledgeBuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
                Resource myResource = ResourceFactory.newReaderResource(new StringReader(ruleSet));
                knowledgeBuilder.add(myResource, ResourceType.DRL);
                if (knowledgeBuilder.hasErrors()) {
                    throw new RuntimeException(knowledgeBuilder.getErrors().toString());
                }
                knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase();
                knowledgeBase.addKnowledgePackages(knowledgeBuilder.getKnowledgePackages());
            }
            long finish = System.currentTimeMillis();
            System.out.println("Execution time = " + (finish-start) + " milliseconds.");
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
Hasitha
  • 558
  • 1
  • 12
  • 26

2 Answers2

3

Apparently you have

class Person {
    String date;
    // ...
}

So that

when
    $person:Person(((date > "20-Jan-2015") ...

results in a string (!) comparison of

"20-Feb-2015" > "20-Jan-2015" && "20-Feb-2015" < "20-Dec-2015"

which may even work at times, but mostly it wont. You should use

class Person {
    java.util.Date date;
    // ...
}

You need to change

person.date = new Date( 115, 1, 20 ); // or, preferably, parse a string 

but you can leave the rule as it is; Drools will convert a string to a Date value (provided it corresponds to your locale setting).

Later After some experiments, I find that 6.3.0 (and probably earlier versions) have a rather weird behaviour when compiling comparisons of java.util.Date to String.

rule x1 when
  Person(date > "10-Jan-2000")              // OK
  Person($date:date, date > "10-Jan-2000")  // OK
  Person($date:date, $date > "10-Jan-2000") // Error (types incompatible)

It is absolutely confusing when a programmer may not rely on the fact that a bound variable behaves like the property to which it is bound.

Finally: Don't use public fields in your fact classes. Stay with the Java Beans model and declare getters and setters. It turns out that Drools isn't using the automatic conversion from String to java.util.Date when a (public) instance variable itself is accessed due to the lack of a getter.

laune
  • 31,114
  • 3
  • 29
  • 42
  • then should I have to convert "20-Dec-2015" into date value using date format (eg: DateFormat format = new SimpleDateFormat("dd-MMM-yyyy", Locale.ENGLISH);). – Hasitha Nov 03 '15 at 12:23
  • You only need to change the assignment to `person.date`. Drools takes care of converting the String containing a date in the condition. - See the addition to my answer. – laune Nov 03 '15 at 12:26
  • Thanks for your support laune. But this gives me an exception. (Comparison operation requires compatible types. Found class java.util.Date and class java.lang.String) – Hasitha Nov 03 '15 at 12:45
  • Well. it works for me, using 6.2.0. Please add the stackdump to your question. – laune Nov 03 '15 at 13:06
  • it gives this exception. java.lang.RuntimeException: Unable to Analyse Expression date > "20-Nov-2015": [Error: Comparison operation requires compatible types. Found class java.util.Date and class java.lang.String] [Near : {... date > "20-Nov-2015" ....}] – Hasitha Nov 03 '15 at 22:50
  • Hashita, you didn't post the exact rule where the problem occurs. - See my addition to the answer. – laune Nov 04 '15 at 05:46
  • My exact rule is also as in original post difference is it has this line. $person.points = ($person.call_count)*0.2; in the "then" clause of the rule. – Hasitha Nov 04 '15 at 06:41
  • I just added my actual rule set and the exception to the original post. – Hasitha Nov 04 '15 at 07:20
  • So this should be it - I can see clearly now that you are accessing instance variables (date, points, call_count) directly. Make sure to use getters and setters. – laune Nov 04 '15 at 09:32
1

You can try with org.apache.commons.lang.time.DateUtils. I got same kind of problem and this one worked for me, may be will work on yours.

import org.apache.commons.lang.time.DateUtils; 

$person: Person((date > DateUtils.parseDate("20-01-2015", "dd-MM-yyyy") && date < DateUtils.parseDate("20-12-2015", "dd-MM-yyyy")) && (call_count >= "299"));
ccc
  • 370
  • 5
  • 19
  • before generating the rule condition I cannot figure out what is the variable type of the left and right side of the condition. Because I'm generating rules dynamically using a database. – Hasitha Nov 03 '15 at 12:31