1

I am trying to extend the Shop Tutorial for easy rules (shop) to run on multiple facts. While writing the conditions in the rule we call functions using the "fact name" as below,

condition: "people.isAdult() == false"

Since, people is just the name of ONE object instance in facts, defining the condition like in above is no good. How can I define the condition such that it will iterate over multiple facts.

What I want to achieve is evaluate the rules on multiple facts in one fire of the rule engine.

When an array of facts with same fact name is supplied to the rule engine, only the last one supplied is evaluated. However, when different names are assigned to each fact, I cannot assign the same condition to each one.

Below you can see the code. Assume Person class have basic get/set functions for all members.

Rules.yml:

---
name: "regex"
description: "Check if regex pattern matches"
priority: 1
condition: "people.getPayload().matches(\".*12.*34$\") == true"
actions:
 - "System.out.println(\"There is a match\");"
---
name: "age"
description: "Check if person's age is > 18 and marks the person as adult"
priority: 2
condition: "people.age > 18"
actions:
  - "people.setAdult(true);"
---
name: "alkol"
description: "children are not allowed to buy alcohol"
priority: 3
condition: "people.isAdult() == false"
actions:
  - "System.out.println(\"Shop: Sorry, you are not allowed to buy portakal\");"

Main:

//create a person instance (fact)   
            Person [] PeopleArray = new Person [100];
            Person [] KidsArray = new Person [300];

        // initialize arrays
            for(int i = 0; i < PeopleArray.length ; i++)
            {
               PeopleArray[i] = new Person("TOM"+i,23);
               PeopleArray[i].setPayload(dummystring);
            }
            for(int i = 0; i < KidsArray.length ; i++)
            {
                KidsArray[i] = new Person("EMRE"+i,13);
                KidsArray[i].setPayload(dummystring);
            }


        Facts facts = new Facts();

        // create a rule set
        Rules rules = MVELRuleFactory.createRulesFrom(new FileReader("rules.yml"));

        RulesEngine rulesEngine = new DefaultRulesEngine();

        System.out.println("Tom: Hi! can I have some Vodka please?");

      //put the facts
        for(int i = 0; i < PeopleArray.length ; i++)
            facts.put("people", PeopleArray[i]);
        long start = System.currentTimeMillis();
        for(int i = 0; i < KidsArray.length ; i++)
            facts.put("people", KidsArray[i]);

        rulesEngine.fire(rules, facts);
E.Bülbül
  • 39
  • 1
  • 9

2 Answers2

3

A Facts object is essentially a hashmap. So by design, you cannot put two facts with the same key.

I see in your example that the people fact is an array. Now how to handle arrays in a mvel expression is another story. To be honest, the best answer I can give is to look at the MVEL docs: https://github.com/imona/tutorial/wiki/MVEL-Guide#arrays

What I want to achieve is evaluate the rules on multiple facts in one fire of the rule engine.

I'm not sure this is possible with easy rules. I think you can:

  • Either flat map your facts
  • Or call fire multiple times on different facts

Hope this helps.

Mahmoud Ben Hassine
  • 28,519
  • 3
  • 32
  • 50
1

Fact in actual terms is just a Map

Map<String, Object> facts = new HashMap<>()       // from source code

If you need to add multiple facts and then use them to fire rule at once, you can do it using a List. One can create a List<Object> and add this List to Facts.


Rule Class:

@Rule(name = "Hello World rule", description = "Always say hello world")
public class HelloWorldRule {

    @Condition
    public boolean when() {
        return true;
    }

    @Action
    public void then(@Fact("facts") List<Object> arr) throws Exception {
        for (Object i : arr) {
            System.out.print(i + ", ");
        }
    }

}


Launcher Class:

public class Launcher {

    public static void main(String[] args) {

        // create facts
        Facts facts = new Facts();
        List<Object> arr = new ArrayList<>();
        arr.add("hello");
        arr.add(5);
        facts.put("facts", arr);

        // create rules
        Rules rules = new Rules();
        rules.register(new HelloWorldRule());

        // create a rules engine and fire rules on known facts
        RulesEngine rulesEngine = new DefaultRulesEngine();
        rulesEngine.fire(rules, facts);

    }
}


Remember, This is really helpful if instead of Object you have all objects of the same type, like in this case Person.

swayamraina
  • 2,958
  • 26
  • 28