Business Rules Examples
Learn more about commonly used business rules for data transformation.
Overview
Business rules are applied during data load to transform data based on defined conditions, allowing you to modify data to ensure accuracy and consistency. Understanding these transformations, and when to use them, will help you efficiently manage your event streams. For more information, seeManage event streams.
Business rules are categorized as follows:
- Predefined business rules: These rules are built into the platform and already exist in Visier's internal rule library.
- Formula business rules: These rules are created and defined using the Visier Business Rule Language (VBRL) to transform subject and event data within your event stream. For more information, see Formula business rules.
Predefined business rules
Predefined business rules are executed using the call tag.
Syntax
call RULENAME(
ARG1,
ARG2,
…
)
call
: Instructs Visier to call the predefined rule in the internal rule library.RULENAME
: Specifies the rule to use. Each rule executes different logic against the arguments in the formula.ARG1, ARG2
: Specifies the arguments, such as subjects or properties, to validate. Rules accept zero, one, or more arguments.
If a predefined rule has optional parameters, you must define parameters in the order listed in the rule syntax. For example, to declare a value for the optional2
parameter in the code below, you must present a value for optional1
.
call predefinedRule(
mandatory1,
mandatory2 [, optional1, optional2]
)
backfillChanges
Fills in missing values by adjusting the initial values of specific properties starting from a defined date. This adds information to the subject when existing values are not set. You can explicitly set the start date withstartDate.
Otherwise, Visier infers the start date from the earliest recorded change in the specified properties.
Syntax
call backfillChanges(
skipNone,
backfillPrimaryStreamOnly,
{properties}
[, startDate]
)
skipNone
(optional, Boolean, default: false): A flag that determines whether properties withNone
values should be ignored during the rule application. When set totrue
, it preventsNone
values from being copied forward, but it does not remove or delete them from the source data.backfillPrimaryStreamOnly
(optional, Boolean, default: false): A flag to include the primary event stream in the rule and exclude the auxiliary event stream.Properties
: Specifies the properties to be adjusted in the backfill.startDate
: (optional): The assigned new date for the data to be backfilled to. If there is no declaredstartDate
the rule sets the date of the first change in any of the specified properties. The format used in the rule is YYYY, MM, DD.
Examples
Say you want to backfill a property to a specific date with skipNone
value set to true
and the backfillPrimaryStreamOnly
set to false
. In this example, the First_Name
property backfills using the preset values to the specified start date of January 1, 2015. This rule ignores all First_Name
property values of None
.
call backfillChanges(
true,
false,
{First_Name},
Date(2015, 01, 01)
)
The following table shows how the data is presented before the backfillChanges
rule.
EmployeeID |
Date |
First_Name |
---|---|---|
ABCD1234 |
2016-01-31 |
None |
ABCD1234 |
2016-06-01 |
John |
The following table shows how the data is presented after the backfillChanges
rule.
EmployeeID |
Date |
First_Name |
---|---|---|
ABCD1234 |
2016-01-31 |
John |
ABCD1234 |
2016-06-01 |
John |
If you want to backfill a specific property to a date with the skipNone
flag set to the default false
value. This example backfills the First_Name
property using the specified start date and includes any First_Name
property with value of None
in the backfill. The backfillPrimaryStreamOnly
flag is not specified, so the default value is false
and the rule applies to both the primary and auxiliary event stream.
call backfillChanges(
{First_Name},
Date(2015, 01, 01)
)
You can backfill a property to a defined date with skipNone
true, and only account for the primary stream when determining the startDate
. In this example the rule fills in all values of the First_Name
property to January 1, 2015 on the primary stream and ignores events in the auxiliary stream.
call backfillChanges(
true,
true,
{First_Name},
Date(2015, 01, 01)
)
You can backfill missing values for multiple attributes in a single application of the backfillChanges
rule. In this example, you can call the backfillChanges
rule on the First_Name
, Last_Name
, Pay_Level
, and Birth_Date
properties. As the startDate
is not included, the backfillChanges
rule resets the values in the specified properties to the value on the date of the earliest recorded change.
call backfillChanges(
false,
{First_Name, Last_Name, Pay_Level, Birth_Date}
)
When dealing with several properties in the same rule, it makes sense to adjust all properties with a few exceptions. To define a set of properties to ignore when backfilling, use allPropertiesExcept
. In this example you will learn how to backfill all properties except NonActive
with the skipNone
flag set to true
. If no properties are specified in the allPropertiesExcept
parameter (allPropertiesExcept({})
), all properties defined before the rule are included in the backfill. Be aware that this may lead to unintended effects on time-sensitive properties like NonActive
or Performance
.
call backfillChanges(
true,
allPropertiesExcept({NonActive})
)
groupAndPick
Picks a single event out of a set of events, and deletes all other events that remain in the set. This is useful for removing duplicate events, resolving inconsistencies between data sources, and handling multiple changes on the same timestamp.
Syntax
call groupAndPick(
picker,
groupBy,
groupFilter
)
picker
: Defines how to select a single event from the grouped set. For more information, see Picker functions.groupBy
(optional): Groups events before passing them to the picker.groupFilter
(optional): Defines which events are considered by the rule. This parameter is used to limit the events deleted bygroupAndPick
.
To use groupAndPick
:
- Select events that satisfy the
eventFunction
in the picker. TheeventFunction
specifies which events are considered for the picking strategy. For more information, see Picker functions. - Filter these events using
groupFilter
. Those that satisfy the filter are candidates of being grouped while others are ignored. - Group the filtered events according to the
groupBy
parameters. - Filter the groups with the
pickFilter
parameter. - For each group, select a single event using the picker. For example,
pickEarliest
orpickLatest
.
Picker functions
A picker function in the groupAndPick
rule limits the events picked after grouping, or extends the range of the picking strategy to more than one date.
pickEarliest(
eventFunction,
pickFilter,
interval,
startDate,
endDate
)
-
pickEarliest
: Returns the earliest event out of a group. eventFunction
: Specifies which events are considered for the picking strategy. For example,FirstName
orPayEvent
.pickFilter
(optional): A filter function that is applied to events before a picking strategy is considered. This filter is used to restrict the events considered for picking.interval
(optional): The interval in which the picking strategy should be applied. For more information about the available options for this parameter, see Count functions.startDate
,endDate
(optional): Parameters that determine the interval of the picker function applied. Events outside of this interval are ignored and remain as they are in the event stream.
For example, pickEarliest(Pay_Change, true, days(10))
will consider a ten day time span when picking a single event from a group.
pickLatest(
eventFunction,
pickFilter,
interval,
startDate,
endDate
)
-
pickLatest
: Returns the latest event out of a group. It accepts both a picking filter to limit the events that are considered to be picked and an interval that could extend the range of the picking strategy to more than one date.
eventFunction
: Specifies which events are considered for the picking strategy. For example,FirstName
orPayEvent
.pickFilter
(optional): A filter function that is applied to events before a picking strategy is considered. This filter is used to restrict the events considered for picking.interval
(optional): The interval in which the picking strategy should be applied. For more information about the available options for this parameter, see Count functions.startDate
,endDate
(optional): Parameters that determine the interval of the picker function applied. Events outside of this interval are ignored and remain as they are in the event stream.
For example, pickLatest(Pay_Change, true, days(10))
considers a ten day time span from the most recent event when picking a single event from a group.
concatenate(
eventFunction,
resultProperty,
First_Name,
delimiter
)
-
concatenate
: Combines groups of property changes to a single property change. This new property change updates theresultProperty
, setting its value to the concatenated results returned byeventFunction
. Concatenation is only applicable to string subject properties and not event properties.
eventFunction
: Specifies which properties will be concatenated. For example,First_Name
.resultProperty
: Where the concatenated result of the group are stored. This could be any string property, including one fromeventFunction
.delimiter
: A constant string used as the delimiter when concatenating values of a group. For example, ;
For example, call
groupAndPick(concatenate(First_Name, First_Name, ";"), 1, First_Name != "Important" )
would concatenate the picker function to apply to First_Name
properties where values equal to Important
are excluded from groupAndPick
operation.
pickByMax(
valueFunction,
addFunction,
resetTrigger
)
-
pickByMax
: Returns an event with the highestvalueFunction
value whenaddFunction
is triggered. The maximum value resets whenresetTrigger
begins.
For example, pickByMax(PayEvent.PayAmount, PayEvent, last(PayEvent))
picks the PayEvent
with the maximum PayAmount
.
pickByMin(
valueFunction,
addFunction,
resetTrigger
)
-
pickByMin
: Returns an event with the lowestvalueFunction
value whenaddFunction
is triggered. The minimum value resets whenresetTrigger
executes.
For example, pickByMin(PayEvent.PayAmount, PayEvent, last(PayEvent))
picks the PayEvent
with the minimum PayAmount
.
Count functions
The following table describes the time span function used in the pickAndGroup
rule. A value of function()
with no numeric count included in the brackets is equivalent to a single instance. For example, one day is day()
, one month is month()
, and one year is year()
.
Function | Description |
---|---|
days(count) |
Events that happen within the specified days of the initial member of a group are grouped together. For example, |
months(count) |
Events that happen within the specified months of the initial member of a group are grouped together. Starts from the first day of the month the initial member of a group appears. For example, |
years(count) |
Events that happen within the specified year of the initial member of a group are grouped together, starting from the first day of the year the initial member of a group appears. For example, |
Examples
This business rule example picks a single event out of all events on a date. This examines all Pay_Change
regular events on a single date and picks the Pay_Change
which either has the most recent timestamp value or whichever event is most recent.
call groupAndPick(
pickLatest(Pay_Change)
)
In this example, the rule selects the first event on a given date, pay changes occurring within two days of the initial member are grouped together, and the earliest one is chosen.
call groupAndPick(
pickEarliest(Pay_Change, true, days(2))
)
In this example, the groupAndPick
rule selects a single event from all Pay_Change
regular events on a date. This example will examine all Pay_Change
regular events on a date and divides them into different categories based on their PayType
value. Then, for each category, only the Pay_Change
with the most recent event is picked.
call groupAndPick(
pickLatest(Pay_Change),
Pay_Change.PayType
)
In this example, the groupAndPick
rule deletes events that are not the Regular
pay type.
call groupAndPick(
pickEarliest(Pay_Change),
"A constant value",
Pay_Change.PayType = "Regular"
)
In addition to groupFunction
, there is another way to control the events that are picked by groupAndPick
. The following example shows how pickEarliest
and pickLatest
have an optional parameter called pickFilter
permitting finer control over picking operation. In this example, the rule excludes events with Amount
less than or equal to fifty from the selection process.
call groupAndPick(
pickEarliest(Compensation_Payout.Payout_Amount > 50),
Compensation_Payout.Payout_Type
)
augmentingMapping
Uses the values of a property to look up values in the target mapping file object and apply them to the properties in the current subject. This rule does not override existing property values, so it is good for adding data to empty properties or for filling in gaps or missing data.
Syntax
call augmentingMapping(
mappingName,
mappingKey,
mappedProperties [, ifUnmapped, filter, ifOutputExist, ignoreIfNoValue]
)
mappingName
: The object name of the mapping file.mappingKey
: The lookup key property in the current subject. This must be an existing property in the subject and should not be a subject ID likeEmployeeID
.mappedProperties
: The properties in the current subject that are mapped to strings corresponding to column names in the target mapping. The syntax isMap[Property -> String]
.ifUnmapped
(optional, useDefault() or ignore(), default: ignore()): Specifies behavior when no match is found in the target object.useDefault()
(optional): Inserts a default-valued event if no lookup value found.ignore()
(optional): Does nothing when lookup key does not exist in lookup stream.filter
(optional): Applies a filter to the lookup process.ifOutputExist
(optional, augmentAnyways() or noAugmentIfValueExist(), default: augmentAnyways()): Determines if augmentation occurs when values already exist.augmentAnyways()
(optional): Always augments the event stream with lookup events.noAugmentIfValueExist()
(optional): Does not augment the event stream if the stream contains existing property values.ignoreIfNoValue
(optional, Boolean, default: false): Iftrue
, it prevents event insertion when no lookup value is found.
Example
Let's say you want to use the values of a property to look up values in the target mapping file object and apply them to the properties in the current subject. This will augment a single mapping by adjusting the property values in the subject property. In this example, the rule assigns the lookup property CompMax
to the Employee.Pay_Level_Compensation_Maximum
property, and CompMin
to the Employee.Pay_Level_Compensation_Minimum
property. This rule only applies if the property is unmapped in the existing mapping and if the date of change is on or after January 1, 2018. This applies to values that already exist by adding the aumentAnyways()
. Because ignoreIfNoValue
is false
, the rule allows event insertion when no lookup value is found. This example relies on the pay_levels_post_stg
lookup existing in the data.
call augmentingMapping(
"pay_levels_post_stg",
Employee.JobPayLevelCode,
{
Employee.Pay_Level_Compensation_Maximum -> "CompMax",
Employee.Pay_Level_Compensation_Minimum -> "CompMin"
},
useDefault(),
dateOf(changeIn(Employee.Pay_Level)) >= date(2018,1,1),
augmentAnyways(),
false
)
augmentingMultipleMapping
The augmentingMultipleMapping
rule is similar to the regular augmenting mapping rule. However, augmentingMultipleMapping
accepts a mapping targetGroup
instead of a single mapping name. By including a targetGroup
, the business rule can be associated with all lookup mappings defined with the target group, and combine information from these mappings into the auxiliary stream.
Syntax
call augmentingMultipleMappings(
targetGroup,
mappingKey,
mappedProperties,
ifUnmapped,
filter,
ifOutputExist,
ignoreIfNoValue
)
targetGroup
: Includes supplemental data in a mapping that is considered separate from the main source. For more information, see Target group.
changeDates
Inserts a value for targetProperty
based on the provided nameChangeDateProperties
. The targetProperty
value is the corresponding string for the property's namedChangeDateProperties
. Use this rule if you want to insert different values based on different dates provided in the source.
Syntax
changeDates(
targetProperty,
{namedChangeDateProperties},
changeAll
)
targetProperty
: The property changed by the rule application.namedChangeDateProperties
: A map of date properties to string values that would be inserted fortargetProperty
. The syntax isnamedChangeDateProperties -> new string value
. For example,Requisition.temp_CreatedDate -> "Created"
.changeAll
(optional, Boolean, default: false): Iftrue
an event is inserted at every timestamp for eachnameChangeDateProperties
profile change event. Iffalse
an event is inserted at the last timestamp for eachnameChangeDateProperties
profile change event.
Examples
The code samples below demonstrate how the changeDates
rule is applied to the source data. In this example it is used to insert a value for the target property, Requisition.Requisition_Status
, based on the provided nameChangeDateProperties
.
call changeDates(
Requisition.Requisition_Status,
{Requisition.temp_CreatedDate -> "Created",
Requisition.temp_ApprovedDate -> "Approved",
Requisition.temp_ClosedDate -> "Closed"}
)
The following table represents the source data stream before applying the rule. The data is presented in a wide format.
Requisition ID |
Created |
Approved |
Closed |
---|---|---|---|
1 |
1-1-2019 |
2-1-2019 |
3-1-2019 |
The following table represents the resulting stream after the changeDates
rule is applied. Because changeAll
is true
, the rule inserts an event at every timestamp for each nameChangeDateProperties
profile change event.
Requisition ID |
Event Date |
Requisition Status |
---|---|---|
1 |
1-1-2019 |
Created |
1 |
2-1-2019 |
Approved |
1 |
3-1-2019 |
Closed |
consolidateRegularEvents
Consolidates multiple regular events on the same date or over a range of dates into a single event. For example, running this rule sums all pay events that happen in the same month as designated by EventDate
.
Syntax
call consolidateRegularEvents(
regularEventSchema,
groupingProperty,
resolutionStrategies
filter,
)
regularEventSchema
: The regular event to be consolidated. For example,PayEvent
.groupingProperty
: The grouping property applied to the regular event. For example,PayEvent.PayType
would group together allPayTypes
in thePayEvent
.resolutionStrategies
: Defines the consolidation method. Several strategies can exist in the same rule, but thegroupingProperty
should never be included in the application ofresolutionStrategies
. This parameter is required for all available properties except for the primary key andEventDate
. The syntax for this parameter isPayEvent.Amount -> sum()
.filter
(optional): Provide exceptions to the above rule when consolidating events. For example,PayEvent.PayType != "Bonus"
would exclude all values for pay labeled asBonus
.
Resolution strategies
The following strategies can be used as part of the resolutionStrategies
parameter.
sum()
: Sums the value of the given attribute for all regular events on the day. Can only be applied to attributes that are integers.average()
: Takes the average value of the given attribute for all regular events on the day. Only applies to attributes that are floating-point numbers, or numbers that have decimal points.ignoreAttribute()
: Ignores the attribute and sets it toNone
in the consolidated event.max()
: Takes the largest value of the attribute for all regular events on the day and stores it in the consolidated regular event.min()
: Take the smallest value of the attribute for all regular events on the day and stores it in the consolidated regular event.
Examples
Say you wanted to consolidate multiple regular events happening on the same date. The example below adds together all Amount
values of each separate PayType
(Labor or Dinner) occurring on the same EventDate
.
call consolidateRegularEvents(
PayEvent,
PayEvent.PayType,
{ PayEvent.Amount -> sum(),
PayEvent.PayLevel -> sum() PayEvent.PaySystem -> ignoreAttribute() }
)
The following table shows how the data is represented before the consolidateRegularEvents
rule is applied.
EmployeeID |
EventDate |
PayType |
Amount |
Hours |
PaySystem |
---|---|---|---|---|---|
ABCD1234 |
2018-06-04 |
Labor |
100.00 |
10 |
ADT |
ABCD1234 |
2018-06-04 |
Labor |
100.00 |
10 |
kronos |
ABCD1234 |
2018-06-04 |
Labor |
100.00 |
10 |
Namely |
ABCD1234 |
2018-06-04 |
Labor |
100.00 |
10 |
ADT |
ABCD1234 |
2018-06-04 |
Labor |
100.00 |
10 |
ADT |
ABCD1234 |
2018-06-04 |
Labor |
-50.00 |
-5 |
kronos |
ABCD1234 |
2018-06-04 |
Dinner |
20.00 |
0 |
Namely |
ABCD1234 |
2018-06-04 |
Dinner |
20.00 |
0 |
Namely |
ABCD1234 |
2018-06-04 |
Dinner |
20.00 |
0 |
kronos |
The following table shows how the data is represented after the consolidateRegularEvents
rule is applied. Because the rule specifies PayEvent.PaySystem -> IgnoreAttribute ()
, the resulting data replaces PaySystem
values with None
.
EmployeeID |
InvoiceDate |
PayType |
Amount |
Hours |
PaySystem |
---|---|---|---|---|---|
ABCD1234 |
2018-06-04 |
Labor |
450.00 |
45 |
None |
ABCD1234 |
2018-07-31 |
Dinner |
40.00 |
0 |
None |
Say you wanted to consolidate distinct events of different types in to separate groups using the EventDate
attribute. These events would be over a span of days and the source data contains attributes that are not relevant to the grouping and should be ignored. In this example, the values for Amount
of each PayType
are added and grouped by the different dates.
call consolidateRegularEvents(
PayEvent,
PayEvent.PayType,
{ PayEvent.Amount -> sum(), PayEvent.PayLevel -> sum() PayEvent.PaySystem -> ignoreAttribute() },
month()
)
The following table shows how the data is represented before the consolidateRegularEvents
rule is applied.
EmployeeID |
EventDate |
PayType |
Amount |
Hours |
PaySystem |
---|---|---|---|---|---|
ABCD1234 |
2018-06-10 |
Labor |
100.00 |
10 |
ADT |
ABCD1234 |
2018-06-11 |
Labor |
100.00 |
10 |
kronos |
ABCD1234 |
2018-06-15 |
Labor |
100.00 |
10 |
Namely |
ABCD1234 |
2018-06-16 |
Dinner |
50.00 |
0 |
ADT |
ABCD1234 |
2018-07-04 |
Labor |
100.00 |
10 |
ADT |
ABCD1234 |
2018-07-10 |
Labor |
-50.00 |
-5 |
kronos |
ABCD1234 |
2018-07-11 |
Dinner |
20.00 |
0 |
Namely |
ABCD1234 |
2018-07-30 |
Labor |
100.00 |
10 |
Namely |
ABCD1234 |
2018-07-31 |
Dinner |
20.00 |
0 |
kronos |
The following table shows how the data is represented after the consolidateRegularEvents
rule is applied. Because the rule specifies that PayEvent.Amount -> sum(), PayEvent.PayLevel -> sum() PayEvent.PaySystem -> ignoreAttribute() } month()
. The Amount
for each PayType
have been summed and filtered by month.
Note: The PaySystem
attribute is ignored in the rule and is replaced by a None
value in the data.
EmployeeID |
InvoiceDate |
PayType |
Amount |
Hours |
PaySystem |
---|---|---|---|---|---|
ABCD1234 |
2018-06-30 |
Labor |
300.00 |
30 |
None |
ABCD1234 |
2018-06-30 |
Dinner |
50.00 |
0 |
None |
ABCD1234 |
2018-07-30 |
Labor |
250.00 |
15 |
None |
ABCD1234 |
2018-07-31 |
Dinner |
40.00 |
0 |
None |
addValidRanges
Adds valid ranges to profile change events and can apply to a single property or several properties in the same instance. These ranges may differ between file types, for example temporal versus transactional. For more information, see Data Load Types, Frequency, and Granularity. This rule will look for profile change events that have a property contained in targetProperties
and will add a valid range if the following conditions are met.
- The profile change event property exists in
targetProperties
. - The profile change events does not have a defined valid range.
- Another profile change event with a defined valid range occurs at the same time as the
targetProperties
.
Syntax
call addValidRanges(
targetProperties [, filter]
)
targetProperties
: The properties to add valid ranges to.filter
(Optional): Controls which properties will be targeted by the rule.
Example
Say you want to add a valid range to an attribute called Employment_Status
on the Employee
subject. In this example, the rule would add a valid range to all None
attributes to match the value of the Employment_Status
attribute on the same date.
Note: How the profile change event on 2018-09-30 is not affected by this rule as there are no other profile events on that date.
call addValidRanges(
{ Employee.Employment_Status }
)
The following table shows how the data is presented before the addValidRanges
rule is applied.
Date | Attribute |
Value |
Valid Range |
---|---|---|---|
2018-06-30 |
Employment_Status |
Active |
None |
2018-06-30 |
Employment_Type |
Labor |
2018-06-01 to 2018-06-30 |
2018-09-30 |
Employment_Status |
Inactive |
None |
The following table shows how the data is presented after the addValidRanges
rule is applied.
Date | Attribute |
Value |
Valid Range |
---|---|---|---|
2018-06-30 |
Employment_Status |
Active |
2018-06-01 to 2018-06-30 |
2018-06-30 |
Employment_Type |
Labor |
2018-06-01 to 2018-06-30 |
2018-09-30 |
Employment_Status |
Inactive |
None |
moveToStartOfDay
Adjusts all events in the event stream to align to 00:00:000 coordinated universal time (UTC). This rule is added when a subject is configured to be day granularity in the application, but the data has finer granularity. Employee data typically uses day-level granularity, where timestamps are uncommon. If new data includes timestamps, either the mapping formula can be updated to exclude them, or the rule can be added to remove the timestamps.
Syntax
call moveToStartOfDay()
dropProfileIfCondition
Looks for a condition in the existing data and removes all events when that condition is met. The condition is specified using conditionFunction
and when satisfied marks all events pertaining to a subject ID for removal. Marked events are then deleted if cancelFunction
is never fired using the default value false
. When removing multiple IDs, due to duplication or the person is no longer employed with the company, the source data will include a column to explicitly state which employees should be removed. This column can be used as a parameter in the function.
Syntax
call dropProfileIfCondition(
conditionFunction[,
cancelFunction,
endDate]
)
conditionFunction
: Specifies all events, both profile and regular, for the current subject that will be marked for deletion.cancelFunction
(optional, default: false): Iftrue
cancels the effect ofconditionFunction
. Events will be marked for deletion but will not be deleted.endDate
(optional): The date specifying the range of the rule. Only events happening before or on theendDate
will be deleted. The format is YYYY, MM, DD
Example
In this example, all conditionFunction
values of First_Name
equal to Test
will be marked for deletion. The cancelFunction
default value is false
and as there is no endDate
specified, all events to the current date will be deleted.
call dropProfileIfCondition(
First_Name = "Test"
endDate(2016, 01, 01)
)
replacementMapping
Replaces the values of a property with values in the target mapping file object. It can be considered an override where the lookup key is the mapping property.
Syntax
call replacementMapping(
mappingName,
mappingKey,
targetColumn [, filter, useDefault, ignoreIfNoValue]
)
mappingName
: Object name of the mapping file object.mappingKey
: The property used as the lookup key. This property must exist in the current subject for the rule to execute correctly.targetColumn
: A column name in the target mapping file object. If the lookup fails, the value oftargetColumn
is set toNone
.filter
(optional): A filter to apply to the mapping events.useDefault
(optional, Boolean, default: true): Iftrue
it will insert a default value for the attribute if no lookup is found. Iffalse
, it will do nothing to the mapping file object. This is only used when the lookup key does not exist in lookup stream.ignoreIfNoValue
(optional, Boolean, default: false): Whentrue
, the rule will not insert events when no lookup value is found.
Examples
In this example, the rule would replace the values in New_Location_Value
with the values from Employee.Location.Location_3
in the Location_Lookup
mapping file. As the lookup key exists in the lookup stream, useDefault
is not necessary and the default value of ignoreIfNoValue
is false
.
call replacementMapping(
"Location_Lookup",
Employee.Location.Location_3,
"New_Location_Value"
)
In this example we will find and correct values in the New_Location_Value
target column by setting the filter
value as true
and the useDefault
as false
. Setting useDefault
to false
means that any value not found will be replaced with Unknown
and the rule will only provide values necessary for the changes.
call replacementMapping(
"Location_Lookup",
Employee.Location.Location_3,
"New_Location_Value", true, false
)
createSubjectMember
Inserts a new subject ID and associated properties if no subject member with the same ID is present in the data. This rule can be used to insert virtual members in dimensions or other placeholder subject members. For example, if you want an organization hierarchy for a subset of people to rollup into a different organization first. You can make this new organization using this rule, and adjust the mappings so the subset of people rollup into this new organization instead.
Syntax
call createSubjectMember(
idProperty,
id [, otherPropertyAssignments, creationDate]
)
idProperty
: Key property of the subject.id
: ID of the created subject member.otherPropertyAssignments
(optional): Extra map of properties to assign in the newly-created member. The mapping format isMap[Property → Value]
.creationDate
(optional): A date to create the subject member. If undeclared it defaults to the earliest possible date in the data.
Examples
Say you wanted to insert a new member in the Organization_Hierarchy
subject. In this example, a new organizationAcme Corp
, with the ID value of -1
is created. If a different ID is desired then it can be supplied as an optional second argument.
call createSubjectMember(
Organization_ID, "-1",
{Organization_Name -> "Acme Corp"}
)
When the idProperty
value is not currently available you can insert an Unknown
parent-child dimension member. This inserts a new member in the Organization_Hierarchy
subject with the default Unknown
value.
call createSubjectMember(
Organization_ID, "-999",
{Organization_Name -> "Unknown",
Parent_Organization_ID -> "-1"}
)
Formula business rules
Formula business rules are created and defined using the Visier Business Rule Language (VBRL) and are written as a single expression that transforms records on a single entity’s event stream, processing events chronologically. Each rule consist of a trigger, optional conditions, and at least one action.
VBRL incorporates many standard programming functions, such as logical operators, comparisons operators, and string functions including case modification, concatenation, and substrings. Understanding these core functions is crucial for writing effective VBRL rules. For a detailed description of the Visier Business Rules Language syntax, seeVisier Business Rule Language (VBRL).
Trigger and conditions
Formula business rules are executed using a trigger, indicated by the when
tag. The trigger is what initiates the rule and is often tied to an event or a change in an attribute's value.
For example, if you want to perform an action based on whether an employee's first name has been changed at least once in their record you would include atLeastOnce
in your trigger. This function looks at the entire history of the event stream, not just the current state and returns true if the property has changed at least once, otherwise it returns false.
when atLeastOnce(changeIn(Employee.First_Name))
When writing your formula, you can also include conditions that do not directly trigger the rule, but must be met for it to proceed. These conditions might involve checking an attribute's current value, comparing a property to its previous value, or confirming that two attributes match.
Note: All events on the same timestamp are processed before trigger conditions are checked. The sort order of events with the same timestamp does not affect the rule's execution.
When you're establishing conditions, it's helpful to distinguish between filter conditions and conditional logic conditions. The key distinction here is whether the action changes based on the condition's outcome, or if no action is taken if the condition is false.
-
Filter conditions: These determine whether an action is taken or not. They are usually defined using
and
oror
. If a filter condition is false, the rule will not perform an action. -
Conditional logic conditions: These alter what action is taken, but do not prevent an action from occurring. They are usually defined using
if
andelse
.
Dates and timing
When forming your trigger and conditions you want to ensure you’re using the correct point in time. Understanding the nuances of currentValueOf
, previousValueOf
, and valueAsOf
is critical for your rule's accuracy.
-
currentValueOf(property)
: Returns the value of the property from the most recent event. -
previousValueOf(property)
: Returns the value of the property from before the most recent event. -
valueAsOf(property, dateObject)
: Returns the value of a property at a specific date.
When your trigger is a change in a particular attribute, use valueAsOf
for retrieving values of a property that did not change simultaneously with the trigger attribute. For example, to get the value of Employee.Job_Name
from the day before the trigger event, the condition would be written as follows:
valueAsOf(Employee.Job_Name, shiftDays(dateOf(current(changeIn(<trigger condition attribute>))), -1))
Common date functions
Effectively managing and manipulating dates is essential for creating time-sensitive business rules. VBRL provides a comprehensive set of date functions to define, format, and extract information from date values. Below are the most commonly used date functions. For a detailed description of all date functions available, see Date functions.
-
date(yearNumber, monthNumber, dayNumber)
: Returns a date object based on the three integer parameters. This is the best way to create constant dates, often used when defining cut-off points for logic. -
dateOf(current(changeIn(AttributeX)))
ordateOf(Regular_Event)
returns the date associated with an event. This can combine with thechangeInAny
function to get the date of the most recent change in any of the attributes mentioned. -
shiftDays(dateObject, numberOfDays)
: Returns a date shifted by a number of days. You can specify a positive or negative value for the number of days. There are equivalent versions for many other time granularities from milliseconds to years. -
daysBetween(dateValue, dateValue)
: Returns the number of complete days between two dates. This function returns a negative value if the first argument is less than the second argument. It's important to carefully consider the time granularity of the events themselves when building logic using these functions. -
getYear(dateObject)
: Returns the year from a date as an integer. -
firstOfYear(dateValue)
: Returns a date value shifted to the first day of the calendar year. -
formatDate(dateFormatString, dateValue)
: Returns a date string in the specified format such asyyyy-MM-dd
oryyyyMM
.
Defining actions
Actions are the last component of a business rule and determine what your rule does once its trigger and all additional conditions are met. A business rule can have more than one action. Primary actions include add
, update
, and delete
.
-
add
: Adds a new regular event or a profile change event by overwriting any previously existing profile change events. -
update
: Updates an existing event or creates a new event if one doesn’t exist for that timestamp. Keep in mind that by default,update
only affects one event per timestamp. To update multiple events on the same timestamp, useupdate all(filter)
for profile change events orforEach <event> update(filter)
for regular events. -
delete
: Deletes events. This is similar to update, where it typically only affects one event per timestamp unless you use theall
function.
Updating effective dates
To update the effective date (or event date) of a profile change event, set the effectiveDate
in addition to the attribute you’re modifying. If the intent is to move a profile change event from one date to another, the original change event must be deleted.
Update a regular event:
update current(Internal_Placement) (Internal_Placement.Placement_Type := "Promotion", effectiveDate := dateOf(changeIn(Employee.Career_Level)))
Change the date of a profile change event:
update First_Name := currentValueOf(First_Name), effectiveDate := shiftMonths(dateOf(current(changeIn(First_Name))), -1)
Move the profile change event from one date to another:
update First_Name := currentValueOf(First_Name), effectiveDate := shiftMonths(dateOf(current(changeIn(First_Name))), -1),
delete current(changeIn(First_Name))
Updating multiple events on the same timestamp
To update multiple events with the same timestamp, there are different options depending on the scenario.
-
Update all(filter)
ordelete all(filter)
: Affects all events in the timestamp matching the specified filter and applies the same function to each event. This is useful for deleting all events on a specific date, or updating all events with a single result regardless of the event’s attributes.
-
forEach
: Updates multiple regular events that occur at the same time while processing the specified rule independently for each event. This allows you to apply conditional logic or update an attribute based on the value of another attribute.
For example to update a compensation payout amount based on an employee’s FTE factor, the formula would be written as follows:
forEach Compensation_Payout update (Compensation_Payout.Payout_Amount := Compensation_Payout.Payout_Amount * Employee.FTE_Factor)
Each Compensation_Payout
event would have its individual payout amount adjusted by the FTE factor, unlike update all(filter)
, which would apply the same adjustment to all events based on the last event encountered.
You can also incorporate the while
function to add filters or conditional logic specific to the events themselves. This is a variant of the above example that behaves differently based on the Payout_Type
event.
`forEach Compensation_Payout update while(Compensation_Payout.Payout_Type = "Overtime")(Compensation_Payout.Payout_Amount := Compensation_Payout.Payout_Amount * 1.5),` `forEach Compensation_Payout update while(Compensation_Payout.Payout_Type = "Hourly")(Compensation_Payout.Payout_Amount := Compensation_Payout.Payout_Amount * 8),` `forEach Compensation_Payout update while(Compensation_Payout.Payout_Type = "Salary")(Compensation_Payout.Payout_Amount := Compensation_Payout.Payout_Amount * Employee.FTE_Factor)`
Logic based on the attributes of the regular event must be within the while
function, however logic based on attributes belonging to the subject entity can be placed directly within the assignment.
Updating attributes on different dates
If you need to update attributes on different dates, use multiple update
statements, each with its own effectiveDate
assignment.
when changeIn(Employee.First_Name)
update Employee.Last_Name := "Smith", effectiveDate := shiftDays(dateOf(current(changeIn(Employee.First_Name))),1),
update Employee.Job_Name := "Developer", effectiveDate := dateOf(first(changeIn(Employee.EmployeeID)))
This rule triggers when a ProfileChangeEvent onFirst_Name
is seen.
It updates Last_Name
to Smith
the day after the First_Name
change and sets Job_Name
to Developer
as of the date of the first change in EmployeeID
.
Conditional logic and actions
A business rule can include multiple actions, however if you want to apply multiple actions based on conditional logic you have to nest the if statement within the :=
assignment, which sets the value of the attribute on the left to the result of the expression on the right.
For example, to update an attribute differently based on a condition, the formula would be written as follows:
when changeIn(Employee.First_Name) update Employee.Last_Name := if (Employee.First_Name = "Bob") "Smith" else "Farmer", update Employee.Gender := "Male"
This rule triggers when the first name changes. It updates the Employee.Gender
to Male
and sets Last_Name
to Smith if First_Name
is Bob
, otherwise it sets Last_Name
to Farmer
. If you only wanted to change Last_Name
if First_Name
is Bob
, the else
statement would be else currentValueOf(Employee.Last_Name)
which adds a profile change event setting the last name to its current value.
For conditional logic involving multiple attributes or options, the match
syntax offers a compact alternative to nested if/else statements. It lets you perform multiple actions when values meet the specified criteria.
Note: The match
syntax can only execute a single action statement, for example, updating multiple attributes in a single statement is possible but deleting changes simultaneously is not.
when changeInAny({Employee.Job_Name,Employee.Job_Family}) match(currentValueOf(Employee.Job_Family) + "_" + currentValueOf(Employee.Job_Name)) {
"Builder_Developer I" -> update Employee.FTE_Factor := 1.0, Employee.Exempt := "Exempt",
"Supporter_Accountant I" -> update Employee.Exempt := "Non Exempt",
else -> update Employee.Exempt := "Unknown", Employee.Job_Group := "Unknown"}
This rule triggers when Employee.Job_Name
or Employee.Job_Family
changes. It then combines the current values of Job_Family
and Job_Name
into a single string, for example, Builder_Developer I
or Supporter_Accountant I
and updates different attributes depending on which case is matched.
Formula business rules examples
Create internal placement events
You may have a scenario where you want to add events based on specific conditions.
In the example below, the rule activates when Career_Level
, Organization_ID
, or both change. It then uses the current and previous day's values to determine the attributes for the new internal placement event.
when changeInAny({Employee.Career_Level,Employee.Organization_ID})
add Internal_Placement(
Internal_Placement.EmployeeID := currentValueOf(Employee.EmployeeID),
Internal_Placement.Placement_Type := if(currentValueOf(Employee.Career_Level) > valueAsOf(Employee.Career_Level,shiftDays(dateOf(changeInAny({Employee.Career_Level,Employee.Organization_ID})), -1)) "Promotion"
else if(currentValueOf(Employee.Career_Level) < valueAsOf(Employee.Career_Level,shiftDays(dateOf(changeInAny({Employee.Career_Level,Employee.Organization_ID})), -1)) "Demotion"
else "Lateral",
Internal_Placement.Old_Organization_ID := valueAsOf(Employee.Organization_ID,shiftDays(dateOf(changeInAny({Employee.Career_Level,Employee.Organization_ID})), -1)),
Internal_Placement.New_Organization_ID := currentValueOf(Employee.Organization_ID)
Keeping the first or last value
You may have a scenario where you only want to keep the first or last value of an attribute or event.
This rule will change First_Name
to its original value every time it changes.
when changeIn(Employee.First_Name) update Employee.First_Name := firstValueOf(Employee.First_Name).
This rule will keep only the first Hire
event.
when Hire and current(Hire) != first(Hire) delete current(Hire)
Moving date attribute changes to the value
Often, when a date attribute is updated, you want the change to reflect as if it occurred on the new date itself, rather than the date the change event was recorded.
In the example below, the rule moves all changes in Tenure_Start_Date
to occur on the date being set. It does this by updating the attribute to its current value with the new effective date, then deleting the original profile change event.
when changeIn(Employee.Tenure_Start_Date)
update
Employee.Tenure_Start_Date := currentValueOf(Employee.Tenure_Start_Date),
effectiveDate := currentValueOf(Employee.Tenure_Start_Date),
delete current(changeIn(Employee.Tenure_Start_Date))