How to achieve below objective in drools - java

I am trying to do below things. Plesse check.
rule "new rule"
salience -101
dialect "mvel"
when
$pricingLineItem : PricingLineItem( $ackId : ackId, $prefix : prefix )
$baseUpChargeConfig : BaseUpChargeConfig( $baseOptionId : baseOptionId,
prefix == $prefix )
$pricingOptionType : PricingOptionType( ackId == $ackId,
$optionId : optionId, $optionValue : optionValue )
$baseOptionConfig : BaseOptionConfig( bOptionValue == $optionValue,
bOptionCode == $optionId ,id == $baseOptionId )
then
$pricingLineItem.increment($baseOptionId);
System.out.println("excuted - "+ $baseOptionId +" "+$baseOptionConfig);
end
There will multiple BaseUpChargeConfig object match for one PricngLineItem. In the BaseUpChargeConfig object, we are getting all related BaseOptionConfig object and then trying to matche with PricingOptionType object of PricingLineItem. I need to take the best BaseUpChargeConfig object having maximum match with the PricingOptionType object of PricngLineItem.
EDIT
Say I have one PricingLineItem object with ackID, prefix value.
Now, I have multiple set of BaseUpChargeConfig object based on prefix value of PricingLineItem.
Now on ackId value, I have certain set of PricingOptionType object in rule engine.
and Also on baseOptionId value, I have multiple BaseOptionConfig object.
In PricingOptionType and BaseOptionConfig object, I need to compare the optioncode and option value.
If both are matching, I need to collect all matched pricing option type for a perticuler BaseUpChrageConfig.
In the same way, this will check for all other BaseUpChrageConfig object BaseOptionConfig and match.
Now the highest matched BaseOptionConfig object ; we will select that BaseUpChargeConfig as best object for our purpose.
I hope it would be clear for you.
Currently I am doing through java method by passing all three and calculating in java.
public void matchOptions(BaseUpChargeConfig config, List pricingOptionList,
List baseOptionList) {
if ((pricingOptionList != null && !pricingOptionList.isEmpty())
&& (baseOptionList != null && !baseOptionList.isEmpty())) {
List<PricingOptionType> matchedOption = null;
matchedOption = new ArrayList<PricingOptionType>();
for (PricingOptionType pOption : pricingOptionList) {
int matchCount = 0;
for (BaseOptionConfig bConfig : baseOptionList) {
boolean optioncodeMatch = pOption.getOptionCode() == bConfig.getBaseOptionCode();
boolean optionValueMatch = pOption.getOptionValue() == bConfig.getBaseOptionValue();
if (optioncodeMatch && optionValueMatch) {
matchedOption.add(pOption);
matchCount++;
}
}
if (matchCount > 0) {
if (bestBaseUpChargeConfig != null) {
optionMatchCount = matchCount;
bestBaseUpChargeConfig = config;
matchedPrcOptionList = matchedOption;
} else if (matchCount == optionMatchCount) {
bestBaseUpChargeConfig = null;
matchedOption = null;
matchedPrcOptionList.clear();
} else if (matchCount > optionMatchCount) {
optionMatchCount = matchCount;
bestBaseUpChargeConfig = config;
matchedPrcOptionList = matchedOption;
} else {
// do nothing
}
}
}
} else {
// do nothing
}
}
Thanks

This compiles with 5.5, so it shouldn't be a problem with 6.x either.
The duplication of the accumulate can't be helped unless you consider a more complicated evaluation involving derived facts.
rule "find best BaseUpChargeConfig"
when
// pick up some PricingLineItem
$pli: PricingLineItem( $prefix: prefix, $ackId : ackId )
// it should have a BaseUpChargeConfig with a matching prefix
$bucc: BaseUpChargeConfig( prefix == $prefix,
$baseOptionId : baseOptionId )
// count BaseOptionConfigs (linked to BaseUpChargeConfig) matching
// PricingOptionTypes, by option id/code and option value
accumulate(
BaseOptionConfig( id == $baseOptionId,
$ocod: bOptionCode, $oval: bOptionValue )
and
PricingOptionType( ackId == $ackId,
optionId == $ocod, optionValue == $oval );
$count: count(1) )
// The $count computed above is the maximum if we don't have another
// BaseUpChargeConfig (for that prefix) where the count of the
// subordinate BaseOptionConfigs is greater than $count
not(
( BaseUpChargeConfig( this != $bucc,
prefix == $prefix,
$baseOptionId2 : baseOptionId )
and
accumulate(
BaseOptionConfig( id == $baseOptionId2,
$ocod2: bOptionCode, $oval2: bOptionValue )
and
PricingOptionType( ackId == $ackId,
optionId == $ocod2, optionValue == $oval2);
$count2: count(1);
$count2 > $count ) ) )
then
System.out.println( "best BaseUpChargeConfig: " + $baseOptionId );
end

Related

Multiple StringBuilders inside StringBuilder. Does it worth?

I receive a list of models. The number of models could be large. This models has a bunch of properties and any of them could be null potentially.
I need to build a string for every model based of it's properties. If property == null then I add some static part to the result string like "property1 is null".
If else property != null then I add something like this "property1 == 'valueOfThePropertyHere'".
The result string should look something like this:
prop1 == 'value1' and prop2 is null and prop3 == 'value3' and prop4 == 'value4' and prop5 is null and ..... propN == 'valueN'
And I generate such string for every model from the list.
Obviously I do this in for loop and I use StringBuilder for this. The thing is that in append method of StringBuilder I check every field of the model for null using ternary operator and based on this I add the result of this check to the result string. But if a property is not null then I need to add some static part + value of the field itself + some more static stuff. And that means I need to add one more StringBuilder for every property I have. Or I can use '+' which will be transformed into StringBuilder anyway and as far as I know it's a bad practise to use '+' inside StringBuilder (but I have to use it anyway).
Example:
List<Model> models = repository.getModels();
for (Model m: models) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder
.append(m.getField1() == null ? "field1 is null" : "field1 == '" + new StringBuiler().append(m.getField1()).append("'").append(" and ").toString()))
.append(m.getField2() == null ? "field2 is null" : "field2 == '" + new StringBuiler().append(m.getField2()).append("'").append(" and ").toString()))
...............
.append(m.getFieldN() == null ? "fieldN is null" : "fieldN == '" + new StringBuiler().append(m.getFieldN()).append("'").append(" and ").toString()));
System.out.println(stringBuilder.toString());
}
In my opinion from the performance perspective it doesn't look so well because for every model from a list of models I create another bunch of StringBuilder objects in heap just to get the result string.
Am I missing something? Are there better ways to do so from the performance perspective? Or it's okay because I don't see other options for now.
Go for simple.
Instead of
stringBuilder
.append(m.getField1() == null ? "field1 is null" : "field1 == '" + new StringBuiler().append(m.getField1()).append("'").append(" and ").toString()))
use:
if (m.getField1() == null) {
stringBuilder.append("field1 is null");
} else {
stringBuilder.append("field1 == '").append(m.getField1()).append("'").append(" and ");
}
Aside from the distinct oddness of using a StringBuilder inside a StringBuilder.append call (and why not just use + anyway...), it's really hard to parse where the : is in the conditional expression. Breaking it into lines is much easier.
If you find yourself having to repeat this code pattern a lot, define a method:
void append(StringBuilder stringBuilder, String name, Object value) {
stringBuilder.append(name);
if (value == null) {
stringBuilder.append(" is null");
} else {
stringBuilder.append(" == '").append(value).append("'").append(" and ");
}
}
and then invoke like:
append(stringBuilder, "field1", m.getField1());
append(stringBuilder, "field2", m.getField2());
append(stringBuilder, "field3", m.getField3());
What a mess! Just because you can chain invocations, doesn't mean you should:
List<Model> models = repository.getModels();
for (Model m: models) {
StringBuilder stringBuilder = new StringBuilder();
String field = m.getField1();
if(field==null) {
stringBuilder.append("field1 is null");
} else {
stringBuilder.append("field1 == ").append(m.getField1()).append("'");
}
if(stringBuilder.length()>0) {
stringBuilder.append(" and ");
}
field = m.getField2();
if(field==null) {
stringBuilder.append("field2 is null");
} else {
stringBuilder.append("field2 == ").append(m.getField1()).append("'");
}
if(stringBuilder.length()>0) {
stringBuilder.append(" and ");
}
...
System.out.println(stringBuilder.toString());
}
To avoid all this potential repetition (depending on number of fields):
void appendField(StringBuilder stringBuilder, String fieldName, String value) {
if(stringBuilder.length()>0) {
stringBuilder.append(" and ");
}
stringBuilder.append(fieldName);
if(value==null) {
stringBuilder.append(" is null");
} else {
stringBuilder.append(" == '").append(value).append("'");
}
}
String toString(Model m) {
StringBuilder stringBuilder = new StringBuilder();
appendField(stringBuilder, "field1", m.getField1());
appendField(stringBuilder, "field2", m.getField2());
...
appendField(stringBuilder, "fieldN", m.getFieldN());
return stringBuilder.toString();
}
List<Model> models = repository.getModels();
for (Model m: models) {
System.out.println(toString(m));
}

How to reuse the variable used for for loop first level inside for loop second level in nested for loop

Someone can tell me how can I reuse rootOpt object inside of my forEach. Is there any way to reuse this variable? I have the following message "Can not resolve symbol rootOpt" when I write rootOpt.getChildOptions() inside my forEach. Please find below what I did:
I have tried to rewrite the for loop below by using stream. Thank you
opts.stream()
.flatMap(rootOpt -> rootOpt.getChildOptions().stream())
.forEach(subOpt -> {
if (subOpt.getOptLogic() != null && subOpt.getOptLogic().getCant() != null && !"".equals(subOpt.getOptLogic().getCant())) {
String[] oldCHs = subOpt.getOptLogic().getCant().split("( )");
OptionList samePriceSibs = getSamePriceS(rootOpt.getChildOptions(), subOpt);
for (String ch : oldCHs) {
Option chRootOpt = childOptCodeToParentOptMap.get(ch.toUpperCase());
if (chRootOpt != null) {
if (!DoesVariableOptionsCompletelyExcludeOther(samePriceSibs, chRootOpt.getChildOptions())) {
List<OptionList> tmp = new ArrayList<OptionList>();
tmp.add(samePriceSibs);
tmp.add(chRootOpt.getChildOptions());
optionsPairsToRemoveCHs.add(tmp);
}
}
}
}
});
for (Option rootOpt : opts) {
for (Option subOpt : rootOpt.getChildOptions()) {
if (subOpt.getOptLogic() != null && subOpt.getOptLogic().getCant() != null && !"".equals(subOpt.getOptLogic().getCant())) {
String[] oldCHs = subOpt.getOptLogic().getCant().split("( )");
OptionList samePriceSibs = getSamePriceS(rootOpt.getChildOptions(), subOpt);
for (String ch : oldCHs) {
Option chRootOpt = childOptCodeToParentOptMap.get(ch.toUpperCase());
if (chRootOpt != null) {
if (!DoesVariableOptionsCompletelyExcludeOther(samePriceSibs, chRootOpt.getChildOptions())) {
List<OptionList> tmp = new ArrayList<OptionList>();
tmp.add(samePriceSibs);
tmp.add(chRootOpt.getChildOptions());
optionsPairsToRemoveCHs.add(tmp);
}
}
}
}
}
}
The scope of rootOpt ends at the closing parenthesis.
You could write it like this instead
opts.stream().forEach(rootOpt ->
rootOpt.getChildOptions().stream().forEach(subOpt -> {
...
});
);
However streams were not really intended to simply replace for loops. A more canonical way of using them would be something like this.
Stream<List<OptionList>> optionsPairsToRemoveCHs = opts.stream()
.flatMap(rootOpt ->
rootOpt.getChildOptions().stream()
.filter(subOpt -> subOpt.getOptLogic() != null && subOpt.getOptLogic().getCant() != null && !"".equals(subOpt.getOptLogic().getCant()))
.flatMap(subOpt -> {
String[] oldCHs = subOpt.getOptLogic().getCant().split("( )");
OptionList samePriceSibs = getSamePriceS(rootOpt.getChildOptions(), subOpt);
return Stream.of(oldCHs)
.map(ch -> childOptCodeToParentOptMap.get(ch.toUpperCase()))
.filter(chRootOpt -> chRootOpt != null && !DoesVariableOptionsCompletelyExcludeOther(samePriceSibs, chRootOpt.getChildOptions()))
.map(chRootOpt -> Arrays.asList(samePriceSibs, chRootOpt.getChildOptions()));
})
);
I didn't test that code though. Also refactoring it into several methods as mike suggested would help making it easier to read.

How build sql query with many parameters from java code?

I have sql select with parameters:
SELECT * FROM tbl t WHERE t.name = ? AND t.age = ? AND t.number = ? AND ... AND t.last_parameter = ? order by t.some desc //many parameterss
I get parameters from form's fields and some fields may be empty. I build sql string:
String sqlStatementText;
MessageFormat sqlStatementTextTemplate = new MessageFormat(Queries.WAR_GET_REPORT_COUNT);
List<Object> parametrs = new ArrayList<>();
if (null == subscriberMSISDN || subscriberMSISDN.length() == 0) {
parametrs.add("");
} else {
parametrs.add(Queries.WAR_REPORT_CALLING_NUMBER);
}
if (null == operatorID || operatorID.length() == 0) {
parametrs.add("");
} else {
parametrs.add(Queries.WAR_REPORT_OPERATOR_AVAYA_ID);
}
if (null == operatorNickname || operatorNickname.length() == 0) {
parametrs.add("");
} else {
parametrs.add(Queries.WAR_REPORT_NICKNAME);
}
if (null == msg1 || msg1.length() == 0) {
parametrs.add("");
} else {
parametrs.add(Queries.WAR_REPORT_MSG1);
}
if (null == msg2 || msg2.length() == 0) {
parametrs.add("");
} else {
parametrs.add(Queries.WAR_REPORT_MSG2);
}
sqlStatementText = sqlStatementTextTemplate.format(parametrs.toArray());
ant them i do it:
try (Connection sqlConnection = connectionPool.getConnection();
PreparedStatement sqlStatment = sqlConnection.prepareStatement(sqlStatementText)) {
int paramID = 1;
sqlStatment.setInt(paramID++, 1);
sqlStatment.setDate(paramID++, new java.sql.Date(fromDate.getTime()));
sqlStatment.setDate(paramID++, new java.sql.Date(toDate.getTime()));
if (null != subscriberMSISDN && subscriberMSISDN.length() != 0) {
sqlStatment.setString(paramID++, subscriberMSISDN);
}
if (null != operatorID && operatorID.length() != 0) {
sqlStatment.setString(paramID++, operatorID);
}
if (null != operatorNickname && operatorNickname.length() != 0) {
sqlStatment.setString(paramID++, operatorNickname);
}
if (null != msg1 && msg1.length() != 0) {
sqlStatment.setString(paramID++, msg1);
}
if (null != msg2 && msg2.length() != 0) {
sqlStatment.setString(paramID++, msg2);
}
try (ResultSet resultSet = sqlStatment.executeQuery()) {
while (resultSet.next()) {
count = resultSet.getInt(1);
}
resultSet.close();
sqlStatment.close();
sqlConnection.close();
}
But i thig it not correctly. But I dont know how build sql query with many paramaters and if some parameters maybe empty.
Switch to an ORM. They will have some form of criteria-like object.
Use the param is null or column = param SQL syntax. select x from y where (? is null OR column1 = ?)
You need to set the value of the param twice, and the input value can not legitimately be null.
There is no way to do it, given the SQL statement you have.
You need to change the SQL statement WHERE conditions from things like t.name = ? to t.name = nvl(?, t.name). Then, you can bind a NULL there and the condition will always evaluate to true (so it's not acting as a filter -- which is what you want when the user leaves the field blank).
Or -- a better approach if you can do it, it's even better to use conditions like you've got them (e.g., t.name= ?), but build the conditions dynamically based on what fields the user give you. That is, for example, if the user leaves the "name" parameter blank, just omit the t.name = ? condition entirely.
That leaves you with a shorter SQL statement that makes the Oracle optimizer's job a little bit easier. With the t.name = nvl(?, t.name) approach I gave you above, you're relying on some pretty advanced optimizer features to get the best performance, because it's not immediately clear whether, say, it would be good or bad for the optimizer to use an index on t.name.

Pig: UDF not returning expected resultset

This is the sample data on which i was working:
Peter Wilkerson 27 M
James Owen 26 M
Matt Wo 30 M
Kenny Chen 28 M
I created a simple UDF for filtering the age like this:
public class IsApplicable extends FilterFunc {
#Override
public Boolean exec(Tuple tuple) throws IOException {
if(tuple == null || tuple.size() > 0){
return false;
}
try {
Object object = tuple.get(0);
if(object == null){
return false;
}
int age = (Integer)object;
return age > 28;
} catch (Exception e) {
throw new IOException(e);
}
}
}
This is the Script I used for using this UDF:
records = LOAD '~/Documents/data.txt' AS (firstname:chararray,lastname:chararray,age:int,gender:chararray);
filtered_records = FILTER records BY com.udf.IsApplicable(age);
dump filtered_records;
Dumping does not display any record. Please let me know where I missed.
tuple.size() > 0 condition is always true in the if stmt, so it will never go to the try block(ie filtering logic), that is the reason you are getting empty result. Can you change the if condition like this?
System.out.println("TupleSize="+tuple.size());
if(tuple == null || tuple.size() < 0){
return false;
}
Sample debug output in console:
2015-02-13 07:40:46,994 [Thread-2] INFO org.apache.pig.backend.hadoop.executionengine.mapReduceLayer.PigMapOnly$Map - Aliases being processed per job phase (AliasName[line,offset]): M: records[3,10],records[-1,-1],filtered_records[4,19] C: R:
TupleSize=1
TupleSize=1
TupleSize=1
This is returning false for all of the rows:
if (tuple == null || tuple.size() > 0) {
return false;
}
This is fetching the userName and not age:
Object object = tuple.get(0);

Ormlite Where issue with parenthesis, building meta-layer query library

I've read the other posts and the docs about how to use the "Where" clause to "create" parenthesis statements.
My requirement is simple:
... WHERE companyID=1 AND (director=true OR officer=true) ;
I'm writing a routine that takes an array of Object, which are then parsed into an Ormlite Where call. a typical call looks like this:
.., "companyID", 1, Q.AND, Q.Bracket, "director", true, Q.OR, "officer", true, Q.Bracket)
The intent is to speed up simple queries. There is no desire to replace Ormlite's querying tools. This is a simple meta-layer on top.
Everything works fine for simple queries, since the parameters are processed sequentially and the where clause is built incrementally.
For parenthesis I am postponing the processing until the bracket is closed.
This is where I am having a problem. The example from the docs I am using shows this:
-- From the OrmLite docs...
Where<Account, String> where = queryBuilder.where();
where.or(
where.and(
where.eq(Account.NAME_FIELD_NAME, "foo"),
where.eq(Account.PASSWORD_FIELD_NAME, "_secret")),
where.and(
where.eq(Account.NAME_FIELD_NAME, "bar"),
where.eq(Account.PASSWORD_FIELD_NAME, "qwerty")));
This produces the following approximate SQL:
SELECT * FROM account
WHERE ((name = 'foo' AND password = '_secret')
OR (name = 'bar' AND password = 'qwerty'))
The key thing I understand from the docs example, is that the same where instance is used in the nested and(...) call. This is precisely what I'm doing but I'm still getting a "Did you forget an AND or an OR" message.
The code implementing the "delayed" processing looks like this:
#SuppressWarnings("unchecked")
private void processWhere(Where<?, ?> where, Q q, List<QValue> list)
{
if (null == list || list.size() < 2)
{
System.err.println("Invalid where passed: " + list);
return;
}
if (q.equals(Q.AND))
where.and(getCondition(where, list.get(0)), getCondition(where, list.get(1)));
else
where.or(getCondition(where, list.get(0)), getCondition(where, list.get(1)));
}
The "QValue" item is just a "holder" for column, condition and value data.
The "getCondition" method is as follows:
#SuppressWarnings("rawtypes")
protected Where getCondition(Where<?, ?> where, QValue qv)
{
if (null != where && null != qv)
return getCondition(where, qv.getType(), qv.getColumn(), qv.getValue(), qv.getValue2());
else
return null;
}
#SuppressWarnings("rawtypes")
protected Where getCondition(Where<?, ?> where, Q cond, String key, Object val, Object val2)
{
if (null == where || null == cond || null == key || null == val)
return null;
SelectArg arg = new SelectArg();
arg.setValue(val);
try
{
switch (cond)
{
case NotNull:
where.isNotNull(key);
break;
case IsNull:
where.isNull(key);
break;
case Equals:
where.eq(key, arg);
break;
case NotEqual:
where.ne(key, arg);
break;
case GreaterThan:
where.gt(key, arg);
break;
case LessThan:
where.lt(key, arg);
break;
case Like:
arg.setValue("%" + val + "%");
where.like(key, arg);
break;
case LikeStart:
arg.setValue("" + val + "%");
where.like(key, arg);
break;
case LikeEnd:
arg.setValue("%" + val);
where.like(key, arg);
break;
case Between:
if (null != val && null != val2)
where.between(key, val, val2);
break;
default:
where.eq(key, arg);
break;
}
}
catch (SQLException e)
{
GlobalConfig.log(e, true);
return null;
}
return where;
}
As far as I can tell, I'm using the Where object correctly, but I am still getting a:
"Did you forget an AND or OR?" message.
I've tried creating "new" Where clauses with QueryBuilder:
Where w1 = qb.where() ;
//process w1 conditions...
return where.query() ;
Which also fails or generates incorrect SQL in the various combinations I've tried. Any suggestions on how to get the and(...) and or(...) methods working properly will be greatly appreciated.
BTW once the library is working properly, I'll put it up as Open Source or donate it to Gray, or both.
Thanks in advance.
Anthony
I faced the same issue and solved it like this:
where.eq("companyID", 1);
where.and(where, where.or(where.eq("director", true), where.eq("officer", true)));
or
where.and(where.eq("companyID", 1), where.or(where.eq("director", true), where.eq("officer", true)));
which in SQL gives us:
((`companyID` = 1 AND `director` = 1 ) OR (`companyID` = 1 AND `officer` = 1 ))
It's not identical to your example clause WHERE companyID=1 AND (director=true OR officer=true) but has the same meaning.

Categories

Resources