Drools : applying same rules on all attributes - drools

I am new to Drools, we are trying to create basic validation rules like a NULL check, etc. using the Drools n Scala framework.
I have a source file which has 200 attributes, need to apply NULL-check rule on all these attributes,
is there any easy way to do this? or do I need to create 200 rules for each attribute?
Thanks in advance.

Assuming you have a POJO ("plain old java object", getters/setters and some private variables to hold values) or modern java Records (effectively the same thing), then the answer is no: you need separate rules. For this scenario, the only way to check that field "name" is null is to actually assert against that field like this:
rule "example - name is null"
when
ExampleObject( name == null )
then
System.out.println("Name is null.");
end
However there exist other data structures -- for example, Map and its sibling types -- where you can reference the fields by name. In this case you could theoretically iterate through all of the field names and find the one whose value is empty.
So, for example, Map has a keySet() method which returns a set of fields -- you could iterate through this keyset and for each key check that there is a non-null value present in the map.
rule "example with map"
when
$map: Map()
$keys: Set() from $map.keySet()
$key: String() from $keys
String( this == null ) from $map.get($key)
// or this might work, not sure if the "this" keyword allows this syntax:
// Map( this[$key] == null ) from $map
then
System.out.println($key + " is missing/null");
end
This would require converting your Java object into a Map before passing into the rules.
However I DO NOT RECOMMEND this approach. Maps are extremely un-performant in rules because of how they serialize/deserialize. You will use a ton of unnecessary heap when firing them. If you look at how a HashMap serializes, for example, by peeking at its source code you'll see that it actually contains a bunch of "child" data structures like entryset and keyset and things like that. When using "new", those child structures are only initialized if and when you need them; but when serializing/deserializing, they're created immediately even if you don't need them.
Another solution would be to use Java reflection to get the list of declared field names, and then iterate through those names using reflection to get the value out for that field. In your place I'd do this in Java (reflection is problematic enough without trying to do it in Drools) and then if necessary invoke such a utility function from Drools.

Related

How to use MDriven OclPs to find all objects matching a list of strings?

In an application I receive a list of strings. Then I want to use OclPs to find all objects where a specific attribute equals any of the strings in the list. E.g. if we have Person objects and receive a list of last names, find all persons whose last name appears in the list.
Although this can surely be done in MDriven's in-memory OCL engine, I can't seem to achieve this in the more limited OclPs (which translates the OCL to SQL and evaluates it as such in the database).
Attempt 1: First assign the list of names to vNames (collection of strings), then:
Person.allInstances->select(p|vNames->exists(n|n = p.LastName))
This gives error "Loop variables can only have class type, not System.String".
Attempt 2: First assign a "|" separated string of the sought names, including leading and trailing "|", to vNames, then:
Person.allInstances->select(p|vNames.SqlLike('%|' + p.LastName + '|%'))
This gives error saying strings cant be added in Firebird. But Firebird does support string concatenation using the || operator.
Trying with .Contains(...) instead of .SqlLike(...) says it's not supported in OclPs. Besides, it would find persons with a last name that is CONTAINED in any of the sought names, i.e. an incorrect search.
I'm out of ideas...
In this case when you have a long list of strings and you want a list of objects I think you best option is to use SQL with sqlpassthroughobjects:
https://wiki.mdriven.net/index.php/OCLOperators_sqlpassthroughobjects
Person.sqlpassthroughobjects('select personid from person where lastname in ('+vNames->collect(n|'\''+n+'\'')->asCommaList+')')
oclPS only implements a quite small subset of OCL because its converting the OCL to sql.
For example, collections of "non-objects" can't be used.
Person.allInstances->select(p|vNames.SqlLike('%|' + p.LastName + '|%'))
Instead, look at sqlpassthroughobjects here
https://wiki.mdriven.net/index.php/OCLOperators_sqlpassthroughobjects
You could also insert the names as objects of a class into the database and then use oclPS.
I suggest that you use a genuine OCL tool. You seem to be demonstrating that MDriven OclPs is not OCL.

Zend\db\sql - prepareStatementForSqlObject - still need to bind or worry about sql injection?

I'm using zf 2.4 and for this example in Zend\db\sql. Do I need to worry about sql injection or do I still need to do quote() or escape anything if I already use prepareStatementForSqlObject()? The below example will do the blind variable already?
https://framework.zend.com/manual/2.4/en/modules/zend.db.sql.html
use Zend\Db\Sql\Sql;
$sql = new Sql($adapter);
$select = $sql->select();
$select->from('foo');
$select->where(array('id' => $id));
$statement = $sql->prepareStatementForSqlObject($select);
$results = $statement->execute();
The Select class will cleverly check your predicate(s) and add them in a safe manner to the query to prevent SQL-injection. I'd recommend you to take a look at the source for yourself so I'll point you to the process and the classes that are responsible for this in the latest ZF version.
Predicate Processing
Take a look at the class PredicateSet. The method \Zend\Db\Sql\Predicate::addPredicates determines the best way to handle your predicate based on their type. In your case you are using an associative array. Every item in that array will be checked and processed based on type:
If an abstraction replacement character (questionmark) is found, it will be turned into an Expression.
If the value is NULL, an IS NULL check will be performed on the column found in the key: WHERE key IS NULL.
If the value is an array, and IN check will be performed on the kolumn found in the key: WHERE key IN (arrayVal1, arrayVal2, ...).
Otherwise, the predicate will be a new Operator of the type 'equals': WHERE key = value.
In each case the final predicate to be added to the Select will be implementing PredicateInterface
Preparing the statement
The method \Zend\Db\Sql\Sql::prepareStatementForSqlObject instructs its adapter (i.e. PDO) to create a statement that will be prepared. From here it gets a little bit more complicated.
\Zend\Db\Sql is where the real magic happens where in method \Zend\Db\Sql::createSqlFromSpecificationAndParameters the function vsprintf is used to build the query strings, as you can see here.
NotePlease consider using the new docs.framework.zend.com website from now on. This website is leading when it comes to documentation of the latest version.

Guava Maps.uniqueIndex doesn't allow duplicates

When I use Maps.uniqueIndex with a List that contains a duplicate value,
java.lang.IllegalArgumentException: duplicate key: 836
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:115)
is thrown.
I find this inconvenient. I suppose it does make some sense, but if a unique collection is required for the function to work correctly, why does it accept an Iterable as an argument in stead of a Set?
List<GroupVO> groups = groupDao.getAll(groupIds);
Map<String,GroupVO> groupMap groupMap = Maps.uniqueIndex(groups, new Function<GroupVO,String>() {
public String apply(GroupVO vo) {
return vo.getId().toString();
}});
It is simply not possible to have multiple values for one key in a plain Map, thus uniqueIndex cannot do anything else.
It accepts Iterable because accepting only Set would restrict its possible usages and still not solve the problem. Not the values in the given Iterable need to be unique, but the result of applying the given function on each of them.
If you need multiple values per key, you can simply use Multimaps.index, which does the same but returns a Multimap (which can contain an arbitrary number of values per key).
I think what confuses people here (including me when I'm not paying attention) is that typical Maps (e.g. HashMap) will quietly accept writing a new value to a key; the new value replaces the old one, so if the values are also the same, it's a silent no-op. The Immutable*.Builder family throws under the same circumstances.

The Scala equivalent of PHP's isset()

How do I test and see if a variable is set in Scala. In PHP you would use isset()
I am looking for a way to see if a key is set in an array.
First, Array in Scala does not have keys. They have indices, and all indices have values in them. See the edit below about how those values might be initialized, though.
You probably mean Map, which has keys. You can check whether a key is present (and, therefore, a value) by using isDefinedAt or contains:
map isDefinedAt key
map contains key
There's no practical difference between the two. Now, you see in the edit that Scala favors the use of Option, and there's just such a method when dealing with maps. If you do this:
map get key
You'll receive an Option back, which will be None if the key (and, therefore, the value) is not present.
EDIT
This is the original answer. I've noticed now that the question is not exactly about this.
As a practical matter, all fields on the JVM are pre-initialized by the JVM itself, which zeroes it. In practice, all reference fields end up pointing to null, booleans are initialized with false and all other primitives are initialized with their version of zero.
There's no such thing in Scala as an "undefined" field -- you cannot even write such a thing. You can write var x: Type = _, but that simply results in the JVM initialization value. You can use null to stand for uninitialized where it makes sense, but idiomatic Scala code tries to avoid doing so.
The usual way of indicating the possibility that a value is not present is using Option. If you have a value, then you get Some(value). If you don't, you get None. See other Stack Overflow questions about various ways of using Option, since you don't use it like variable.isDefined in idiomatic code either (though that works).
Finally, note that idiomatic Scala code don't use var much, preferring val. That means you won't set things, but, instead, produce a new copy of the thing with that value set to something else.
PHP and Scala are so different that there is no direct equivalent. First of all Scala promotes immutable variables (final in Java world) so typically we strive for variables that are always set.
You can check for null:
var person: Person = null
//...
if(person == null) {//not set
//...
}
person = new Person()
if(person == null) {//set
//...
}
But it is a poor practice. The most idiomatic way would be to use Option:
var person: Option[Person] = None
//...
if(person.isDefined) {//not set
//...
}
person = Some(new Person())
if(person.isDefined) {//set
//...
}
Again, using isDefined isn't the most idiomatic ways. Consider map and pattern matching.

how to deal with complex branching in iReport or Jasper

I need to fill a field in a report based on the end result of a complex branching statement. How do I do this in iReport? The report needs to show a different string depending on what is in different fields in the database. Do I make a really complex SQL statement? Do I use variables?
So, for instance,
If field x=1
IF y=1
IF z=1
Field should read A
If x=1
IF y=1
IF z=2
Field should read B
You could do something similar to the following:
( $F{staff_type} == null ? new String("") :
( $F{staff_type}.equalsIgnoreCase("Permanent") ? new String("1") :
( $F{staff_type}.equalsIgnoreCase("Non-permanent") ? new String("2") : new String("")
)))
Basically, you need to use nested condition expressions.
in the textfieldexpression write an expression like this
(($F{PAYMENTMODE}.equals("CS")) ? "Cash":($F{PAYMENTMODE}.equals("CQ"))? "Cheque":"Bank")e
I think the easiest way to do this would be to have the field(s) filled by a parameter passed from the backing bean. The jdbc connection is created in the bean and passed to the report, it should be relatively easy to access the field or fields you need and run the data through a method which determines the branching outcome. Assign the outcome to the parameter and pass it to the report in the jasperParameter variable of JasperFillManager.fillReport(file, parameters, jdbcConnection).
Usually, I handle all the programming logic before passing the data to the Jasper Report Engine, but in some cases, post-processing or post-checking is required. If it is that scenario and If I had MANY cases (strings) to check, I would code a 'Jasper Report Scriptlet' and handle that logic there (so that the code/report is readable and maintainable and also for code reuse). If it is just 2 or 3 strings to check, I would use the 'Ternary' operator.
If you want to use report scriptlet, create a scriptlet class (or use an existing one), code a method to handle this logic (Ex: 'checkString' method) and put $P{REPORT_SCRIPTLET}.checkString(someString) in the TextField's expression.