Package org.apache.lucene.facet.search

Source Code of org.apache.lucene.facet.search.FacetResult

package org.apache.lucene.facet.search;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.lucene.facet.params.FacetIndexingParams;
import org.apache.lucene.facet.taxonomy.CategoryPath;
import org.apache.lucene.facet.taxonomy.TaxonomyReader;
import org.apache.lucene.util.CollectionUtil;

/*
* 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.
*/

/**
* Result of faceted search.
*
* @lucene.experimental
*/
public class FacetResult {
 
  private static FacetResultNode addIfNotExist(Map<CategoryPath, FacetResultNode> nodes, FacetResultNode node) {
    FacetResultNode n = nodes.get(node.label);
    if (n == null) {
      nodes.put(node.label, node);
      n = node;
    }
    return n;
  }

  /**
   * A utility for merging multiple {@link FacetResult} of the same
   * (hierarchical) dimension into a single {@link FacetResult}, to reconstruct
   * the hierarchy. The results are merged according to the following rules:
   * <ul>
   * <li>If two results share the same dimension (first component in their
   * {@link CategoryPath}), they are merged.
   * <li>If a result is missing ancestors in the other results, e.g. A/B/C but
   * no corresponding A or A/B, these nodes are 'filled' with their label,
   * ordinal and value (obtained from the respective {@link FacetArrays}).
   * <li>If a result does not share a dimension with other results, it is
   * returned as is.
   * </ul>
   * <p>
   * <b>NOTE:</b> the returned results are not guaranteed to be in the same
   * order of the input ones.
   *
   * @param results
   *          the results to merge
   * @param taxoReader
   *          the {@link TaxonomyReader} to use when creating missing ancestor
   *          nodes
   * @param dimArrays
   *          a mapping from a dimension to the respective {@link FacetArrays}
   *          from which to pull the nodes values
   */
  public static List<FacetResult> mergeHierarchies(List<FacetResult> results, TaxonomyReader taxoReader,
      Map<String, FacetArrays> dimArrays) throws IOException {
    final Map<String, List<FacetResult>> dims = new HashMap<String,List<FacetResult>>();
    for (FacetResult fr : results) {
      String dim = fr.getFacetRequest().categoryPath.components[0];
      List<FacetResult> frs = dims.get(dim);
      if (frs == null) {
        frs = new ArrayList<FacetResult>();
        dims.put(dim, frs);
      }
      frs.add(fr);
    }

    final List<FacetResult> res = new ArrayList<FacetResult>();
    for (List<FacetResult> frs : dims.values()) {
      FacetResult mergedResult = frs.get(0);
      if (frs.size() > 1) {
        CollectionUtil.introSort(frs, new Comparator<FacetResult>() {
          @Override
          public int compare(FacetResult fr1, FacetResult fr2) {
            return fr1.getFacetRequest().categoryPath.compareTo(fr2.getFacetRequest().categoryPath);
          }
        });
        Map<CategoryPath, FacetResultNode> mergedNodes = new HashMap<CategoryPath,FacetResultNode>();
        FacetArrays arrays = dimArrays != null ? dimArrays.get(frs.get(0).getFacetRequest().categoryPath.components[0]) : null;
        for (FacetResult fr : frs) {
          FacetRequest freq = fr.getFacetRequest();
          OrdinalValueResolver resolver = null;
          if (arrays != null) {
            resolver = freq.createFacetsAggregator(FacetIndexingParams.DEFAULT).createOrdinalValueResolver(freq, arrays);
          }
          FacetResultNode frn = fr.getFacetResultNode();
          FacetResultNode merged = mergedNodes.get(frn.label);
          if (merged == null) {
            CategoryPath parent = frn.label.subpath(frn.label.length - 1);
            FacetResultNode childNode = frn;
            FacetResultNode parentNode = null;
            while (parent.length > 0 && (parentNode = mergedNodes.get(parent)) == null) {
              int parentOrd = taxoReader.getOrdinal(parent);
              double parentValue = -1;
              if (arrays != null) {
                parentValue = resolver.valueOf(parentOrd);
              }
              parentNode = new FacetResultNode(parentOrd, parentValue);
              parentNode.label = parent;
              parentNode.subResults = new ArrayList<FacetResultNode>();
              parentNode.subResults.add(childNode);
              mergedNodes.put(parent, parentNode);
              childNode = parentNode;
              parent = parent.subpath(parent.length - 1);
            }

            // at least one parent was added, so link the final (existing)
            // parent with the child
            if (parent.length > 0) {
              if (!(parentNode.subResults instanceof ArrayList)) {
                parentNode.subResults = new ArrayList<FacetResultNode>(parentNode.subResults);
              }
              parentNode.subResults.add(childNode);
            }

            // for missing FRNs, add new ones with label and value=-1
            // first time encountered this label, add it and all its children to
            // the map.
            mergedNodes.put(frn.label, frn);
            for (FacetResultNode child : frn.subResults) {
              addIfNotExist(mergedNodes, child);
            }
          } else {
            if (!(merged.subResults instanceof ArrayList)) {
              merged.subResults = new ArrayList<FacetResultNode>(merged.subResults);
            }
            for (FacetResultNode sub : frn.subResults) {
              // make sure sub wasn't already added
              sub = addIfNotExist(mergedNodes, sub);
              if (!merged.subResults.contains(sub)) {
                merged.subResults.add(sub);
              }
            }
          }
        }
       
        // find the 'first' node to put on the FacetResult root
        CategoryPath min = null;
        for (CategoryPath cp : mergedNodes.keySet()) {
          if (min == null || cp.compareTo(min) < 0) {
            min = cp;
          }
        }
        FacetRequest dummy = new FacetRequest(min, frs.get(0).getFacetRequest().numResults) {
          @Override
          public FacetsAggregator createFacetsAggregator(FacetIndexingParams fip) {
            throw new UnsupportedOperationException("not supported by this request");
          }
        };
        mergedResult = new FacetResult(dummy, mergedNodes.get(min), -1);
      }
      res.add(mergedResult);
    }
    return res;
  }

  private final FacetRequest facetRequest;
  private final FacetResultNode rootNode;
  private final int numValidDescendants;
 
  public FacetResult(FacetRequest facetRequest, FacetResultNode rootNode,  int numValidDescendants) {
    this.facetRequest = facetRequest;
    this.rootNode = rootNode;
    this.numValidDescendants = numValidDescendants;
  }
 
  /**
   * Facet result node matching the root of the {@link #getFacetRequest() facet request}.
   * @see #getFacetRequest()
   * @see FacetRequest#categoryPath
   */
  public final FacetResultNode getFacetResultNode() {
    return rootNode;
  }
 
  /**
   * Number of descendants of {@link #getFacetResultNode() root facet result
   * node}, up till the requested depth.
   */
  public final int getNumValidDescendants() {
    return numValidDescendants;
  }
 
  /**
   * Request for which this result was obtained.
   */
  public final FacetRequest getFacetRequest() {
    return this.facetRequest;
  }

  /**
   * String representation of this facet result.
   * Use with caution: might return a very long string.
   * @param prefix prefix for each result line
   * @see #toString()
   */
  public String toString(String prefix) {
    StringBuilder sb = new StringBuilder();
    String nl = "";
   
    // request
    if (this.facetRequest != null) {
      sb.append(nl).append(prefix).append("Request: ").append(
          this.facetRequest.toString());
      nl = "\n";
    }
   
    // total facets
    sb.append(nl).append(prefix).append("Num valid Descendants (up to specified depth): ").append(
        this.numValidDescendants);
    nl = "\n";
   
    // result node
    if (this.rootNode != null) {
      sb.append(nl).append(this.rootNode.toString(prefix + "\t"));
    }
   
    return sb.toString();
  }
 
  @Override
  public String toString() {
    return toString("");
  }
 
}
TOP

Related Classes of org.apache.lucene.facet.search.FacetResult

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.