Modifying the data in the working memory

In the previous chapters, we've already seen the basic structure for a business rule: conditions and actions. When a specific set of conditions defined in a rule are met, we trigger specific actions defined in that rule. So far, these actions have only been basic modifications of Java beans or system logs. However, the Drools rule language allows us to do much more.

When a rule becomes too complex or it comprises of multiple complex conditions, defining them in one single rule might not be the best way to go. In imperative programming (such as Java and C++), we would break down a method or function, which is complex, into many smaller, simpler methods. In Drools, we should follow a similar structure defining multiple, simpler rules that work together.

Due to the nature of declarative programming that the DRL language follows, we cannot call one rule from another one, therefore, this splitting has to be done differently. To be able to split our rules into simpler ones, the actions of a rule requires to add or modify data so that other rules will re-evaluate themselves against the data and see if they should trigger their actions or not.

The insert keyword

In the then part of our rules, new information might be inferred by a rule. More data can be made available to the working memory for further evaluations against all the rules using the insert keyword, as follows:

rule "Classify Customer - Gold"
    when
        $c: Customer( category == Customer.Category.GOLD )
    then
        insert(new IsGoldCustomer($c));
end

rule "Classify Item - Low price"
   when
      $i: Item(cost < 10.00)
   then
      insert(new IsLowRangeItem($i));
end

rule "Suggest gift"
   when
      IsGoldCustomer($c: customer)
      IsLowRangeItem($i: item)
   then
      System.out.println("Suggest giving a gift of item "+$i.getName()+" to customer +"$c.getName());
end

In the previous example (which we can find in the chapter-04/chapter-04-kjar/src/main/resources/chapter04/workingMemoryModif/classify-item-rules.drl file in the code bundle), we can see a simple example of inserting and breaking down a rule into multiple ones. We can just check whether a client has a gold category and we have a low-cost product, and make a suggestion for the gift product all in the same rule. However, we're breaking down the rule in three parts and inserting new model elements (IsGoldCustomer and IsLowRangeItem) to let another rule make the main decision for us based on these two elements.

By breaking down the rule in smaller sections, having some rules in charge of determining what a gold customer is and other rules in charge of what to do with them, we can define a gold customer in many different ways. Later on, all the rules have to rely on the IsGoldCustomer fact and any extra condition to determine what to do.

The modify and update keywords

We can also take the already existing data, which might have triggered the condition of a rule, and notify the engine should re-evaluate it. This can be done using the modify keyword, as follows:

rule "Categorize Customer - Gold"
  when
    $c: Customer( category == Customer.Category.NA )
    $o: Order(customer == $c, orderLines.size() > 10)
  then
    modify($c){setCategory(Customer.Category.GOLD);}
end

This rule is modifying the Customer object to set it to gold category. After this modification, the engine will be notified that this object needs to be re-evaluated for all rules in the engine. This means that if we insert a Customer with no category and an Order on its name with over 10 items, it will not only set the corresponding category, but also trigger any rule that depends on this condition.

Another word that you can use instead of modify in the consequence of the rule is the update keyword. The update keyword does not take a code block as the modify keyword. Instead, it just receives a variable so that the modifications have to be done beforehand to the variable. The following code example would replace the modify keyword in the previous code section, as follows:

$c.setCategory(Customer.Category.GOLD);
update($c);

However, the use of update is discouraged in favor of modify blocks as they allow to syntactically group the fact's execution that should trigger rule re-evaluations.

As you can see, the modification of the working memory is essential to split a rule into multiple ones as all the rules will be triggered depending—at first—on the data that the engine has available. By modifying the data, the engine can continue triggering rules until no more rules match the available data. If we don't modify/update the data, the engine will not be able to see the changes that you may do in a fact and this object won't trigger any more rules than the ones that had already matched before the change.

The delete/retract keywords

Also, we might let the engine know it should re-evaluate rules as one element of data, which was present in the working memory, is no longer there. This could cancel future rules, which weren't evaluated yet, or trigger other rules, as follows:

rule "Init current date"
   when
   then insert(new Date());
end

rule "Expire coupons"
   when
      $now: Date()
      $cp: Coupon( validUntil before $now )
   then
      delete($cp);
end

rule "Execute coupon"
   when
      $o: Order()
      $cp: Coupon(order == $o)
   then
      System.out.println("We have a coupon for this order!");
end

In the previous rule, we first make sure that we have a current-date object available for comparison with the Init current date rule (which, as its condition is empty, will evaluate to true). After that, if we have an expired coupon in the working memory, the second rule will remove it. Even if we add an order with this associated coupon, the second rule will not be triggered because even after matching, the execution of the first rule will cancel it.

The delete and retract keyword are both valid to remove the objects from the working memory—though the retract keyword is deprecated. Their syntax is equivalent and they are mutually exchangeable in the DRL code.

As you can see, changing the working memory in the rules means that, when we call fireAllRules on our KieSession, not only the rules that matched the data at that moment will fire, but also new rules might be triggered and fired or cancelled during the rule execution.

This is both a very powerful tool as it allows us to control the execution of rules without explicitly calling them and dangerous as, without any control, it could quite easily lead to infinite loops. We will see this problem and how to avoid it in the next section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.224.226