Saturday, September 11, 2010

How to escape quotes in Velocity

Jan 14, 2011. Thanks to Michael Fienen's comment below, quoting Strings in Velocity is not so hard; just use ${esc.q}.

#set($searchTerm = "red house" )
#pullContent("+structureInode:6333 +live:true +text1:${esc.q}$searchTerm${esc.q}" '0' 'random')

You can ignore the rest of this post - I leave it here purely for the sake of posterity. :)

Short story: skip to the bit about how to escape quotes in Velocity.

In dotCMS, I use Velocity to perform various Lucene searches through macros such as #pullContent, for which there is a well defined Lucene Query Parser Syntax. A simple example: find all content belonging to some structure called "Historic Buildings" whose name includes the token "house". Let's say it has an inode value of 6333 and and it's name field (a.k.a. the "variable" column in a Structure) is text1 (a.k.a. "index name" column in a Structure). Below is a query to find all Historic Buildings whose name includes the token "house" - in random order.

#set($searchTerm = "house" )
#pullContent("+structureInode:6333 +live:true +text1:$searchTerm" '0' 'random')

That's easy enough, and it will find all values that have "house" anywhere in their value (as a whole word). A phrase search is required when you want to search for an unbroken string, for example you want to search for "red house" and match "the big red house" but not match "red (recently painted) house". In Lucene, a phrase search is easy: just contain the string in double quotes: "red house"

How do you escape quotes in a Velocity string? My first thought was to use backslash as an escape character - which is common to many languages.

#set($searchTerm = "\"red house\"" )
#pullContent("+structureInode:6333 +live:true +text1:$searchTerm" '0' 'random')

Unfortunately Velocity doesn't recognise the backslash as an escape character and you will get a parser error from this. As I learned from this Apache mailing list response: escape double quotes it seems that escaping quotes in Velocity is very ugly. Here is what seems to me to be the most robust method. 

#set($searchTerm = "red house" )
#set($Q = '"')
#set($quotedTerm = "${Q}$searchTerm${Q}")
#pullContent("+structureInode:6333 +live:true +text1:$quotedTerm" '0' 'random')

Btw, a very good way to build your Lucene query strings is to use the dotCMS Admin interface. When you use it to search for content, click the "Show Query" button at the bottom left (below the search results and other buttons) and you will see the query string for the search just completed.

My dotCMS notes.