pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / contrib / facet / src / java / org / apache / lucene / facet / search / params / FacetRequest.java
1 package org.apache.lucene.facet.search.params;
2
3 import java.io.IOException;
4
5 import org.apache.lucene.index.IndexReader;
6
7 import org.apache.lucene.facet.index.params.CategoryListParams;
8 import org.apache.lucene.facet.search.CategoryListIterator;
9 import org.apache.lucene.facet.search.FacetArrays;
10 import org.apache.lucene.facet.search.FacetResultsHandler;
11 import org.apache.lucene.facet.search.TopKFacetResultsHandler;
12 import org.apache.lucene.facet.search.TopKInEachNodeHandler;
13 import org.apache.lucene.facet.search.aggregator.Aggregator;
14 import org.apache.lucene.facet.search.cache.CategoryListData;
15 import org.apache.lucene.facet.search.cache.CategoryListCache;
16 import org.apache.lucene.facet.taxonomy.CategoryPath;
17 import org.apache.lucene.facet.taxonomy.TaxonomyReader;
18
19 /**
20  * Licensed to the Apache Software Foundation (ASF) under one or more
21  * contributor license agreements.  See the NOTICE file distributed with
22  * this work for additional information regarding copyright ownership.
23  * The ASF licenses this file to You under the Apache License, Version 2.0
24  * (the "License"); you may not use this file except in compliance with
25  * the License.  You may obtain a copy of the License at
26  *
27  *     http://www.apache.org/licenses/LICENSE-2.0
28  *
29  * Unless required by applicable law or agreed to in writing, software
30  * distributed under the License is distributed on an "AS IS" BASIS,
31  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32  * See the License for the specific language governing permissions and
33  * limitations under the License.
34  */
35
36 /**
37  * Request to accumulate facet information for a specified facet and possibly 
38  * also some of its descendants, upto a specified depth.
39  * <p>
40  * The facet request additionally defines what information should 
41  * be computed within the facet results, if and how should results
42  * be ordered, etc.
43  * <P>
44  * An example facet request is to look at all sub-categories of "Author", and
45  * return the 10 with the highest counts (sorted by decreasing count). 
46  * 
47  * @lucene.experimental
48  */
49 public abstract class FacetRequest implements Cloneable {
50
51   /**
52    * Default depth for facets accumulation.
53    * @see #getDepth()
54    */
55   public static final int DEFAULT_DEPTH = 1;
56
57   /**
58    * Default sort mode.
59    * @see #getSortBy()
60    */
61   public static final SortBy DEFAULT_SORT_BY = SortBy.VALUE;
62
63   /**
64    * Default result mode
65    * @see #getResultMode()
66    */
67   public static final ResultMode DEFAULT_RESULT_MODE = ResultMode.GLOBAL_FLAT;
68
69   private final CategoryPath categoryPath;
70   private final int numResults;
71   private int numLabel;
72   private int depth;
73   private SortOrder sortOrder;
74   private SortBy sortBy;
75
76   /**
77    * Computed at construction, this hashCode is based on two final members
78    * {@link CategoryPath} and <code>numResults</code>
79    */
80   private final int hashCode;
81   
82   private ResultMode resultMode = DEFAULT_RESULT_MODE;
83
84   /**
85    * Initialize the request with a given path, and a requested number of facets
86    * results. By default, all returned results would be labeled - to alter this
87    * default see {@link #setNumLabel(int)}.
88    * <p>
89    * <b>NOTE:</b> if <code>numResults</code> is given as
90    * <code>Integer.MAX_VALUE</code> than all the facet results would be
91    * returned, without any limit.
92    * <p>
93    * <b>NOTE:</b> it is assumed that the given {@link CategoryPath} is not
94    * modified after construction of this object. Otherwise, some things may not
95    * function properly, e.g. {@link #hashCode()}.
96    * 
97    * @throws IllegalArgumentException if numResults is &le; 0
98    */
99   public FacetRequest(CategoryPath path, int numResults) {
100     if (numResults <= 0) {
101       throw new IllegalArgumentException("num results must be a positive (>0) number: " + numResults);
102     }
103     if (path == null) {
104       throw new IllegalArgumentException("category path cannot be null!");
105     }
106     categoryPath = path;
107     this.numResults = numResults;
108     numLabel = numResults;
109     depth = DEFAULT_DEPTH;
110     sortBy = DEFAULT_SORT_BY;
111     sortOrder = SortOrder.DESCENDING;
112     
113     hashCode = categoryPath.hashCode() ^ this.numResults;
114   }
115
116   @Override
117   public Object clone() throws CloneNotSupportedException {
118     // Overridden to make it public
119     return super.clone();
120   }
121   
122   public void setNumLabel(int numLabel) {
123     this.numLabel = numLabel;
124   }
125
126   public void setDepth(int depth) {
127     this.depth = depth;
128   }
129
130   public void setSortOrder(SortOrder sortOrder) {
131     this.sortOrder = sortOrder;
132   }
133
134   public void setSortBy(SortBy sortBy) {
135     this.sortBy = sortBy;
136   }
137
138   /**
139    * The root category of this facet request. The categories that are returned
140    * as a result of this request will all be descendants of this root.
141    * <p>
142    * <b>NOTE:</b> you should not modify the returned {@link CategoryPath}, or
143    * otherwise some methonds may not work properly, e.g. {@link #hashCode()}.
144    */
145   public final CategoryPath getCategoryPath() {
146     return categoryPath;
147   }
148
149   /**
150    * How deeply to look under the given category. If the depth is 0,
151    * only the category itself is counted. If the depth is 1, its immediate
152    * children are also counted, and so on. If the depth is Integer.MAX_VALUE,
153    * all the category's descendants are counted.<br>
154    * TODO (Facet): add AUTO_EXPAND option  
155    */
156   public final int getDepth() {
157     return depth;
158   }
159
160   /**
161    * If getNumLabel()<getNumResults(), only the first getNumLabel() results
162    * will have their category paths calculated, and the rest will only be
163    * available as ordinals (category numbers) and will have null paths.
164    * <P>
165    * If Integer.MAX_VALUE is specified, all 
166    * results are labled.
167    * <P>
168    * The purpose of this parameter is to avoid having to run the whole
169    * faceted search again when the user asks for more values for the facet;
170    * The application can ask (getNumResults()) for more values than it needs
171    * to show, but keep getNumLabel() only the number it wants to immediately
172    * show. The slow-down caused by finding more values is negligible, because
173    * the slowest part - finding the categories' paths, is avoided.
174    * <p>
175    * Depending on the {@link #getResultMode() LimitsMode},
176    * this limit is applied globally or per results node.
177    * In the global mode, if this limit is 3, 
178    * only 3 top results would be labeled.
179    * In the per-node mode, if this limit is 3,
180    * 3 top children of {@link #getCategoryPath() the target category} would be labeled,
181    * as well as 3 top children of each of them, and so forth, until the depth defined 
182    * by {@link #getDepth()}.
183    * @see #getResultMode()
184    */
185   public final int getNumLabel() {
186     return numLabel;
187   }
188
189   /**
190    * The number of sub-categories to return (at most).
191    * If the sub-categories are returned.
192    * <p>
193    * If Integer.MAX_VALUE is specified, all 
194    * sub-categories are returned.
195    * <p>
196    * Depending on the {@link #getResultMode() LimitsMode},
197    * this limit is applied globally or per results node.
198    * In the global mode, if this limit is 3, 
199    * only 3 top results would be computed.
200    * In the per-node mode, if this limit is 3,
201    * 3 top children of {@link #getCategoryPath() the target category} would be returned,
202    * as well as 3 top children of each of them, and so forth, until the depth defined 
203    * by {@link #getDepth()}.
204    * @see #getResultMode()
205    */
206   public final int getNumResults() {
207     return numResults;
208   }
209
210   /**
211    * Sort options for facet results.
212    */
213   public enum SortBy { 
214     /** sort by category ordinal with the taxonomy */
215     ORDINAL, 
216
217     /** sort by computed category value */ 
218     VALUE 
219   }
220
221   /** Specify how should results be sorted. */
222   public final SortBy getSortBy() {
223     return sortBy;
224   }
225
226   /** Requested sort order for the results. */
227   public enum SortOrder { ASCENDING, DESCENDING }
228
229   /** Return the requested order of results. */
230   public final SortOrder getSortOrder() {
231     return sortOrder;
232   }
233
234   @Override
235   public String toString() {
236     return categoryPath.toString()+" nRes="+numResults+" nLbl="+numLabel;
237   }
238
239   /**
240    * Creates a new {@link FacetResultsHandler} that matches the request logic
241    * and current settings, such as {@link #getDepth() depth},
242    * {@link #getResultMode() limits-mode}, etc, as well as the passed in
243    * {@link TaxonomyReader}.
244    * 
245    * @param taxonomyReader taxonomy reader is needed e.g. for knowing the
246    *        taxonomy size.
247    */
248   public FacetResultsHandler createFacetResultsHandler(TaxonomyReader taxonomyReader) {
249     try {
250       if (resultMode == ResultMode.PER_NODE_IN_TREE) {
251         return new TopKInEachNodeHandler(taxonomyReader, (FacetRequest) clone());
252       } 
253       return new TopKFacetResultsHandler(taxonomyReader, (FacetRequest) clone());
254     } catch (CloneNotSupportedException e) {
255       // Shouldn't happen since we implement Cloneable. If it does happen, it is
256       // probably because the class was changed to not implement Cloneable
257       // anymore.
258       throw new RuntimeException(e);
259     }
260   }
261
262   /**
263    * Result structure manner of applying request's limits such as 
264    * {@link #getNumLabel()} and
265    * {@link #getNumResults()}.
266    */
267   public enum ResultMode { 
268     /** Limits are applied per node, and the result has a full tree structure. */
269     PER_NODE_IN_TREE, 
270
271     /** Limits are applied globally, on total number of results, and the result has a flat structure. */
272     GLOBAL_FLAT
273   }
274
275   /** Return the requested result mode. */
276   public final ResultMode getResultMode() {
277     return resultMode;
278   }
279
280   /**
281    * @param resultMode the resultMode to set
282    * @see #getResultMode()
283    */
284   public void setResultMode(ResultMode resultMode) {
285     this.resultMode = resultMode;
286   }
287
288   @Override
289   public int hashCode() {
290     return hashCode; 
291   }
292   
293   @Override
294   public boolean equals(Object o) {
295     if (o instanceof FacetRequest) {
296       FacetRequest that = (FacetRequest)o;
297       return that.hashCode == this.hashCode &&
298           that.categoryPath.equals(this.categoryPath) &&
299           that.numResults == this.numResults &&
300           that.depth == this.depth &&
301           that.resultMode == this.resultMode &&
302           that.numLabel == this.numLabel;
303     }
304     return false;
305   }
306
307   /**
308    * Create an aggregator for this facet request. Aggregator action depends on
309    * request definition. For a count request, it will usually increment the
310    * count for that facet.
311    * 
312    * @param useComplements
313    *          whether the complements optimization is being used for current
314    *          computation.
315    * @param arrays
316    *          provider for facet arrays in use for current computation.
317    * @param indexReader
318    *          index reader in effect.
319    * @param taxonomy
320    *          reader of taxonomy in effect.
321    * @throws IOException
322    */
323   public abstract Aggregator createAggregator(boolean useComplements,
324       FacetArrays arrays, IndexReader indexReader,
325       TaxonomyReader taxonomy) throws IOException;
326
327   /**
328    * Create the category list iterator for the specified partition.
329    * If a non null cache is provided which contains the required data, 
330    * use it for the iteration.
331    */
332   public CategoryListIterator createCategoryListIterator(IndexReader reader,
333       TaxonomyReader taxo, FacetSearchParams sParams, int partition)
334       throws IOException {
335     CategoryListCache clCache = sParams.getClCache();
336     CategoryListParams clParams = sParams.getFacetIndexingParams().getCategoryListParams(categoryPath);
337     if (clCache!=null) {
338       CategoryListData clData = clCache.get(clParams);
339       if (clData!=null) {
340         return clData.iterator(partition);
341       }
342     }
343     return clParams.createCategoryListIterator(reader, partition);
344   }
345
346   /**
347    * Return the value of a category used for facets computations for this
348    * request. For a count request this would be the count for that facet, i.e.
349    * an integer number. but for other requests this can be the result of a more
350    * complex operation, and the result can be any double precision number.
351    * Having this method with a general name <b>value</b> which is double
352    * precision allows to have more compact API and code for handling counts and
353    * perhaps other requests (such as for associations) very similarly, and by
354    * the same code and API, avoiding code duplication.
355    * 
356    * @param arrays
357    *          provider for facet arrays in use for current computation.
358    * @param idx
359    *          an index into the count arrays now in effect in
360    *          <code>arrays</code>. E.g., for ordinal number <i>n</i>, with
361    *          partition, of size <i>partitionSize</i>, now covering <i>n</i>,
362    *          <code>getValueOf</code> would be invoked with <code>idx</code>
363    *          being <i>n</i> % <i>partitionSize</i>.
364    */
365   public abstract double getValueOf(FacetArrays arrays, int idx);
366   
367   /**
368    * Indicates whether this facet request is eligible for applying the complements optimization.
369    */
370   public boolean supportsComplements() {
371     return false; // by default: no
372   }
373
374   /** Indicates whether the results of this request depends on each result document's score */
375   public abstract boolean requireDocumentScore();
376
377 }