/*********************************************************************************************************************** | |
* Copyright (c) 2008 empolis GmbH and brox IT Solutions GmbH. All rights reserved. This program and the accompanying | |
* materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, | |
* and is available at http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: Peter Wissel (brox IT Solutions GmbH) - initial API and implementation | |
**********************************************************************************************************************/ | |
package org.eclipse.smila.solr.search; | |
import static java.lang.String.format; | |
import static org.apache.commons.lang.StringUtils.defaultString; | |
import static org.apache.commons.lang.StringUtils.isBlank; | |
import static org.apache.commons.lang.StringUtils.isNotBlank; | |
import static org.apache.commons.lang.StringUtils.startsWith; | |
import java.text.MessageFormat; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Map.Entry; | |
import org.apache.commons.collections.Factory; | |
import org.apache.commons.collections.MapUtils; | |
import org.apache.commons.collections.map.LazyMap; | |
import org.apache.commons.lang.BooleanUtils; | |
import org.apache.commons.lang.NullArgumentException; | |
import org.apache.commons.lang.StringUtils; | |
import org.apache.commons.lang.math.NumberUtils; | |
import org.apache.solr.client.solrj.SolrQuery; | |
import org.apache.solr.client.solrj.SolrQuery.ORDER; | |
import org.apache.solr.common.params.CommonParams; | |
import org.apache.solr.common.params.FacetParams; | |
import org.apache.solr.common.params.GroupParams; | |
import org.apache.solr.common.params.ShardParams; | |
import org.eclipse.smila.datamodel.Any; | |
import org.eclipse.smila.datamodel.AnyMap; | |
import org.eclipse.smila.datamodel.AnySeq; | |
import org.eclipse.smila.datamodel.InvalidValueTypeException; | |
import org.eclipse.smila.datamodel.Value; | |
import org.eclipse.smila.datamodel.impl.DefaultDataFactoryImpl; | |
import org.eclipse.smila.search.api.QueryConstants; | |
import org.eclipse.smila.solr.SolrConstants; | |
import org.eclipse.smila.solr.util.SolrQueryUtils; | |
/** | |
* This class converts a smila's solr search record into a solr(j) query. | |
* | |
* @author tmenzel | |
* | |
*/ | |
public class SolrQueryConverter { | |
private static final String SOLR_LOCAL_PARAMS_OPERATOR = "o.op"; | |
private static final String SMILA_RANKING_PARAMETER_OPERATOR = "Operator"; | |
/** The logger. */ | |
protected final org.apache.commons.logging.Log _log = org.apache.commons.logging.LogFactory.getLog(this | |
.getClass()); | |
/** holds the filter groups, keyed by their name. */ | |
private final Map<String, FilterGroup> _filterGroups; | |
/** | |
* shared buffer to build the query strings and obtained thru {@link #resetBuffer()}. | |
*/ | |
private transient StringBuilder _buffer = new StringBuilder(0xfff); | |
/** | |
* The SolrQueryParameterAccessor. | |
*/ | |
private final SolrQueryParameterAccessor _accessor; | |
/** | |
* The SolrQuery. | |
*/ | |
private final SolrQuery _solrQuery = new SolrQuery(); | |
/** | |
* Constructor. | |
* | |
* @param accessor | |
* the solr query parameter accessor. | |
*/ | |
@SuppressWarnings("unchecked") | |
public SolrQueryConverter(final SolrQueryParameterAccessor accessor) { | |
_accessor = accessor; | |
_filterGroups = LazyMap.decorate(new HashMap<String, FilterGroup>(), new Factory() { | |
@Override | |
public Object create() { | |
return new FilterGroup(); | |
} | |
}); | |
} | |
/** | |
* Convert the record to a solr query string. | |
* | |
* @return the SolrQuery. | |
*/ | |
public SolrQuery toSolrQuery(final List<String> schemaAttributes) { | |
final String qt = _accessor.getRequestHandler(); | |
_solrQuery.setQueryType(qt); | |
doQuery(schemaAttributes); | |
doSortOrder(); | |
doTermsSettings(); | |
doSmilaFilters(); | |
doFacetSettings(); | |
doFilterGroups(); | |
doGroupSettings(); | |
if (_solrQuery.getQuery() != null || _solrQuery.getFields() != null) { | |
doQuerySettings(); | |
doHighlightingSettings(); | |
doShardsSettings(); | |
doSpellCheckSettings(); | |
doMoreLikeThis(); | |
} | |
final AnyMap map = _accessor.getSolrQueryParams().getMap(QueryConstants.NATIVE_PARAMETERS); | |
addAsSolrParameters(map); | |
return _solrQuery; | |
} | |
/** | |
* Build the query string. Takes it either from smila's query string and which must be then a native solr query OR if | |
* that is empty, constructs one from smila's fielded search syntax. | |
*/ | |
private void doQuery(final List<String> schemaAttributes) { | |
final String q = _accessor.getQuery(); | |
if (StringUtils.isNotEmpty(q)) { | |
_filterGroups.get(SolrConstants.FILTER_GROUP_Q).filterStrings.add(q); | |
} else { | |
// search in dedicated fields instead of simple query string | |
if (schemaAttributes != null && !schemaAttributes.isEmpty()) { | |
final StringBuilder fieldQuery = resetBuffer(); | |
for (final String field : schemaAttributes) { | |
final List<Value> fieldQueryValues = _accessor.getQueryAttributeValues(field); | |
if (fieldQueryValues != null) { | |
// there seems to be no SolrQuery API for querying | |
// fields so we have to build our query manually | |
SolrQueryUtils.appendFieldQueryPart(fieldQuery, field, fieldQueryValues); | |
} | |
} | |
final String fq = fieldQuery.toString().trim(); | |
if (StringUtils.isNotEmpty(fq)) { | |
_filterGroups.get(SolrConstants.FILTER_GROUP_Q).filterStrings.add(fq); | |
} | |
} | |
} | |
if (_filterGroups.containsKey(SolrConstants.FILTER_GROUP_Q)) { | |
// set operator via localParams (o.op) from SMILA ranking | |
final Any ranking = _accessor.getRankingConfig(); | |
if (ranking != null) { | |
final Value value = ranking.asMap().getValue(SMILA_RANKING_PARAMETER_OPERATOR); | |
if (value != null) { | |
_filterGroups.get(SolrConstants.FILTER_GROUP_Q).localParams.put(SOLR_LOCAL_PARAMS_OPERATOR, value); | |
} | |
} | |
} | |
} | |
/** | |
* Do query settings such as offset, maxCount, ... | |
*/ | |
private void doQuerySettings() { | |
// Paging | |
final int start = _accessor.getOffset(); | |
_solrQuery.setStart(start); | |
final int rows = _accessor.getMaxCount(); | |
_solrQuery.setRows(rows); | |
// Field list | |
final String[] fl = _accessor.getResultAttributes().toArray(new String[_accessor.getResultAttributes().size()]); | |
_solrQuery.setFields(fl); | |
// TODO: setIncludeScore is evidently not working, add score field | |
// manually. | |
// _solrQuery.setIncludeScore(true); | |
// must always have these or else building the result will fail | |
_solrQuery.addField(SolrConstants.CORE_FIELD_SCORE); | |
_solrQuery.addField(SolrConstants.CORE_FIELD_ID); | |
} | |
/** | |
* | |
*/ | |
private void doSortOrder() { | |
// for (Entry<String, SortOrder> item : | |
// _accessor.getSortByConfig().entrySet()) { | |
// final ORDER sortOrder = item.getValue() == SortOrder.ASCENDING ? | |
// ORDER.asc : ORDER.desc; | |
// _solrQuery.addSortField(item.getKey(), sortOrder); | |
// } | |
// faster impl. than above but more prone to migration changes | |
final List<AnyMap> annotations = _accessor.getSubParameters(QueryConstants.SORTBY); | |
if (annotations != null) { | |
for (final AnyMap annotation : annotations) { | |
final String attributeName = annotation.getStringValue(QueryConstants.ATTRIBUTE); | |
final String orderModeValue = annotation.getStringValue(QueryConstants.ORDER); | |
if (startsWith(orderModeValue, "asc")) { | |
_solrQuery.addSortField(attributeName, ORDER.asc); | |
} else { | |
_solrQuery.addSortField(attributeName, ORDER.desc); | |
} | |
} | |
} | |
} | |
/** | |
* process the filter groups by ANDing all parts and prefixng the tags. Note, that the main query q is also a special | |
* filter group. | |
*/ | |
private void doFilterGroups() { | |
// process the | |
for (final Entry<String, FilterGroup> entry : _filterGroups.entrySet()) { | |
final FilterGroup filterGroup = entry.getValue(); | |
final String groupName = entry.getKey(); | |
final StringBuilder fq = resetBuffer(); | |
// local params | |
appendLocalParams(fq, _accessor.getFilterLocalParams(groupName, false), filterGroup.localParams); | |
// make all fq parts as MUST (ANDing) | |
switch (filterGroup.filterStrings.size()) { | |
case 0: | |
throw new IllegalStateException(); | |
case 1: | |
fq.append(filterGroup.filterStrings.get(0)); | |
break; | |
default: | |
for (final String part : filterGroup.filterStrings) { | |
if (!("*:*".equals(part) || isBlank(part))) { | |
fq.append("+("); | |
fq.append(part); | |
fq.append(")"); | |
} | |
} | |
} | |
final String solrQueryString = fq.toString(); | |
if (SolrConstants.FILTER_GROUP_Q.equals(groupName)) { | |
_solrQuery.setQuery(solrQueryString); | |
} else { | |
_solrQuery.addFilterQuery(solrQueryString); | |
} | |
if (_log.isTraceEnabled()) { | |
_log.trace(format("adding filter group: %s=%s", groupName, solrQueryString)); | |
} // if | |
} | |
addFilterSolrSyntax(); | |
} | |
/** | |
* Adds the filter in smila syntax to the filter groups. | |
*/ | |
private void doSmilaFilters() { | |
if (!_accessor.hasFilters()) { | |
return; | |
} | |
// Iterate over all filter configurations | |
final List<AnyMap> filters = _accessor.getSubParameters(QueryConstants.FILTER); | |
for (int filterConfigIndex = 0; filterConfigIndex < filters.size(); filterConfigIndex++) { | |
final AnyMap filterConfig = filters.get(filterConfigIndex); | |
final String attribute = filterConfig.getStringValue(QueryConstants.ATTRIBUTE); | |
if (StringUtils.isEmpty(attribute)) { | |
final String msg = | |
MessageFormat.format("Skip filter configuration at index: {0}. No attribute defined", filterConfigIndex); | |
_log.warn(msg); | |
continue; | |
} | |
final String groupName = defaultString(filterConfig.getStringValue(SolrConstants.FILTER_GROUP), attribute); | |
// Add filter query to matching StringBuilder | |
final StringBuilder fq = resetBuffer(); | |
// addLocalParams(fq, localParams); | |
addFilterSmilaSyntax(fq, attribute, filterConfig, filterConfigIndex); | |
_filterGroups.get(groupName).filterStrings.add(fq.toString()); | |
} | |
} | |
/** | |
* adds the filters for the given attribute. | |
*/ | |
private void addFilterSmilaSyntax(final StringBuilder fq, final String attribute, final AnyMap filter, | |
final int index) { | |
for (final Entry<String, Any> condition : filter.entrySet()) { | |
final String filterType = condition.getKey(); | |
if (QueryConstants.ATTRIBUTE.equals(filterType) || SolrConstants.FILTER_GROUP.equals(filterType)) { | |
continue; | |
} else if (QueryConstants.FILTER_ONEOF.equals(filterType)) { | |
final AnySeq values = condition.getValue().asSeq(); | |
appendFilterSetExpression(fq, attribute, values, ""); | |
} else if (QueryConstants.FILTER_ALLOF.equals(filterType)) { | |
final AnySeq values = condition.getValue().asSeq(); | |
appendFilterSetExpression(fq, attribute, values, "+"); | |
} else if (QueryConstants.FILTER_NONEOF.equals(filterType)) { | |
final AnySeq values = condition.getValue().asSeq(); | |
appendFilterSetExpression(fq, attribute, values, "-"); | |
} else if (QueryConstants.FILTER_ATLEAST.equals(filterType)) { | |
final String value = condition.getValue().asValue().asString(); | |
appendFilterRangeExpression(fq, attribute, value, "*", false); | |
} else if (QueryConstants.FILTER_GREATERTHAN.equals(filterType)) { | |
final String value = condition.getValue().asValue().asString(); | |
appendFilterRangeExpression(fq, attribute, value, "*", true); | |
} else if (QueryConstants.FILTER_ATMOST.equals(filterType)) { | |
final String value = condition.getValue().asValue().asString(); | |
appendFilterRangeExpression(fq, attribute, "*", value, false); | |
} else if (QueryConstants.FILTER_LESSTHAN.equals(filterType)) { | |
final String value = condition.getValue().asValue().asString(); | |
appendFilterRangeExpression(fq, attribute, "*", value, true); | |
} else { | |
_log.warn(format("unknown (filter) element in filters encountered on attribute %s: %s[%s]. It is ignored.", | |
attribute, filterType, index)); | |
} | |
if (fq.length() == 0) { | |
_log.warn(format("Filter on attribute %s: %s[%d] has no values and is ignored!", attribute, filterType, | |
index)); | |
} else { | |
final String fqString = fq.toString(); | |
if (_log.isDebugEnabled()) { | |
_log.debug(format("Filter converted from Smila Syntax on attribute %s: %s[%d]: %s", attribute, | |
filterType, index, fqString)); | |
} // if | |
} | |
} | |
} | |
/** | |
* @param attribute | |
* @param fq | |
*/ | |
private void appendFilterRangeExpression(final StringBuilder fq, final String attribute, String lower, | |
String upper, final boolean exclusive) { | |
if (isBlank(lower)) { | |
throw new IllegalArgumentException("lower bound must not be blank"); | |
} | |
if (isBlank(upper)) { | |
throw new IllegalArgumentException("upper bound must not be blank"); | |
} | |
lower = appendFilterRangeExpressionBoundExclusionIfNotStar(fq, attribute, lower, exclusive); | |
upper = appendFilterRangeExpressionBoundExclusionIfNotStar(fq, attribute, upper, exclusive); | |
fq.append(" +("); | |
fq.append(attribute); | |
fq.append(":["); | |
fq.append(lower); | |
fq.append(" TO "); | |
fq.append(upper); | |
fq.append("])"); | |
} | |
/** | |
* appends a not expresion if a range bound if it is {@code != "*" } and exlusive is true. | |
*/ | |
private String appendFilterRangeExpressionBoundExclusionIfNotStar(final StringBuilder fq, final String attribute, | |
String value, final boolean exclusive) { | |
if (!"*".equals(value)) { | |
value = SolrQueryUtils.escapeQuery(value, SolrQueryUtils.ESCAPE_CHARS_WS); | |
// in case of exclusive we must add a NOT query | |
if (exclusive) { | |
fq.append(" -("); | |
fq.append(attribute); | |
fq.append(":"); | |
fq.append(value); | |
fq.append(")"); | |
} | |
} | |
return value; | |
} | |
/** | |
* converts the smila filter syntax into lucene's for the filters: oneOf, noneOf, allOf. | |
* | |
* @param fq | |
* the fq | |
* @param attribute | |
* the attribute | |
* @param values | |
* @param operator | |
* of of '+', '-', "" (for OR == oneOf) | |
*/ | |
private void appendFilterSetExpression(final StringBuilder fq, final String attribute, final AnySeq values, | |
final String operator) { | |
final List<String> filterValues; | |
try { | |
filterValues = values.asStrings(); | |
} catch (final InvalidValueTypeException e) { | |
throw new InvalidValueTypeException( | |
"Filter Syntax Error: Filter must be of Type Seq and contain only Val elements.", e); | |
} | |
for (final String filterValue : filterValues) { | |
final String filterValueEscaped = SolrQueryUtils.escapeQuery(filterValue, SolrQueryUtils.ESCAPE_CHARS_WS); | |
fq.append(operator); | |
fq.append("("); | |
fq.append(attribute); | |
fq.append(":"); | |
fq.append(filterValueEscaped); | |
fq.append(")"); | |
} | |
} | |
/** | |
* just adds all {@link SolrConstants#QUERY_MAP}/fq Vals as filters to solr. | |
*/ | |
private void addFilterSolrSyntax() { | |
final AnySeq filterQueries = _accessor.getFilterQuery(); | |
if (filterQueries != null && filterQueries.size() > 0) { | |
final String[] fq = filterQueries.asStrings().toArray(new String[filterQueries.size()]); | |
_solrQuery.setFilterQueries(fq); | |
} | |
} | |
/** | |
* Do facet settings. | |
*/ | |
private void doFacetSettings() { | |
final List<AnyMap> facetByConfigs = _accessor.getFacetByConfig(); | |
if (facetByConfigs.isEmpty()) { | |
return; | |
} | |
// turn on facetting if the map is present | |
_solrQuery.add(FacetParams.FACET, "true"); | |
int facetByIndex = 0; | |
for (final AnyMap facetConfig : facetByConfigs) { | |
facetByIndex++; | |
/* | |
* PERF: do this faster by just iterating thru all childeren and collecting all vars on the fly instead of pulling | |
* them from the map | TM @ Jan 18, 2012 | |
*/ | |
final String attribute = facetConfig.getStringValue(QueryConstants.ATTRIBUTE); | |
if (attribute == null) { | |
_log | |
.warn(MessageFormat.format("no attribute defined for facet @ index: {0}. It is ignored.", facetByIndex)); | |
continue; | |
} | |
/* | |
* BETTER | TM | facetting: apply record attribute -> solr field mapping from piplet config ? | TM @ Jan 18, 2012 | |
*/ | |
final String fieldName = attribute; | |
final String facetType = | |
defaultString(facetConfig.getStringValue(SolrConstants.FACET_TYPE), FacetParams.FACET_FIELD); | |
final String facetName = defaultString(facetConfig.getStringValue(SolrConstants.FACET_NAME), attribute); | |
final boolean multiselect = BooleanUtils.isTrue(facetConfig.getBooleanValue(SolrConstants.FACET_MULTISELECT)); | |
// custom range facet | |
if (facetType.equals(SolrConstants.FACET_TYPE_CUSTOM_RANGES)) { | |
addCustomRangesFacet(fieldName, facetName, multiselect, facetConfig); | |
continue; | |
} | |
// Handle localParams | |
final AnyMap localParams = facetConfig.getMap(SolrConstants.LOCAL_PARAMS, true); | |
if (!facetName.equals(attribute)) { | |
localParams.put("key", facetName); | |
} | |
final String filterGroupName; | |
if (multiselect) { | |
filterGroupName = "facet_" + facetName; | |
localParams.put("ex", filterGroupName); | |
} else { | |
filterGroupName = | |
defaultString(facetConfig.getStringValue(SolrConstants.FILTER_GROUP), "facet_" + facetName); | |
} | |
// the local params as well as the field e.g. | |
// facet.field={!key=facetName ex=groupName} | |
final StringBuilder facetParam = resetBuffer(); | |
appendLocalParams(facetParam, localParams); | |
facetParam.append(fieldName); | |
// Add facet with with localParams | |
_solrQuery.add(facetType, facetParam.toString()); | |
// Add maxCount | |
final String maxCount = facetConfig.getStringValue(QueryConstants.MAXCOUNT); | |
if (maxCount != null) { | |
addFieldParameter(fieldName, FacetParams.FACET_LIMIT, maxCount); | |
} | |
// Add sortby | |
final AnyMap sortby = facetConfig.getMap(QueryConstants.SORTBY); | |
if (sortby != null) { | |
final String criterion = sortby.getStringValue(QueryConstants.FACETBY_SORTCRITERION); | |
addFieldParameter(fieldName, FacetParams.FACET_SORT, criterion); | |
if (_log.isWarnEnabled()) { | |
if (sortby.containsKey(QueryConstants.ORDER)) { | |
_log.warn(format("facet config for field %s contains value for unsupported sort order. It is ignored", | |
fieldName)); | |
} | |
} // if | |
} | |
// add all native parameters as given for the current field | |
final AnyMap map = facetConfig.getMap(QueryConstants.NATIVE_PARAMETERS); | |
addAsSolrParameters(map, fieldName); | |
// Add filter | |
final AnySeq filterConfig = facetConfig.getSeq(QueryConstants.FILTER_ONEOF); | |
if (filterConfig != null) { | |
final StringBuilder fq = resetBuffer(); | |
appendFilterSetExpression(fq, attribute, filterConfig, ""); | |
if (fq.length() > 0) { | |
final FilterGroup filterGroup = _filterGroups.get(filterGroupName); | |
filterGroup.filterStrings.add(fq.toString()); | |
filterGroup.localParams.put(SolrConstants.LOCAL_PARAM_TAG, filterGroupName); | |
} | |
} | |
} // facet config | |
} | |
/** | |
* handles custom range facets. | |
* | |
* @todo BETTER this method doesnt use the generic {@link #_filterGroups} but should, if it makes sense at all. | |
* However, this needs to be investigated for which i dont have the time right now. | |
*/ | |
private void addCustomRangesFacet(final String attribute, final String facetName, final Boolean multiselect, | |
final AnyMap facetConfig) { | |
// add facet query | |
final AnySeq ranges = facetConfig.getSeq(SolrConstants.FACET_RANGES); | |
if (ranges == null) { | |
return; | |
} | |
int position = 0; | |
for (final Any range : ranges) { | |
final StringBuilder facetQuery = new StringBuilder(); | |
String ex = ""; | |
if (multiselect) { | |
ex = " ex=facet_" + facetName; | |
} | |
facetQuery.append("{!key="); | |
facetQuery.append(facetName); | |
facetQuery.append("_"); | |
facetQuery.append(position); | |
facetQuery.append(ex); | |
facetQuery.append("}"); | |
facetQuery.append(attribute); | |
facetQuery.append(":"); | |
facetQuery.append(range.asValue().asString()); | |
position++; | |
_solrQuery.addFacetQuery(facetQuery.toString()); | |
} | |
// add filter query, if present | |
final AnySeq oneOf = facetConfig.getSeq(QueryConstants.FILTER_ONEOF); | |
if (oneOf != null) { | |
final StringBuilder filterQuery = new StringBuilder(); | |
if (multiselect) { | |
filterQuery.append("{!tag=facet_" + facetName + "}"); | |
} | |
for (final Any filter : oneOf) { | |
try { | |
final String filterReferenze = filter.asValue().asString(); | |
final String[] split = StringUtils.split(filterReferenze, "_"); | |
final int index = NumberUtils.toInt(split[split.length - 1]); | |
final String resolvedFilter = ranges.getStringValue(index); | |
filterQuery.append(""); | |
filterQuery.append("("); | |
filterQuery.append(attribute); | |
filterQuery.append(":"); | |
filterQuery.append(resolvedFilter); | |
filterQuery.append(")"); | |
} catch (Exception e) { | |
throw new RuntimeException(format("custom range filter cannot be resolved: %s oneOf %s", facetName, | |
filter.asValue().asString()), e); | |
} | |
} | |
_solrQuery.addFilterQuery(filterQuery.toString()); | |
} | |
} | |
/** | |
* resets the global buffer for this object and returns it. Must not use this method in a nested method, as the parent | |
* might be using the same instance! | |
*/ | |
private StringBuilder resetBuffer() { | |
_buffer.setLength(0); | |
return _buffer; | |
} | |
/** | |
* Adds the values in the given maps as local params in same order. The surrounding !{} are added, if any params got | |
* really added | |
* | |
* @param sb | |
* the sb | |
* @param localParams | |
* the local params | |
* @return the string builder | |
*/ | |
private StringBuilder appendLocalParams(final StringBuilder sb, final AnyMap... localParams) { | |
final int originalLength = sb.length(); | |
if (localParams != null) { | |
sb.append("{!"); | |
for (AnyMap anyMap : localParams) { | |
if (!MapUtils.isEmpty(anyMap)) { | |
for (final Entry<String, Any> localParam : anyMap.entrySet()) { | |
appendLocalParam(sb, localParam.getKey(), localParam.getValue()); | |
} | |
} | |
} | |
if (sb.length() == originalLength + 2) { // no local params were | |
// added -> remove {! | |
sb.setLength(originalLength); | |
} else { | |
sb.setCharAt(sb.length() - 1, '}'); | |
} | |
} | |
return sb; | |
} | |
/** | |
* append {@code name[=value.asValue().asString()]+ " "}. Since a space is always appended it should be removed by the | |
* calling method before closing the local params. | |
* | |
* @param sb | |
* @param name | |
* @param value | |
*/ | |
private void appendLocalParam(final StringBuilder sb, final String name, final Any value) { | |
sb.append(name); | |
final String stringValue = value.asValue().asString(); | |
if (isNotBlank(stringValue)) { | |
sb.append("="); | |
sb.append(stringValue); | |
} | |
sb.append(" "); | |
} | |
/** | |
* Do terms settings. | |
*/ | |
private void doTermsSettings() { | |
final AnyMap terms = _accessor.getTerms(); | |
if (terms != null) { | |
_solrQuery.setParam(CommonParams.QT, "/terms"); | |
addAsSolrParameters(terms); | |
} | |
} | |
/** | |
* Do highlighting settings. | |
*/ | |
private void doHighlightingSettings() { | |
if (_accessor.hasHighlightConfig()) { | |
for (Any config : _accessor.getHighlightConfig()) { | |
if (config.isMap()) { | |
final AnyMap highlight = config.asMap(); | |
final String fieldName = highlight.getStringValue(QueryConstants.ATTRIBUTE); | |
_solrQuery.addHighlightField(fieldName); | |
final AnyMap nativeParameters = highlight.getMap(QueryConstants.NATIVE_PARAMETERS); | |
addAsSolrParameters(nativeParameters, fieldName); | |
} | |
} | |
} | |
} | |
/** | |
* Adds contained values as solr query parameters. Only Value items is supported. An item with the name "attribute" is | |
* skipped. | |
* | |
* @param map | |
* the map containing parameter as key value pairs. If null does nothing. | |
* @param field | |
* if not blank then the config is added for the given field name (field level config, e.g. | |
* f.${field}.${key}) otherwise global | |
*/ | |
private void addAsSolrParameters(final AnyMap map, final String field) { | |
if (map == null) { | |
return; | |
} | |
for (final Entry<String, Any> entry : map.entrySet()) { | |
final String key = entry.getKey(); | |
if (key.equals(QueryConstants.ATTRIBUTE)) { | |
continue; | |
} | |
final String value = entry.getValue().asValue().asString(); | |
if (isBlank(field)) { | |
_solrQuery.add(key, value); | |
} else { | |
addFieldParameter(field, key, value); | |
} | |
} | |
} | |
/** | |
* Adds contained values as solr query parameters. Value and Seq items are supported, the latter result in multiple | |
* params with the same name. | |
* | |
* @param map | |
* the map containing parameter as key value pairs. If null does nothing. | |
*/ | |
private void addAsSolrParameters(final AnyMap map) { | |
if (map == null) { | |
return; | |
} | |
for (final Entry<String, Any> entry : map.entrySet()) { | |
final String key = entry.getKey(); | |
final Any value = entry.getValue(); | |
if (value.isSeq()) { | |
final AnySeq values = value.asSeq(); | |
for (Any any : values) { | |
_solrQuery.add(key, any.asValue().asString()); | |
} | |
} else if (value.isValue()) { | |
final String single = value.asValue().asString(); | |
_solrQuery.add(key, single); | |
} | |
} | |
} | |
/** | |
* adds the given parameter for a field after the form f.${field}.${paramName}. | |
*/ | |
private void addFieldParameter(final String field, final String paramName, final String value) { | |
final String parmName = SolrConstants.FIELD_PREFIX + field + SolrConstants.FIELD_SUFFIX + paramName; | |
_solrQuery.add(parmName, value); | |
} | |
/** | |
* Do shards settings. | |
*/ | |
private void doShardsSettings() { | |
final AnySeq seq = _accessor.getShards(); | |
if (seq != null) { | |
final String shards = StringUtils.join(seq.asStrings(), ","); | |
_solrQuery.setParam(ShardParams.SHARDS, shards); | |
} | |
} | |
/** | |
* Do spellcheck settings. | |
*/ | |
private void doSpellCheckSettings() { | |
final AnyMap spellcheck = _accessor.getSpellcheck(); | |
if (spellcheck != null) { | |
addAsSolrParameters(spellcheck); | |
} | |
} | |
/** | |
* | |
*/ | |
private void doMoreLikeThis() { | |
final AnyMap moreLikeThis = _accessor.getMoreLikeThis(); | |
if (moreLikeThis != null) { | |
addAsSolrParameters(moreLikeThis); | |
} | |
} | |
/** | |
* Do group settings. | |
*/ | |
private void doGroupSettings() { | |
// only process if a groupby config is present | |
if (_accessor.hasGroupbyConfig()) { | |
AnySeq method = null; | |
String asMainResult = null; | |
// Iterate thru groupby AnyMap | |
for (Entry<String, Any> entry : _accessor.getGroupbyConfig().entrySet()) { | |
final String key = entry.getKey(); | |
final Any any = entry.getValue(); | |
// if AnySeq check if key is 'attributes' | |
if (any.isSeq()) { | |
if (key.equals(SolrConstants.GROUPBY_METHOD_ATTRIBUTES)) { | |
method = any.asSeq(); | |
} | |
// if AnyMap get the underlying AnySeq 'attributes' | |
} else if (any.isMap()) { | |
method = any.asMap().getSeq(SolrConstants.GROUPBY_METHOD_ATTRIBUTES); | |
// if Value check if key is 'asMainResult' | |
} else if (any.isValue()) { | |
if (key.equals(SolrConstants.GROUPBY_AS_MAIN_RESULT)) { | |
asMainResult = any.asValue().asString(); | |
} | |
} | |
} | |
// check if a groupby method is defined | |
if (method == null) { | |
throw new NullArgumentException("groupby method AnySeq"); | |
} | |
// handle first AnyMap within groupby method AnySeq | |
final AnyMap groupby = method.getMap(0); | |
if (groupby != null) { | |
// turn on grouping (field collapsing) | |
_solrQuery.add(GroupParams.GROUP, "true"); | |
// set group.field = attribute | |
final String attribute = groupby.getStringValue(QueryConstants.ATTRIBUTE); | |
if (StringUtils.isEmpty(attribute)) { | |
throw new NullArgumentException("attribute"); | |
} | |
_solrQuery.add(GroupParams.GROUP_FIELD, attribute); | |
// set group.limit = maxcount | |
final Long maxcount = groupby.getLongValue(QueryConstants.MAXCOUNT); | |
if (maxcount != null) { | |
_solrQuery.add(GroupParams.GROUP_LIMIT, String.valueOf(maxcount)); | |
} | |
// if Value 'asMainResult' is not empty set group.main = true | |
if (StringUtils.isNotEmpty(asMainResult)) { | |
_solrQuery.add(GroupParams.GROUP_MAIN, "true"); | |
} | |
} | |
} | |
} | |
} | |
// CHECKSTYLE:OFF | |
/** | |
* simple struct to hold elements regarding the filter | |
*/ | |
class FilterGroup { | |
final List<String> filterStrings = new ArrayList<String>(); | |
final AnyMap localParams = DefaultDataFactoryImpl.INSTANCE.createAnyMap(); | |
} | |
// CHECKSTYLE:ON |