Search code examples
drools

Drools String version of Accumulate


The basis of what I am trying to do is get a concatenated string from an group of objects that meet a certain condition in the same way you get a sum of numbers from an accumulate.

The rule below is attempting to take any given IEP object and find all the services that belong to that IEP, sum up their frequencyduration and then create a report of the Names of each of those services and list the durationfrequency of each.

What is the correct way to do this? Is there something inherently wrong with my approach or am I just missing subtle errors.

dialect  "mvel"

import superObjects.IL.*
import objects.*;
import rules.ValidationHelper;
import java.util.List;
import java.util.ArrayList;

function String serviceDesc(ArrayList sers){
    String s = "";
    for(int i = 0 ; i<sers.size(); i++){
         Super_Service ser = (Super_Service) sers.get(i);             
         s+=ser.getName()+": "+ser.getDuration()*ser.getFrequency()+" Per Week <br>";
     }
     return s;
}

rule "Calculate Service Minutes"
    when
        $siep: Super_Iep();
           $counter : Number() from accumulate(
              $ser: Super_Service(Measurable != "Yes" && ValidationHelper.contains(Documentid, $siep.getIid()+"")),
              sum($ser.getFrequency()*$ser.getDuration())
        );
       $sers: ArrayList() from collect(Super_Service(Measurable != "Yes" && ValidationHelper.contains(Documentid, $siep.getIid()+"")));
    then
        ValidationHelper.storeCalc($siep, $counter, "Text", "SERVICE_MINUTES");
        ValidationHelper.storeCalc($siep, serviceDesc($sers), "Text", "SERVICE_REASON");
end

I cannot easily include ValidationHelper because its thousands of lines of code but the two functions in this block of code are a simple contains alternative that does aditional things like trimming both strings, and storeCalc is just a utility function for saving a field to the database.


Solution

  • What you accumulate and collect here is a single

    Map<String,Number> dur2min
    

    i.e., the number of minutes per service (and per week). How that data is to be taken from the facts is rightly part of the business logic and belongs into rules, but the way the "report" is composed and presented (HTML!) should not be spread out here, repeating the selection logic (!) and the iteration of the list of collected facts.

    I would move both into a function that takes the list and calculates bot in a single iteration,

    function void serviceDesc(Super_Iep iep, ArrayList sers){
        int sum = 0;
        StringBuilder sb = new StringBuilder();
        for( Object obj: sers ){
            //... add and append
        }
        ValidationHelper.storeCalc( iep, sum, "Text", "SERVICE_MINUTES");
        ValidationHelper.storeCalc( iep, sb.toString(), "Text", "SERVICE_REASON");
    }
    

    Whether this should be here or in ValidationHelper itself remains to be discussed but I'd rather not have it in DRL.

    Finally compare the resulting rule:

    rule "Calculate Service Minutes"
    when
        $siep: Super_Iep();
        $sers: ArrayList() from collect(Super_Service(Measurable != "Yes" && ValidationHelper.contains(Documentid, $siep.getIid()+"")));
    then
        serviceDesc($siep, $sers);
    end