+++ /dev/null
-package org.apache.lucene.facet.search;
-
-import java.io.IOException;
-import java.util.ArrayList;
-
-import org.apache.lucene.facet.search.params.FacetRequest;
-import org.apache.lucene.facet.search.results.FacetResult;
-import org.apache.lucene.facet.search.results.FacetResultNode;
-import org.apache.lucene.facet.search.results.MutableFacetResultNode;
-import org.apache.lucene.facet.search.results.IntermediateFacetResult;
-import org.apache.lucene.facet.taxonomy.TaxonomyReader;
-import org.apache.lucene.facet.taxonomy.TaxonomyReader.ChildrenArrays;
-import org.apache.lucene.facet.util.ResultSortUtils;
-
-/**
- * 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.
- */
-
-/**
- * Generate Top-K results for a particular FacetRequest.
- * <p>
- * K is global (among all results) and is defined by {@link FacetRequest#getNumResults()}.
- * <p>
- * Note: Values of 0 (Zero) are ignored by this results handler.
- *
- * @lucene.experimental
- */
-public class TopKFacetResultsHandler extends FacetResultsHandler {
-
- /**
- * Construct top-K results handler.
- * @param taxonomyReader taxonomy reader
- * @param facetRequest facet request being served
- */
- public TopKFacetResultsHandler(TaxonomyReader taxonomyReader,
- FacetRequest facetRequest) {
- super(taxonomyReader, facetRequest);
- }
-
- // fetch top K for specific partition.
- @Override
- public IntermediateFacetResult fetchPartitionResult(FacetArrays facetArrays, int offset)
- throws IOException {
- TopKFacetResult res = null;
- int ordinal = taxonomyReader.getOrdinal(facetRequest.getCategoryPath());
- if (ordinal != TaxonomyReader.INVALID_ORDINAL) {
- double value = 0;
- if (isSelfPartition(ordinal, facetArrays, offset)) {
- int partitionSize = facetArrays.getArraysLength();
- value = facetRequest.getValueOf(facetArrays, ordinal % partitionSize);
- }
-
- // TODO (Facet): should initial value of "residue" depend on aggregator if not sum?
- MutableFacetResultNode parentResultNode =
- new MutableFacetResultNode(ordinal, value);
-
- Heap<FacetResultNode> heap = ResultSortUtils.createSuitableHeap(facetRequest);
- int totalFacets = heapDescendants(ordinal, heap, parentResultNode, facetArrays, offset);
- res = new TopKFacetResult(facetRequest, parentResultNode, totalFacets);
- res.setHeap(heap);
- }
- return res;
- }
-
- // merge given top K results into current
- @Override
- public IntermediateFacetResult mergeResults(IntermediateFacetResult... tmpResults) throws IOException {
-
- int ordinal = taxonomyReader.getOrdinal(facetRequest.getCategoryPath());
- MutableFacetResultNode resNode = new MutableFacetResultNode(ordinal, 0);
-
- int totalFacets = 0;
- Heap<FacetResultNode> heap = null;
-
- // merge other results in queue
- for (IntermediateFacetResult tmpFres : tmpResults) {
- // cast should succeed
- TopKFacetResult fres = (TopKFacetResult) tmpFres;
- totalFacets += fres.getNumValidDescendants();
- // set the value for the result node representing the facet request
- resNode.increaseValue(fres.getFacetResultNode().getValue());
- Heap<FacetResultNode> tmpHeap = fres.getHeap();
- if (heap == null) {
- heap = tmpHeap;
- continue;
- }
- // bring sub results from heap of tmp res into result heap
- for (int i = tmpHeap.size(); i > 0; i--) {
-
- FacetResultNode a = heap.insertWithOverflow(tmpHeap.pop());
- if (a != null) {
- resNode.increaseResidue(a.getResidue());
- }
- }
- }
-
- TopKFacetResult res = new TopKFacetResult(facetRequest, resNode, totalFacets);
- res.setHeap(heap);
- return res;
- }
-
- /**
- * Finds the top K descendants of ordinal, which are at most facetRequest.getDepth()
- * deeper than facetRequest.getCategoryPath (whose ordinal is input parameter ordinal).
- * Candidates are restricted to current "counting list" and current "partition",
- * they join the overall priority queue pq of size K.
- * @return total number of descendants considered here by pq, excluding ordinal itself.
- */
- private int heapDescendants(int ordinal, Heap<FacetResultNode> pq,
- MutableFacetResultNode parentResultNode, FacetArrays facetArrays, int offset) {
- int partitionSize = facetArrays.getArraysLength();
- int endOffset = offset + partitionSize;
- ChildrenArrays childrenArray = taxonomyReader.getChildrenArrays();
- int[] youngestChild = childrenArray.getYoungestChildArray();
- int[] olderSibling = childrenArray.getOlderSiblingArray();
- FacetResultNode reusable = null;
- int localDepth = 0;
- int depth = facetRequest.getDepth();
- int[] ordinalStack = new int[2+Math.min(Short.MAX_VALUE, depth)];
- int childrenCounter = 0;
-
- int tosOrdinal; // top of stack element
-
- int yc = youngestChild[ordinal];
- while (yc >= endOffset) {
- yc = olderSibling[yc];
- }
- // make use of the fact that TaxonomyReader.INVALID_ORDINAL == -1, < endOffset
- // and it, too, can stop the loop.
- ordinalStack[++localDepth] = yc;
-
- /*
- * stack holds input parameter ordinal in position 0.
- * Other elements are < endoffset.
- * Only top of stack can be TaxonomyReader.INVALID_ORDINAL, and this if and only if
- * the element below it exhausted all its children: has them all processed.
- *
- * stack elements are processed (counted and accumulated) only if they
- * belong to current partition (between offset and endoffset) and first time
- * they are on top of stack
- *
- * loop as long as stack is not empty of elements other than input ordinal, or for a little while -- it sibling
- */
- while (localDepth > 0) {
- tosOrdinal = ordinalStack[localDepth];
- if (tosOrdinal == TaxonomyReader.INVALID_ORDINAL) {
- // element below tos has all its children, and itself, all processed
- // need to proceed to its sibling
- localDepth--;
- // change element now on top of stack to its sibling.
- ordinalStack[localDepth] = olderSibling[ordinalStack[localDepth]];
- continue;
- }
- // top of stack is not invalid, this is the first time we see it on top of stack.
- // collect it, if belongs to current partition, and then push its kids on itself, if applicable
- if (tosOrdinal >= offset) { // tosOrdinal resides in current partition
- int relativeOrdinal = tosOrdinal % partitionSize;
- double value = facetRequest.getValueOf(facetArrays, relativeOrdinal);
- if (value != 0 && !Double.isNaN(value)) {
- // Count current ordinal -- the TOS
- if (reusable == null) {
- reusable = new MutableFacetResultNode(tosOrdinal, value);
- } else {
- // it is safe to cast since reusable was created here.
- ((MutableFacetResultNode)reusable).reset(tosOrdinal, value);
- }
- ++childrenCounter;
- reusable = pq.insertWithOverflow(reusable);
- if (reusable != null) {
- // TODO (Facet): is other logic (not add) needed, per aggregator?
- parentResultNode.increaseResidue(reusable.getValue());
- }
- }
- }
- if (localDepth < depth) {
- // push kid of current tos
- yc = youngestChild[tosOrdinal];
- while (yc >= endOffset) {
- yc = olderSibling[yc];
- }
- ordinalStack[++localDepth] = yc;
- } else { // localDepth == depth; current tos exhausted its possible children, mark this by pushing INVALID_ORDINAL
- ordinalStack[++localDepth] = TaxonomyReader.INVALID_ORDINAL;
- }
- } // endof while stack is not empty
-
- return childrenCounter; // we're done
- }
-
- @Override
- public FacetResult renderFacetResult(IntermediateFacetResult tmpResult) {
- TopKFacetResult res = (TopKFacetResult) tmpResult; // cast is safe by contract of this class
- if (res != null) {
- Heap<FacetResultNode> heap = res.getHeap();
- MutableFacetResultNode resNode = (MutableFacetResultNode)res.getFacetResultNode(); // cast safe too
- for (int i = heap.size(); i > 0; i--) {
- resNode.insertSubResult(heap.pop());
- }
- }
- return res;
- }
-
- @Override
- public FacetResult rearrangeFacetResult(FacetResult facetResult) {
- TopKFacetResult res = (TopKFacetResult) facetResult; // cast is safe by contract of this class
- Heap<FacetResultNode> heap = res.getHeap();
- heap.clear(); // just to be safe
- MutableFacetResultNode topFrn = (MutableFacetResultNode) res.getFacetResultNode(); // safe cast
- for (FacetResultNode frn : topFrn.getSubResults()) {
- heap.add(frn);
- }
- int size = heap.size();
- ArrayList<FacetResultNode> subResults = new ArrayList<FacetResultNode>(size);
- for (int i = heap.size(); i > 0; i--) {
- subResults.add(0,heap.pop());
- }
- topFrn.setSubResults(subResults);
- return res;
- }
-
- @Override
- // label top K sub results
- public void labelResult(FacetResult facetResult) throws IOException {
- if (facetResult != null) { // any result to label?
- FacetResultNode facetResultNode = facetResult.getFacetResultNode();
- if (facetResultNode != null) { // any result to label?
- facetResultNode.getLabel(taxonomyReader);
- int num2label = facetRequest.getNumLabel();
- for (FacetResultNode frn : facetResultNode.getSubResults()) {
- if (--num2label < 0) {
- break;
- }
- frn.getLabel(taxonomyReader);
- }
- }
- }
- }
-
- ////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////
-
- /**
- * Private Mutable implementation of result of faceted search.
- */
- private static class TopKFacetResult extends FacetResult implements IntermediateFacetResult {
-
- // TODO (Facet): is it worth to override PriorityQueue.getSentinelObject()
- // for any of our PQs?
- private Heap<FacetResultNode> heap;
-
- /**
- * Create a Facet Result.
- * @param facetRequest Request for which this result was obtained.
- * @param facetResultNode top result node for this facet result.
- * @param totalFacets - number of children of the targetFacet, up till the requested depth.
- */
- TopKFacetResult(FacetRequest facetRequest, MutableFacetResultNode facetResultNode, int totalFacets) {
- super(facetRequest, facetResultNode, totalFacets);
- }
-
- /**
- * @return the heap
- */
- public Heap<FacetResultNode> getHeap() {
- return heap;
- }
-
- /**
- * Set the heap for this result.
- * @param heap heap top be set.
- */
- public void setHeap(Heap<FacetResultNode> heap) {
- this.heap = heap;
- }
-
- }
-
- //////////////////////////////////////////////////////
-}