Elements Connect

How to write conditional queries with Apache FreeMarker

Elements Connect for Jira Cloud supports Apache FreeMarker for dynamic and conditional query construction. FreeMarker is a powerful templating engine that lets Jira admins write queries that adapt to data conditions, for example JQL queries that change based on issue priority, user input, custom field values, or the current user. This guide covers the FreeMarker syntax you can use in Elements Connect Cloud, with practical examples for both connected custom fields and connected items.

FreeMarker language is available for database and REST API data sources:

  • For Custom Fields:

    • in field query/path

    • in field request body (for REST API with POST method)

    • in path location (JSONPath)

    • in field template

  • For Connected Items:

    • in field query/path

    • in field template

FreeMarker basic syntax

This section is a subset of what you can do with FreeMarker.
For a full guide, see the official Apache FreeMarker Manual.

Directives

You use FTL tags to call directives. There are two kind of FTL tags:

  • Start-tag: <#directivename parameters>

  • End-tag: </#directivename>

This is similar to HTML or XML syntax, except that the tag name starts with #.

Comment

Similar to HTML comments, but they are delimited by <#-- and -->. They won't get into the output because FreeMarker skips them.

<#-- This is a comment -->
This is not a comment

Set a variable

Use <#assign> to define and use variables in your templates.

<#assign variableName = "value"> 

Access a variable

Use ${x} to define and use variables in your templates.

<#assign variableName = "value"> 
${variableName}

Define conditions

Use #if, #elseif, and #else for conditional logic.

<#if condition>
  // Code if condition is true
<#elseif otherCondition>
  // Code if otherCondition is true
<#else>
  // Code if no conditions are met
</#if>

Loops
Use <#list> to iterate over lists.

<#list list as item>
  ${item}
</#list>

Expressions: built-in references & operations

Built-ins are like methods that are added to the objects by FreeMarker. To prevent name clashes with actual methods and other sub-variables, instead of dot (.), you separate them from the parent object with question mark (?).

Empty check method (for sequence)

Use seq?size != 0 to check if a variable exists and is not null or empty.

<#assign user = "" />
<#if user?size != 0>
 /search?jql=order by created DESC

Join (for sequence)

 

  <#assign projects = issue.customfield_10074?join(",")>
  /search?jql=project IN (${projects})

URL Encoding (for string)

Use ${stringVariable?url}

to encode a string for use in a URL.

<#assign searchTerm = "elements connect">
${searchTerm?url}  <!-- Output: elements%20connect -->

Check on value (for sequence)
Use ${seq?seq_contains("searchedValue")?string("resultIfTrue", "resultIfFalse")} to check if the sequence contains a specified value

<#assign x = ["red", 16, "blue", "cyan"]>
"blue": ${x?seq_contains("blue")?string("yes", "no")}
"yellow": ${x?seq_contains("yellow")?string("yes", "no")}
16: ${x?seq_contains(16)?string("yes", "no")}
"16": ${x?seq_contains("16")?string("yes", "no")}

Some other expressions that might come useful:

Arithmetic Operations
Use +, -, *, / to make basic arithmetic operations on numeric variables.


${a * b}  <!-- Output: 50 -->
${a / b}  <!-- Output: 2 -->

Logical Operations

Just the usual logical operators:

  • Logical or: ||

  • Logical and: &&

  • Logical not: !

The operators will work with boolean values only. Otherwise an error will abort the template processing.

<#if x < 12 && color == "green">
  We have less than 12 things, and they are green.
</#if>
<#if !hot> <#-- here hot must be a boolean -->
  It's not hot.
</#if>

Comparison

Use == to test two values for equality.

To test two values for inequality you use !=.

<#if user == "Big Joe">
  It is Big Joe
</#if>
<#if user != "Big Joe">
  It is not Big Joe
</#if>

Concatenation

Use + to concatenate sequences in the same way as strings

<#list ["Joe", "Fred"] + ["Julia", "Kate"] as user>
- ${user}
</#list>

Variables

Most of the variables that a typical template works with come from the data model. However, templates can also define their own variables—usually to hold loop counters, temporary results, macros, etc.

Issue variables
Using Connected items

The Issue variable is data that acts like a JSON. It stores other variables (the so called sub variables) by a lookup name (e.g., "priority", "reporter" or "key").
When using FreeMarker, no need to use '$' to access Connect variables, like issue. Connect variables are available like any FreeMarker variable.

What you can retrieve from the issue:

issue.summary

issue.type

issue.priority

issue.assignee.emailAddress

issue.assignee.accountId

issue.reporter.emailAddress

issue.reporter.accountId

issue.customfield_XXXXX

 

  • Basic check to determine if a field is empty:

    <#if issue.customfield_XXXXX?has_content>
      /project/${issue.customfield_XXXXX}
    <#else>
      /project
    </#if>
    
  • Advanced check, in case the field to be checked is an Object:

    <#if issue?keys?seq_contains("customfield_XXXXX")>
      /project/${issue.customfield_XXXXX.id}
    <#else>
      /project
    </#if>
    

⚠️ This advanced check is only relevant when configuring connected items queries. For custom fields, you can only use the simpler has_content() function. See below for more information.

Issue Variables
Using Connect Custom Fields

As with Connected Items, the Issue variable holds data in a JSON-like structure.

However, when you work with Connect custom fields, you can access all variables returned by the JIRA Issue API.

To explore the available data, you can view the JSON content returned by the API by calling https://<instance>.atlassian.net/rest/api/3/issue/<ISSUE-KEY>.

image-20250703-094843.pngExample of a Jira issue JSON response from the Jira REST API, showing the fields accessible via FreeMarker variables in Elements Connect

To access a field’s attributes, simply omit the “fields” prefix.

issue.key

issue.id

issue.customfield_12133

issue.statusCategory.name

🤝 Standard Connect variables detailed in this documentation are still available.


If an issue has an ID (i.e., it’s already created), we retrieve the watcher; otherwise, we retrieve the users.

<#if issue.key?has_content && (issue.watches.watchCount > 0)>
  /issue/${issue.key}/watchers
<#else>
  /users
</#if>

If the issue has components, we retrieve the first one; otherwise, we retrieve all the project components.

<#if (issue.components?size > 0)>
  /component/${issue.components[0].id}
<#else>
  /project/${issue.project.key}/components
</#if>

If an issue has a fix version, we retrieve the number of issues with that same fix version; otherwise, we retrieve the number of issues using the default fix version ID.

<#assign fixVersionId="10001">
<#if issue.fixVersions?size != 0>
  <#assign fixVersionId = issue.fixVersions[0].id>
</#if>
/version/${fixVersionId}/relatedIssueCounts

info These examples are for illustration only and may not reflect real-world use cases.

Project variables

project.key

project.id

project.name

project.type

project.category

<#if project.key == "CC">
  /search?jql=project=CC
<#else>
  /search?jql=project!=CC
</#if>

Other variables

Many other variables can be used in FreeMarker templates.

currentUser variables

request context variables

<#if currentUser.emailAddress == "johndoe@example.com">
  /search?jql=project IN (SUP, KAN)
<#else>
  /search?jql=project=CS
</#if>

 

User Input

 

<#if userInput?has_content>
  /search?jql=summary ~ "${userInput}"
<#else>
  /search?jql=ORDER BY created DESC
</#if>

Set/replace a variable

Use <#assign> to define and use variables in your templates.

<#assign variableName = "value"> 
${variableName}

 Example of Freemarker usage for field query

FreeMarker is used to create dynamic queries by allowing you to embed conditional logic and data-driven elements into your queries. This enables more flexible and powerful interactions with external data sources.

Here are some examples of usage in your field path with a Jira REST API data source.

  • [if/else] Dynamic JQL Query Based on Priority

<#if $issue.priority == "High"> /search?jql=priority=high 
<#elseif $issue.priority == "Low"> /search?jql=priority=low 
<#else> /search?jql=priority=medium 
</#if> 
  • [?join][?size][if/else] Query Excluding Projects

Let’s assume customfield_10074 is a multiple value project picker fields, containing a list of project keys:

"customfield_10074": [
"PJT1",
"PJT2"
],

Then, the query to retrieve all issues except those related to previously selected projects could be:

<#if issue.customfield_10074?size != 0>
  <#assign projects = issue.customfield_10074?join(",")>
  /search?jql=project NOT IN (${projects})
<#else>
  /search?jql=order by created DESC
</#if>

 → The ?join(",") built-in is mandatory here to transform the array into a string for the JQL.

  • [assign][?split][?map][?join] Retrieve all ticket potentially linked to same topic

<#-- Extract keywords from the summary by splitting on spaces -->
<#assign keywords = issue.summary?split(" ")>
<#-- Join keywords with OR for a flexible JQL query -->
<#assign keywordQuery = keywords?map(word -> "summary ~ \"" + word + "\"")?join(" OR ")>
/search?jql=${keywordQuery} ORDER BY created DESC

That’s what this query could look like in a read-only custom field:

Read-only Elements Connect custom field displaying related Jira issues retrieved with a FreeMarker keyword extraction query on the issue summary

Example of Freemarker usage for field template

FreeMarker can also be used to control conditional display and format how data is shown within Elements Connect templates.

  • [if/else] Dynamic template based on data source payload

<#if row.id??>
${row.name} (${row.id})
<#else> 
${row.name} 
</#if> 
  • [list] Iteration on a list

<#list data as d>
${d.key}-${d.name}<#if d_has_next>, </#if>
</#list>

Result with a data source returning a list of Jira project:

FIN-Finance project, ITSM-IT Support Project, SOF-My SOFT Agency

  • Date formating

<#assign currentDay = .now?string("EEEE, MMMM d yyyy, HH'h'mm")>
${currentDay}

Best Practices

  • Ensure your FreeMarker expressions are correctly validated with the query tester to avoid runtime errors (query only)

  • Optimize your queries and templates for performance, especially with large datasets.

  • For complex requests, don’t hesitate to use comments to facilitate maintenance 

Usage limitations

  • Number of characters in the query is limited to 5000.

Additional Resources

Frequently asked questions

Can i used FreeMarker in both queries and field templates?

Yes. FreeMarker is supported in field queries/paths, field request bodies (POST methods), JSON paths and field templates, for both connected custom fields and connected items

How do I check if a Jira issue custom field is empty in FreeMarker?

For custom fields, use `<#if issue.customfield_XXXXX?has_content>`. For connected items where the field can be an Object, use `<#if issue?keys?seq_contains("customfield_XXXXX")>`.

Can FreeMarker access data from external systems like Salesforce or Azure AD?

No. FreeMarker operates on the Jira context (issue, project, current user, user input). External data is retrieved by the data source itself, then displayed using FreeMarker in the field template.

Does Elements Connect support all FreeMarker features?

Most core FreeMarker features (directives, built-ins, expressions) are supported. For the full language reference, see the [Apache FreeMarker Manual](https://freemarker.apache.org/docs/index.html ).