I want to evaluate facts dynamically using drool engine. Rule conditions attributes & their conditional operators are stored in database and load in to WM when engine start.
So I want to use that operator in rule file as below.
$dynCx : DynCustomer()
$attrib : Attribute() from $dynCx.attributes
$offer : Offer($ofCode : offer_code, $domainName : domainName )
$rdef : OfferRuleDef($entity : entity,
$code : code,
$value : value,
$atrName : attributeName,
$atrVal : attributeVal,
$op : operation,
$entity == $domainName,
$code == "OFFER_CODE",
$value == $ofCode,
$atrName == $attrib.name,
$atrVal $op $attrib.value
)
but I'm getting below error
Caused by: java.lang.RuntimeException: [59,14]: [ERR 102] Line 59:14 mismatched input '$op' in rule "Evaluate Generic Offer Eligibility"
[0,0]: Parser returned a null Package
How we can achieve this?
Operators can't be resolved at runtime, so you can't write that kind of rule. Based on the version you are using and the number of operators you envision, you have a few options.
1) Write a rule for each operator:
OfferRuleDef( ..., operator == "==", attributeVal == $attrib.value )
to avoid excessive code repetition, consider that rules can "extend" each other.
2) In recent versions, create a static helper function and pass the three values:
OfferRuleDef( ..., MyHelper.applyOperator($attrValue, $op, $attrib.value) )
Related
I am trying to write a drools rule that would check if a student has an average of more than 5 at a discipline and i get this error whenever i try to run a static method in the when clause. Moreover, i couldn't find in the drools documentation if it's possible to call a static method in the when section.
This is the rule :
rule "student with at least two grades and average less than 5" salience 8
when
$student : Student(grades.size() >= 2)
$list : ArrayList() from eval(computeAverageGrade($student))
$value : Double() from $list
Boolean(booleanValue() == true) from $value < 5.0
then
System.out.println(2);
student.setValid(false)
end
This is the method's header:
public static List<Double> computeAverageGrade(Student student)
and this is the error i am getting :
Caused by: org.mvel2.PropertyAccessException: [Error: null pointer or function not found: eval]
[Near : {... eval(computeAverageGrade($stud ....}]
^
[Line: 1, Column: 1]
The error explains what's wrong: that's not how you use eval.
Unlike other languages like Javascript, eval doesn't execute arbitary code and return a value: it executes arbitrary code and evaluates a boolean.
You should have declared $list like this:
$list: ArrayList() from computeAverageGrade($student)
$value : Double(this < 5.0) from $list
An example for how you'd use eval, would be to evaluate some sort of truthiness from arbitrary code ... for example like this:
eval( $someValue < someExampleFunctionCall() )
Of course you should always avoid the use of eval because Drools is very good at simplifying conditions for efficient processing, and eval forces it to evaluate your code as-written, thus losing all efficiencies/performance improvements the engine could otherwise give you.
Almost any place you can use eval can be rewritten in a better way -- the example above can, for instance, be rewritten as Double(this > $someValue) from someExampleFunctionCall().
Solved it by replacing ArrayList with Object() and removing eval from the static method call.
found it here : https://docs.drools.org/7.73.0.Final/drools-docs/html_single/index.html#drl-rules-WHEN-con_drl-rules
Let's suppose we have these classes, which are also Hibernate entities, in a Java EE application:
public class A {
private String code;
private List<B> tests;
}
public class B {
private String code;
private List<C> steps;
}
public class C {
private String code;
private List<D> subSteps;
}
In last years, some Swing windows were created to let configurators users create ad deploy DRools packages rules in order to customize the workflow required by our customers. These windows, in some way, convert Swing components into Drool Mvel text to avoid configurators users writing raw code. These rules are then saved and deployed into their DB tables in BLOB fields and executed when required.
The problem is that now we are required to implement a new hierarchy of facts on which to perform assertions to trigger rules.
Using a fact of class A from my example code, this code is generated from our DRools windows:
rule "RULE_TRY"
dialect "mvel"
salience 10
enabled true
no-loop false
when
$a : A( )
$b : B( code == "testCode" ) from $a.tests
$c : C( code== "stepCode" ) from $a.tests.steps
then
end
It is clear that this rule and facts mirror our DB structure, where "code" is the PK or a FK in corresponding relative tables.
But this code causes a DRools error to be raised when package is compiled and deployed:
Unable to build expression for 'from' : Failed to compile: 1 compilation error(s):
- (1,26) unqualified type in strict mode for: steps'$a.tests.steps' : [Rule name='RULE_TRY']
Maybe, is this the right syntax:
when
$a : A( )
$b : B( code == "testCode" ) from $a
$c : C( code== "stepCode" ) from $b
?
Because, considering Mvel code as a sort of getter/setter, I would expect that a syntax like from $a.tests returns a List<B> and only on a single B-ith element I could eval .steps.
I don't know why rules are converted with that syntax, as there are too many EJBs without comment doing this in our core application, written by people who do not work with us anymore. But before correct them or write new ones for these requirements, I need to know if DRools can support an arbitrary nesting level with java List in facts and how to correctly access them, as it the first time our product has had to support these kinds of facts with many lists in rules.
Our version of DRools is 5.0.1
For the moment, I see that final String in DRL language is created by calling method dump() on class DrlDumper.
I think the syntax you are looking for is:
rule "RULE_TRY"
dialect "mvel"
salience 10
enabled true
no-loop false
when
$a : A( )
$b : B( code == "testCode" ) from $a.tests
$c : C( code== "stepCode" ) from $b.steps
then
//...
end
Hope it helps,
We are trying to map data using Talend DI tool. In that, we have to capture transformation which is related to Conditional operator.(Because of limitation of tool it won't allow if-then-else syntax instead it does support conditional operator.
Sample Data :
I am trying to write this expression into talend tmap component . How to write this expression into tmap component expression builder using ternary operator. Additional, i have to check for null values.
case when [TCode]='(00) PRE-PAID' then '00'when[TCode]='(01) C.O.D.' then '01'when[TCode]='(02) EOM' then '02'when[TCode]='10' then '(10) NET 10 DAYS'when[TCode]='15' then '(15) NET 15 DAYS'when[TCode]='21' then '(21) 2 % 30 NET 31'when[TCode]='23' then '(23) 2% NET 30 DAYS'when[TCode]='3' then '(3) CHECK'when[TCode]='30' then '(30) NET 30 DAYS' else [TCode]end as TCode
Tried this conditional operator :
"(00) PRE-PAID".equals(row.tCode) ?"00" :
"(01) C.O.D".equals(row.tCode) ?"01" :
"(02) EOM".equals(row.tCode) ? "02" :
"Unknown"
Getting error when tried above conditional operator :
Exception in thread "main" java.lang.Error: Unresolved compilation problems:
XML_API_tXMLMap_1 cannot be resolved to a type
XML_API_tXMLMap_1 cannot be resolved to a type
Syntax error on token ""(00) PRE-PAID"", delete this token
Thanks in advance !
Analysis
According to the link in your comment, you try to execute the following:
row1.tcode==null?null:row1.tcode.length()==0?null:row1.tcode.toUpperCase()
"(00) PRE-PAID".equals(row.tCode) ?"00" :
"(01) C.O.D".equals(row.tCode) ?"01" :
"(02) EOM".equals(row.tCode) ? "02" :
"Unknown"
and you are getting the following error:
Exception in thread "main" java.lang.Error: Unresolved compilation problems:
XML_API_tXMLMap_1 cannot be resolved to a type
XML_API_tXMLMap_1 cannot be resolved to a type
Syntax error on token ""(00) PRE-PAID"", delete this token
NOTE: Java is a case sensitive language. So either tcode or tCode is correct, not both.
Explanation
You provided two separate rows of code and Java doesn't know how to interpret this.
Your first line of code is (usually ended with a ;):
row1.tcode==null?null:row1.tcode.length()==0?null:row1.tcode.toUpperCase()
and the second line (albeit it has more lines in itself it is seen as "one line") of code is:
"(00) PRE-PAID".equals(row.tCode) ?"00" :
"(01) C.O.D".equals(row.tCode) ?"01" :
"(02) EOM".equals(row.tCode) ? "02" :
"Unknown"
We need to put those two instructions together.
Solution
(row1.tCode != null && !row1.tCode.equals("")) ? (
"(00) PRE-PAID".equals(row.tCode.toUpperCase()) ? "00" :
"(01) C.O.D".equals(row.tCode.toUpperCase()) ? "01" :
"(02) EOM".equals(row.tCode.toUpperCase()) ? "02" :
"Unknown") : "Unknown"
Alternatively, to shorten the first row, you could set a Default value for tCode if that makes any sense. The default value could be "" and you don't have to check for null anymore. Again, this depends on your use case.
Based on the example rule AssertingHasSiblingMulti1 for multi-slot setting given in AddingRuleWithJessTab I have created the following Jess rule for setting multi-slot values on my property foundPollutionSources:
(defrule findPHPolluters
(declare (salience 553))
(object
(is-a http..#PollutionSources)
(OBJECT ?sitepoll)
(http..potentialPollutant
$? ?b&:(eq (instance-name ?b)(instance-name http..#pH)) $?)
(http..#pollutionSourceName ?psName)
(http..#pollutionType ?psType)
)
(object
(is-a http..#MeasurementSite)
(OBJECT ?loc)
(http..#hasSourcesOfPollution $?sitepoll_list)
)
(object
(is-a http..#ModeratePHMeasurement)
(OBJECT ?mob)
(http..#observationResultLocation ?loc)
(http..#foundPollutionSources $?existing_poll_list)
)
=>
(if (not (member$ ?sitepoll $?sitepoll_list)) then
(printout t "pH pollution source: " ?psName " (Location: " ?psType ")" crlf)
(slot-set ?mob http..#foundPollutionSources (create$ $?existing_poll_list ?sitepoll))
)
)
But, when I run this rule the following exception appears:
Jess reported an error in routine ValueVector.set while executing
rule LHS (MTELN) while executing rule LHS (TECT). Message: Bad
index 117 in call to set() on this vector:...
There is a deviation from the usual Jess usage pattern of a variable bound to (part of) a multislot value. Observe:
(object
...
(http..#foundPollutionSources $?existing_poll_list))
The prefixed '$' causes all values in the foundPollutionSources slot to be bound to ?existing_poll_list. The usual usage (cf. the Jess manual and the linked example) would be, for instance:
(printout t "pollution sources " ?existing_poll_list crlf))
But your RHS code has
(create$ $?existing_poll_list ...)
Note the spurious '$' - I'd omit it and try again.
I don't know what protege and/or Jess might make of this, and I don't have the time to research it in the latter.
I keep getting compile errors when I try to write my rules.
I am trying to translate this condition into drools
if(model.type.series != null && model.type.series.name.mathes(".*FANR.*") ||
model.type.series.name.matches(".*SANA.*"))
//do something....
This is what I have...
rule "Rule 01" salience 0
when
m : model(type.series != null,
type.series.name.matches(".*FANR.*") ||
type.series.name.matches(".*SANA.*")
a : Result(state == Result.GOOD )
then
a.setState(RESULT.BAD);
....
end
What I was trying to do is to use regular expression to match the part of the string where the 'name' is String type. As I am fair new to drools I don't see where it can cause problems, any help would be appreciated
Use correct Drools syntax, according to the matches operator, as described in the Drools manual.
rule FANRorSANA
when
$n: model($v: type.series.name matches ".*(FANR|SANA).*")
then
And you can use the power of regular expressions for testing alternatives.