I'd like to declare a global variable that is scoped to only my rules file.
For example: variable $reUseMe is only declared once.
rule 1
$reUseMe : POJO(val = 1)
//other conditions
rule 2
$reUseMe > val
you can refer to globals in LHS via eval:
global SomeType variable
rule ...
when
...
eval(variable > something)
There are no scoped global variables, but in some cases rule inheritance helps.
rule "Rule 1"
when
$reUseMe :POJO( val == 1 )
then
end
rule "Rule 2" extends "Rule 1"
when
# You can use the variables from Rule 1
POJO( val > $reUseMe.val )
then
end
Only LHS is added. RHS from Rule 1 is ignored in Rule 2.
Related
How i can use temporary variables in macros for building code in the Crystal Lang. For examle, i have:
module Rule
macro included
{% rules = [] of TypeDeclaration %}
macro rule(declaration)
raise("Should be TypeDeclaration") unless \{{declaration.is_a?(TypeDeclaration)}}
\{% {{rules}} << declaration %}
end
macro finished
\{% for declaration in rules %}
puts \{{declaration}}
\{% end %}
end
end
end
class StringRule
include Rule
rule min : Int32 = 0
rule max : Int32 = 255
end
And i have compile error
> 6 | {% [] << declaration %}
^
Error: for empty arrays use '[] of ElementType'
I need store all rules for reusing it in a hook finished. Is it posible?
Variables in macro expressions are scoped to the respective macro context. So a variable defined in macro included is not visible in macro rule.
But you can use constants for this: RULES = [] of _. The _ underscore makes the array untyped, it is only to be used during compilation and not for actual code. If you refer to RULES outside a macro expression, the compiler will complain.
module Rule
macro included
RULES = [] of _
macro rule(declaration)
\{% raise("Should be TypeDeclaration") unless declaration.is_a?(TypeDeclaration) %}
\{% RULES << declaration %}
end
macro finished
\{% for declaration in RULES %}
puts \{{ declaration.stringify }}
\{% end %}
end
end
end
class StringRule
include Rule
rule min : Int32 = 0
rule max : Int32 = 255
end
I am using Drools rule engine and I was wondering if it was possible to define when a rule can be executed. For example:
If executing a rule A leads to executing 10 rules B1 to B10, is it possible to choose to execute only one of the Bi rules as a result of rule A??
An example would be :
rule "Rule A"
When
$var : Data(value>10)
then
doSmthg();
Event e = new Event();
insert(e);
end;
rule "Rule B"
When
$var : Data(range >100)
then
doSmthg();
Event e = new Event();
insert(e);
end;
rule "Rule C"
When
$e : Event()
then
doSmthg();
end;
Firing Rule A would lead to the execution of rule C.
Firing Rule B would lead to the execution of rule C.
Is there a way for me to make it so that rule C won't be fired even after rule A is executed? At the same time, firing B should still lead to the execution of rule C.
EDIT:
I have other rules that I need to be fired if Rule A is executed for example :
rule "Rule D"
When
$e : Event()
then
doSmthgElse();
end;
so in this case, I just want to disable Rule C from being fired without altering my other rules.
You are triggering subsequent rules by inserting data into working memory. If you don't want a rule to trigger a 'downstream' rule, don't insert the data. Or, alternatively, update the downstream rule to not trigger on the condition.
Consider these three rules, which are the same as yours but with syntax cleaned up and indentations fixed.
rule "Rule A"
when
Data( value>10 )
then
doSmthg();
insert(new Event());
end
rule "Rule B"
when
Data(range > 100)
then
doSmthg();
insert(new Event());
end
rule "Rule C"
when
$e : Event()
then
doSmthg();
end
Let's assume your input is Data( value = 100, range = 500 ). This is what will happen:
Rule A will hit because the condition value > 10 is met.
Rule B will hit because the condition range > 100 is met.
Rule C will hit twice because there are two instances of Event in working memory (one added by Rule A, one added by Rule B.)
Per your question, we don't want Rule C to fire because of Rule A in this condition. To do this, we need to change Rule A to not trigger Rule C. This is simple: remove the insert(new Event()) from the right hand side of Rule A.
rule "New Rule A"
when
Data( value > 10 )
then
doSmthg();
// No insert!
end
Rule B will still trigger because its condition (range > 100) is still met and it still inserts the Event instance into working memory.
Alternatively if what you're actually trying to do is have both A and B trigger C, but only to trigger C once, you can do that by inserting a semaphor into working memory to indicate that C is fired.
Here is an example:
rule "Rule A"
when
Data( value>10 )
then
doSmthg();
insert(new Event());
end
rule "Rule B"
when
Data(range > 100)
then
doSmthg();
insert(new Event());
end
rule "Rule C"
when
not( EventTriggered() )
$e : Event()
then
doSmthg();
insert( new EventTriggered() )
end
For the same input ( Data( value = 50, range = 500 ) ) this is what will happen:
Rule A will insert Event into working memory
Rule B will insert Event into working memory
Rule C will trigger from one of these Events. It will insert an instance of EventTriggered into working memory.
Rule C will not trigger a second time because the not() condition is not satisfied.
Using this setup, an input of Data( value = 5, range = 500 ) will still trigger Rule C via B:
Rule A will NOT fire (condition not met, value <= 10)
Rule B will fire, Event inserted into working memory
Rule C will fire.
And further, an input of Data( value = 50, range = 0) will also trigger Rule C via A:
Rule A will fire, Event inserted into working memory
Rule B will NOT fire (condition not met, range <= 100)
Rule C will fire.
Which solution you choose depends on what your actual requirements are.
If your requirement is that Rule A should never trigger Rule C, then the first solution (removing the insert statement) is the way to go.
If your requirement is that both Rule A and Rule B should trigger Rule C, but only once, then the second solution is the way to go.
I have a simple rule case here
salience 50
no-loop true
rule "1"
when
input: Input(a == 20, b == 16026)
then
modify(input) {setZ(3)}
end
salience 40
no-loop true
rule "2"
when
input: Input(a == 20, c == 209)
then
modify(input) {setZ(9)}
end
If I leave the above rules as is, they go into a continuous loop.
However, if I modify both rules from:
modify(input) {setZ(9)}
to:
input.setZ(9);
Then the rules execute in order as expected.
My question is: Do I need to use the modify keyword? What does the modify keyword do?
modify (or update) must be used if the Drools Engine is to re-evaluate rules according to the new value for the modified fact object. Omitting this will not trigger rules where constraints match the new value.
For these two rules, modify is not necessary. But if there is a rule
rule x
when
Input( z == 9 || == 3 )
then ... end
you would have to use it. In this case, add constraints to your rules:
Input( ..., z != 3 )
and
Input( ..., z != 9 )
respectively, and it will work and you won't even need no-loop any more.
I came across a particular scenario when updating composed objects in Drools:
declare A
#propertyReactive
flag: Boolean
end
declare B
flag: Boolean
end
declare C
#propertyReactive
attrA: A
attrB: B
end
rule "Create A"
when
not A()
then
insert(new A());
System.out.println("OBJECT A CREATED");
end
rule "Create B"
when
not B()
then
insert(new B());
System.out.println("OBJECT B CREATED");
end
rule "Create C"
when
$A:A()
$B:B()
not C()
then
insert(new C($A,$B));
System.out.println("OBJECT C CREATED");
end
rule "Modify A"
when
$A:A(flag == false)
C()
then
modify($A) {setFlag(true)};
String $output = "Now A is " + $A.getFlag();
System.out.println($output);
end
rule "Print C when C is False"
when
C($A:attrA, attrA.flag == false, $B:attrB)
then
String $output = "A is " + $A.getFlag() + " and B is " + $B.getFlag();
System.out.println($output);
end
rule "Print C when C is True"
when
C($A:attrA, attrA.flag == true, $B:attrB)
then
String $output = "A is " + $A.getFlag() + " and B is " + $B.getFlag();
System.out.println($output);
end
rule "Print C when C is True 2"
when
C($A:attrA, $B:attrB)
A(this == $A, flag == true)
then
String $output = "2 A is " + $A.getFlag() + " and B is " + $B.getFlag();
System.out.println($output);
end
The output is:
OBJECT A CREATED
OBJECT B CREATED
OBJECT C CREATED
A is false and B is false
Now A is true
2 A is true and B is false
So I have the following questions:
Why is rule "Print C when C is True" not firing?
Why do I need to rewrite that rule as "Print C when C is True 2" to make it work?
Does anybody have an explanation for this?
It looks like Drools has an issue in working with accessors of nested classes...
Thank you very much.
Drools only reacts on changes performed to the objects used in your patterns and not to any nested object reference they may contain. And this is not entirely true either.
When you modify a fact in Drools (or any of its nested object references), you have the option to let Drools know about this modification or not. In your case, you are never notifying Drools about them. That is why your rules depend on the (not deterministic) evaluation order.
If you want to let Drools "know" about the modifications on a fact, then you must use the modify or update functions in the RHS of the rules where these modifications happen.
In your particular case, you have not only nested objects, but nested facts: A and C. Even if A is a fact, changes on A (even if properly notified to Drools) will never trigger the re-evaluation of a pattern like this:
C(attrA.flag == true)
The reason why is because the type of the pattern is C and not A. In these kind of scenarios, a rule written like your Print C when C is True 2 rule is a better approach.
Hope it helps,
I am new to Drools. I am creating a rule but I get a compile time error
"field is not visible'.
I've tried to check with Jboss examples, where they use dialect "mvel". It compiled. I didn't understand about dialect. So what is dialect=mvel?
mvel, or the MVFLEX Expression Language has a rich syntax, many of which allow for more concise and expressive code (and less imperative) than java, e.g.
Shorthand for get()ters / set()ters (e.g. encapsulating private fields) to be accessed in an alternative property style syntax (similar to VB or C# properties in .Net)
ie. instead of
myObject.setSomeField("SomeValue");
int x = myObject.getSomeIntField();
You can use the syntax (note the subtle capitalization switch as well):
myObject.someField = "SomeValue"
x = myObject.someIntField // Type inferrence
The return statement is optional (a convention found in many functional languages like Scala), as is the semi-colon, unless you have multiple statements per line:
x // i.e. return x;
Shorthand array creation and indexing by ordinal
foos = {2, 4, 6, 8, 10}
foos[3] // foos.get(3)
Similarly for Maps (Dictionaries)
bars = ["a" : "Apple", "b" : "Basket"] // Hashmap, with put
bars["a"]
bars.a // Similar to dynamically typed object e.g. in javascript, if key is a string.
Null Safe navigation operator (like the null-conditional operator in Rosyln)
foo.?bar.baz // if (foo.bar != null) { return foo.bar.baz; } else { return null; }
Based on
Drools JBoss Rules 5.0 Developer's Guide
Dialect is used to specify the syntax in any code expression that is in a condition or consequence. The default value is Java. Drools currently supports one more dialect called mvel.
Specifically mvel is an expression language for Java-based applications. And it's based on Java syntax. more info about mvel
rule "validate holiday"
dialect "mvel"
dialect "java"
when
$h1 : Holiday( month == "july" )
then
System.out.println($h1.name + ":" + $h1.month);
end
The purpose of dialect "mvel" is to point the Getter and Setters of the variables of your Plain Old Java Object (POJO) classes. Consider the above example, in which a Holiday class is used and inside the circular brackets (parentheses) "month" is used. So with the help dialect "mvel" the getter and setters of the variable "month" can be accessed.
Dialect "java" is used to help us write our Java code in our rules. There is one restriction or characteristic on this. We cannot use Java code inside "when" part of the rule but we can use Java code in "then" part.
We can also declare a Reference variable $h1 without the $ symbol. There is no restriction on this. The main purpose of putting the $ symbol before the variable is to mark the difference between variables of POJO classes and Rules.
Regards.
if you use dialect mvel - will fix your error.
Otherwise the scope of that variable is private by default, so use the default getter.
getField(). replace "Field" with yoru fieldname.
You can see the source code of the class in Data Objects -> class -> source tab in Business Central.
I found some thing on this.I shared with that.Drools was supported Java or MVEL scripting language.To get object properties values.For Example,The Fibonacci has bean and having multiple properties i.e.,sequence
rule Recurse
salience 10
when
not ( Fibonacci ( sequence == 1 ) )
f : Fibonacci ( value == -1 )
then
insert( new Fibonacci( f.sequence - 1 ) );
System.out.println( "recurse for " + f.sequence ); end
we need to check the if sequence ==1 then value is -1 .The sequence values are setting into Fibonacci object.We are checked the values based on MVEL or java.MVEL is a superset of Java.