/**
* =============================================================================
*
* ORCID (R) Open Source
* http://orcid.org
*
* Copyright (c) 2012-2014 ORCID, Inc.
* Licensed under an MIT-Style License (MIT)
* http://orcid.org/open-source-license
*
* This copyright and license information (including a link to the full license)
* shall be included in its entirety in all copies or substantial portion of
* the software.
*
* =============================================================================
*/
package org.orcid.core.utils;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
/**
* Class to build up a solr query to perform simple field searches on a solr
* instance. Not yet able to do facet searches, highlighting etc, only for
* building a simple string with optional wildcards
*
* @author jamesb
*
*/
public class SolrQueryBuilder {
private StringBuilder queryString;
private static final String AND_CONDITION = " AND ";
private static final String OR_CONDITION = " OR ";
private static final String NOT_CONDITION = " -";
private static final String OPEN_BRACKET = "(";
private static final String CLOSE_BRACKET = ")";
private static final String OPEN_EDISMAX = "{!edismax qf='";
private static final String CLOSE_EDISMAX = "'}";
// Need to escape + - && || ! ( ) { } [ ] ^ " ~ : \ and any white space
private static final Pattern SPECIAL_CHARACTER_PATTERN = Pattern.compile("([+\\-&|!(){}\\[\\]^\"~:\\\\])");
public SolrQueryBuilder(String firstQuery) {
queryString = new StringBuilder(firstQuery);
}
public SolrQueryBuilder() {
queryString = new StringBuilder();
}
public void appendFieldValuePair(String field, String value) {
queryString.append(safelyFormatFieldValue(field, value));
}
/**
* Method to add wildcard searching to a field by converting it lowercase
* <b>This may change in line with the workings of the SOLR config as
* different configurations are used so use with caution, knowing this
* method may change</b> *
*
* @param field
* @param value
*/
public void appendFieldValuePairAsLowercaseWildcard(String field, String value) {
String basicQueryExpression = safelyFormatFieldValue(field, value);
if (!StringUtils.isBlank(basicQueryExpression)) {
queryString.append(basicQueryExpression.toLowerCase());
}
}
public void appendLowercaseWildcardANDCondition(String field, String value) {
String basicQueryExpression = safelyFormatFieldValue(field, value);
if (!StringUtils.isBlank(basicQueryExpression)) {
queryString.append(AND_CONDITION).append(basicQueryExpression.toLowerCase());
}
}
public void appendANDCondition(String field, String value) {
queryString.append(safelyFormatConditionFieldValue(AND_CONDITION, field, value));
}
public void appendORCondition(String field, String value) {
queryString.append(safelyFormatConditionFieldValue(OR_CONDITION, field, value));
}
public void appendLowercaseWildcardORCondition(String field, String value) {
String basicQueryExpression = safelyFormatFieldValue(field, value);
if (!StringUtils.isBlank(basicQueryExpression)) {
queryString.append(OR_CONDITION).append(basicQueryExpression.toLowerCase());
}
}
public void appendNOTCondition(String field, Collection<String> values) {
String basicQueryExpression = safelyFormatFieldValue(field, values);
if (!StringUtils.isBlank(basicQueryExpression)) {
queryString.append(NOT_CONDITION).append(basicQueryExpression.toLowerCase());
}
}
public void appendValue(String value) {
queryString.append(escapeValue(value));
}
public void appendEDisMaxQuery(List<SolrFieldWeight> fields) {
queryString.append(OPEN_EDISMAX);
StringBuilder fieldQuery = new StringBuilder();
for (SolrFieldWeight field : fields) {
fieldQuery.append(MessageFormat.format("{0}^{1,number,0.0} ", new Object[] { field.getField(), field.getWeight() }));
}
// Snip off the extra space character and append
queryString.append(fieldQuery.toString().trim());
queryString.append(CLOSE_EDISMAX);
}
public void openNestedANDOperation() {
queryString.append(AND_CONDITION).append(OPEN_BRACKET);
}
public void openNestedOROperation() {
queryString.append(OR_CONDITION).append(OPEN_BRACKET);
}
public void closeNestedOROperation() {
queryString.append(CLOSE_BRACKET);
}
public void closeNestedANDOperation() {
queryString.append(CLOSE_BRACKET);
}
public String retrieveQuery() {
return queryString.toString();
}
private String safelyFormatConditionFieldValue(String condition, String field, String value) {
if (!StringUtils.isBlank(condition)) {
String basicQueryExpression = safelyFormatFieldValue(field, value);
if (!StringUtils.isBlank(basicQueryExpression)) {
return condition + basicQueryExpression;
}
}
return "";
}
private String safelyFormatFieldValue(String field, String value) {
if (!StringUtils.isBlank(field) && !StringUtils.isBlank(value)) {
return MessageFormat.format("{0}:{1}", new Object[] { field, escapeValue(value) });
}
return "";
}
private String safelyFormatFieldValue(String field, Collection<String> values) {
if (values != null) {
String joined = StringUtils.join(values, " ");
if (!StringUtils.isBlank(joined)) {
return MessageFormat.format("{0}:({1})", new Object[] { field, escapeValue(joined) });
}
}
return "";
}
private Object escapeValue(String value) {
if (value == null) {
return null;
}
Matcher matcher = SPECIAL_CHARACTER_PATTERN.matcher(value);
return matcher.replaceAll("\\\\$1");
}
public boolean isEmpty() {
return StringUtils.trimToEmpty(queryString.toString()).length() == 0;
}
}