/** * Copyright (c) 2015 Lemur Consulting Ltd. * <p/> * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * <p/> * http://www.apache.org/licenses/LICENSE-2.0 * <p/> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package uk.co.flax.biosolr; import java.io.IOException; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.Set; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.params.FacetParams; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.handler.component.FacetComponent; import org.apache.solr.handler.component.ResponseBuilder; import org.apache.solr.search.QueryParsing; import org.apache.solr.search.SyntaxError; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Extension to the default {@code FacetComponent} that adds tree * facet building capabilities. * * @author mlp */ public class TreeFacetComponent extends FacetComponent { public static final String FACET_TREE = FacetParams.FACET + ".tree"; public static final String FACET_TREE_FIELD = FACET_TREE + ".field"; public static final String FACET_TREE_SIMPLE_PRUNE_NODES = FACET_TREE + ".prune.simple.nodes"; private static final Logger LOGGER = LoggerFactory.getLogger(TreeFacetComponent.class); private FacetTreeParameters parameters; @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void init(NamedList args) { super.init(args); this.parameters = new FacetTreeParameters(args); } @Override public void prepare(ResponseBuilder rb) throws IOException { super.prepare(rb); // Do we need to create a facet tree? if (rb.doFacets && rb.req.getParams().getBool(FACET_TREE, false)) { try { LOGGER.debug("{} set to true - adding tree fields to facets", FACET_TREE); // Make sure the facet tree field is in the facet field list addTreeFieldsToFacets(rb); } catch (SyntaxError e) { throw new SolrException(ErrorCode.BAD_REQUEST, e); } } } private void addTreeFieldsToFacets(ResponseBuilder rb) throws SyntaxError { String[] ftFields = rb.req.getParams().getParams(FACET_TREE_FIELD); if (ftFields == null || ftFields.length == 0) { LOGGER.warn("No facet tree fields specified - ignoring facet trees"); } else { // Take a modifiable copy of the incoming params ModifiableSolrParams params = new ModifiableSolrParams(rb.req.getParams()); // Put the original facet fields (if any) into a Set Set<String> facetFields = new LinkedHashSet<>(); if (params.getParams(FacetParams.FACET_FIELD) != null) { facetFields.addAll(Arrays.asList(params.getParams(FacetParams.FACET_FIELD))); } // Add the facet tree fields for (String ftField : ftFields) { // Parse the facet tree field, so we only add the field value, // rather than the whole string (ensure it's unique) facetFields.add(QueryParsing.getLocalParams(ftField, params).get(QueryParsing.V)); } // Add the (possibly) new facet fields params.set(FacetParams.FACET_FIELD, facetFields.toArray(new String[facetFields.size()])); // Re-set the params in the request rb.req.setParams(params); } } @Override public void process(ResponseBuilder rb) throws IOException { // Handle the initial facets super.process(rb); // And do the facet tree, if required if (rb.doFacets && rb.req.getParams().getBool(FACET_TREE, false)) { HierarchicalFacets ftp = new HierarchicalFacets(rb.req, rb.getResults().docSet, rb.req.getParams(), rb, parameters); @SuppressWarnings("rawtypes") SimpleOrderedMap<NamedList> ftpResponse = ftp.process(rb.req.getParams().getParams(FACET_TREE_FIELD)); @SuppressWarnings("unchecked") NamedList<Object> facetCounts = (NamedList<Object>) rb.rsp.getValues().get("facet_counts"); if (facetCounts != null) { facetCounts.add("facet_trees", ftpResponse); } else { facetCounts = new NamedList<>(); facetCounts.add("facet_trees", ftpResponse); rb.rsp.add("facet_counts", facetCounts); } } } }