user6548046 user6548046 - 5 months ago 24
Java Question

Drools not matching nested object obtained from deserialization

I'm trying to create a rule in Drools that would trigger based on a hypothetical student getting straight As.

import Student
import Semester
import java.util.*

dialect "mvel"

rule "straight As1"
when
$s : Student(
grades != null,
$g : grades
)
forall(
Semester(reading == "A", writing == "A", math == "A") from $g
)
then
System.out.println($s.getId() + ": s all As1: " + $s);
System.out.println($s.getId() + ": g all As1: " + $g);
end

rule "straight As2"
when
$s : Student(
grades != null,
$g : grades
)
$a : List(size() == $g.size) from
collect (Semester(reading == "A", writing == "A", math == "A") from $g)
then
System.out.println($s.getId() + ": s all As2: " + $s);
System.out.println($s.getId() + ": g all As2: " + $g);
end


The output of this:

001: s all As1: Student{id='001', grades=[{writing=A, reading=A, math=A}, {writing=A, reading=A, math=A}], name='Albert'}
001: g all As1: [{writing=A, reading=A, math=A}, {writing=A, reading=A, math=A}]
----------------
002: s all As1: Student{id='002', grades=[{writing=B, reading=B, math=B}], name='Bob'}
002: g all As1: [{writing=B, reading=B, math=B}]


The problem here is that Bob doesn't have All As. I am not sure how to get either of the
straight As
rules to fire for Albert and not Bob - the
As1
attempt fires for everything while
As2
attempt doesn't fire for anything.

I've been able to write rules to filter based on name. In debugging this I've had the breakpoint on
getGrades()
trigger... but none of the breakpoints on
getMath()
,
getWriting()
or
getReading()
have been hit.

A Student object (tightened up and toString() removed) is:

public class Student {
private String id;
private List<Semester> grades;
private String name;

public Student() { }
public String getName() { return name; }
public String getId() { return id; }
public List<Semester> getGrades() { return grades; }
public void setName(String name) { this.name = name; }
public void setId(String id) { this.id = id; }
public void setGrades(List<Semester> grades) { this.grades = grades; }
}


The corresponding Semester object (also tightened up and toString() removed):

public class Semester {
private String reading;
private String writing;
private String math;

public Semester() { }
public String getReading() { return reading; }
public String getWriting() { return writing; }
public String getMath() { return math; }
public void setReading(String reading) { this.reading = reading; }
public void setWriting(String writing) { this.writing = writing; }
public void setMath(String math) { this.math = math; }
}


These objects are instantiated with the code snippet:

YamlReader reader = new YamlReader(new FileReader(file));
Student student = reader.read(Student.class);
System.out.println(student.toString());
rv.add(student);


and an yaml object such as:

id:
001
grades:
-
reading: A
writing: A
math: A
-
reading: A
writing: A
math: A
name:
Albert

Answer

Use the simplest possible approach:

rule "straight As1"
when
    $s : Student( grades != null, $g : grades )
    not Semester(reading != "A" || writing != "A" || math != "A") from $g
then
    System.out.println($s.getId() + ": s all As1: " + $s);
    System.out.println($s.getId() + ": g all As1: " + $g);
end

My Main, using your Student and Semester:

public class Main {
private KieSession kieSession;
private KieScanner kieScanner;

public void build() throws Exception {
    KieServices kieServices = KieServices.Factory.get();
    KieFileSystem kfs = kieServices.newKieFileSystem();

    FileInputStream fis = new FileInputStream( "alla/alla.drl" );
    kfs.write( "src/main/resources/simple.drl",
                kieServices.getResources().newInputStreamResource( fis ) );

    KieBuilder kieBuilder = kieServices.newKieBuilder( kfs ).buildAll();

    Results results = kieBuilder.getResults();
    if( results.hasMessages( Message.Level.ERROR ) ){
        System.out.println( results.getMessages() );
        throw new IllegalStateException( "### errors ###" );
    }

    KieContainer kieContainer =
    kieServices.newKieContainer( kieServices.getRepository().getDefaultReleaseId() );

    KieBase kieBase = kieContainer.getKieBase();

    kieSession = kieBase.newKieSession();
}

public void exec(){
    Student albert = new Student();
    albert.setName( "Albert" );
    albert.setId( "001" );
    albert.getGrades().add( new Semester( "A", "A", "A" ) );
    albert.getGrades().add( new Semester( "A", "A", "A" ) );
    kieSession.insert( albert );

    Student bob = new Student();
    bob.setName( "Bob" );
    bob.setId( "002" );
    bob.getGrades().add( new Semester( "B", "B", "B" ) );
    kieSession.insert( bob );

    kieSession.fireAllRules();
}

public static void main( String[] args ) throws Exception {
Main m = new Main();
    m.build();
    m.exec();
}
}