Home » Devtech review » LangsNTechs » Java » AquaLogic Commerce Services » 6.0 » ElasticPath 6.1.2/ALCS6.0: nasty keywords

ElasticPath 6.1.2/ALCS6.0: nasty keywords

Support for logical operators in ElasticPath search phrase is one of those features that are probably not really needed in most cases, thus not very beneficial. And together with somewhat sloppy implementation that adds potential for errors, such feature is more likely to lead some unaware end-users to frustration than be of any use for them.

The proper implementation of logical operators support would require parsing search phrase, identifying logical operators, and building corresponding SOLR/Lucene query condition. But this would be rather heavy. Much easier is to allow key phrase pass to query condition directly, and let SOLR/Lucene parser deal with it. Naturally, if the condition will be ill-syntaxed and unparsable, error will happen on SOLR side, and will be harder to deal with.

What happens to your out-of-the-box EP6.1.2 (or ALCS6.0) when you do searches like these:

OR something
anything OR
do NOT
hfs +
budget -
('')
(')

Many involved have addressed the problem, but some (one: a, b, c, d, e, f, g; two: a, b, c, d, e, f, g) are still displaying user-frustrating error messages.

So yes, capitalized or-s, and-s and not-s, as the plus and minus signs, are the logical conditions. And the trick with brackets and apostrophe just shows problems with escaping special characters. Hmm, not nice.

The obvious solution is to:
A. Lowercase the query and thus treat all as text, not logical conditions – if user enters OR he might mean just mean OR, not any logical condition (and that’s precisely our case for production, where OR is short for “onderwijsmanagement” or something like this).
Yes, this will not allow users to have logical conditions in queries, but in our case it’s rather doubting someone would need such feature.
B. Escape the special characters for SOLR/Lucene syntax, so (”) and (‘) and the like won’t cause errors.

Since we’re “disabling” some of the default functionality, we wanted to do this with easily undo-able change. So we decided not to change existing EP logic, but add escaping on top of it.

To do this, we extended com.elasticpath.service.search.solr.SolrQueryFactoryImpl:

public class ExtendedSolrQueryFactoryImpl extends SolrQueryFactoryImpl {
	private static final Logger LOG = Logger.getLogger(ExtendedSolrQueryFactoryImpl.class);
	
	public SolrQuery composeKeywordQuery(KeywordSearchCriteria searchCriteria, int startIndex, int maxResults, SearchConfig searchConfig, boolean fuzzyQuery) {
		String unescapedKeywords = searchCriteria.getKeyword(); // Get original keywords
		String luceneEscapedKeywords = escapeSpecialChars(unescapedKeywords.toLowerCase()); // Escape them with our Lucene search conditions value escaping
		searchCriteria.setKeyword(luceneEscapedKeywords); // Put escaped keyWords in search criteria
		SolrQuery result = super.composeKeywordQuery(searchCriteria, startIndex, maxResults, searchConfig, fuzzyQuery); // Create SOLR search query
		searchCriteria.setKeyword(unescapedKeywords); // Put back original keyWords value in order to be properly displayed in breadcrumb/searchform etc.
		return result; // Return SOLR search query
	}
	

	private static final Pattern specialCharsPattern = Pattern.compile("\\;|\\+|\\-|\\&|\\||\\!|\\(|\\)|\\{|\\}|\\[|\\]|\\^|\\\"|\\~|\\*|\\?|\\:|\\\\");	
	// Can be moved (with pattern together) to separate helper as static method, and thus be reused by others who need Lucene/SOLR escaping
	private String escapeSpecialChars(String value) {
		String result = value;
		try {
			result = specialCharsPattern.matcher(result).replaceAll("\\\\$0");
		} catch (Exception e) {
			LOG.error("Error escaping special characters in string "+value, e);
		}
		return result;
	}
}

As you can see, after creating SolrQuery we’re reverting changes in KeywordSearchCriteria, because there is code that gets keyphrase back from it and displays, and it will display it lowercased and with escape slashes if we don’t revert it back to original value.

Now we want our extended query factory to be used instead of the original one, so we find places in Spring configs where original query factory is instantiated, like this:

...
<bean id="solrQueryFactory" class="com.elasticpath.service.search.solr.SolrQueryFactoryImpl">
...

and change it to (note your package name for ExtendedSolrQueryFactoryImpl will probably differ (-: ):

...
<bean id="solrQueryFactory" class="com.mycompany.myproject.elasticpath.service.search.solr.impl.ExtendedSolrQueryFactoryImpl">
...

Notes:
Actually in our code the escaping logic was put into separate helper class, but to keep example shorter I’ve put it right into ExtendedSolrQueryFactoryImpl.

In case you don’t care if you’ll have to modify EP code, and not to add your own code over, it might be more reasonable to fix the problem right inside composeKeywordQuery method of com.elasticpath.service.search.solr.SolrQueryFactoryImpl. Then you would not have to hassle with KeywordSearchCriteria keyword setting and reverting.
This can be done by changing these lines:

query.setQueryType(DISMAX_REQUEST_HANDLER);
query.setQuery(searchCriteria.getKeyword());

into these (assuming you also add escapeSpecialChars method of course):

query.setQueryType(DISMAX_REQUEST_HANDLER);
query.setQuery(escapeSpecialChars(searchCriteria.getKeyword().toLowerCase()));

And you’re done. Easy! (-:

So once again, open source code is a good thing (-:

Hope this post was helpful and/or interesting. Best regards!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s