Skip to content

FreeMarker template language


FreeMarker is the template language used with Funnelback's modern UI. FreeMarker is a generic template engine popular in the Java world (although you don't need to know Java to write FreeMarker templates).

Its purpose is to generate a HTML output by merging the data model with a search template. Funnelback will take care of generating a relevant data model based on your query terms and collection and merge it with the template of your choice. All you need to do is write a template to display the search results.

FreeMarker template file ends with the .ftl extension (stands for FreeMarker Template Library). A default template is provided when you create a collection, named simple.ftl. It's usually a good starting point for customisation.

A FreeMarker template contains 3 element types, in addition to HTML code:

  • ${...} statements that will get replaced by data model values (e.g. ${result.title})
  • FreeMarker tags, either core directives like <#if [condition]> ... </#if> or custom tags like <@s.AfterSearchOnly> ... </@s.AfterSearchOnly>.
  • FreeMarker comments: <#-- ... -->

Data model variables

The ${...} syntax allows you to access and display content from the data model. You must use the full path of the data model node you want to display, for example:

Searching for ${question.query} on collection ${}
${response.resultPacket.resultsSummary.totalMatching} results found.

HTTP request information

FreeMarker templates also have access to a special variable httpRequest which is not part of the data model but contains useful information about the HTTP request. This variable is of type HttpServletRequest and its fields can be accessed like other data model nodes, e.g. request.requestURL.

Freemarker built-in functions

Freemarker has an extensive set of built-in functions that can be called when a value is printed. A built-in function is called by appending a question mark (?) to the variable name and adding the function. E.g. ${s.result.title?upper_case} prints the result title as an uppercase string. Function calls can also be chained. ${s.result.title?upper_case?html} will convert the title to upper case then HTML encode it.

The built-in functions provide a rich set of tools that can be used to manipulate or transform the variables allowing a huge amount of flexibility to be built into the search result templates.


HTML escaped query: ${question.query?html}
Query transformation: ${question.query?replace("cat", "dog")}

The most commonly used built-in is !. It allows you to specify a default value if the value you're looking for is not defined: ${question.query!"No query"}. This will default to "No query" if query is not defined. Note that if question is not defined the template rendering will fail with an error. To use the default value built-in over the whole expression you must use parenthesis: ${(question.query)!"No query"}. This way if question or query is undefined, the default value will be used instead.

See: Freemarker built-in functions reference

Variable escaping

Freemarker has built-in support for the automatic escaping of variables that are being represented as HTML, JSON or URLs. When one of these functions is called the relevant special characters will be automatically escaped by Freemarker.

This can be accessed using Freemarker's ?html, ?json_string and ?url string built-in functions.

Escaping can be applied to a complete template by using the <#escape> function - this can be used to tell Freemarker to escape every variable that is printed within the tag without explicitly needing to call the built-in function. E.g. <#escape x as x?html> will HTML encode any variable that is printed unless it’s wrapped in a <#noescape> region.

Automatic HTML escaping is turned on in Funnelback's default template (simple.ftl), with the escape function called immediately after the <#import> directives. Configuring escaping is an important part of ensuring the template is secure as it prevents injection attacks affecting any of the escaped variables.

Note: <#escape> only applies to the current template and isn't inherited by imported templates - these need to apply their own <#escape> directives.

FreeMarker core directives

FreeMarker provides several core directives, the most commonly used being <#if /> and <#list />.

Conditional display

The <#if/> directive allows you to conditionally skip a section of the template. For example, if you want to display only "PDF" search results you could do:

<#if r.fileType == "PDF">

The block contained inside the <#if > ... </#if> statement will be executed only if the condition specified is true (in that case the file type of the result equals "PDF").

The condition itself is generally:

  • Based on string equality: r.title == "PDF" or r.title != "PDF"
  • Based on numeric comparison: r.rank == 10
    • Note: When using greater than (>) or lesser than (<) operators you must use their encoded form to avoid conflicts with opening or closing FreeMarker tags. For example you must use <#if r.rank &gt; 10> instead of <#if r.rank > 10>. You can alternatively use parenthesis: <#if (r.rank > 10)>
  • Based on FreeMarker built-ins: r.title?exists or r.title?starts_with("document")

<#if /> statements can be nested without limitation:

<#if r.fileType == "PDF">
  <#if r.rank > 10>

FreeMarker also provides an <#else> directive:

<#if r.fileType == "PDF">
  <p class="error">Result ${r.title} is not a PDF document</p>

Iterating over the data model

The <#list /> directive allows you to iterate over a list of items and execute a block for each item. For example, if you want to iterate over search results and display the title of each result you could do:

  <#list response.resultPacket.results as r>

The syntax used is: <#list [name of list] as [single item]> ... </#list> with [name of list] being a list of items from the data model and [single item] a name you choose that will contain the single item extracted from the list, for each iteration.

Funnelback custom tags

Funnelback ships with two libraries of Freemarker macros, providing quick pre-built functions that can be used to build your search interface.

The libraries are loaded into the search template using <#import> directives located at the top of the template file.

<#ftl encoding="utf-8" />
<#import "/web/templates/modernui/funnelback_classic.ftl" as s/>
<#import "/web/templates/modernui/funnelback.ftl" as fb/>

The two import directives above import the funnelback_classic and funnelback macro libraries into the s and fb namespaces respectively.

The macros included within these libraries can be accessed once imported by prefixing the macro call with the namespace. The libraries contain functions that implement conditional logic (e.g. whatever appears inside this tag is run only after a search keyword is entered) or handle logic required to implement the display of a feature.

E.g. <@s.Results> calls the Results macro from the s namespace (which corresponds to the funnelback_classic macro library).

  • funnelback_classic: contains macros providing access to Funnelback’s core functionality.
  • funnelback: contains macros providing access to extended and newer functionality.

See also: Javadoc documentation for unnelback_classic and funnelback Freemarker libraries.

FreeMarker comments

You can use <#-- ... --> to indicate comments to FreeMarker. Those comments will not be processed by the template engine and won't be shown in the resulting HTML output.

For example if your template contains a mix of HTML and FreeMarker comments as in:

<div class="title">
  <!-- This is the title -->
  <#-- We first check that it's not empty -->
  <#if r.title?exists>${r.title}</#if>

Then the HTML output will be (Assuming the title is "Title of the result"):

<div class="title">
  <!-- This is the title -->
  Title of the result

FreeMarker documentation

FreeMarker is extensively documented in its official documentation, the FreeMarker manual, available here:

The following guides are of specific relevance for Funnelback templates:

Note: ensure that you are viewing the manual for the correct version of Freemarker as the available built-in functions can change from version to version. See: Funnelback - Freemarker versions to check the version of Freemarker used with your version of Funnelback.

See also


Funnelback logo