/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * 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 org.apache.solr.handler.component; import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.solr.common.params.FacetParams; import org.apache.solr.common.util.NamedList; import org.apache.solr.handler.component.FacetComponent.FacetBase; /** * Models a single instance of a "pivot" specified by a {@link FacetParams#FACET_PIVOT} * param, which may contain multiple nested fields. * * This class is also used to coordinate the refinement requests needed from various * shards when doing processing a distributed request */ public class PivotFacet extends FacetBase { /** * Local param used to indicate that refinements are required on a pivot. Should * also be used as the prefix for concatenating with the value to determine the * name of the multi-valued param that will contain all of the values needed for * refinement. */ public static final String REFINE_PARAM = "fpt"; // TODO: is this really needed? can't we just loop over 0<=i<rb.shards.length ? public final BitSet knownShards = new BitSet(); private final Map<Integer, List<PivotFacetValue>> queuedRefinements = new HashMap<>(); // if null, then either we haven't collected any responses from shards // or all the shards that have responded so far haven't had any values for the top // field of this pivot. May be null forever if no doc in any shard has a value // for the top field of the pivot private PivotFacetField pivotFacetField; public PivotFacet(ResponseBuilder rb, String facetStr) { super(rb, FacetParams.FACET_PIVOT, facetStr); } /** * Tracks that the specified shard needs to be asked to refine the specified * {@link PivotFacetValue} * * @see #getQueuedRefinements */ public void addRefinement(int shardNumber, PivotFacetValue value) { if (!queuedRefinements.containsKey(shardNumber)) { queuedRefinements.put(shardNumber, new ArrayList<PivotFacetValue>()); } queuedRefinements.get(shardNumber).add(value); } /** * An immutable List of the {@link PivotFacetValue}s that need to be * refined for this pivot. Once these refinements have been processed, * the caller should clear them using {@link #removeAllRefinementsForShard} * * @see #addRefinement * @see #removeAllRefinementsForShard * @return a list of the values to refine, or an empty list. */ public List<PivotFacetValue> getQueuedRefinements(int shardNumber) { List<PivotFacetValue> raw = queuedRefinements.get(shardNumber); if (null == raw) { raw = Collections.<PivotFacetValue>emptyList(); } return Collections.unmodifiableList(raw); } /** * Clears the list of queued refinements for the specified shard * * @see #addRefinement * @see #getQueuedRefinements */ public void removeAllRefinementsForShard(int shardNumber) { queuedRefinements.remove(shardNumber); } /** * If true, then additional refinement requests are needed to flesh out the correct * counts for this Pivot * * @see #getQueuedRefinements */ public boolean isRefinementsRequired() { return ! queuedRefinements.isEmpty(); } /** * A recursive method for generating <code>NamedLists</code> for this pivot * suitable for including in a pivot facet response to the original distributed request. * * @see PivotFacetField#trim * @see PivotFacetField#convertToListOfNamedLists */ public List<NamedList<Object>> getTrimmedPivotsAsListOfNamedLists() { if (null == pivotFacetField) { // no values in any shard for the top field of this pivot return Collections.<NamedList<Object>>emptyList(); } pivotFacetField.trim(); return pivotFacetField.convertToListOfNamedLists(); } /** * A recursive method for determining which {@link PivotFacetValue}s need to be * refined for this pivot. * * @see PivotFacetField#queuePivotRefinementRequests */ public void queuePivotRefinementRequests() { if (null == pivotFacetField) return; // NOOP pivotFacetField.sort(); pivotFacetField.queuePivotRefinementRequests(this); } /** * Recursively merges the response from the specified shard, tracking the known shards. * * @see PivotFacetField#contributeFromShard * @see PivotFacetField#createFromListOfNamedLists */ public void mergeResponseFromShard(int shardNumber, ResponseBuilder rb, List<NamedList<Object>> response) { knownShards.set(shardNumber); if (pivotFacetField == null) { pivotFacetField = PivotFacetField.createFromListOfNamedLists(shardNumber, rb, null, response); } else { pivotFacetField.contributeFromShard(shardNumber, rb, response); } } public String toString() { return "[" + facetStr + "] | " + this.getKey(); } }