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

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.

Related

What is the exact meaning of `pl.col("")` expression with empty string argument

The example in a section about 'list context' in the polars-book uses pl.col("") expression with an empty string "" as the argument.
# the percentage rank expression
rank_pct = pl.col("").rank(reverse=True) / pl.col("").count()
From the context and the output I can guess what pl.col("") expression does. But the API documentation does not seem to cover a case of empty string as the argument to pl.col and I would like to know the precise meaning in this use case. Any helpful answer is greatly appreciated!
The precise meaning is to act as a 'root' Expression to start a chain of Expressions inside a List context, i.e., inside arr.eval(....). I'll need to take a step back to explain...
'Root' Expressions
In general, only certain types of Expressions are allowed to start (or be the 'root' of) an Expression. These 'root' Expressions work with a particular context (select, filter,with_column, etc..) to identify what data is being addressed.
Some examples of root Expressions are polars.col, polars.apply, polars.map, polars.first, polars.last, polars.all, and polars.any. (There are others.)
Once we declare a "root" Expression, we can then chain other, more-generic Expressions to perform work. For example, polars.col("my_col").sum().over('other_col').alias('name').
The List context
A List context is slightly different from most contexts. In a List context, there is no ambiguity as to what data is being addressed. There is only a list of data. As such, polars.col and polars.first were chosen as "root" Expressions to use within a List context.
Normally, a polars.col root Expression contains information such as a string to denote a column name or a wildcard expression to denote multiple columns. However, this is not needed in a List context. There is only one option - the single list itself.
As such, any string provided to polars.col is ignored in a List context. For example, from the code from the Polars Guide, this code also works:
# Notice that I'm referring to columns that do not exist...
rank_pct = pl.col("foo").rank(reverse=True) / pl.col("bar").count()
Since any string provided to a polars.col Expression will be ignored in a List context, a single empty string "" is often supplied, just to prevent unnecessary clutter.
Edit: New polars.element expression
Polars now has a polars.element expression designed for use in list evaluation contexts. Using polars.element is now considered idiomatic for list contexts, as it avoids confusion associated with using col(“”).

Drools : applying same rules on all attributes

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.

How to get second #entitie.literal when I have two or more "same" entitity on same phrase

For example, my input text are:
You can I talk with someone
on entity I have:
#pron:aboutme = I, Me
#pron:aboutother = someone, anyone, everyone, Richard
So... I want get #pron:aboutother literal
The problem is #pron.literal returns "I" and not "someone"
How can get #pron:aboutother input literal for this case?
#sys-number is a shorthand syntax. In this case, you need to use full syntax <?entities['pron'].get(1).literal?> to get the literal of the second detected entity. It might be good to check if there are two entities of the type detected in the input before (otherwise you get arrayoutofbounds exception).

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.

Lucene.Net Underscores causing token split

I've scripted a MsSqlServer databases tables,views and stored procedures into a directory structure that I am then indexing with Lucene.net. Most of my table, view and procedure names contain underscores.
I use the StandardAnalyzer. If I query for a table named tIr_InvoiceBtnWtn01, for example, I recieve hits back for tIr and for InvoiceBtnWtn01, rather than for just tIr_InvoiceBtnWtn01.
I think the issue is the tokenizer is splitting on _ (underscore) since it is punctuation.
Is there a (simple) way to remove underscores from the punctuation list or is there another analyzer that I should be using for sql and programming languages?
Yes, the StandardAnalyzer splits on underscore. WhitespaceAnalyzer does not. Note that you can use a PerFieldAnalyzerWrapper to use different analyzers for each field - you might want to keep some of the standard analyzer's functionality for everything except table/column name.
WhitespaceAnalyzer only does whitespace splitting though. It won't lowercase your tokens, for example. So you might want to make your own analyzer which combines WhitespaceTokenizer and LowercaseFilter, or look into LowercaseTokenizer.
EDIT: Simple custom analyzer (in C#, but you can translate it to Java pretty easily):
// Chains together standard tokenizer, standard filter, and lowercase filter
class MyAnalyzer : Analyzer
{
public override TokenStream TokenStream(string fieldName, System.IO.TextReader reader)
{
StandardTokenizer baseTokenizer = new StandardTokenizer(Lucene.Net.Util.Version.LUCENE_29, reader);
StandardFilter standardFilter = new StandardFilter(baseTokenizer);
LowerCaseFilter lcFilter = new LowerCaseFilter(standardFilter);
return lcFilter;
}
}