diff --git a/lucene/facet/src/java/org/apache/lucene/facet/search/FacetRequest.java b/lucene/facet/src/java/org/apache/lucene/facet/search/FacetRequest.java index fcdada09b0f..b63e621896a 100644 --- a/lucene/facet/src/java/org/apache/lucene/facet/search/FacetRequest.java +++ b/lucene/facet/src/java/org/apache/lucene/facet/search/FacetRequest.java @@ -2,6 +2,7 @@ package org.apache.lucene.facet.search; import java.io.IOException; +import org.apache.lucene.facet.params.CategoryListParams.OrdinalPolicy; import org.apache.lucene.facet.taxonomy.CategoryPath; import org.apache.lucene.facet.taxonomy.TaxonomyReader; @@ -23,30 +24,36 @@ import org.apache.lucene.facet.taxonomy.TaxonomyReader; */ /** - * Request to accumulate facet information for a specified facet and possibly - * also some of its descendants, upto a specified depth. + * Defines an aggregation request for a category. Allows specifying the + * {@link #numResults number of child categories} to return as well as + * {@link #getSortOrder() which} categories to consider the "top" (highest or + * lowest ranking ones). *

- * The facet request additionally defines what information should - * be computed within the facet results, if and how should results - * be ordered, etc. - *

- * An example facet request is to look at all sub-categories of "Author", and - * return the 10 with the highest counts (sorted by decreasing count). + * If the category being aggregated is hierarchical, you can also specify the + * {@link #setDepth(int) depth} up which to aggregate child categories as well + * as how the result should be {@link #setResultMode(ResultMode) constructed}. * * @lucene.experimental */ public abstract class FacetRequest { /** - * Result structure manner of applying request's limits such as - * {@link FacetRequest#getNumLabel()} and {@link FacetRequest#numResults}. - * Only relevant when {@link FacetRequest#getDepth()} is > 1. + * When {@link FacetRequest#getDepth()} is greater than 1, defines the + * structure of the result as well as how constraints such as + * {@link FacetRequest#numResults} and {@link FacetRequest#getNumLabel()} are + * applied. */ public enum ResultMode { - /** Limits are applied per node, and the result has a full tree structure. */ + /** + * Constraints are applied per node, and the result has a full tree + * structure. Default result mode. + */ PER_NODE_IN_TREE, - /** Limits are applied globally, on total number of results, and the result has a flat structure. */ + /** + * Constraints are applied globally, on total number of results, and the + * result has a flat structure. + */ GLOBAL_FLAT } @@ -54,59 +61,50 @@ public abstract class FacetRequest { * Specifies which array of {@link FacetArrays} should be used to resolve * values. When set to {@link #INT} or {@link #FLOAT}, allows creating an * optimized {@link FacetResultsHandler}, which does not call - * {@link FacetRequest#getValueOf(FacetArrays, int)} for every ordinals. + * {@link FacetRequest#getValueOf(FacetArrays, int)} for every ordinal. *

* If set to {@link #BOTH}, the {@link FacetResultsHandler} will use * {@link FacetRequest#getValueOf(FacetArrays, int)} to resolve ordinal * values, although it is recommended that you consider writing a specialized * {@link FacetResultsHandler}. + *

+ * Can also be set to {@link #NONE}, to indicate that this + * {@link FacetRequest} does not use {@link FacetArrays} to aggregate its + * result categories. Such requests won't use {@link FacetResultsHandler}. */ - public enum FacetArraysSource { INT, FLOAT, BOTH } + public enum FacetArraysSource { INT, FLOAT, BOTH, NONE } - /** Requested sort order for the results. */ + /** + * Defines which categories to return. If {@link #DESCENDING} (the default), + * the highest {@link FacetRequest#numResults} weighted categories will be + * returned, otherwise the lowest ones. + */ public enum SortOrder { ASCENDING, DESCENDING } - - /** - * Default depth for facets accumulation. - * @see #getDepth() - */ - public static final int DEFAULT_DEPTH = 1; - - /** - * Default result mode - * @see #getResultMode() - */ - public static final ResultMode DEFAULT_RESULT_MODE = ResultMode.PER_NODE_IN_TREE; - + + /** The category being aggregated in this facet request. */ public final CategoryPath categoryPath; + + /** The number of child categories to return for {@link #categoryPath}. */ public final int numResults; private int numLabel; - private int depth; - private SortOrder sortOrder; + private int depth = 1; + private SortOrder sortOrder = SortOrder.DESCENDING; + private ResultMode resultMode = ResultMode.PER_NODE_IN_TREE; - /** - * Computed at construction, this hashCode is based on two final members - * {@link CategoryPath} and numResults - */ + // Computed at construction; based on categoryPath and numResults. private final int hashCode; - private ResultMode resultMode = DEFAULT_RESULT_MODE; - /** - * Initialize the request with a given path, and a requested number of facets - * results. By default, all returned results would be labeled - to alter this - * default see {@link #setNumLabel(int)}. - *

- * NOTE: if numResults is given as - * Integer.MAX_VALUE than all the facet results would be - * returned, without any limit. - *

- * NOTE: it is assumed that the given {@link CategoryPath} is not - * modified after construction of this object. Otherwise, some things may not - * function properly, e.g. {@link #hashCode()}. + * Constructor with the given category to aggregate and the number of child + * categories to return. * - * @throws IllegalArgumentException if numResults is ≤ 0 + * @param path + * the category to aggregate. Cannot be {@code null}. + * @param numResults + * the number of child categories to return. If set to + * {@code Integer.MAX_VALUE}, all immediate child categories will be + * returned. Must be greater than 0. */ public FacetRequest(CategoryPath path, int numResults) { if (numResults <= 0) { @@ -118,9 +116,6 @@ public abstract class FacetRequest { categoryPath = path; this.numResults = numResults; numLabel = numResults; - depth = DEFAULT_DEPTH; - sortOrder = SortOrder.DESCENDING; - hashCode = categoryPath.hashCode() ^ this.numResults; } @@ -147,123 +142,125 @@ public abstract class FacetRequest { @Override public boolean equals(Object o) { if (o instanceof FacetRequest) { - FacetRequest that = (FacetRequest)o; - return that.hashCode == this.hashCode && + FacetRequest that = (FacetRequest) o; + return that.hashCode == this.hashCode && that.categoryPath.equals(this.categoryPath) && that.numResults == this.numResults && that.depth == this.depth && that.resultMode == this.resultMode && - that.numLabel == this.numLabel; + that.numLabel == this.numLabel && + that.sortOrder == this.sortOrder; } return false; } /** - * How deeply to look under the given category. If the depth is 0, - * only the category itself is counted. If the depth is 1, its immediate - * children are also counted, and so on. If the depth is Integer.MAX_VALUE, - * all the category's descendants are counted.
+ * How deeply to look under {@link #categoryPath}. By default, only its + * immediate children are aggregated (depth=1). If set to + * {@code Integer.MAX_VALUE}, the entire sub-tree of the category will be + * aggregated. + *

+ * NOTE: setting depth to 0 means that only the category itself should + * be aggregated. In that case, make sure to index the category with + * {@link OrdinalPolicy#ALL_PARENTS}, unless it is not the root category (the + * dimension), in which case {@link OrdinalPolicy#ALL_BUT_DIMENSION} is fine + * too. */ public final int getDepth() { - // TODO add AUTO_EXPAND option + // TODO an AUTO_EXPAND option could be useful return depth; } /** - * Returns the {@link FacetArraysSource} this {@link FacetRequest} uses in + * Returns the {@link FacetArraysSource} this request uses in * {@link #getValueOf(FacetArrays, int)}. */ public abstract FacetArraysSource getFacetArraysSource(); /** - * If getNumLabel() < getNumResults(), only the first getNumLabel() results - * will have their category paths calculated, and the rest will only be - * available as ordinals (category numbers) and will have null paths. - *

- * If Integer.MAX_VALUE is specified, all results are labled. - *

- * The purpose of this parameter is to avoid having to run the whole faceted - * search again when the user asks for more values for the facet; The - * application can ask (getNumResults()) for more values than it needs to - * show, but keep getNumLabel() only the number it wants to immediately show. - * The slow-down caused by finding more values is negligible, because the - * slowest part - finding the categories' paths, is avoided. + * Allows to specify the number of categories to label. By default all + * returned categories are labeled. *

- * Depending on the {@link #getResultMode() LimitsMode}, this limit is applied - * globally or per results node. In the global mode, if this limit is 3, only - * 3 top results would be labeled. In the per-node mode, if this limit is 3, 3 - * top children of {@link #categoryPath the target category} would be labeled, - * as well as 3 top children of each of them, and so forth, until the depth - * defined by {@link #getDepth()}. - * - * @see #getResultMode() + * This allows an app to request a large number of results to return, while + * labeling them on-demand (e.g. when the UI requests to show more + * categories). */ public final int getNumLabel() { return numLabel; } - /** Return the requested result mode. */ + /** Return the requested result mode (defaults to {@link ResultMode#PER_NODE_IN_TREE}. */ public final ResultMode getResultMode() { return resultMode; } - /** Return the requested order of results. */ + /** Return the requested order of results (defaults to {@link SortOrder#DESCENDING}. */ public final SortOrder getSortOrder() { return sortOrder; } /** - * Return the value of a category used for facets computations for this - * request. For a count request this would be the count for that facet, i.e. - * an integer number. but for other requests this can be the result of a more - * complex operation, and the result can be any double precision number. - * Having this method with a general name value which is double - * precision allows to have more compact API and code for handling counts and - * perhaps other requests (such as for associations) very similarly, and by - * the same code and API, avoiding code duplication. + * Return the weight of the requested category ordinal. A {@link FacetRequest} + * is responsible for resolving the weight of a category given the + * {@link FacetArrays} and {@link #getFacetArraysSource()}. E.g. a counting + * request will probably return the value of the category from + * {@link FacetArrays#getIntArray()} while an average-weighting request will + * compute the value using both arrays. * * @param arrays - * provider for facet arrays in use for current computation. - * @param idx - * an index into the count arrays now in effect in - * arrays. E.g., for ordinal number n, with - * partition, of size partitionSize, now covering n, - * getValueOf would be invoked with idx - * being n % partitionSize. + * the arrays used to aggregate the categories weights. + * @param ordinal + * the category ordinal for which to return the weight. */ // TODO perhaps instead of getValueOf we can have a postProcess(FacetArrays) // That, together with getFacetArraysSource should allow ResultHandlers to // efficiently obtain the values from the arrays directly - public abstract double getValueOf(FacetArrays arrays, int idx); + public abstract double getValueOf(FacetArrays arrays, int ordinal); @Override public int hashCode() { return hashCode; } + /** + * Sets the depth up to which to aggregate facets. + * + * @see #getDepth() + */ public void setDepth(int depth) { this.depth = depth; } + /** + * Sets the number of categories to label. + * + * @see #getNumLabel() + */ public void setNumLabel(int numLabel) { this.numLabel = numLabel; } /** - * @param resultMode the resultMode to set + * Sets the {@link ResultMode} for this request. + * * @see #getResultMode() */ public void setResultMode(ResultMode resultMode) { this.resultMode = resultMode; } - + + /** + * Sets the {@link SortOrder} for this request. + * + * @see #getSortOrder() + */ public void setSortOrder(SortOrder sortOrder) { this.sortOrder = sortOrder; } @Override public String toString() { - return categoryPath.toString()+" nRes="+numResults+" nLbl="+numLabel; + return categoryPath.toString() + " nRes=" + numResults + " nLbl=" + numLabel; } }