1 package org.apache.lucene.facet.search.results;
3 import java.io.IOException;
4 import java.util.ArrayList;
7 import org.apache.lucene.facet.taxonomy.CategoryPath;
8 import org.apache.lucene.facet.taxonomy.TaxonomyReader;
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed with
13 * this work for additional information regarding copyright ownership.
14 * The ASF licenses this file to You under the Apache License, Version 2.0
15 * (the "License"); you may not use this file except in compliance with
16 * the License. You may obtain a copy of the License at
18 * http://www.apache.org/licenses/LICENSE-2.0
20 * Unless required by applicable law or agreed to in writing, software
21 * distributed under the License is distributed on an "AS IS" BASIS,
22 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 * See the License for the specific language governing permissions and
24 * limitations under the License.
28 * Mutable implementation for Result of faceted search for a certain taxonomy node.
30 * @lucene.experimental
32 public class MutableFacetResultNode implements FacetResultNode {
35 * Empty sub results to be returned when there are no results.
36 * We never return null, so that code using this can remain simpler.
38 private static final ArrayList<FacetResultNode> EMPTY_SUB_RESULTS = new ArrayList<FacetResultNode>();
41 private CategoryPath label = null;
43 private double residue;
44 private List<FacetResultNode> subResults;
47 * Create a Facet Result Node.
50 * ordinal in the taxonomy of the category of this result.
54 public MutableFacetResultNode(int ordinal, double value) {
55 this(ordinal, value, 0, null, null);
59 * Reset a facet Result Node.
61 * Used at the population of facet results, not intended for regular use by
65 * ordinal in the taxonomy of the category of this result.
67 * value of this result.
69 public void reset(int ordinal, double value) {
70 this.ordinal = ordinal;
72 if (subResults != null) {
80 * Create a Facet Result Node.
83 * ordinal in the taxonomy of the category of this result.
85 * value of this result.
87 * Value of screened out sub results.
89 * label of the category path of this result.
91 * - sub results, usually descendants, sometimes child results, of
92 * this result - depending on the request.
94 public MutableFacetResultNode(int ordinal, double value, double residue,
95 CategoryPath label, List<FacetResultNode> subResults) {
96 this.ordinal = ordinal;
98 this.residue = residue;
100 this.subResults = subResults;
104 * Create a mutable facet result node from another result node
105 * @param other other result node to copy from
106 * @param takeSubResults set to true to take also sub results of other node
108 public MutableFacetResultNode(FacetResultNode other, boolean takeSubResults) {
109 this(other.getOrdinal(), other.getValue(), other.getResidue(), other
110 .getLabel(), takeSubResults ? resultsToList(other.getSubResults())
114 private static List<FacetResultNode> resultsToList(
115 Iterable<? extends FacetResultNode> subResults) {
116 if (subResults == null) {
119 ArrayList<FacetResultNode> res = new ArrayList<FacetResultNode>();
120 for (FacetResultNode r : subResults) {
127 public String toString() {
132 * Number of sub results.
134 private int numSubResults() {
135 if (subResults == null) {
138 return subResults.size();
145 * org.apache.lucene.facet.search.results2.FacetResultNode#toString(java.lang.
148 public String toString(String prefix) {
149 StringBuilder sb = new StringBuilder(prefix);
151 sb.append("Facet Result Node with ").append(numSubResults()).append(
152 " sub result nodes.\n");
155 sb.append(prefix).append("Name: ").append(getLabel()).append("\n");
158 sb.append(prefix).append("Value: ").append(value).append("\n");
161 sb.append(prefix).append("Residue: ").append(residue).append("\n");
163 if (subResults != null) {
165 for (FacetResultNode subRes : subResults) {
166 sb.append("\n").append(prefix).append("Subresult #").append(i++)
167 .append("\n").append(subRes.toString(prefix + "\t"));
171 return sb.toString();
174 public final int getOrdinal() {
178 public final CategoryPath getLabel() {
183 * Set the label of the category of this result.
184 * @param label the label to set.
187 public void setLabel(CategoryPath label) {
191 public final double getValue() {
196 * Set the value of this result.
202 public void setValue(double value) {
207 * increase the value for this result.
208 * @param addedValue the value to add
211 public void increaseValue(double addedValue) {
212 this.value += addedValue;
215 public final double getResidue() {
221 * @param residue the residue to set
224 public void setResidue(double residue) {
225 this.residue = residue;
229 * increase the residue for this result.
230 * @param addedResidue the residue to add
233 public void increaseResidue(double addedResidue) {
234 this.residue += addedResidue;
237 public final Iterable<? extends FacetResultNode> getSubResults() {
238 return subResults != null ? subResults : EMPTY_SUB_RESULTS;
242 * Trim sub results to a given size.
244 * Note: Although the {@link #getResidue()} is not guaranteed to be
245 * accurate, it is worth fixing it, as possible, by taking under account the
248 public void trimSubResults(int size) {
249 if (subResults == null || subResults.size() == 0) {
253 ArrayList<FacetResultNode> trimmed = new ArrayList<FacetResultNode>(size);
254 for (int i = 0; i < subResults.size() && i < size; i++) {
255 MutableFacetResultNode trimmedNode = toImpl(subResults.get(i));
256 trimmedNode.trimSubResults(size);
257 trimmed.add(trimmedNode);
261 * If we are trimming, it means Sampling is in effect and the extra
262 * (over-sampled) results are being trimmed. Although the residue is not
263 * guaranteed to be accurate for Sampling, we try our best to fix it.
264 * The node's residue now will take under account the sub-nodes we're
267 for (int i = size; i < subResults.size(); i++) {
268 increaseResidue(subResults.get(i).getValue());
271 subResults = trimmed;
275 * Set the sub results.
276 * @param subResults the sub-results to set
278 public void setSubResults(List<FacetResultNode> subResults) {
279 this.subResults = subResults;
283 * Append a sub result (as last).
284 * @param subRes sub-result to be appended
286 public void appendSubResult(FacetResultNode subRes) {
287 if (subResults == null) {
288 subResults = new ArrayList<FacetResultNode>();
290 subResults.add(subRes);
294 * Insert sub result (as first).
295 * @param subRes sub-result to be inserted
297 public void insertSubResult(FacetResultNode subRes) {
298 if (subResults == null) {
299 subResults = new ArrayList<FacetResultNode>();
301 subResults.add(0, subRes);
308 * org.apache.lucene.facet.search.results.FacetResultNode#getLabel(org.apache.lucene
309 * .facet.taxonomy.TaxonomyReader)
311 public final CategoryPath getLabel(TaxonomyReader taxonomyReader)
314 label = taxonomyReader.getPath(ordinal);
322 * @see org.apache.lucene.facet.search.results.FacetResultNode#getNumSubResults()
324 public final int getNumSubResults() {
325 return subResults == null ? 0 : subResults.size();
329 * Internal utility: turn a result node into an implementation class
330 * with richer API that allows modifying it.
332 * In case that input result node is already of an implementation
333 * class only casting is done, but in any case we pay the price
334 * of checking "instance of".
335 * @param frn facet result node to be turned into an implementation class object
337 public static MutableFacetResultNode toImpl(FacetResultNode frn) {
338 if (frn instanceof MutableFacetResultNode) {
339 return (MutableFacetResultNode) frn;
341 return new MutableFacetResultNode(frn, true);