Expression language is a powerful special language syntax used for dynamically injecting, substituting, building, querying or manipulating data during the execution of events, workflows, forms and any other place in the application that can incorporate logic.


It can be used for a wide variety of purposes in Blue Relay, ranging from the simple use case of obtaining the title of a file/folder to the more advanced use case of using item/user attributes to query a dataset, finding a specific/list of values and then subsequently use the results to further perform another action.


Basic Syntax and Structure


To demonstrate the basic syntax and structure of expression languages, let's consider the following expression to access the title of a newly created file within an event with the "On File Create" trigger:

(( #file.title ))


Whenever writing an expression language (with the exception of a couple of places), it must always be enclosed with preceding and succeeding double parenthesis. This tells the system that the text within should be evaluated as an expression language and not interpreted as regular text. 


The # prefix is a special notation used to indicated the attached preceding text is a variable/object/data with potentially various properties and functions. In the example above, #file is used to access the newly created/uploaded file, which has many properties including the title. So the full expression #file.title is used to access the title of the new file.


 Available Data within Expression Languages


Depending on where and the context of the written expression language, they are different data available and accessible for use within your written expression. The type of data available can essentially be broken down into three categories: "always available" data, "helper/utility" data and finally situational/contextual data. 



Always Available Data


Regardless of where you specific an expression language, the user and company objects will always be available. Both are contextual in the sense that they will always be the user (in some cases "system") and company that resulted in the execution of the expression. 


For example, if there was an event with an "On File Create" trigger and User A uploaded a new file in the system, then the expression #user.name would evaluate to User A's full name and if another User B uploaded a file then #user.name would evaluate to their full name and so on for different users. 


This always available contextual data is great for if you need to action on specific Users performing specific actions. The same also applies to the company object which may not be useful if there's only one company within the system but very useful if there's sub-companies involved.


(( #user.propery ))

where some common useful properties are: id, name, firstName, lastName, defaultEmail, username, activeTasks


(( #company.property ))

where some common useful properties are: id, name, shortName



Available Helper/Utility Functions


This category of data almost exactly the same as the previous category with the exception that it is not contextual but it instead provides helper and utility functions to perform specific operations within expression languages. These operations can range from searching for an item to sorting a list. 


There are currently three utility functions: ExpressionUtil (#expr), ListUtil (#list) and MapUtil (#map)


- ExpressionUtil: provides a set of functions for a variety of purposes. There are functions for performing data set lookups, file/folder solr searches (#expr.search), finding items by path (#expr.findByPath), user/group searches (#expr.entitySearch), and converting a string to time (#expr.stringToTime) to name a few common ones. Refer to the Common and advanced section below for detailed examples and uses.


- ListUtil: provides a set of functions for operating on a set of objects within expression language. For example suppose you had obtained a list of files within a folder (via the expression #folder.items) and you wanted to get the list of titles of all those files. To achieve that, you could leverage the function explodeProperty (#list.explodeProperty) to obtain such a list (i.e #list.explodeProperty( #folder.items, 'title' ) ). Refer to the Common and advanced section below for detailed examples and uses.


- MapUtil: provides a set of functions for operating on a mapping of data. Usage of this is less common but not unrare. Refer to the Common and advanced section below for detailed examples and uses. 



Situational/Contextual Data


This is data only available in the context in which the expression language is written in. As stated previously, expression language can be used within workflows, tasks (actions) , forms, events (triggers and actions)


- Tasks/Workflows: task and item and option. Also collection criteria

- Events: depends on the type of trigger

- Can dynamically search and inject datasets, items, solr search, tasks

- form question rules




Some example expressions, their meanings and common use cases:


Expression

Meaning

Common Uses

#item.title

The title of the item

Everywhere. Item is typically either the file or folder.

#item.ancestors[0]
#item.ancestors[1]
#item.ancestors[0].title

If item is a file, [0] is stub, [1] is folder.

If item is a folder, [0] is parent folder

If item is a form of a file, [0] is file, [1] is stub and [2] is parent folder

If item is a form of a folder, [0] is parent folder.

Basically just need to account for stub if a file is involved.

Lots of places. If the event is on a file, but you want to get the folder.

#item.creator.username

The creator of the item

For sending notifications to the creator of the file/folder.

#answer.display

#answer.content

The user-friendly form of answer to a form question.

The “raw” form answer saved in the DB 

With OnQuestionAnswer triggers. Useful to do something with the answer someone just made on a form.

#item.attributes.get('Attribute Name').value

The value of the attribute “Attribute Name”

Lots of places.

#item.hasSystemAttribute(“Attribute Name)
#item.getSystemAttributeValue(“Attribute Name”)

A boolean of whether or not a system attribute Attribute Name exists.
The actual value in a null safe fetch.

For system processing. System attributes are different than attributes in that they’re not displayed on the UI. These are convenience methods.

#item.title.split("_")[2]
#item.title.split("_")[2].substring(0,4)
#item.title.replaceAll(“X”, “Y”)

String functions that can be used for further parsing/processing.

Parsing system generated names like filenames and using it to extract meaningful content to rename a file or save an attribute.

#item.title == ‘ABC’ ? ‘Yes’ : ‘No’

A ternary statement. If the title is ABC, the value Yes is returned. Otherwise, No is returned.

Becoming more common. Just provides some conditional logic when needed.

#form.questions[10].answers[0].display

Gets the value of question #11 (zero-based index) on a single dimensional form. Multi dimensional forms don’t have as clean a way to operate on at the moment.

With OnFormSubmit triggers. Useful to pull multiple questions in a single event when the form is submitted.

#user.username
#user.name

#user.id
#company.name

Some session specific data. Note that some events occur in the background by the system (e.g. Local File Create), but most will have this data

To use/store the current user for whatever need.

#list.findBy( #item.items, ‘status’, ‘New’ )
#list.findUniqueBy( #item.items, ‘status’, ‘New’ )

Finds one or all the child items that have a status of New

To pick a file/folder for the user to process based on status.

#list.implode( #list.explodeProperty( #list.reverse( #item.ancestors ), 'title'), '/' )

Return item’s file path

Doesn’t work. See below row.

((#list.implode(#list.explodeProperty( #list.reverseList(#item.ancestors), 'title' ), '/')))

Save above to Context Variable then use the below to remove “Home/” and filename from path:

((#path_long.substring(5, #path_long.length()-#filename.length())))

“list.reverse” doesn’t work.  Created new function “list.reverseList” to make it work. 

Used by Advantasure

(( #form.questions[11].answers[0].display != null && #form.questions[11].answers[0].displayValues.?[#this == 'Current Members']?.size() == #form.questions[11].answers[0].displayValues?.size() ))

This format is for lists with mutli-select. The expressions says - the form question with ordinal 12 is not null and the selected answer is only 'Current Members'. Please note - it means Current Members is the only selected option and does not mean that “Current Members“ is one of the selected options.

Since there can be multiple possible selections, we cannot do answer[0].matches….. format. That’s why #form.questions[11].answers[0].displayValues returns the list of all the selected options for that question. Then displayValues.?[#this == 'Current Members']?.size() here #this is used to represent each selected option (similar to a foreach loop). Then we make the comparison and get the length of the array.

Then using this #form.questions[11].answers[0].displayValues.?[#this == 'Current Members']?.size() == #form.questions[11].answers[0].displayValues?.size() we can make sure that “Current Members“ is the only option selected by comparing the size on both the sides.

Used by BCBSM for creating attributes and also at other places wherever a mutli-select list is involved.

(( new java.text.SimpleDateFormat("yyyy-MM-dd").format(T(org.apache.commons.lang.time.DateUtils).addDays(new java.text.SimpleDateFormat("yyyy-MM-dd").parse(#item.attributes.get('Usage End Date')), -60)) ))

Date Manipulation. The example results in a date = #item.attributes.get('Usage End Date') - 60 days

addDays can be replaced with addMonths, addYears, etc

(( new java.text.SimpleDateFormat("yyyy-MM-dd").format( new java.util.Date() ) ))

((#expr.dateFormat( new java.util.Date(), 'yyyy-MM-dd HH:mm:ssZ' ) ))

SPEL expression to evaluate to current date

Adding an attribute with a value set to current 

(( #item.attributes != null && #item.attributes.get('Complete Refreshes Counter').value != null && ? new Integer(#item.attributes.get('Complete Refreshes Counter').value) + 1 : 1 ))

Terniary Operator inside expression

(( new Integer(#item.attributes.get('Counter').value) ))

Parse String as Integer

/api/dataSet/101/column/Group or Individual/values?unique=true&Plan={{form.questions[1].answers[0].content | urlEncode}}

This is the string that can be used as a “Source” on a Form Question.

The given example returns all the unique values for the column “Group or Individual“ from dataset id 101 where the column “Plan“ = answer of the second question of the form.

Form Question Source field.

#item.attributes.get('Plan and Type').value.match('(Anthem-Out-of-Area Materials|Arkansas-Out-of-Area Materials|BCBSM & BCNA Joint-Provider Communication \(Part C\)|BCBSM & BCNA Joint-Provider Communication \(Parts B & D\))')

FORM QUESTION VISIBILTY CONDITION

This checks if the attribute “Plan and Type“ is either of those values separated by the pipe simple.

Form Question Visiblity/Required conditions

#expr.entitySearch( 'NAME|ID', '#entities' )

Search for a user or group by either the name or the ID. The result can be further filtered by applying an additional condition to the second parameter

#expr.all( list, 'condition' )

#expr.any( list, 'condition' )

#expr.none( list, 'condition' )

#expr.count( list, 'condition' )

Helper functions to check if a list of items satisfy a condition. Returns boolean

Checking if all items in a folder have the same status

or if any or none do

#expr.search( ‘searchTerm', ‘queryField' )

#expr.find( String id )

#expr.findByPath( ‘path’ )

#expr.findByPath( ‘path’, true )

Programmatic solr query search

Get an item by id

Find item by path in BR

Try to find item by path in BR and/or create item and path if non-existent 

#expr.isMemberOf('Developers')

Returns true if the current user logged in is a member of the listed groups, in this case the group “Developers”. False otherwise.

Will also return true if the user is not directly a member of Developers, but is a member of a sub-team of Developers. 

Will also return true if the user is not directly a member of Developers, but is a backup for another user who is directly a member of Developers.

BCBSM, hide Question 4 of the Request Form from users who are not Developers. 

((#item.getSystemAttributeValue('Developer')))

This is found on most reassignments in BCBSM in the “To” field. Useful to know.

((#statusCode))

((#result))

Web Service event action error details.

Web Service event actions have the option to call downstream events on Success and on Error.

In the downstream event, you can access the error message using these expressions

TODO: add example expression for adding a condition to event actions.