Rule Pipeline Reference
The complete list of pipeline steps. For what a pipeline is and when to use one, read Rule Authoring Methodology first.
Every test below that can be called on a plain value is also an operator in the rule builder, under the builder’s grouped operator list; see Rule and Code Builder.
The step grammar
Every test in the tables below is available in five forms, by prefixing its name:
| Prefix | Test holds | Test fails |
|---|---|---|
Match | Decide: matched | Continue |
Reject | Decide: rejected | Continue |
Break | Decide: broken | Continue |
Require | Continue | Decide: rejected |
Ensure | Continue | Decide: broken |
So Greater in the Double table is available as MatchGreater, RejectGreater, BreakGreater, RequireGreater and EnsureGreater. Once a pipeline has decided, every step after it is skipped. A pipeline converts to True only when it has decided matched, otherwise to False.
The five outcomes are:
| Outcome | Meaning |
|---|---|
| Undecided | Nothing has decided yet, steps continue to run |
| Matched | A Match step fired; the only outcome that converts to True |
| Rejected | A Reject step fired, or a Require guard failed |
| Broken | A Break step fired, or an Ensure precondition failed |
| Errored | A step could not evaluate, for example text that is not a number |
Beginning a pipeline
A test step called on a plain value begins a pipeline. There is no Start():
Matched = Payload.Amount.MatchGreater(100)
Matched = TTLCounter.CardPaymentsLastHour.RequireGreater(5).Against(Payload.CurrencyAmount).MatchIsJustBelowThreshold(10000, 10)
Every test in the tables below exists for a plain Double, Integer, String, DateTime and Boolean value, and again on a pipeline, so the first step and the later steps are written the same way.
The steps that only make sense once a pipeline exists cannot begin one: Against, Label, OtherwiseMatch, OtherwiseReject, Reset, Fail, AddScore, AddScoreWhen, CapScore, MatchWhen, RejectWhen, BreakWhen, RequireThat, EnsureThat and the score decisions. Begin with a test step, or with Start(), for example Payload.Amount.Start().AddScoreWhen(Payload.Amount > 100, 0.5).
A chain may also begin with a transformer. Lower, Upper, Trimmed, TextLength, the Parse steps, HourOfDay and ToZone begin a pipeline and are null safe. Their names deliberately do not match a plain member of the value’s own type – String.Trim and String.Length are always available regardless of the rule token allow-list, so a step named the same would make an unrelated, unregistered name compile as a side effect of registering the step; the pipeline forms are therefore Trimmed and TextLength, not Trim and Length. Plus, Minus, Times and DividedBy on a plain number just return a number (NaN if it is undefined), and ToDoubleOrNaN() on text returns a number, NaN if it is not one, so they fit a rule that returns a number such as an Abstraction Calculation. A few transformer names are also plain methods, and on a plain value the plain method is used, which returns an ordinary value that the next test step then begins from:
| Name | On a plain value | Note |
|---|---|---|
Abs, AgeInDays | The library method | Returns a plain number. |
Round | The library method with no argument | Round(digits) on a plain Double is read by VB as the static Double.Round. Use Payload.Amount.Start().Round(2). |
FuzzyBestScore, FuzzyMatchCount, FuzzyBestMatch | The library method | Returns a plain number or string. |
Control steps
These are available on a pipeline of any value type.
| Step | Description |
|---|---|
Start() | Optional, and harmless to repeat. Every test step called on a plain value begins a pipeline by itself. |
Against(value) | Switches to testing another value, keeping the outcome, score and label. |
Match(), Reject(), Break() | Decide unconditionally, if not already decided. |
Fail(label) | Decide errored, with a reason. |
OtherwiseMatch(), OtherwiseReject() | Decide only if nothing has decided yet (the Else). |
Reset() | Return to undecided and clear the score and label. |
Label(text) | Attach a reason that is carried to the decision. |
MatchWhen(condition), RejectWhen(condition), BreakWhen(condition) | The Match, Reject and Break forms driven by any Boolean expression. |
RequireThat(condition), EnsureThat(condition) | The Require and Ensure forms driven by any Boolean expression. |
AddScore(weight) | Add to the score (negative subtracts). |
AddScoreWhen(condition, weight) | Add to the score only if the condition holds. |
CapScore(maximum) | Limit the score. |
MatchIfScoreAtLeast(k), BreakIfScoreAtLeast(k), RejectIfScoreBelow(k), RequireScoreAtLeast(k) | Decide from the accumulated score. |
IsMatched(), IsRejected(), IsBroken(), IsUndecided(), IsErrored() | Read the outcome as a Boolean. |
ToBoolean() | The Boolean the pipeline converts to. |
Or, And, Not | Combine pipelines, and a pipeline with a Boolean written after it. See Combining pipelines. |
ToScore() | The accumulated score, for rules that return a number. |
Converting and transforming
These change the value being tested, and leave the outcome untouched.
| Step | On | Description |
|---|---|---|
ParseDouble(), ParseInteger(), ParseDate(), ParseBoolean() | String | Read text as another type using the invariant culture. Text that is not valid errors the pipeline rather than throwing. |
Trimmed(), Lower(), Upper() | String | Trim white space, or change case, invariant. Null stays null. |
TextLength() | String | The length as an integer, zero for null. |
Abs(), Round(digits), Plus(x), Minus(x), Times(x) | Double | Arithmetic on the value. |
DividedBy(x) | Double | Division. A zero divisor errors the pipeline. |
ToNumber() | Double, Integer pipeline | The value as a plain number, NaN if the pipeline errored, for a rule that returns a number. |
HourOfDay(), AgeInDays() | DateTime | The hour, or the whole days since the date, as an integer. |
Tests
Tests are null safe. A null or NaN value fails the test, never throws, except where the test name says it is testing for null or invalidity (IsNull, IsNotValidEmail), which are true for null.
Double tests
Begin from Payload.<Numeric field>, or from any token of that type (TTLCounter, Abstraction, Dictionary and so on).
| Test | Arguments | True when |
|---|---|---|
Greater | double other | The value is greater than the argument. |
GreaterOrEqual | double other | The value is greater than or equal to the argument. |
Less | double other | The value is less than the argument. |
LessOrEqual | double other | The value is less than or equal to the argument. |
Equal | double other | The value equals the argument. NaN never equals anything. |
NotEqual | double other | The value differs from the argument. NaN on either side is false. |
Between | double minimum, double maximum | Strictly between the two bounds (both bounds excluded). |
InRange | double minimum, double maximum | Between the two bounds, both bounds included. |
OutsideRange | double minimum, double maximum | Below the minimum or above the maximum. NaN is false. |
In | double... values | The value is one of the listed numbers. |
NotIn | double... values | The value is none of the listed numbers. |
IsZero | none | The value is zero. |
IsPositive | none | The value is greater than zero. |
IsNegative | none | The value is less than zero. |
IsNaN | none | The value is not a number. |
IsFinite | none | The value is neither infinite nor NaN. |
IsWholeNumber | none | The value has no fractional part. |
IsMultipleOf | double divisor | The value is an exact multiple of the divisor. A zero divisor is false. |
WithinPercentOf | double target, double percent | The value is within the percentage of the target, in either direction. |
DeviatesFromByPercent | double target, double percent | The value is further than the percentage from the target. |
IncreasedByMoreThanPercent | double previous, double percent | The percentage increase over the previous value exceeds the percentage. An increase from zero is infinite, so true. |
DecreasedByMoreThanPercent | double previous, double percent | The percentage decrease from the previous value exceeds the percentage. |
ZScoreAbove | double mean, double standardDeviation, double threshold | The z-score against the mean and standard deviation is above the threshold. |
ZScoreBelow | double mean, double standardDeviation, double threshold | The z-score is below the threshold. |
ZScoreOutside | double mean, double standardDeviation, double threshold | The absolute z-score is above the threshold, in either direction. |
RatioAbove | double denominator, double threshold | The value divided by the denominator is above the threshold. A zero denominator is false. |
RatioBelow | double denominator, double threshold | The value divided by the denominator is below the threshold. A zero denominator is false. |
IsJustBelowThreshold | double threshold, double marginPercent | Below the threshold but within the margin percentage of it (structuring). |
IsJustAboveThreshold | double threshold, double marginPercent | Above the threshold but within the margin percentage of it. |
IsRoundAmount | double nearest | An exact multiple of the rounding unit, for example 1000. |
WithinRadiusKm | double longitude1, double latitude2, double longitude2, double radiusKm | The value is latitude 1. Within the radius in kilometres of the second point. |
OutsideRadiusKm | double longitude1, double latitude2, double longitude2, double radiusKm | The value is latitude 1. Further than the radius in kilometres from the second point. |
DistanceKmAbove | double longitude1, double latitude2, double longitude2, double kilometers | The value is latitude 1. The distance to the second point is above the kilometres. |
DistanceKmBelow | double longitude1, double latitude2, double longitude2, double kilometers | The value is latitude 1. The distance to the second point is below the kilometres. |
ImpliedSpeedAbove | double longitude1, double latitude2, double longitude2, double hoursElapsed, double kilometersPerHour | The value is latitude 1. The speed implied by travelling to the second point in the hours is above the limit (impossible travel). Zero hours is infinite speed. |
ImpliedSpeedBelow | double longitude1, double latitude2, double longitude2, double hoursElapsed, double kilometersPerHour | The value is latitude 1. The implied speed is below the limit. |
HasValue | none | The value is a real number (not NaN). Use it after a calculation that can be undefined, such as RatioOf. |
HasNoValue | none | The value is NaN, for example an undefined calculation. |
RatioInRange | double denominator, double minimum, double maximum | The value divided by the denominator is between the bounds, both included. A zero denominator is false. |
RatioOutsideRange | double denominator, double minimum, double maximum | The ratio is below the minimum or above the maximum. A zero denominator is false. |
IsJustBelowAnyThreshold | double marginPercent, double... thresholds | Just below any of several limits, within the margin percentage (for example 3,000, 10,000 and 15,000). |
IsJustAboveAnyThreshold | double marginPercent, double... thresholds | Just above any of several limits, within the margin percentage. |
WithinRadiusMiles | double longitude1, double latitude2, double longitude2, double radiusMiles | The value is latitude 1. Within the radius in miles of the second point. |
OutsideRadiusMiles | double longitude1, double latitude2, double longitude2, double radiusMiles | The value is latitude 1. Further than the radius in miles from the second point. |
DistanceMilesAbove | double longitude1, double latitude2, double longitude2, double miles | The value is latitude 1. The distance to the second point is above the miles. |
DistanceMilesBelow | double longitude1, double latitude2, double longitude2, double miles | The value is latitude 1. The distance to the second point is below the miles. |
ImpliedSpeedMphAbove | double longitude1, double latitude2, double longitude2, double hoursElapsed, double milesPerHour | The value is latitude 1. The implied speed in miles per hour is above the limit. Zero hours is infinite speed. |
ImpliedSpeedMphBelow | double longitude1, double latitude2, double longitude2, double hoursElapsed, double milesPerHour | The value is latitude 1. The implied speed in miles per hour is below the limit. |
Integer tests
Begin from Payload.<Integer field>, or from any token of that type (TTLCounter, Abstraction, Dictionary and so on).
| Test | Arguments | True when |
|---|---|---|
Greater | int other | Greater than the argument. |
GreaterOrEqual | int other | Greater than or equal to the argument. |
Less | int other | Less than the argument. |
LessOrEqual | int other | Less than or equal to the argument. |
Equal | int other | Equals the argument. |
NotEqual | int other | Differs from the argument. |
Between | int minimum, int maximum | Strictly between the bounds. |
InRange | int minimum, int maximum | Between the bounds, both included. |
OutsideRange | int minimum, int maximum | Below the minimum or above the maximum. |
In | int... values | One of the listed integers. |
NotIn | int... values | None of the listed integers. |
IsZero | none | Zero. |
IsPositive | none | Greater than zero. |
IsNegative | none | Less than zero. |
IsEven | none | An even number. |
IsOdd | none | An odd number. |
IsMultipleOf | int divisor | An exact multiple of the divisor. A zero divisor is false. |
IsPrime | none | A prime number. |
String tests
Begin from Payload.<String field>, or from any token of that type (TTLCounter, Abstraction, Dictionary and so on).
| Test | Arguments | True when |
|---|---|---|
IsNull | none | The value is null. |
IsEmpty | none | Present but zero length. |
IsNullOrEmpty | none | Null or zero length. |
IsNullOrWhiteSpace | none | Null, empty or only white space. |
IsNotNullOrEmpty | none | Present and not zero length. |
IsNotNullOrWhiteSpace | none | Present with visible content. |
Equal | string other | Equals the argument, case sensitive. Null never equals. |
EqualIgnoreCase | string other | Equals the argument, ignoring case. |
NotEqual | string other | Both present and different. |
In | string... values | One of the listed strings, case sensitive. |
InIgnoreCase | string... values | One of the listed strings, ignoring case. |
NotIn | string... values | Present and none of the listed strings. |
InList | List list | A member of the List token, case sensitive. |
InListIgnoreCase | List list | A member of the List token, ignoring case. |
NotInList | List list | Present and not a member of the List token. |
Contains | string substring | Contains the substring, case sensitive. |
ContainsIgnoreCase | string substring | Contains the substring, ignoring case. |
ContainsAny | string... values | Contains at least one of the listed strings. |
ContainsAnyIgnoreCase | string... values | Contains at least one of the listed strings, ignoring case. |
ContainsAll | string... values | Contains every listed string. |
ContainsNone | string... values | Present and contains none of the listed strings. |
ContainsAnyInList | List list | Contains at least one entry of the List token. |
ContainsAnyInListIgnoreCase | List list | Contains at least one entry of the List token, ignoring case. |
ContainsAllInList | List list | Contains every entry of the List token. |
ContainsNoneInList | List list | Present and contains no entry of the List token. |
StartsWith | string prefix | Starts with the prefix. |
EndsWith | string suffix | Ends with the suffix. |
StartsWithAnyInList | List list | Starts with any entry of the List token. |
Matches | string pattern | Matches the regular expression. A bad pattern or a 100ms timeout is false, never an exception. |
LengthEqual | int length | Has exactly the length. |
LengthGreater | int length | Longer than the length. |
LengthLess | int length | Shorter than the length. |
LengthInRange | int minimum, int maximum | Length between the bounds, both included. |
IsNumeric | none | Non-empty, digits only. |
IsAlpha | none | Non-empty, letters only. |
IsAlphaNumeric | none | Non-empty, letters and digits only. |
IsAllSameCharacter | none | Non-empty and one repeated character. |
HasSequentialDigits | int minimumRunLength | Has an ascending or descending run of digits at least the minimum length (synthetic identifiers). |
ShannonEntropyAbove | double threshold | Non-empty and entropy above the threshold (generated looking values). |
ShannonEntropyBelow | double threshold | Non-empty and entropy below the threshold. |
IsValidEmail | none | A well formed email address. |
IsNotValidEmail | none | Null, empty or not a well formed email address. |
IsValidIp | none | A valid IPv4 address. |
IsNotValidIp | none | Null, empty or not a valid IPv4 address. |
IsValidIban | none | Passes the IBAN checksum. |
IsNotValidIban | none | Null or fails the IBAN checksum. |
IsValidBic | none | A valid BIC or SWIFT code. |
IsNotValidBic | none | Null or not a valid BIC. |
IsValidLuhn | none | Passes the Luhn checksum. |
IsNotValidLuhn | none | Null or fails the Luhn checksum. |
IsValidCardExpiry | none | A card expiry that has not elapsed. |
IsNotValidCardExpiry | none | Null, malformed or elapsed. |
EmailDomainEqual | string domain | The domain of the email equals the argument, ignoring case. |
EmailDomainIn | string... domains | The domain of the email is one of the listed domains. |
EmailDomainInList | List list | The domain of the email is an entry of the List token. |
EmailAliasNormalisedEqual | string other | Equal to the argument after removing case and any plus tag (multi-accounting). |
SoundexEqual | string other | Sounds the same as the argument. |
JaroWinklerAtLeast | string other, double threshold | Jaro-Winkler similarity to the argument is at least the threshold. |
SimilarityAtLeast | string other, double threshold | Levenshtein based similarity is at least the threshold, from 0 to 1. |
LevenshteinAtMost | string other, int distance | Edit distance to the argument is at most the distance. |
EndsWithAnyInList | List list | Ends with any entry of the List token. |
ContainsAllIgnoreCase | string... values | Contains every listed string, ignoring case. |
ContainsNoneIgnoreCase | string... values | Present and contains none of the listed strings, ignoring case. |
ContainsAllInListIgnoreCase | List list | Contains every entry of the List token, ignoring case. |
ContainsNoneInListIgnoreCase | List list | Present and contains no entry of the List token, ignoring case. |
ContainsFuzzy | string term, double threshold | A run of words in the text is at least the Jaro-Winkler threshold similar to the term (a misspelt name inside a narrative). |
ContainsFuzzyAny | double threshold, string... terms | As ContainsFuzzy, for any of the listed terms. The threshold comes first. |
ContainsFuzzyAnyInList | List list, double threshold | As ContainsFuzzy, for any entry of the List token. |
LevenshteinAtMostAnyInList | List list, int distance | Within the edit distance of any entry of the List token, whole string against whole string. |
SimilarityAtLeastAnyInList | List list, double threshold | At least the similarity of any entry of the List token. |
JaroWinklerAtLeastAnyInList | List list, double threshold | At least the Jaro-Winkler similarity of any entry of the List token. |
SomehowInList | List list | Fuzzily in the List token using the default profile: Jaro-Winkler or token sort, at least 0.92, after folding case, accents and punctuation. |
SomehowInListWith | List list, string options | Fuzzily in the List token using an options string. |
SomehowLike | string other | Fuzzily like a single value, using the default profile. |
SomehowLikeWith | string other, string options | Fuzzily like a single value, using an options string. |
ContainsSomehowInList | List list | Some entry of the List token is found fuzzily inside the longer text, using the default profile. |
ContainsSomehowInListWith | List list, string options | Some entry is found fuzzily inside the longer text, using an options string (scope is always within). |
NormalisedInList | List list | Equal to an entry after folding case, accents and punctuation. |
LevenshteinInList | List list, int maxDistance | Within the edit distance of an entry (insertions, deletions, substitutions). |
DamerauInList | List list, int maxDistance | Within the edit distance of an entry, counting a swap of adjacent letters as one edit. |
SimilarInList | List list, double threshold | The Levenshtein ratio to an entry is at least the threshold. |
JaroWinklerInList | List list, double threshold | The Jaro-Winkler similarity to an entry is at least the threshold. |
DiceInList | List list, double threshold | The bigram (Dice) overlap with an entry is at least the threshold. |
SoundexInList | List list | Every word sounds like the same word of an entry, in order. |
TokenSortInList | List list, double threshold | The same words as an entry in any order, allowing typos. |
TokenSetInList | List list, double threshold | The share of an entry’s words that are found, allowing typos and missing words. |
InitialsInList | List list | Matches an entry where a first name may be an initial (J Smith for John Smith). |
FuzzyScoreAtLeast | List list, double threshold, string options | The best score across the List token, from 0 to 1, is at least the threshold. Options string may be null. |
FuzzyMatchCountAtLeast | List list, int count, string options | At least the count of entries in the List token match. Options string may be null. |
DateTime tests
Begin from Payload.<Date field>, or from any token of that type (TTLCounter, Abstraction, Dictionary and so on).
| Test | Arguments | True when |
|---|---|---|
IsPast | none | Earlier than now. |
IsFuture | none | Later than now. |
IsToday | none | On the current date. |
IsWeekday | none | Monday to Friday. |
IsWeekend | none | Saturday or Sunday. |
IsMorning | none | Before 12:00. |
IsAfternoon | none | From 12:00. |
Before | DateTime other | Earlier than the argument. |
After | DateTime other | Later than the argument. |
Between | DateTime rangeStart, DateTime rangeEnd | Strictly between the two dates. |
InRange | DateTime rangeStart, DateTime rangeEnd | Between the two dates, both included. |
SameDayAs | DateTime other | On the same calendar date as the argument. |
WithinBusinessHours | int startHour, int endHour | The hour is from the start hour up to, but not including, the end hour. |
OutsideBusinessHours | int startHour, int endHour | The hour is outside those hours. |
OlderThanDays | int days | The date is more than the number of days in the past. |
YoungerThanDays | int days | The date is fewer than the number of days in the past. |
WithinMinutesOf | DateTime other, double minutes | Within the minutes of the argument, in either direction. |
WithinHoursOf | DateTime other, double hours | Within the hours of the argument, in either direction. |
WithinDaysOf | DateTime other, double days | Within the days of the argument, in either direction. |
MoreThanMinutesAfter | DateTime other, double minutes | More than the minutes after the argument. |
MoreThanDaysAfter | DateTime other, double days | More than the days after the argument. |
DayOfWeekIn | int... days | Day of week is one of the numbers, where 0 is Sunday and 6 is Saturday. |
IsStartOfMonth | none | The first day of the month. |
IsEndOfMonth | none | The last day of the month. |
WithinBusinessHoursInZone | string zoneId, int startHour, int endHour | The value, read as UTC, falls in the hours in that time zone (for example America/New_York). An unknown zone is false. |
OutsideBusinessHoursInZone | string zoneId, int startHour, int endHour | The value, read as UTC, falls outside the hours in that time zone. An unknown zone is false. |
WithinWeekdayBusinessHours | int startHour, int endHour | A weekday, within the hours, using the value’s own clock. |
OutsideWeekdayBusinessHours | int startHour, int endHour | A weekend, or outside the hours, using the value’s own clock. |
WithinWeekdayBusinessHoursInZone | string zoneId, int startHour, int endHour | A weekday in that time zone and within the hours there. An unknown zone is false. |
OutsideWeekdayBusinessHoursInZone | string zoneId, int startHour, int endHour | A weekend in that time zone, or outside the hours there. An unknown zone is false. |
IsWeekdayInZone | string zoneId | A weekday in that time zone. An unknown zone is false. |
IsWeekendInZone | string zoneId | A weekend in that time zone. An unknown zone is false. |
Boolean tests
Begin from Payload.<Boolean field>, or from any token of that type (TTLCounter, Abstraction, Dictionary and so on).
| Test | Arguments | True when |
|---|---|---|
IsTrue | none | The value is true. |
IsFalse | none | The value is false. |
Fuzzy list matching options
Any step ending in With, and the string extensions IsSomehowInList, IsSomehowLike, ContainsSomehowInList, FuzzyBestScore, FuzzyMatchCount and FuzzyBestMatch, take an options string: a comma separated list of the options below. It is plain text, because rules cannot name types. Options may be in any order, are case insensitive, and an invalid string fails closed (nothing matches) rather than throwing. Use IsValidFuzzyOptions() and FuzzyOptionsError() on an options string to check it while authoring. A blank or null string means the default profile.
Matched = Payload.Name.IsSomehowInList(List.Whatever)
Matched = Payload.Name.IsSomehowInList(List.Whatever, "damerau=1,soundex,notitles,minlength=4")
Matched = Payload.Narrative.ContainsSomehowInList(List.Whatever, "lev=2,nosuffixes")
Matched = Payload.Name.FuzzyBestScore(List.Whatever, "tokensort,initials") > 0.9
Algorithms
Name one or more. With no algorithm, the default profile applies (Jaro-Winkler or token sort, both at 0.92). By default any algorithm passing is a match; add all to require every one.
| Option | Threshold | Matches when |
|---|---|---|
exact | none | Identical after normalisation |
levenshtein=N, lev=N | Whole number of edits, 0 to 10, default 1 | At most N insertions, deletions or substitutions |
damerau=N, osa=N | As above | As above, and swapping two adjacent letters is one edit (Jhon for John) |
similarity=T, sim=T | 0 to 1, default 0.85 | The Levenshtein ratio is at least T |
jarowinkler=T, jw=T | 0 to 1, default 0.92 | Jaro-Winkler similarity is at least T, favouring a shared start |
dice=T, bigram=T | 0 to 1, default 0.85 | Overlap of two letter pairs is at least T |
soundex | none | Every word sounds the same as the entry’s word in that position |
tokensort=T, sort=T | 0 to 1, default 0.92 | The same words in any order (Smith John), allowing typos |
tokenset=T, set=T | 0 to 1, default 0.85 | The share of the entry’s words found, allowing typos and missing words |
initials | none | Same words, where a word may be an initial, with at least one full word matching (J Smith) |
Normalisation
Applied to both the value and each entry before comparing.
| Option | Effect | Default |
|---|---|---|
nocase / case | Ignore case, or make it significant | Ignore |
nodiacritics / diacritics | Fold accents and letters such as ß, ø, ł, or keep them | Fold |
nopunct / punct | Delete apostrophes and turn other punctuation into spaces, or keep it | Strip |
nospaces / keepspaces | Remove all spaces (JohnSmith), or keep them | Keep |
notitles | Drop leading titles such as Mr, Dr, Prof, Sir, Herr | Off |
nosuffixes | Drop trailing company forms such as Ltd, LLC, Inc, GmbH, PLC | Off |
Word based algorithms (soundex, tokensort, tokenset, initials) need spaces, so do not combine them with nospaces.
Scope, blocking and combination
| Option | Effect |
|---|---|
whole (default) | The whole value is compared with each entry |
within | Each entry is searched for inside the value, a run of words at a time, so a name inside a narrative is found. Never matches inside a word |
all / any (default) | Require every algorithm to pass, or any one |
minlength=N | Skip values shorter than N characters after normalisation. Recommended for short names, where similarity is noisy |
firstletter | Only compare with entries that start with the same letter. Faster, and stricter |
Results other than yes or no
| Extension | Returns |
|---|---|
FuzzyBestScore(list, options) | The best score across the list, from 0 to 1, even if nothing passed |
FuzzyMatchCount(list, options) | How many entries pass |
FuzzyBestMatch(list, options) | The original text of the best entry, or null |
The same three exist as pipeline steps that change the value being tested (a number, a count, and a string), so the result feeds the ordinary tests: Payload.Name.FuzzyBestScore(List.Whatever, "jw").MatchGreaterOrEqual(0.9).
Performance
The list is normalised once per normalisation setting and cached against the list object, and is rebuilt when the list changes (its size, or its first, middle or last entry). Every event still compares against every entry, so for very large lists prefer firstletter, minlength, or a cheaper algorithm first in a pipeline, and put exact checks before fuzzy ones.
Reading an HTTP Adaptation without an exception
HttpAdaptation.Name.Value.OrNaN() reads the adaptation’s value as a number and is NaN when it was suppressed, so it fails every comparison. OrZero() reads a suppressed value as zero instead. GetValueOrDefault is not a permitted token.
Calculations on continuous values
These calculations, and the one step derive-and-compare tests that use them, are first class in a rule. As a standard, though, endeavour to write the calculation in an Abstraction Calculation and let the Activation Rule compare the stored result, because it can be reused and organises concerns (see Derived Values: Use an Abstraction Calculation). Work it out in the rule where a calculation is not available, in a Gateway Rule or where a model score is involved. The examples below show the calculation in a rule so that its result is visible.
A calculation is called on a number, takes the other numbers it needs as arguments, and returns a number. That is the reading order of a rule: Payload.Amount.RatioOf(Payload.AnotherAmount) is the amount as a ratio of the other amount. Any number token can be the receiver or an argument, so counters, abstractions, dictionary values and payload fields mix freely. The result is an ordinary number, so the next step is a comparison, or another calculation.
Matched = TTLCounter.DeclinedSpend.RatioOf(TTLCounter.TotalSpend).MatchGreater(0.3)
Matched = TTLCounter.DeclinedSpend.ShareOfSumWith(TTLCounter.ApprovedSpend).MatchInRange(0.25, 0.5)
Matched = TTLCounter.DeclinedSpend.RatioOf(TTLCounter.TotalSpend).Complement().MatchLess(0.7)
Every calculation is also available as four comparisons in all five prefixes, with the extra values first and the bounds last (the bounds come first when the calculation takes a list of values, because a list must be last):
| Comparison | True when the calculation is |
|---|---|
<Calculation>Above(..., bound) | Above the bound |
<Calculation>Below(..., bound) | Below the bound |
<Calculation>InRange(..., lowerBound, upperBound) | Between the bounds, both included |
<Calculation>OutsideRange(..., lowerBound, upperBound) | Below the lower or above the upper bound |
So TTLCounter.DeclinedSpend.MatchRatioOfAbove(TTLCounter.TotalSpend, 0.3) is the same test as the first example, and TTLCounter.DeclinedSpend.MatchSumWithAbove(900, TTLCounter.ApprovedSpend) puts the bound first because SumWith takes a list.
A calculation that is undefined returns NaN, never zero and never infinity. That is a division by zero, a log of a number that is not positive, a range whose ends are equal, an empty total, and any NaN or infinite input. NaN fails every comparison, so a rule fails closed on an empty counter instead of matching or faulting. To test for it use HasNoValue() or HasValue(). To turn an undefined result into zero, for example so that an Abstraction Calculation stores a defined number, end the calculation with ZeroIfUndefined().
| Calculation | Arguments | Returns |
|---|---|---|
RatioOf | number denominator | The value divided by the denominator. |
PercentOf | number total | The value as a percentage of the total. |
ShareOfSumWith | number other | The value’s share of the value plus the other (a decline ratio is declined over declined plus approved). |
Complement | none | One minus the value (an approval ratio from a decline ratio). |
LogRatioOf | number denominator | The natural log of the value over the denominator, symmetric for increases and decreases. |
RatioOfRatios | number denominator, number otherNumerator, number otherDenominator | The value over its denominator, divided by another numerator over its own denominator. |
InverseRatioOf | number other | The other value over this value. |
DifferenceFrom | number other | The value minus the other. |
AbsoluteDifferenceFrom | number other | The size of the gap between the value and the other. |
PercentDifferenceFrom | number other | The gap between the two as a percentage of their average size, the same either way round. |
RelativeChangeFrom | number previous | The change from the previous value as a fraction of it. |
GrowthFactorFrom | number previous | The value as a multiple of the previous value. |
SlopeFrom | number previous, number elapsed | The change from the previous value per unit of time. |
CompoundGrowthRate | number previous, number periods | The steady growth rate per period that takes the previous value to this one. |
PercentDeviationFromMean | number mean | The percentage the value sits above or below the mean. |
ModifiedZScore | number median, number medianAbsoluteDeviation | A robust z-score from the median and the median absolute deviation, unmoved by outliers. |
CoefficientOfVariation | number mean | The value, as a standard deviation, over the size of the mean. |
MinMaxNormalise | number minimum, number maximum | Where the value sits between the minimum (0) and maximum (1). |
PercentOfRange | number minimum, number maximum | Where the value sits between the minimum (0) and maximum (100). |
SumWith | number... others | The value plus the others. |
MeanWith | number... others | The average of the value and the others. |
MaxWith | number... others | The largest of the value and the others. |
MinWith | number... others | The smallest of the value and the others. |
SpreadWith | number... others | The gap between the largest and smallest of the value and the others. |
WeightedMeanWith | number weight, number other, number otherWeight | The weighted average of the value and another value. |
GeometricMeanWith | number other | The geometric mean of the value and another, for growth factors and multiples. |
HarmonicMeanWith | number other | The harmonic mean of the value and another, for averaging rates. |
ShareOfMax | number... others | The value over the largest of it and the others. |
HerfindahlWith | number... others | How concentrated the total is in a few of the value and the others, from 1 over the count to 1. |
EntropyOfSharesWith | number... others | The Shannon entropy in bits of how the total is shared across the value and the others. |
HeadroomTo | number limit | The limit minus the value, the room left. |
ExcessOver | number limit | How far the value is over the limit, zero when within it. |
ShortfallBelow | number minimum | How far the value is under the minimum, zero when at or above it. |
DistanceToThreshold | number threshold | The gap between the value and the threshold in either direction. |
PercentBelowThreshold | number threshold | How far under the threshold the value is as a percentage of it, negative when over. |
ImbalanceWith | number other | How one sided the value and the other are, from -1 (all the other) through 0 to 1 (all this value). |
RetentionAfter | number outflow | The share of the value kept after the outflow, negative when more went out than came in. |
Recipes
| Business question | Rule |
|---|---|
| Decline ratio by value | TTLCounter.DeclinedSpend.RatioOf(TTLCounter.TotalSpend).MatchGreater(0.3) |
| Decline ratio against approved plus declined | TTLCounter.DeclinedSpend.ShareOfSumWith(TTLCounter.ApprovedSpend).MatchGreater(0.3) |
| Approval ratio | TTLCounter.DeclinedSpend.RatioOf(TTLCounter.TotalSpend).Complement().MatchLess(0.7) |
| Decline ratio by count against by value | TTLCounter.DeclinedCount.RatioOfRatios(TTLCounter.TotalCount, TTLCounter.DeclinedSpend, TTLCounter.TotalSpend).MatchGreater(2) |
| Chargeback or refund ratio | TTLCounter.Refunds.RatioOf(TTLCounter.Sales).MatchGreater(0.05) |
| Average ticket size | TTLCounter.Spend.RatioOf(TTLCounter.Transactions).MatchGreater(500) |
| Spend per day | TTLCounter.Spend7Days.RatioOf(7).MatchGreater(1000) |
| Spend acceleration, 1 hour against the daily average | TTLCounter.Spend1Hour.RatioOf(TTLCounter.Spend24Hours.RatioOf(24)).MatchGreater(4) |
| Percentage of a card limit used | Payload.Balance.PercentOf(Payload.CreditLimit).MatchGreater(90) |
| Room left under a limit | Payload.Balance.HeadroomTo(Payload.CreditLimit).MatchLess(100) |
| Amount over a limit | Payload.Amount.ExcessOver(Payload.DailyLimit).MatchGreater(0) |
| Amount short of a minimum | Payload.Amount.ShortfallBelow(Payload.MinimumSpend).MatchGreater(0) |
| Money passing straight through (mule) | TTLCounter.Outbound.RatioOf(TTLCounter.Inbound).MatchInRange(0.9, 1.05) |
| Share of inflow kept | TTLCounter.Inbound.RetentionAfter(TTLCounter.Outbound).MatchLess(0.1) |
| One sided flow, in against out | TTLCounter.Inbound.ImbalanceWith(TTLCounter.Outbound).MatchGreater(0.8) |
| Cash out against cash in | TTLCounter.Withdrawals.RatioOf(TTLCounter.Deposits).MatchGreater(0.95) |
| Spike against the customer’s own mean | Payload.Amount.PercentDeviationFromMean(Abstraction.MeanAmount).MatchGreater(300) |
| Outlier that ignores outliers in the baseline | Payload.Amount.ModifiedZScore(Abstraction.MedianAmount, Abstraction.MadAmount).MatchGreater(3.5) |
| Noisy customer, spread against mean | Abstraction.StdDevAmount.CoefficientOfVariation(Abstraction.MeanAmount).MatchGreater(1.5) |
| Growth on last period | TTLCounter.SpendThisWeek.RelativeChangeFrom(TTLCounter.SpendLastWeek).MatchGreater(2) |
| Multiple of last period | TTLCounter.SpendThisWeek.GrowthFactorFrom(TTLCounter.SpendLastWeek).MatchGreater(3) |
| Steady growth per period | Payload.Balance.CompoundGrowthRate(Payload.OpeningBalance, 6).MatchGreater(0.5) |
| Rate of change per hour | Payload.Balance.SlopeFrom(Payload.PreviousBalance, Payload.HoursElapsed).MatchGreater(1000) |
| Symmetric growth or shrinkage | TTLCounter.SpendThisWeek.LogRatioOf(TTLCounter.SpendLastWeek).MatchOutsideRange(-1, 1) |
| Two systems disagree | Payload.LedgerBalance.PercentDifferenceFrom(Payload.CoreBalance).MatchGreater(2) |
| Invoice against purchase order | Payload.InvoiceAmount.AbsoluteDifferenceFrom(Payload.PurchaseOrderAmount).MatchGreater(50) |
| Just under a reporting threshold | Payload.Amount.PercentBelowThreshold(10000).MatchInRange(0, 10) |
| Distance to a threshold either side | Payload.Amount.DistanceToThreshold(10000).MatchLess(250) |
| One merchant dominates spend | TTLCounter.TopMerchantSpend.ShareOfMax(TTLCounter.SecondMerchantSpend, TTLCounter.ThirdMerchantSpend).MatchGreater(0.9) |
| Spend concentrated across merchants | TTLCounter.MerchantA.HerfindahlWith(TTLCounter.MerchantB, TTLCounter.MerchantC).MatchGreater(0.6) |
| Spend spread evenly (structuring) | TTLCounter.MerchantA.EntropyOfSharesWith(TTLCounter.MerchantB, TTLCounter.MerchantC).MatchGreater(1.5) |
| Largest of several amounts | Payload.Amount.MaxWith(Payload.PreviousAmount, Payload.Amount2).MatchGreater(9000) |
| Combined total across products | TTLCounter.CardSpend.SumWith(TTLCounter.AccountSpend, TTLCounter.LoanSpend).MatchGreater(50000) |
| Combined average | TTLCounter.CardSpend.MeanWith(TTLCounter.AccountSpend).MatchGreater(5000) |
| Gap between highest and lowest of several | Payload.Amount.SpreadWith(Payload.Amount2, Payload.Amount3).MatchGreater(10000) |
| Blended risk score | Payload.ModelScore.WeightedMeanWith(0.7, Payload.RulesScore, 0.3).MatchGreater(0.8) |
| Average of rates | Payload.RateA.HarmonicMeanWith(Payload.RateB).MatchGreater(0.4) |
| Average of growth multiples | Payload.FactorA.GeometricMeanWith(Payload.FactorB).MatchGreater(1.5) |
| Position within a range | Payload.Amount.MinMaxNormalise(Abstraction.MinAmount, Abstraction.MaxAmount).MatchGreater(1) |
| Percent of a range | Payload.Amount.PercentOfRange(Abstraction.MinAmount, Abstraction.MaxAmount).MatchGreater(100) |
| How many times a limit fits | Payload.Amount.InverseRatioOf(Payload.DailyLimit).MatchLess(2) |
Using existing extension methods
Every existing extension method that returns a Boolean can be used in a pipeline through MatchWhen, RejectWhen, BreakWhen, RequireThat and EnsureThat, so nothing in the extension catalogue is out of reach:
Matched = Payload.Amount.RequireGreater(0).RequireThat(Payload.Amount.IsRoundAmount(1000)).MatchGreater(9000)