Rule support with built-in functions
In the rule file, you can use the following pre-built functions defined in the RulesUtils.java and CsvCacheHelper.java classes. Rule samples are available in the custom_rules_sample.drl and rules_v1.drl files.
Resolve and Escalate decisions
The functions used in every rule inside the then section to define a fired decision are as follows:
resolve("comment", rulesDecision, score)makes aRESOLVEdecision.escalate("comment", rulesDecision, score)makes aNO_DECISIONdecision.rule "blacklist_country_strong"
activation-group "default"
salience 1700
when
eval (countryMatch.blackListed != null && countryMatch.blackListed == true && StringUtils.isNoneBlank(countryMatch.blackListedStr))
eval (countryMatch.blacklistedSource == BlacklistedSource.COUNTRY)
then
RulesUtils.escalate("Blacklisted country strong. Found \"" + countryMatch.blackListedStr + "\" blacklisted country.", rulesDecision);
end
Field validation
isValidStr(String value)checks if a string value is valid.isValidMap(Map<String, String> valueMap)checks if a map is valid.isHieroglyphsChars(String text)returnstrueif the given string does not contain hieroglyph characters.assertTrue(isHieroglyphsChars("Some test"));
assertTrue(isHieroglyphsChars("eli wool"));
assertTrue(isHieroglyphsChars("Günter"));
assertTrue(isHieroglyphsChars("İbrahim Bıçakçı"));
assertFalse(isHieroglyphsChars("李"));
assertFalse(isHieroglyphsChars("γλύφω"));
assertFalse(isHieroglyphsChars("日本人の氏名"));
assertFalse(isHieroglyphsChars("اسم"));isLatinChars(String text)returnstrueif the given string contains only Latin characters.assertTrue(isLatinChars("Some test"));
assertTrue(isLatinChars("eli wool"));
assertFalse(isLatinChars("Günter"));
assertFalse(isLatinChars("İbrahim Bıçakçı"));
assertFalse(isLatinChars("李"));
assertFalse(isLatinChars("γλύφω"));
assertFalse(isLatinChars("日本人の氏名"));
assertFalse(isLatinChars("اسم"));
Text normalization
String prepareConcatValue(String value)returns text in uppercase, without spaces and punctuation.String prepareValue(String value)returns text in uppercase, with trimmed spaces and no punctuation. Punctuation marks are replaced with spaces.
String manipulation
textContainsValue(String text, String value)checks if text contains a value, usingprepareConcatValueas a normalizer.assertFalse(RulesUtils.textContainsValue(" Monkey:- on\n the tree", ""));
assertFalse(RulesUtils.textContainsValue(" Monkey:- on\n the tree", "tree on"));
assertTrue(RulesUtils.textContainsValue(" Monkey:- on\n the tree", "[the tree]"));
assertTrue(RulesUtils.textContainsValue(" Monkey:- on\n the tree", "key"));
assertTrue(RulesUtils.textContainsValue("HBUKGB4BXXX", "GB"));isNameInText(String text, String name, boolean tokensOnly)returnstrueif all name tokens are found in the text. ThetokensOnlyflag is considered when the name is a single token. UsesprepareValueas a normalizer.assertFalse(RulesUtils.isNameInText("", "", true));
assertFalse(RulesUtils.isNameInText("", "", false));
assertFalse(RulesUtils.isNameInText(null, null, true));
assertFalse(RulesUtils.isNameInText(null, null, false));
assertFalse(RulesUtils.isNameInText("MIRANDA INVESTMENT BANK", "IRAN", false));
assertFalse(RulesUtils.isNameInText("IRA,NOVA,TABLE", "IRAN", false));
assertFalse(RulesUtils.isNameInText("WORLD #1, BEST BANK OF IRAN", "IRAN REPUBLIC", false));
assertFalse(RulesUtils.isNameInText("WORLD #1, BEST BANK OF IRAN", "IR", false));
assertFalse(RulesUtils.isNameInText("WORLD #1, BEST BANK OF IRAN", "IRN", false));
assertFalse(RulesUtils.isNameInText("WORLD #1, BEST BANK OF IRAN", "IRN", true));
assertFalse(RulesUtils.isNameInText("Anna \nIranova", "IRAN", true));
assertTrue(RulesUtils.isNameInText("WORLD BANK OF /IRAN", "IRAN", false));
assertTrue(RulesUtils.isNameInText("WORLD BANK IR", "IR", false));
assertTrue(RulesUtils.isNameInText("WORLD BANK IRN", "IRN", false));
assertTrue(RulesUtils.isNameInText("WORLD #1, BEST BANK OF [IRAN]", "IRAN BANK", false));
assertTrue(RulesUtils.isNameInText("WORLD #1, BEST BANK OF IRAN", "best Of Iran", false));
assertTrue(RulesUtils.isNameInText("FIRST IRANIAN BANK", "IRAN", false));
assertTrue(RulesUtils.isNameInText("Anna \nIranova", "IRAN", false));
assertTrue(RulesUtils.isNameInText("WORLD BANK\r\n OF CUBA", "cuba", false));
assertTrue(RulesUtils.isNameInText("best CUBAN cigars", "cuba", false));isNamesInText(String text, List<String> names, boolean tokensOnly)returnstrueif all tokens of any name in the list are found in the text. Each name is processed by theisNameInTextfunction above.hasMultipleTokens(String value)returnstrueif a string has multiple tokens separated by spaces or punctuation.hasNTokens(String value, int n)returnstrueif a string is more than N tokens separated by spaces or punctuation.isNameInclusive(String one, String two, boolean bidirectional)checks if one name token is fully contained within another name token. The boolean parameter controls whether name inclusion works both ways (true). Otherwise (false), it returnstrueif name two is fully contained within name one.assertTrue(isNameInclusive("Silverman Goldstein, Peter Jay", "Peter Goldstein", true));
assertTrue(isNameInclusive("Silverman Goldstein, Peter Jay", "Peter Silverman", true));
assertTrue(isNameInclusive("Silverman Goldstein, Peter Jay", "Jay Goldstein", true));
assertTrue(isNameInclusive("Silverman Goldstein, Peter Jay", "Jay Silverman", true));
assertTrue(isNameInclusive("Silverman-Goldstein, Peter [Jay]", "Goldstein, Peter", true));
assertTrue(isNameInclusive("Silverman Goldstein, Peter Jay", "Peter Jay Goldstein", true));
assertTrue(isNameInclusive("Silverman Goldstein, Peter' Jay", "Peter Jay Goldstein", true));
assertTrue(isNameInclusive("Silverman Goldstein, Peter Jay", "Jay Peter Goldstein", true));
assertTrue(isNameInclusive("Silverman Goldstein, Peter Jay", "Peter Jay Silverman", true));
assertTrue(isNameInclusive("Peter Jay Silverman", "Silverman Goldstein, Peter Jay", true));
assertTrue(isNameInclusive("Silverman Goldstein, Peter Jay", "Jay Goldstein", false));
Assertions.assertFalse(isNameInclusive("Silverman Goldstein, Peter Jay", "Peter Jey Silverman", true));
Assertions.assertFalse(isNameInclusive("Peter (Jey) Silverman", "Silverman Goldstein, Peter Jay", true));
Assertions.assertFalse(isNameInclusive("Jay Goldstein", "Silverman Goldstein, Peter Jay", false));int charsDifferenceDirectional(String one, String two)returns the minimal number of characters from string one that must be removed from (or added to) string two to make the strings equal.Assertions.assertEquals(0, RulesUtils.charsDifferenceDirectional("Max Johnson", "Johnson Max"));
Assertions.assertEquals(2, RulesUtils.charsDifferenceDirectional("Maxim Johnson", "Johnson Max"));
Assertions.assertEquals(3, RulesUtils.charsDifferenceDirectional("Max Johnson", "Xam Jonson"));int charsDifference(String one, String two)returns the minimal number of characters that must be removed from (or added to) the respective strings to make them equal.Assertions.assertEquals(0, RulesUtils.charsDifference("Johnson", "Johnson"));
Assertions.assertEquals(2, RulesUtils.charsDifference("Yohnvson", "Johnson"));
Assertions.assertEquals(7, RulesUtils.charsDifference(null, "Johnson"));
Assertions.assertEquals(7, RulesUtils.charsDifference(StringUtils.EMPTY, "Johnson"));
Assertions.assertEquals(7, RulesUtils.charsDifference("Johnson", StringUtils.SPACE));
Assertions.assertEquals(3, RulesUtils.charsDifference("Max Johnson", "Johnson"));
Assertions.assertEquals(2, RulesUtils.charsDifference("Max Johnson", "Rex Johnson"));
Assertions.assertEquals(2, RulesUtils.charsDifference("Max Johnson", "Xam Johnson"));
Assertions.assertEquals(3, RulesUtils.charsDifference("Max Johnson", "Xam Jonson"));
Assertions.assertEquals(4, RulesUtils.charsDifference("Maxim Johnson", "Xam Jonson"));
Assertions.assertEquals(3, RulesUtils.charsDifference("Maxim Johnson", "Max Jongerson"));
Assertions.assertEquals(0, RulesUtils.charsDifference("Max Johnson", "Johnson Max"));
Assertions.assertEquals(0, RulesUtils.charsDifference("Max Gay Johnson", "Johnson, Max Gay"));
Assertions.assertEquals(0, RulesUtils.charsDifference("Max Gay Johnson", "Johnson, Gay Max"));
Assertions.assertEquals(0, RulesUtils.charsDifference("Max Gay Gay Johnson", "Johnson, Gay Max"));
Assertions.assertEquals(1, RulesUtils.charsDifference("Max Gay Johnson", "Johnson, Gray Max"));
Assertions.assertEquals(3, RulesUtils.charsDifference("Max Gay Gay Johnson", "Johnson, Gray Max"));
Assertions.assertEquals(10, RulesUtils.charsDifference("John Gay, Robin Johnson", "John Robin"));transliterate(String text)transliterates a given string to Latin characters.Assertions.assertEquals("Some test", transliterate("Some test"));
Assertions.assertEquals("Gunter", transliterate("Günter"));
Assertions.assertEquals("Ibrahim Bıcakcı", transliterate("İbrahim Bıçakçı"));
Assertions.assertEquals("li", transliterate("李"));
Assertions.assertEquals("glypho", transliterate("γλύφω"));
Assertions.assertEquals("ri ben renno shi ming", transliterate("日本人の氏名"));
Assertions.assertEquals("asm", transliterate("اسم"));
Assertions.assertEquals("samy alghamdy", transliterate("سامي الغامدي"));
Assertions.assertEquals("some data\nOOO 'RIH'", transliterate("some data\nООО 'РИХ'"));
Assertions.assertEquals(StringUtils.EMPTY, transliterate(null));
Assertions.assertEquals(StringUtils.EMPTY, transliterate(StringUtils.EMPTY));isNameAliasInText(String text, List<AliasInfo> aliases)checks if any alias name matches the text, using thetextContainsValuefunction for comparison.textMatchRegex(String text, String regex, boolean fullMatch)matches or finds a RegEx pattern in the given text, with support for thePattern.DOTALL,Pattern.CASE_INSENSITIVE, andPattern.MULTILINEflags.text = "[TRANSACT ] FREE_FORMAT\n[CURRENCY ] GBP\nSomeValue\nAnother Line";
//Exact match (full string)
assertTrue(RulesUtils.textMatchRegex(text, ".*\\[TRANSACT\\s+\\]\\sFREE_FORMAT.*", true));
//Substring match (find FREE_FORMAT line)
assertTrue(RulesUtils.textMatchRegex(text, "\\[TRANSACT\\s+\\]\\sFREE_FORMAT", false));
//Match case-insensitive
assertTrue(RulesUtils.textMatchRegex(text, "free_format", false));
//Match with anchors (start of line, multiline)
assertTrue(RulesUtils.textMatchRegex(text, "^SomeValue$", false));
//Match with dotall to span across newlines
assertTrue(RulesUtils.textMatchRegex("Start\nMiddle\nEnd", "Start.*End", true));
//Use character classes
assertTrue(RulesUtils.textMatchRegex("Price: $99.99", "\\$\\d+\\.\\d{2}", false));
Dates
minimumDistance(String date1, String date2)calculates the distance between two dates.assertEquals(0L, RulesUtils.minimumDistance("", ""));
assertEquals(0L, RulesUtils.minimumDistance(null, null));
assertEquals(0L, RulesUtils.minimumDistance("34-99", "56|99"));
assertEquals(0L, RulesUtils.minimumDistance("76", null));
assertEquals(0L, RulesUtils.minimumDistance("76", "76|77"));
assertEquals(0L, RulesUtils.minimumDistance("76", "1976|19"));
assertEquals(1, RulesUtils.minimumDistance("01/01/1976", "01/02/1976"));
assertEquals(31, RulesUtils.minimumDistance("01/01/1976", "02/01/1976"));
assertEquals(31, RulesUtils.minimumDistance("01/01/1976", "02/1976"));
assertEquals(31, RulesUtils.minimumDistance("01/01/1976", "1976-02-01"));
assertEquals(31, RulesUtils.minimumDistance("01/01/1976", "1976-02-01| 1977-01-01"));
assertEquals(365, RulesUtils.minimumDistance("01/01/1975", "1976-01-01| 1977-01-01"));
Maps
mapContainsValue(Map<String, String> messageMap, String key, String value)returnstrueif a map contains a key with the specified value.textContainsMapValue(String text, Map<String, String> valueMap)checks if text contains any value from the map.
Location in text
isWleLocationInText(String text, PaymentWatchListEntity wle)checks if any sanctioned location elements (name, city, or country) are found in text. This is intended to be used when a sanctioned type isLOCATION.isWleLocationInText(String text, PaymentWatchListEntity wle, boolean searchName)checks if any sanctioned location elements (name, city, or country) are found in text. By settingboolean searchNametofalse, it excludes name searching, allowing this function to be used for any entity type or for searching the entire content for matching locations.isLocationAnyMatch(String scrCountry, String wleCountry)handles countries that can contain multiple pipe-separated values likeRU|GB.
Support comparison against CSV file
The functions defined in CsvCacheHelper allow values to be compared against CSV data downloaded from the S3-compatible object storage in the doc-upload bucket under the payment_sanctions_screening/rules/data directory.
These functions support multiple action types to compare strings, numbers, dates, and regular expressions. See the CsvMatchConditionTest.java file for examples.
// String matches
EQUALS,NOT_EQUALS,CONTAINS,NOT_CONTAINS,STARTS_WITH,ENDS_WITH,
FIRST_N_CHARS,LAST_N_CHARS,IS_EMPTY,IS_NOT_EMPTY,IN_LIST,
// Regex
REGEX_MATCH,REGEX_NOT_MATCH,REGEX_CONTAINS,REGEX_NOT_CONTAINS,
// Numeric
EQUALS_NUMERIC,NOT_EQUALS_NUMERIC,GREATER_THAN,LESS_THAN,BETWEEN,
// Date
DATE_EQUALS,DATE_NOT_EQUALS,DATE_BEFORE,DATE_AFTER,DATE_BETWEEN,DATE_WITHIN_DAYS
matchExists(String csvFileName, String factor, Type actionType, String columnName)compares the parameter to each row in the specified CSV column using the provided comparison type.// Sample rule to match data factors against set of values defined in CSV file
// Simplified version of rowMatches() to match factor against big list of values
rule "Example matchExists"
activation-group "default"
salience 1498
when
$hit: PaymentMessageHit()
eval(CsvCacheHelper.matchExists("company_entries.csv", $hit.hitText, CsvMatchCondition.Type.CONTAINS, "Reference Label"))
then
RulesUtils.resolve("Hit matches company_entries.csv", rulesDecision);
endrowMatches(String csvFileName, List<CsvMatchCondition> conditions, MatchMode matchMode)compares the list of conditions to each row in the CSV file. ForMatchMode.ALL, all conditions must match; forMatchMode.ANY, any condition can trigger a match.// Sample rule to match data factors against set of values defined in CSV file
// "company_account_entries.csv" file should be placed under payment_sanctions_screening/rules/data/ S3 directory
// All conditions should match within the same CSV row
rule "CSV data match example"
activation-group "default"
salience 1499
when
$hit: PaymentMessageHit()
$msg: PaymentMessage()
eval($msg.payment != null && $msg.payment.sender != null)
eval(CsvCacheHelper.rowMatches(
"company_account_entries.csv",
Arrays.asList(
new CsvMatchCondition($hit.hitText, CsvMatchCondition.Type.CONTAINS, "Reference Label"),
new CsvMatchCondition($msg.payment.sender.name, CsvMatchCondition.Type.CONTAINS, "Company Name"),
new CsvMatchCondition($msg.payment.sender.accountNumber, CsvMatchCondition.Type.ENDS_WITH, "Account Number")
),
CsvMatchCondition.MatchMode.ALL
))
then
RulesUtils.resolve("CSV data match example", rulesDecision);
end