I'm trying to set up a drools rule that would achieve something like this:
rule "Rule 1"
salience 100
activation-group "..."
when
$fact: Fact exists, or fact does not exist
then
if $fact exists then do X. Else, do Y.
I know I can do this by having two different rules, one for when the fact exists, and one for when the fact does not exist. Is there any way I can combine these conditions into one? For the when statement, I have tried:
$fact: Fact() || not Fact()
which didn't work. Is there a correct way to write the "when" statement in drools?
If we revert to boolean logic, A || !A is the same as true so you can omit the condition entirely.
You're already doing this correctly. Rule 1: if X exists, then do A(). Rule 2: if X does not exist, then do B(). That is the correct way to do things:
rule "Fact exists"
when
exists(Fact())
then
doX()
end
rule "Fact does not exist"
when
not(Fact())
then
doY()
end
That's good rule design and how you should do this. I can't think of a good reason to compress it into one rule. If you can clarify a legitimate use case for why you'd want to collapse it into a single rule, it might be possible to give a better or more concrete response.
You may be able to abuse conditional and named consequences for this, though it's definitely not designed for this (nor recommended.)
Related
I have just started learning Drools and have written the following rule
rule "matematikk"
when
class( code == "MAT1003") or class( code == "MAT1008")
or
class( code == "REA3022") or class( code == "REA3024") or class( code == "REA3026") or class( code == "REA3028")
or
class(code == "MAT1006") and class(code == "MAT1010") or class( code == "MAT1001") and class( code == "MAT1005")
or
class(code == "MAT1002") or class(code == "MAT1007")
then
logger.info("passed");
end
when I run this rule I will get three passed prints in the terminal? which is correct since only three of the line are true, but how do I make it process the entire "when" block and after that go to the "then" section.
cheers,
ehsan
also if I remove the "or" between the lines I won't get any results.
Your rule doesn't really make sense. You've got "ands" and "ors" mixed up in here in no particular order. Setting things on separate lines doesn't create any sort of "grouping". Proximity similarly doesn't provide any sort of inherent relationship. The "and"s on the third 'line' are particularly confusing -- I have no idea what they're supposed to be and-ing.
Generally, if you want to represent an "or" condition, you'd write the conditional like this:
class( code in ("MAT1003", "MAT1008", ... ))
(Where "..." represents additional values.)
This is read as "There is a 'class' that has code MAT1003 or MAT1008."
For an "and" you'd simply have two different statements; only if both match will the rule be 'triggered'. For example:
exists(class(code == "MAT1006"))
exists(class(code == "MAT1010"))
This would trigger if there are at least 2 "class" instances in working memory, and there is at least one with code MAT1006 and at least one with code MAT1010.
I used exists because you don't actually need a reference to the matching instance for use in the right hand side; I also presumed you only need to trigger once regardless of how many instances matched your condition.
Finally -- I strongly suggest not naming your class "class" since it's a reserved word in Java. If it is representing some sort of educational class (eg at university), may I suggest calling it Course instead?
I am using CodeEffects rule editor for writing business rules in our application. I have many if-else conditions which are actually nested-if type rules. With no support of nested-if, I need to re-write all if conditions every time.
But I can't find any way to write rule as mentioned below.
if (Condition1)
(
if (sub-condition1)( ....)
else if (....)
)
else
(
....
)
The best way to substitute nested IFs is to use the same sub-conditions multiple times:
if condition1 and sub-condition1 then DoOneThing
else if sub-condition1 then DoAnotherThing
else DoSomethingElse
It's not perfect but it'll do.
I'm wondering if there is anything in drools that can be used to determine how close a rule is (or has been) to being activated?
From all that I can tell, the standard drools doesn't support anything like it, I just wondered if I might have missed something.
I glanced at Drools Chance (https://github.com/droolsjbpm/drools-chance), but it seems that it hasn't been developed a lot anymore recently and doesn't seem ready for Drools 6.x.
I know that AgendaEventListeners can be used to intercept when a rule has fired but it doesn't look like there is anything to intercept if a single condition of a rule has been evaluated to true.
Am I missing something or is this a current limitation of drools to not have any support for this kind of thing?
Thanks!
Perhaps you could use a work around. Construct a set of extra rules that write metrics for when they fire depending on your definition of close--that could be 1..n parts of the LHS for the rule of interest and/or thresholds for nearness to any part of the LHS (say you want to know when a value approaches to with 90% of another value). For complex conditionals in source code (not Drools related), I've used approaches like the below to trace complicated and nested logic:
boolean a1 = property1 > property2
boolean a2 = (!isHigh || isMedium)
boolean a3 = property 4 == property5
System.out.println ("rule2: " + a1 + " " + a2 + " " + a3);
if (a1 && a2 && a3) {
...do something
}
This is related what I called "learning the reason for failure". Consider that you have to pass n qualifications. Rather than being told that you have failed you'd like to have a list of the "pass" (and "fail") criteria.
One rule evaluating all of this in a lump sum is no good. You have to write one rule for each of the n criteria and collect the positives with the fact holding the properties under survey. Finally, one low-priority rule can check whether you have all n ("hooray") and another one can tell you "sorry, no", but it can give you a list what succeeded (and what not).
Lots of effort, but good information is always costly.
I'm trying to use Drools backward chaining to find out which facts are needed to get an object inserted in the working memory.
In the following example, I expect to get the fact "go2".
rule "ins a"
when
String( this == "go2" )
then
insert(new A());
end
rule "Run"
when
then
insert(new String("go1"));
end
rule "Test isThereAnyA"
when
String( this == "go1" )
isThereAnyA(a;)
then
System.out.println( "you can get " + a );
end
query isThereAnyA (A a)
a := A()
end
I've been looking at examples in the official documentation
http://docs.jboss.org/drools/release/6.1.0.Final/drools-docs/html_single/index.html#d0e21289
but they show a different situation (the rules in those examples doesn't creates new fact)
From the chart
http://docs.jboss.org/drools/release/6.1.0.Final/drools-docs/html_single/index.html#d0e21240
I think it should work but I haven't found a way to specify a query that gives me the expected results.
Thank you in advance.
Short answer:
Unfortunately backward chaining can not be used for this purpose.
It will not give you "go2" in this case.
Long answer:
In Drools, Backward chaining (BC) is a way to query the WM in a goal-driven fashion, not a way to trace back the derivation graph of a normal forward chaining inference process.
BC allows rule "Test" to retrieve As through the query "isThereAnyA", and possibly invoke other queries, but will not allow to find the "production" link between "A" and "go2". The reason is that "when..then..insert.." does not create any link between the triggering facts and the asserted conclusion, and backward chaining will not change it.
What you could do with BC is this:
query isThereAnyA_InPresenceOfA_String( A a )
isThereAnyString( $s ; )
a := A()
end
query isThereAnyString( String $s )
$s := String( this == "go2" )
end
This will pick up As only if a String "go2" is (still) present. However you'll notice that the connection between a particular instance of A and a the particular String which led to its assertion is still missing.
To know exactly which objects led to the assertion of another object you may need a different approach. Options include:
make the connection explicit : new A( $s ) // $s bound to "go2"
use "insertLogical" to establish a dependency between "go2" and A, then query the TruthMaintenanceSystem
The TMS-based one would be my tentative choice, but it also depends on your exact requirements.
This use case is common, there may be other options, including a few which are experimental as they are being developed in 6.3, but I'd rather ask a few questions first.
That is: when do you need exactly to discover the facts - during the execution of the rules, or "offline"? Is it purely for auditing purposes, or does it impact your business logic? Can you have multiple rules asserting the "same" object?
Hope this helps
Davide
The most basic use of from clause is not working, even when I know there are elements in the list and there is no conditions for the elements being extracted from that list, and I have other rules working properly.
Here is what happens, there are many variables in my problem, but I have simplified to this:
Having this two rules, the firstone is made to demostrate that fixedShipmentValueData list, has at least one element. However, the second rule isn't fired, even when the only different thing it does from the firstone, is to use the from clause and put a variable name.
rule "Print list value"
ruleflow-group "fixed-values"
no-loop
when
cParams: CustomerParameters(list: fixedShipmentValueData)
then
System.out.format("There are %s elements at fixedShipmentValue and fixed value is %s%n",list.size(),
((ParameterValues)list.get(0)).getFixedShipmentValue());
end
rule "Do something with the list"
ruleflow-group "fixed-values"
no-loop
when
cParams: CustomerParameters(list: fixedShipmentValueData)
ParameterValues($fixedShipmentValue: fixedShipmentValue) from list
then
System.out.format("fixed Shipment Value is %s%n", $fixedShipmentValue);
end
This looks so simple... I have expent enough time as to be out of ideas.
Without seeing the Java code for CustomerParameters, ParameterValues and the application part that creates, composes and inserts the fact(s) there's just one scenario I know that can reproduce the effect as you have told it. Consider this:
A List<Base> is field list in class X. There are subclasses SubA and SubB both extending Base. Create X, add objects of class SubA to list. A rule (like "Print list value") will show that the list is not empty.
However, a rule using a pattern such as
X( $list: list )
SubB() from $list
will never fire, since there are no SubB's in Base.list.