This page lists a collection of function recipes you can use throughout the Appian application. Similar to a cookbook, it shows the individual ingredients that go into creating an expression using Appian functions, the order in which to combine them, and tips on when to use them or modify for preference.
The expressions listed below are not the only way to accomplish each desired outcome. This page is intended to teach you methods for creating expressions using Appian functions that are practical and efficient, as well as provide working expressions you can implement as is. Just as with cooking recipes, you may want to alter the values in each recipe to accommodate your specific requirements.
The function recipes are categorized by the type of output. Each recipe is titled with the question in mind, "What do you want to do?"
Note: For function recipes that require a rule as an Ingredient, create the rule as listed before implementing the function recipe.
You want to retrieve next year's anniversary date based on a start date, such as an employee's hire date of 02/05/2011.
1
2
3
4
5
if(
and(month(ri!start) <= month(today()),day(ri!start) <= day(today())),
date(1 + year(today()), month(ri!start), day(ri!start)),
date(year(today()), month(ri!start), day(ri!start))
)
You want to set a timer to start exactly one minute after a process starts, as compared to automatically.
Configure this in the "Delay until the date and time specified by this expression" on the Setup tab for the Timer Event.
pp!start_time + minute(1)
Note: To change the time difference from 1 minute to more, modify the value in the minute()
function as desired.
You want to add ten minutes to a datetime value saved as pv!datetime.
pv!datetime + intervalds(0,10,0)
Note: To change interval added to the datetime value, modify the values in intervalds()
as desired.
Users are able to enter any date-time value, but you want to perform an extra validation after users submit the form to determine if a date-time value (saved as ri!dt) falls between your company's working hours of 6:00 AM and 5:00 PM.
The idea is to work with one consistent time scale, which is done here by the use of timestamps rather than both hours and minutes. This function constructs new timestamps for the boundaries matching the input.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
or(
ri!dt > gmt(
datetime(
year(ri!dt),
month(ri!dt),
day(ri!dt),
17,
0,
0
)
),
ri!dt < gmt(
datetime(
year(ri!dt),
month(ri!dt),
day(ri!dt),
6,
0,
0
)
)
)
Note: To change the working hours, modify the values within the datetime()
function as desired
You want to tell users how many business days are left before a deal closes, if the deal has already closed, or if the deal is closing today.
1
2
3
4
5
6
7
8
9
if(
networkdays(today(), ri!closeDate)<=0,
"This deal has closed.",
if(
networkdays(today(), ri!closeDate)=1,
"This deal will close at the end of today.",
"This deal will close in" & networkdays(today(), ri!closeDate) & "business days."
)
)
Note: To change the text that displays, modify the text as desired. To base the number of days on a system calendar, replace any instance of networkdays(today(), ri!closeDate)
with calworkdays(today(), ri!closeDate, "companyCalendar")
where companyCalendar
is the name of your system calendar. To include all days (including weekends), replace any instance of networkdays(today(), ri!closeDate)
with tointeger(ri!closeDate - today())
.
See also: calworkdays() and tointeger()
You have a localized XML string representing a date and time and need to convert it to a value of type Datetime in the GMT timezone.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
=a!localVariables(
local!fullArray: split(ri!dateText, "T"),
local!dateArray: split(local!fullArray[1], "-"),
local!timeArray: split(left(local!fullArray[2], 12), ":"),
local!smsArray: split(local!timeArray[3], "."),
local!timeZone: "GMT" & right(local!fullArray[2], 6),
local!rawDate: datetime(
local!dateArray[1],
local!dateArray[2],
local!dateArray[3],
local!timeArray[1],
local!timeArray[2],
local!smsArray[1],
local!smsArray[2]
),
gmt(
local!rawDate,
local!timeZone
)
)
yields 9/25/2013 1:50 AM GMT+00:00
where ri!dateText
= 2013-09-24T19:50:24.192-06:00
You want to quickly display how many employee records exist in your application.
myQueryRule(topaginginfo(1,0)).totalCount
Note: When using a batchsize of 0 and attempting to only retrieve a total count based on a query, the function detailed above is better to use than count(myQueryRule())
. If you used count(myQueryRule())
, the system would run the main query; whereas using .totalcount
, the system only executes a count query against the database.
You want to show the number of times a document has had a new version saved.
document(ri!doc,"totalNumberOfVersions")-1)
You want to return a random integer between two integers, inclusive.
ri!min + tointeger(rand() * (ri!max - ri!min))
You want to truncate the remaining part of a text value once it surpasses 50 characters by replacing the excess text with an ellipsis (…).
1
2
3
4
5
if(
len(ri!text) > 50,
left(ri!text, 50) & "...",
ri!text
)
Note: To change the amount of characters allowed before truncating, modify the numeric value as desired.
You want to display the full name of a user despite knowing only the username. Additionally, the field that is storing user information may not always contain a value.
Tip: If the User record type has data sync enabled, you can reference the firstAndLastName
record field instead of building this expression.
1
2
3
4
5
if(
isnull(ri!user),
"",
user(ri!user, "firstName") & " " & user(ri!user, "lastName")
)
Note: To change the user information that is populated, modify the second parameter in the user()
function as desired.
You want to convert a text value to display as a title, which will capitalize the first word, and all words not found in a "no capitalize" list.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
a!localVariables(
/* Breaks up the title into an array of words and removes whitespaces. */
local!titleArray: split(trim(ri!title), " "),
/* A list of words that will be ignored. Adjust this list to change what does not get capitalized. */
local!noCaps: {"a","an","the","at","by","in","of","up","as","and","but","or","for","nor","etc.","etc","on","at","to","from", "that"},
/* If the word is in local!noCaps but not the first word, don't capitalize. Otherwise capitalize the word. */
concat(
a!forEach(
items: local!titleArray,
expression: if(
and(not(fv!isFirst), contains(local!noCaps, fv!item)),
fv!item,
proper(fv!item)
) & if(fv!isLast, "", " ")
)
)
)
Note: To produce initial caps for your own text, modify the text value as desired.
You want to remove all values in an array so it is considered empty.
ldrop(ri!array, count(ri!array))
Note: Instead of the word array, enter the name or array reference as needed.
You want to repeat each item in an array (for example, local!textList
) a certain number of times as defined by the values in a different array (for example, local!frequencyList
).
local!textList = {"a", "b", "c"}
and local!frequencyList = {2, 3, 1}
, the output would be {"a", "a", "b", "b", "b", "c"}
.1
2
3
4
5
6
7
8
9
10
11
load(
local!textList: {"a","b","c"},
local!frequencyList:{2,3,1},
a!forEach(
items:local!textList,
expression: repeat(
local!frequencyList[fv!index],
fv!item
)
)
)
Note: To change which variables to use, modify the textList value to the array variable that should determine the text to repeat and modify the frequencyList value to the array variable that should determine how many times each text value in the aforementioned array should be repeated.
You want to extract the list of data written to an entity (for example, Opportunity) based on the output generated by using the Write to Multiple Data Store Entities Smart Service to write to three entities: Opportunity, Contact, and Line Item.
See also: Write to Multiple Data Store Entities Smart Service
The explanation of the configuration is longer than the solution. If you configure the Write to Multiple Data Store Entities Smart Service to update data for three entities (Opportunity, Contact, and Line Item), each entity may show up more than once in the EntityData input array. Consequently, the output of the smart service ("Stored Values") would contain a dynamic number of elements for each EntityData.
To get the list of all Opportunities that were updated by the smart service, you need to append every Data field where the entity is equal to Opportunity.
entityData
is the array to operate on and entity
is the context parameter.Within the expression editor of a new custom output:
1
2
3
4
5
6
7
8
a!forEach(
items: merge(ac!StoredValues, cons!OPPORTUNITY_ENTITY),
expression: if(
fv!item[1].entity = fv!item[2],
fv!item.Data,
{}
)
)
Note: To change the entity by which to pull the list of data from, modify the OPPORTUNITY_ENTITY value as desired.
You want to sort an array of values.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
todatasubset(
a!forEach(
ri!array,
{
value: fv!item
}
),
a!pagingInfo(
startIndex: 1,
batchSize: -1,
sort: a!sortInfo(
field:"value",
ascending: true
)
)
).data.value
You want to see if any of the items in an array are contained within another array.
1
2
3
4
5
6
or(
a!forEach(
items:ri!originalArray,
expression: contains(ri!compareArray, fv!item)
)
)
You have an array of department CDTs, each containing a field called Id
, and you want to retrieve the CDT with an id
value matching the value of a process variable.
ri!departmentid
(Integer)ri!departments
(CDT Array)Custom Data Type:
1
2
3
department (Text)
|- id (Integer)
|- address (Text)
displayvalue(ri!departmentId, ri!departments.id, ri!departments, "none")
Note: To change the value that displays if the ri!departmentId
doesn't match any ri!department.id
values, modify the "none" text value as desired.
Function Recipes