0

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

1 Answers1

0

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();
}
}
laune
  • 31,114
  • 3
  • 29
  • 42
  • While I'll grant that this is a _much_ more elegant approach, I'm still having the problem that As1 (and this as As3) fire for both Albert and Bob, and As2 doesn't fire for either of them... and none of the breakpoints on the Semester class are hit. I am not sure that this is a problem with logic of the rules, but rather "how do I get this data to there". FIWI, I've put the full code for the project at https://github.com/shagie/StudentNotify –  Jul 04 '16 at 16:42
  • Can't help you there. My rule works for me, using the setup I've added. (I'm not going to mess around with that "esoteric" stuff.) – laune Jul 04 '16 at 17:25
  • Your demo actually identified where the problem was for me. Thank you. Because of type erasure and/or reflection, yaml-beans was stuffing in a list of Object - not a list of Semester. Thus, drools wasn't identifying the object under it to be able to match. Adding type hints for yaml beans ([diff](https://github.com/shagie/StudentNotify/commit/059ed8e3afb711f102e7f45ac31730275a2f1541#diff-22345e6eb216a125a21d839232446a20)) created the correct underlying object and drools is happy. –  Jul 04 '16 at 17:56