<html xmlns="http://www.w3.org/TR/xhtml1/transitional"><head xmlns="">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>3.6. Advanced Conditional Elements</title><link rel="stylesheet" href="../shared/css/html.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets Vsnapshot_6797"><link rel="start" href="title.html" title="Drools"><link rel="up" href="ch03.html" title="Chapter 3. The Rule Language"><link rel="prev" href="ch03s05.html" title="3.5. Rule"><link rel="next" href="ch03s07.html" title="3.7. Query"><base xmlns="http://www.w3.org/TR/xhtml1/transitional" target="body"></base></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div xmlns="" class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">3.6. Advanced Conditional Elements</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s05.html">Prev</a> </td><th width="60%" align="center">Chapter 3. The Rule Language</th><td width="20%" align="right"> <a accesskey="n" href="ch03s07.html">Next</a></td></tr></table><hr></div><div xmlns="" class="section" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e2677"></a>3.6. Advanced Conditional Elements</h2></div></div></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p><em class="replaceable"><code>(updated to Drools 4.0)</code></em></p></div><p>Drools 4.0 introduces a whole new set of conditional elements in order
to support full First Order Logic expressiveness, as well as some facilities
for handling collections of facts. This section will detail the following
new Conditional Elements:</p><div class="itemizedlist"><ul type="disc"><li><p>from</p></li><li><p>collect</p></li><li><p>accumulate</p></li><li><p>forall</p></li></ul></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2699"></a>3.6.1. From</h3></div></div></div><p>The <span class="bold"><strong>from</strong></span> Conditional Element allows
users to specify a source for patterns to reason over. This allows the
engine to reason over data not in the Working Memory. This could be a
sub-field on a bound variable or the results of a method call. It is a
powerful construction that allows out of the box integration with other
application components and frameworks. One common example is the
integration with data retrieved on-demand from databases using hibernate
named queries.</p><p>The expression used to define the object source is any expression
that follows regular MVEL syntax. I.e., it allows you to easily use object
property navigation, execute method calls and access maps and collections
elements.</p><p>Here is a simple example of reasoning and binding on another pattern
sub-field:</p><pre class="programlisting">rule "validate zipcode"
when
Person( $personAddress : address )
Address( zipcode == "23920W") from $personAddress
then
# zip code is ok
end
</pre><p>With all the flexibility from the new expressiveness in the Drools
engine you can slice and dice this problem many ways. This is the same but
shows how you can use a graph notation with the 'from':</p><pre class="programlisting">rule "validate zipcode"
when
$p : Person( )
$a : Address( zipcode == "23920W") from $p.address
then
# zip code is ok
end
</pre><p>Previous examples were reasoning over a single pattern. The
<span class="bold"><strong>from</strong></span> CE also support object sources that
return a collection of objects. In that case, <span class="bold"><strong>from</strong></span> will iterate over all objects in the
collection and try to match each of them individually. For instance, if we
want a rule that applies 10% discount to each item in an order, we could
do:</p><pre class="programlisting">rule "apply 10% discount to all items over US$ 100,00 in an order"
when
$order : Order()
$item : OrderItem( value > 100 ) from $order.items
then
# apply discount to $item
end
</pre><p>The above example will cause the rule to fire once for each item
whose value is greater than 100 for each given order.</p><p>The next example shows how we can reason over the results of a
hibernate query. The Restaurant pattern will reason over and bind with
each result in turn:</p></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2733"></a>3.6.2. Collect</h3></div></div></div><p>The <span class="bold"><strong>collect</strong></span> Conditional Element
allows rules to reason over collection of objects collected from the given
source or from the working memory. A simple example:</p><pre class="programlisting">import java.util.ArrayList
rule "Raise priority if system has more than 3 pending alarms"
when
$system : System()
$alarms : ArrayList( size >= 3 )
from collect( Alarm( system == $system, status == 'pending' ) )
then
# Raise priority, because system $system has
# 3 or more alarms pending. The pending alarms
# are $alarms.
end
</pre><p>In the above example, the rule will look for all pending alarms in
the working memory for each given system and group them in ArrayLists. If
3 or more alarms are found for a given system, the rule will fire.</p><p>The <span class="bold"><strong>collect</strong></span> CE result pattern can
be any concrete class that implements tha java.util.Collection interface
and provides a default no-arg public constructor. I.e., you can use
default java collections like ArrayList, LinkedList, HashSet, etc, or your
own class, as long as it implements the java.util.Collection interface and
provide a default no-arg public constructor.</p><p>Both source and result patterns can be constrained as any other
pattern.</p><p>Variables bound before the <span class="bold"><strong>collect</strong></span>
CE are in the scope of both source and result patterns and as so, you can
use them to constrain both your source and result patterns. Although, the
<span class="emphasis"><em>collect( ... )</em></span> is a scope delimiter for bindings,
meaning that any binding made inside of it, is not available for use
outside of it.</p><p>Collect accepts nested <span class="bold"><strong>from</strong></span>
elements, so the following example is a valid use of <span class="bold"><strong>collect</strong></span>:</p><pre class="programlisting">import java.util.LinkedList;
rule "Send a message to all mothers"
when
$town : Town( name == 'Paris' )
$mothers : LinkedList()
from collect( Person( gender == 'F', children > 0 )
from $town.getPeople()
)
then
# send a message to all mothers
end
</pre></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2770"></a>3.6.3. Accumulate</h3></div></div></div><p>The <span class="bold"><strong>accumulate</strong></span> Conditional Element
is a more flexible and powerful form of <span class="bold"><strong>collect</strong></span> Conditional Element, in the sense that it
can be used to do what <span class="bold"><strong>collect</strong></span> CE does
and also do things that <span class="bold"><strong>collect</strong></span> CE is not
capable to do. Basically what it does is it allows a rule to iterate over
a collection of objects, executing custom actions for each of the
elements, and at the end return a result object.</p><p>The general syntax of the <span class="bold"><strong>accumulate</strong></span> CE is:</p><pre class="programlisting"><em class="replaceable"><code><result pattern></code></em> from accumulate( <em class="replaceable"><code><source pattern></code></em>,
init( <em class="replaceable"><code><init code></code></em> ),
action( <em class="replaceable"><code><action code></code></em> ),
reverse( <em class="replaceable"><code><reverse code></code></em> ),
result( <em class="replaceable"><code><result expression></code></em> ) )
</pre><p>The meaning of each of the elements is the following:</p><div class="itemizedlist"><ul type="disc"><li><p><span class="bold"><strong><source pattern></strong></span>: the
source pattern is a regular pattern that the engine will try to match
against each of the source objects.</p></li><li><p><span class="bold"><strong><init code></strong></span>: this is a
semantic block of code in the selected dialect that will be executed
once for each tuple, before iterating over the source objects.</p></li><li><p><span class="bold"><strong><action code></strong></span>: this is a
semantic block of code in the selected dialect that will be executed
for each of the source objects.</p></li><li><p><span class="bold"><strong><reverse code></strong></span>: this is
an optional semantic block of code in the selected dialect that if
present will be executed for each source object that no longer matches
the source pattern. The objective of this code block is to "undo" any
calculation done in the <action code> block, so that the engine
can do decremental calculation when a source object is modified or
retracted, hugely improving performance of these operations.</p></li><li><p><span class="bold"><strong><result expression></strong></span>: this
is a semantic expression in the selected dialect that is executed
after all source objects are iterated.</p></li><li><p><span class="bold"><strong><result pattern></strong></span>: this is
a regular pattern that the engine tries to match against the object
returned from the <result expression>. If it matches, the
<span class="bold"><strong>accumulate</strong></span> conditional element
evaluates to <span class="bold"><strong>true</strong></span> and the engine
proceeds with the evaluation of the next CE in the rule. If it does
not matches, the <span class="bold"><strong>accumulate</strong></span> CE
evaluates to <span class="bold"><strong>false</strong></span> and the engine
stops evaluating CEs for that rule.</p></li></ul></div><p>It is easier to understand if we look at an example:</p><pre class="programlisting">rule "Apply 10% discount to orders over US$ 100,00"
when
$order : Order()
$total : Number( doubleValue > 100 )
from accumulate( OrderItem( order == $order, $value : value ),
init( double total = 0; ),
action( total += $value; ),
reverse( total -= $value; ),
result( total ) )
then
# apply discount to $order
end
</pre><p>In the above example, for each Order() in the working memory, the
engine will execute the <span class="bold"><strong>init code</strong></span>
initializing the total variable to zero. Then it will iterate over all
OrderItem() objects for that order, executing the <span class="bold"><strong>action</strong></span> for each one (in the example, it will sum
the value of all items into the total variable). After iterating over all
OrderItem, it will return the value corresponding to the <span class="bold"><strong>result expression</strong></span> (in the above example, the value
of the total variable). Finally, the engine will try to match the result
with the Number() pattern and if the double value is greater than 100, the
rule will fire.</p><p>The example used java as the semantic dialect, and as such, note
that the usage of ';' is mandatory in the init, action and reverse code
blocks. The result is an expression and as such, it does not admit ';'. If
the user uses any other dialect, he must comply to that dialect specific
syntax.</p><p>As mentioned before, the <span class="bold"><strong>reverse
code</strong></span> is optional, but it is strongly recommended that the user
writes it in order to benefit from the <span class="emphasis"><em>improved performance on
update and retracts</em></span>.</p><p>The <span class="bold"><strong>accumulate</strong></span> CE can be used to
execute any action on source objects. The following example instantiates
and populates a custom object:</p><pre class="programlisting">rule "Accumulate using custom objects"
when
$person : Person( $likes : likes )
$cheesery : Cheesery( totalAmount > 100 )
from accumulate( $cheese : Cheese( type == $likes ),
init( Cheesery cheesery = new Cheesery(); ),
action( cheesery.addCheese( $cheese ); ),
reverse( cheesery.removeCheese( $cheese ); ),
result( cheesery ) );
then
// do something
end</pre><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="d0e2888"></a>3.6.3.1. Accumulate Functions</h4></div></div></div><p>The accumulate CE is a very powerful CE, but it gets real
declarative and easy to use when using predefined functions that are
known as Accumulate Functions. They work exactly like accumulate, but
instead of explicitly writing custom code in every accumulate CE, the
user can use predefined code for common operations.</p><p>For instance, the rule to apply discount on orders written in the
previous section, could be written in the following way, using
Accumulate Functions:</p><pre class="programlisting">rule "Apply 10% discount to orders over US$ 100,00"
when
$order : Order()
$total : Number( doubleValue > 100 )
from accumulate( OrderItem( order == $order, $value : value ),
sum( $value ) )
then
# apply discount to $order
end
</pre><p>In the above example, sum is an AccumulateFunction and will sum
the $value of all OrderItems and return the result.</p><p>Drools 4.0 ships with the following built in accumulate
functions:</p><div class="itemizedlist"><ul type="disc"><li><p>average</p></li><li><p>min</p></li><li><p>max</p></li><li><p>count</p></li><li><p>sum</p></li></ul></div><p>These common functions accept any expression as input. For
instance, if someone wants to calculate the average profit on all items
of an order, a rule could be written using the average function:</p><pre class="programlisting">rule "Average profit"
when
$order : Order()
$profit : Number()
from accumulate( OrderItem( order == $order, $cost : cost, $price : price )
average( 1 - $cost / $price ) )
then
# average profit for $order is $profit
end
</pre><p>Accumulate Functions are all pluggable. That means that if needed,
custom, domain specific functions can easily be added to the engine and
rules can start to use them without any restrictions. To implement a new
Accumulate Functions all one needs to do is to create a java class that
implements the org.drools.base.acumulators.AccumulateFunction interface
and add a line to the configuration file or set a system property to let
the engine know about the new function. As an example of an Accumulate
Function implementation, the following is the implementation of the
"average" function:</p><pre class="programlisting">/*
* Copyright 2007 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Created on Jun 21, 2007
*/
package org.drools.base.accumulators;
/**
* An implementation of an accumulator capable of calculating average values
*
* @author etirelli
*
*/
public class AverageAccumulateFunction implements AccumulateFunction {
protected static class AverageData {
public int count = 0;
public double total = 0;
}
/* (non-Javadoc)
* @see org.drools.base.accumulators.AccumulateFunction#createContext()
*/
public Object createContext() {
return new AverageData();
}
/* (non-Javadoc)
* @see org.drools.base.accumulators.AccumulateFunction#init(java.lang.Object)
*/
public void init(Object context) throws Exception {
AverageData data = (AverageData) context;
data.count = 0;
data.total = 0;
}
/* (non-Javadoc)
* @see org.drools.base.accumulators.AccumulateFunction#accumulate(java.lang.Object, java.lang.Object)
*/
public void accumulate(Object context,
Object value) {
AverageData data = (AverageData) context;
data.count++;
data.total += ((Number) value).doubleValue();
}
/* (non-Javadoc)
* @see org.drools.base.accumulators.AccumulateFunction#reverse(java.lang.Object, java.lang.Object)
*/
public void reverse(Object context,
Object value) throws Exception {
AverageData data = (AverageData) context;
data.count--;
data.total -= ((Number) value).doubleValue();
}
/* (non-Javadoc)
* @see org.drools.base.accumulators.AccumulateFunction#getResult(java.lang.Object)
*/
public Object getResult(Object context) throws Exception {
AverageData data = (AverageData) context;
return new Double( data.count == 0 ? 0 : data.total / data.count );
}
/* (non-Javadoc)
* @see org.drools.base.accumulators.AccumulateFunction#supportsReverse()
*/
public boolean supportsReverse() {
return true;
}
}
</pre><p>The code for the function is very simple, as we could expect, as
all the "dirty" integration work is done by the engine. Finally, to plug
the function into the engine, we added it to the configuration
file:</p><pre class="programlisting">drools.accumulate.function.average = org.drools.base.accumulators.AverageAccumulateFunction
</pre><p>Where "drools.accumulate.function." is a prefix that must always
be used, "average" is how the function will be used in the rule file,
and "org.drools.base.accumulators.AverageAccumulateFunction" is the
fully qualified name of the class that implements the function
behavior.</p></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2931"></a>3.6.4. Forall</h3></div></div></div><p><span class="bold"><strong>Forall</strong></span> is the Conditional Element
that completes the First Order Logic support in Drools. The syntax is very
simple: </p><pre class="programlisting">forall( <em class="replaceable"><code><select pattern></code></em> <em class="replaceable"><code><constraint patterns></code></em> )</pre><p>The <span class="bold"><strong>forall</strong></span> Conditional Element will
evaluate to true when all facts that match the <em class="replaceable"><code><select
pattern></code></em> match all the <em class="replaceable"><code><constraint
patterns></code></em>. Example:</p><pre class="programlisting">rule "All english buses are red"
when
forall( $bus : Bus( type == 'english')
Bus( this == $bus, color = 'red' ) )
then
# all english buses are red
end
</pre><p>In the above rule, we "select" all Bus object whose type is
"english". Then, for each fact that matchs this pattern we evaluate the
following patterns and if they match, the forall CE will evaluate to true.
Another example:</p><pre class="programlisting">rule "all employees have health and dental care programs"
when
forall( $emp : Employee()
HealthCare( employee == $emp )
DentalCare( employee == $emp )
)
then
# all employees have health and dental care
end
</pre><p>Forall can be nested inside other CEs for complete expressiveness.
For instance, <span class="bold"><strong>forall</strong></span> can be used inside a
<span class="bold"><strong>not</strong></span> CE:</p><pre class="programlisting">rule "not all employees have health and dental care"
when
not forall( $emp : Employee()
HealthCare( employee == $emp )
DentalCare( employee == $emp )
)
then
# not all employees have health and dental care
end
</pre><p>As a side note, forall Conditional Element is equivalent to
writing:</p><pre class="programlisting">not( <em class="replaceable"><code><select pattern></code></em> and not ( and <em class="replaceable"><code><constraint patterns></code></em> ) )</pre><p>Also, it is important to note that <span class="bold"><strong>forall is a
scope delimiter</strong></span>, so it can use any previously bound variable,
but no variable bound inside it will be available to use outside of
it.</p></div></div><div xmlns="" class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch03s05.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch03.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch03s07.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">3.5. Rule </td><td width="20%" align="center"><a accesskey="h" href="title.html">Home</a> | <a accesskey="t" href="bk01-toc.html">ToC</a></td><td width="40%" align="right" valign="top"> 3.7. Query</td></tr></table></div></body></html>