Skip to content

Commit 292a069

Browse files
author
Senthil Nathan
committed
Changes done for v1.2.0.
1 parent 16e96b5 commit 292a069

File tree

8 files changed

+238
-87
lines changed

8 files changed

+238
-87
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Rule processor for IBM Streams
22

33
## Note from the toolkit author
4-
This toolkit created by Senthil Nathan is a value differentiator for key customers. He created it on his own initiative and with his original intellectual ideas and not out of any formally managed or funded work by IBM. He did the entire work needed for this toolkit much after IBM divested its Streams product to another company around May/2021. He is now an independent software consultant. To benefit from the compelling features of this asset, for any enhancements, any need for a new powerful toolkit as well as for creating new streaming data analytics solutions, customers can email [email protected] to reach him. Thank you.
4+
This toolkit created by Senthil Nathan is a value differentiator for key customers. He created it on his own initiative and with his original intellectual ideas and not out of any formally managed or funded work by IBM. He did the entire work needed for this toolkit much after IBM divested its Streams product to another company around May/2021 as well as after Oct/2024 when he left IBM. He is now an independent software consultant. To benefit from the compelling features of this asset, for any enhancements, any need for a new powerful toolkit as well as for creating new streaming data analytics solutions, customers can email [email protected] to reach him. Thank you.
55

66
## Purpose
77
This toolkit offers an improved and a simpler facility for users to let their externally authored business rules to be consumed either statically or dynamically from within the IBM Streams SPL application code and then process (evaluate) them as the data flows through the application pipeline. Such an evaluation returns a true or false result for every rule that gets processed to indicate whether the rule expression criteria is met or not met.
@@ -287,6 +287,10 @@ This toolkit came into existence for a specific need with which a large enterpri
287287

288288
(Value.Status.Event equalsCI 'PATCHING') && ((Value.Status.UserName containsCI 'EWR') || (Value.Status.EntityState equalsCI 'ENGINEERING') || ((Value.Properties.OwnedBy containsCI 'FER') && ((Value.Status.Availability equalsCI 'Up') || ((Value.Status.Availability equalsCI 'Down') && (Value.Status.StatusCategory3 containsCI 'StatusCategory3'))))) && (Value.Status.StatusCategory4 equalsCI 'StatusCategory4')
289289

290+
((city notContainsCI 'Boston') && (city notContainsCI 'Chicago') && (city notContainsCI 'Miami') && (city notContainsCI 'Denver') && (((city equalsCI 'New York') && ((city containsCI 'New York') || (city containsCI 'New') || (city containsCI 'York'))) || ((city containsCI 'ork') || (city containsCI 'W Y') || (city containsCI 'ew') || (city containsCI 'New') || (city containsCI 'York') || ( city containsCI 'ew Yo'))))
291+
292+
((city notContainsCI 'Boston') && (city notContainsCI 'Chicago') && (city notContainsCI 'Miami') && (city notContainsCI 'Denver')) && (((city equalsCI 'New York') && ((city containsCI 'New York') || (city containsCI 'New') || (city containsCI 'York'))) || ((city containsCI 'ork') || (city containsCI 'W Y') || (city containsCI 'ew') || (city containsCI 'New') || (city containsCI 'York') || ( city containsCI 'ew Yo')))
293+
290294
## Source code
291295
The complete C++ logic for the **eval_predicate** function is available in the [eval_predicate.h](com.ibm.streamsx.eval_predicate/impl/include/eval_predicate.h) file of this repository.
292296

com.ibm.streamsx.eval_predicate/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changes
22

3+
## v1.2.0
4+
* Jul/16/2025
5+
* Added support for handling a rule expression format where two self-enclosed multi-level subexpressions are placed on each side of a logical operator.
6+
* Both the EvalPredicateExample and FunctionalTests applications are added with the required test cases for the new feature mentioned above.
7+
38
## v1.1.9
49
* Mar/05/2024
510
* Rearranged this toolkit's directory to have a top-level directory that in turn contains two subdirectories i.e. com.ibm.streamsx.eval_predicate subdirectory containing the main C++ code for this toolkit and the samples subdirectory containing two comprehensive examples showcasing the eval_predicate features.

com.ibm.streamsx.eval_predicate/impl/include/eval_predicate.h

Lines changed: 81 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,17 @@
77
/*
88
============================================================
99
First created on: Mar/05/2021
10-
Last modified on: July/06/2025
11-
Author(s): Senthil Nathan ([email protected])
10+
Last modified on: Jul/16/2025
11+
Author(s): Senthil Nathan [email protected]
1212

1313
Senthil wrote the code below on his own initiative and with his
1414
original intellectual ideas and not out of any formally managed or
1515
funded work by IBM. He did the entire work needed for this toolkit
16-
much after IBM divested its Streams product to another company around May/2021.
17-
The core logic below and its inner workings are derivatives of Senthil's work and
18-
effort. Senthil has plans to make changes to it in the future to create other
16+
much after IBM divested its Streams product to another company around
17+
May/2021 as well as after Oct/2024 when he left IBM to become an
18+
independent software consultant. The core logic below and its
19+
inner workings are derivatives of Senthil's work and effort.
20+
Senthil has plans to make changes to it in the future to create other
1921
assets centered around the functions and features he infused into this toolkit.
2022

2123
This toolkit's public GitHub URL:
@@ -2415,7 +2417,7 @@ namespace eval_predicate_functions {
24152417
of multi-level nested SEs that are not handled adequately.
24162418
If that happens in the field, I will have to do more
24172419
enhancements in the future as needed as I did on these dates:
2418-
Sep/27/2023, Oct/11/2023
2420+
Sep/27/2023, Oct/11/2023, Jul/16/2025
24192421

24202422
A few multi-level nested subexpression examples are shown below.
24212423

@@ -2448,7 +2450,7 @@ namespace eval_predicate_functions {
24482450
NestedSubexpressionId="2.2.1.2.4.1", Logical operator="||"
24492451

24502452
Test cases covering the multi-level nested subexpressions can be found in the
2451-
EvalPredicateExample.spl (3.7 to 3.12) and FunctionalTests.spl (A51.7 to A51.24).
2453+
EvalPredicateExample.spl (3.7 to 3.14) and FunctionalTests.spl (A51.7 to A51.26).
24522454
*********************************************************
24532455
*/
24542456

@@ -2594,8 +2596,8 @@ namespace eval_predicate_functions {
25942596
// Senthil changed the following statement on Sep/20/2023.
25952597
if(isCurrentOpenParenthesisForAnEnclosedSE == true &&
25962598
breakFromOpenParenthesisProcessingWhileLoopIfNeeded == true) {
2597-
// Senthil added this block of code on Sep/20/2023.
2598-
// We are processing this OP after encountering an
2599+
// Senthil added this block of code on Sep/20/2023.
2600+
// We are processing this OP after encountering an
25992601
// enclosed single SE earlier. It appears that the
26002602
// current OP is also enclosing a single SE.
26012603
// It is very likely that we are seeing a sequence of
@@ -2800,9 +2802,9 @@ namespace eval_predicate_functions {
28002802
if(currentNestedSubexpressionLevel == 0) {
28012803
// We are not in a nested expression.
28022804

2803-
// Senthil added this on Sep/20/2023.
2805+
// Senthil added this on Sep/20/2023.
28042806
// This is a self enclosed, non-nested SE.
2805-
// If it is a OP and CP count match, then it is
2807+
// If it is a OP and CP count match, then it is
28062808
// a self-enclosed, non-nested SE. In that case,
28072809
// set the following flag to true.
28082810
// Refer to the very first SE that starts this example expression.
@@ -3009,7 +3011,7 @@ namespace eval_predicate_functions {
30093011
Functions::Collections::clearM(subexpressionLayoutList);
30103012
}
30113013

3012-
// Senthil added this statement on Sep/20/2023.
3014+
// Senthil added this statement on Sep/20/2023.
30133015
// Since it is an unequal open and close parenthesis count, reset this for now.
30143016
// It can only be set to true in an equal open and
30153017
// close parenthesis count processing block later in this method.
@@ -3047,8 +3049,8 @@ namespace eval_predicate_functions {
30473049
// If SELOL is not empty, we can complete the current
30483050
// nested subexpression where this CP is at now.
30493051
if(selolSize > 0) {
3050-
// Senthil added this logic on Sep/20/2023.
3051-
// Calculate the current depth of the nested SE if it is present.
3052+
// Senthil added this logic on Sep/20/2023.
3053+
// Calculate the current depth of the nested SE if it is present.
30523054
int32 nestedLevel = currentNestedSubexpressionLevel;
30533055

30543056
// If we have a consecutive CP situation, then we will
@@ -3166,14 +3168,14 @@ namespace eval_predicate_functions {
31663168
currentNestedSubexpressionLevel = 0;
31673169
// Just because OP == CP now, we can reset this flag.
31683170
consecutiveCloseParenthesisFound = false;
3169-
// Senthil added this on Sep/20/2023.
3171+
// Senthil added this on Sep/20/2023.
31703172
// Since we reset the consecutive CP in the previous
31713173
// statement, we are no longer within a nested SE.
31723174
// We can reset the following as well.
3173-
currentDepthOfNestedSubexpression = 0;
3174-
// Senthil added this on Sep/20/2023.
3175-
// Since it is a OP and CP count match, set this to true.
3176-
openCloseParenthesisCntMatchedInPreviouslyProcessedSubExpression = true;
3175+
currentDepthOfNestedSubexpression = 0;
3176+
// Senthil added this on Sep/20/2023.
3177+
// Since it is a OP and CP count match, set this to true.
3178+
openCloseParenthesisCntMatchedInPreviouslyProcessedSubExpression = true;
31773179
break;
31783180
} // End of the (OP != CP) if block.
31793181

@@ -5440,24 +5442,24 @@ namespace eval_predicate_functions {
54405442
", closeParenthesisCnt=" << closeParenthesisCnt << endl;
54415443
}
54425444

5443-
// Senthil added this on Sep/20/2023.
5444-
// Since we reset the consecutive CP in the previous
5445-
// statement, we are no longer within a nested SE.
5446-
// We can reset the following as well.
5445+
// Senthil added this on Sep/20/2023.
5446+
// Since we reset the consecutive CP in the previous
5447+
// statement, we are no longer within a nested SE.
5448+
// We can reset the following as well.
54475449
if(consecutiveCloseParenthesisFound == true) {
54485450
multiPartSubexpressionPartsCnt = 0;
54495451
consecutiveCloseParenthesisFound = false;
54505452
currentDepthOfNestedSubexpression = 0;
54515453
}
54525454
} else {
5453-
// We are still within the same subexpression.
5454-
// Append the current intra subexpression logical operator we just found.
5455-
// This else block will cover both the cases where we are
5456-
// either within a nested or non-nested subexpression.
5457-
// In the nested case, we will come to this else block only
5458-
// when SELOL has non-zero size. Refer to the nested
5459-
// subexpression processing steps 3b1 and 3b2 at the
5460-
// top of this method.
5455+
// We are still within the same subexpression.
5456+
// Append the current intra subexpression logical operator we just found.
5457+
// This else block will cover both the cases where we are
5458+
// either within a nested or non-nested subexpression.
5459+
// In the nested case, we will come to this else block only
5460+
// when SELOL has non-zero size. Refer to the nested
5461+
// subexpression processing steps 3b1 and 3b2 at the
5462+
// top of this method.
54615463
Functions::Collections::appendM(subexpressionLayoutList,
54625464
mostRecentLogicalOperatorFound);
54635465
// Increment the parts count for this multi-part subexpression.
@@ -5793,12 +5795,12 @@ namespace eval_predicate_functions {
57935795
subexpressionId, subexpressionLayoutList);
57945796
}
57955797

5796-
// Senthil added this code to insert into a map on Sep/20/2023.
5798+
// Senthil added this code to insert into a map on Sep/20/2023.
57975799
// If it is a multi-level nested SE id, we will insert into a
57985800
// few relevant maps that will come handy later during the expression evaluation.
57995801
// Note that we are sending the OP count for the most recently
58005802
// processed LHS.
5801-
rstring myOp = "";
5803+
rstring myOp = "";
58025804
insertMultiLevelNestedSeIdAndLogicalOperatorIntoMaps('O', subexpressionId,
58035805
myOp, openParenthesisCntForRecentlyProcessedLhs,
58045806
closeParenthesisCnt, intraNestedSubexpressionLogicalOperatorsMap,
@@ -6063,10 +6065,6 @@ namespace eval_predicate_functions {
60636065
} else {
60646066
// This is a multi-level SE id. We have to do the
60656067
// logical operator homogeneity check differently for this sequence id.
6066-
// This can take a pattern as shown here:
6067-
// "2.1", "2.2.1", "2.2.2.1", "2.2.2.2"
6068-
insideMultiLevelNestedSubexpression = true;
6069-
60706068
// During the full expression validation done above, we have already
60716069
// gathered the related multi-level SE ids associated with a particular
60726070
// logical operator within that multi-level SE. We can use that
@@ -6076,8 +6074,33 @@ namespace eval_predicate_functions {
60766074
// identifier for the previous SE id we are validating.
60776075
int32 groupId = -1;
60786076

6079-
if(Functions::Collections::has(multiLevelNestedSubExpressionIdMap,
6080-
previousSubExpressionIdString) == true) {
6077+
// Senthil added this block of code on Jul/16/2025 to get
6078+
// the size of the previous subexpression id string and then
6079+
// to use that size value in the following if condition.
6080+
SPL::list<rstring> previousSeIdStringTokens =
6081+
Functions::String::tokenize(previousSubExpressionIdString, ".", false);
6082+
6083+
if((Functions::Collections::has(multiLevelNestedSubExpressionIdMap,
6084+
previousSubExpressionIdString) == true) &&
6085+
(Functions::Collections::size(previousSeIdStringTokens) > 2)) {
6086+
// Senthil made a change on Jul/16/2025 to move the
6087+
// following statement from just above this if block to
6088+
// here to solve a problem reported by our customer.
6089+
// For the sample expression pattern, refer to the
6090+
// functional test cases A51.25 and A51.26 in this
6091+
// toolkit's samples directory.
6092+
// In connection with this change, we added a second
6093+
// check in this code block's if condition above.
6094+
// Main idea is to skip SE id x.y (e-g: 2.1) and go to
6095+
// the else block below if x.y appears inside a
6096+
// multi-level nested subexpression as the first entry.
6097+
// Refer to the functional test case mentioned above.
6098+
//
6099+
//
6100+
// This can take a pattern as shown here:
6101+
// "2.1", "2.2.1", "2.2.2.1", "2.2.2.2"
6102+
insideMultiLevelNestedSubexpression = true;
6103+
60816104
groupId = multiLevelNestedSubExpressionIdMap.at(previousSubExpressionIdString);
60826105

60836106
if(trace == true) {
@@ -6170,8 +6193,19 @@ namespace eval_predicate_functions {
61706193
} // End of while loop.
61716194
} else {
61726195
if(trace == true) {
6173-
cout << "_HHHHH_33 Error-Check-115: Subexpression id " <<
6174-
previousSubExpressionIdString << "is not present in the " <<
6196+
// Senthil added more to this debug print on Jul/16/2025.
6197+
cout << "_HHHHH_33 Error-Check-115: " <<
6198+
i+1 << " of " << nestedSubexpressionIdsListSize <<
6199+
", idString=" << idString << ", currentLogicalOperator=" <<
6200+
currentLogicalOperator << ", currentId=" << currentId <<
6201+
", previousSubExpressionId=" << previousSubExpressionId <<
6202+
", insideMultiLevelNestedSubexpression=" <<
6203+
insideMultiLevelNestedSubexpression <<
6204+
", previousSubExpressionIdString=" << previousSubExpressionIdString <<
6205+
", previousLogicalOperator=" << previousLogicalOperator <<
6206+
", groupId=" << groupId << ". " <<
6207+
"Subexpression id " <<
6208+
previousSubExpressionIdString << " is either not present in or not part of the " <<
61756209
"multiLevelNestedSubExpressionIdMap. So, we are "
61766210
"skipping the logical operator homogeneity check "
61776211
"for it inside a multi-level nested subexpression." << endl;
@@ -6249,7 +6283,7 @@ namespace eval_predicate_functions {
62496283
SPL::list<boolean> nestedSubexpressionEvalResults;
62506284
SPL::list<boolean> interSubexpressionEvalResults;
62516285

6252-
// Senthil added the following five variables on Sep/20/2023.
6286+
// Senthil added the following five variables on Sep/20/2023.
62536287
SPL::list<rstring> const & subexpressionIdsList =
62546288
evalPlanPtr->getSubexpressionsMapKeys();
62556289
SPL::map<rstring, rstring> const & intraNestedSubexpressionLogicalOperatorsMap =
@@ -6262,12 +6296,12 @@ namespace eval_predicate_functions {
62626296
// We can find everything we need to perform the evaluation inside the
62636297
// eval plan class passed to this function. It contains the following members.
62646298
//
6265-
// rstring expression --> Needed when evaluation is needed for list<TUPLE>.
6266-
// rstring tupleSchema --> Not needed.
6267-
// SPL::map<rstring, SPL::list<rstring> > subexpressionsMap --> Needed very much.
6268-
// SPL::list<rstring> subexpressionsMapKeys --> Needed very much.
6299+
// rstring expression --> Needed when evaluation is needed for list<TUPLE>.
6300+
// rstring tupleSchema --> Not needed.
6301+
// SPL::map<rstring, SPL::list<rstring> > subexpressionsMap --> Needed very much.
6302+
// SPL::list<rstring> subexpressionsMapKeys --> Needed very much.
62696303
// SPL::map<rstring, rstring> intraNestedSubexpressionLogicalOperatorsMap --> Needed very much.
6270-
// SPL::list<rstring> interSubexpressionLogicalOperatorsList --> Needed very much.
6304+
// SPL::list<rstring> interSubexpressionLogicalOperatorsList --> Needed very much.
62716305
//
62726306
int32 subexpMapSize =
62736307
Functions::Collections::size(subexpressionIdsList);

com.ibm.streamsx.eval_predicate/info.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<info:identity>
55
<info:name>com.ibm.streamsx.eval_predicate</info:name>
66
<info:description>Toolkit for user defined rule (expression) processing</info:description>
7-
<info:version>1.1.9</info:version>
7+
<info:version>1.2.0</info:version>
88
<info:requiredProductVersion>4.2.1.6</info:requiredProductVersion>
99
</info:identity>
1010
<info:dependencies/>

0 commit comments

Comments
 (0)