package org.apache.lucene.facet.search.results; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.lucene.facet.taxonomy.CategoryPath; import org.apache.lucene.facet.taxonomy.TaxonomyReader; /* * 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. */ /** * Mutable implementation for Result of faceted search for a certain taxonomy node. * * @lucene.experimental */ public class MutableFacetResultNode implements FacetResultNode { /** * Empty sub results to be returned when there are no results. * We never return null, so that code using this can remain simpler. */ private static final ArrayList<FacetResultNode> EMPTY_SUB_RESULTS = new ArrayList<FacetResultNode>(); private int ordinal; private CategoryPath label = null; private double value; private double residue; private List<FacetResultNode> subResults; /** * Create a Facet Result Node. * * @param ordinal * ordinal in the taxonomy of the category of this result. * @param value * value this result. */ public MutableFacetResultNode(int ordinal, double value) { this(ordinal, value, 0, null, null); } /** * Reset a facet Result Node. * <p> * Used at the population of facet results, not intended for regular use by * applications. * * @param ordinal * ordinal in the taxonomy of the category of this result. * @param value * value of this result. */ public void reset(int ordinal, double value) { this.ordinal = ordinal; this.value = value; if (subResults != null) { subResults.clear(); } label = null; residue = 0; } /** * Create a Facet Result Node. * * @param ordinal * ordinal in the taxonomy of the category of this result. * @param value * value of this result. * @param residue * Value of screened out sub results. * @param label * label of the category path of this result. * @param subResults * - sub results, usually descendants, sometimes child results, of * this result - depending on the request. */ public MutableFacetResultNode(int ordinal, double value, double residue, CategoryPath label, List<FacetResultNode> subResults) { this.ordinal = ordinal; this.value = value; this.residue = residue; this.label = label; this.subResults = subResults; } /** * Create a mutable facet result node from another result node * @param other other result node to copy from * @param takeSubResults set to true to take also sub results of other node */ public MutableFacetResultNode(FacetResultNode other, boolean takeSubResults) { this(other.getOrdinal(), other.getValue(), other.getResidue(), other .getLabel(), takeSubResults ? resultsToList(other.getSubResults()) : null); } private static List<FacetResultNode> resultsToList( Iterable<? extends FacetResultNode> subResults) { if (subResults == null) { return null; } ArrayList<FacetResultNode> res = new ArrayList<FacetResultNode>(); for (FacetResultNode r : subResults) { res.add(r); } return res; } @Override public String toString() { return toString(""); } /** * Number of sub results. */ private int numSubResults() { if (subResults == null) { return 0; } return subResults.size(); } /* * (non-Javadoc) * * @see * org.apache.lucene.facet.search.results2.FacetResultNode#toString(java.lang. * String) */ public String toString(String prefix) { StringBuilder sb = new StringBuilder(prefix); sb.append("Facet Result Node with ").append(numSubResults()).append( " sub result nodes.\n"); // label sb.append(prefix).append("Name: ").append(getLabel()).append("\n"); // value sb.append(prefix).append("Value: ").append(value).append("\n"); // residue sb.append(prefix).append("Residue: ").append(residue).append("\n"); if (subResults != null) { int i = 0; for (FacetResultNode subRes : subResults) { sb.append("\n").append(prefix).append("Subresult #").append(i++) .append("\n").append(subRes.toString(prefix + "\t")); } } return sb.toString(); } public final int getOrdinal() { return ordinal; } public final CategoryPath getLabel() { return label; } /** * Set the label of the category of this result. * @param label the label to set. * @see #getLabel() */ public void setLabel(CategoryPath label) { this.label = label; } public final double getValue() { return value; } /** * Set the value of this result. * * @param value * the value to set * @see #getValue() */ public void setValue(double value) { this.value = value; } /** * increase the value for this result. * @param addedValue the value to add * @see #getValue() */ public void increaseValue(double addedValue) { this.value += addedValue; } public final double getResidue() { return residue; } /** * Set the residue. * @param residue the residue to set * @see #getResidue() */ public void setResidue(double residue) { this.residue = residue; } /** * increase the residue for this result. * @param addedResidue the residue to add * @see #getResidue() */ public void increaseResidue(double addedResidue) { this.residue += addedResidue; } public final Iterable<? extends FacetResultNode> getSubResults() { return subResults != null ? subResults : EMPTY_SUB_RESULTS; } /** * Trim sub results to a given size. * <p> * Note: Although the {@link #getResidue()} is not guaranteed to be * accurate, it is worth fixing it, as possible, by taking under account the * trimmed sub-nodes. */ public void trimSubResults(int size) { if (subResults == null || subResults.size() == 0) { return; } ArrayList<FacetResultNode> trimmed = new ArrayList<FacetResultNode>(size); for (int i = 0; i < subResults.size() && i < size; i++) { MutableFacetResultNode trimmedNode = toImpl(subResults.get(i)); trimmedNode.trimSubResults(size); trimmed.add(trimmedNode); } /* * If we are trimming, it means Sampling is in effect and the extra * (over-sampled) results are being trimmed. Although the residue is not * guaranteed to be accurate for Sampling, we try our best to fix it. * The node's residue now will take under account the sub-nodes we're * trimming. */ for (int i = size; i < subResults.size(); i++) { increaseResidue(subResults.get(i).getValue()); } subResults = trimmed; } /** * Set the sub results. * @param subResults the sub-results to set */ public void setSubResults(List<FacetResultNode> subResults) { this.subResults = subResults; } /** * Append a sub result (as last). * @param subRes sub-result to be appended */ public void appendSubResult(FacetResultNode subRes) { if (subResults == null) { subResults = new ArrayList<FacetResultNode>(); } subResults.add(subRes); } /** * Insert sub result (as first). * @param subRes sub-result to be inserted */ public void insertSubResult(FacetResultNode subRes) { if (subResults == null) { subResults = new ArrayList<FacetResultNode>(); } subResults.add(0, subRes); } /* * (non-Javadoc) * * @see * org.apache.lucene.facet.search.results.FacetResultNode#getLabel(org.apache.lucene * .facet.taxonomy.TaxonomyReader) */ public final CategoryPath getLabel(TaxonomyReader taxonomyReader) throws IOException { if (label == null) { label = taxonomyReader.getPath(ordinal); } return label; } /* * (non-Javadoc) * * @see org.apache.lucene.facet.search.results.FacetResultNode#getNumSubResults() */ public final int getNumSubResults() { return subResults == null ? 0 : subResults.size(); } /** * Internal utility: turn a result node into an implementation class * with richer API that allows modifying it. * <p> * In case that input result node is already of an implementation * class only casting is done, but in any case we pay the price * of checking "instance of". * @param frn facet result node to be turned into an implementation class object */ public static MutableFacetResultNode toImpl(FacetResultNode frn) { if (frn instanceof MutableFacetResultNode) { return (MutableFacetResultNode) frn; } return new MutableFacetResultNode(frn, true); } }