In some situations, we might need to check special conditions on collections that are different from the working memory, such as attributes of some objects, global variables, or subsets of the working memory itself that we can create dynamically. To be able to do so, Drools provides the from clause, which we can be used to define a specific search space outside of the working memory.
The following rule is a simple example of how we can use the
from clause to look for specific attributes:
rule "For every notebook order apply points coupon" when $o: Order($c: customer, $lines: orderLines) OrderLine($item: item) from $lines Item(name == "notebook") from $item then insertLogical(new Coupon($c, $o, CouponType.POINTS)); end
The previous rule looks for orders in our working memory first. It will store both the order lines collection as a
$lines variable and the customer in a
$c variable (this last variable is stored to use it from the consequence of the rule). After finding an order, it looks in the
$lines variable (which holds all the order lines) and stores the item in another variable. After that, the third line of the rule condition directly searches a single object (the item), and checks whether the item is a notebook.
As you can see, if we only have the Order object in our working memory (and not all of its subcomponents), we can still go deep into its structure in a way that the following conditions are satisfied:
- It is easy to read and break down into multiple conditions
- It can take into account the situations where we might have collections of objects to dig into (such as the
orderLinesattribute in the second part of our rule condition)
- It can make a rule easier to read
from clause is a very versatile tool. It can be used to get data from multiple sources and not only from attributes. You can invoke methods from global variables that return specific values or lists and use it from the
from clause. You can basically use it with anything that returns a value.
One common use of global variables is to retrieve data that doesn’t need to be in the working memory all the time. Thanks to the
from clause, we can directly filter this data from our rules as soon as we execute a global variable method, as follows:
global QueryService qServ rule "get stored coupons for new order" when Order(totalItems > 10, $c:customer) Coupon(customer == $c) from qServ.queryCoupons() then System.out.println("We have a stored coupon"); end
This can be useful to compare the data between the working memory and outside storages, such as databases and web services. We need to be careful when using these external queries by realizing when the engine will invoke the global variable method, though. In the previous example, every time we add a new Order object to the working memory, the first line will be evaluated once again. If the first line fills the required conditions, it will call the second line, invoking the global variable method. If we add 50 orders that fill the first condition, the global variable method will be called 50 times by this rule.
Another thing to be careful about when using from clauses like these is how deep you nest them; if you have to execute five global variable methods one after the other in the same rule and they are all process-intensive, you will have a very slow rule. If you need to do something of this type, it is best to work with caches for your slow methods whenever possible.
Note that if the return value of a global variable method changes, it will not retrigger a rule that invokes it, even if it is the first condition of the rule. This is because global variables are outside the working memory, and therefore, they are not re-evaluated when they change. If you need to re-evaluate things used in the
from clause, the best way to go is to use a subset of the working memory, which you can create using collect or accumulate keywords along with the
from clause. These keywords and their uses are described in the following two sections.