package com.browseengine.bobo.facets; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import org.apache.log4j.Logger; import org.apache.lucene.util.PriorityQueue; import com.browseengine.bobo.api.BrowseFacet; import com.browseengine.bobo.api.DoubleFacetIterator; import com.browseengine.bobo.api.FacetAccessible; import com.browseengine.bobo.api.FacetIterator; import com.browseengine.bobo.api.FacetSpec; import com.browseengine.bobo.api.FacetSpec.FacetSortSpec; import com.browseengine.bobo.api.FloatFacetIterator; import com.browseengine.bobo.api.IntFacetIterator; import com.browseengine.bobo.api.LongFacetIterator; import com.browseengine.bobo.api.ShortFacetIterator; import com.browseengine.bobo.facets.impl.CombinedDoubleFacetIterator; import com.browseengine.bobo.facets.impl.CombinedFacetIterator; import com.browseengine.bobo.facets.impl.CombinedFloatFacetIterator; import com.browseengine.bobo.facets.impl.CombinedIntFacetIterator; import com.browseengine.bobo.facets.impl.CombinedLongFacetIterator; import com.browseengine.bobo.facets.impl.CombinedShortFacetIterator; /** * @author nnarkhed * */ public class CombinedFacetAccessible implements FacetAccessible { private static final Logger log = Logger.getLogger(CombinedFacetAccessible.class); protected final List<FacetAccessible> _list; protected final FacetSpec _fspec; protected boolean _closed; public CombinedFacetAccessible(FacetSpec fspec, List<FacetAccessible> list) { _list = list; _fspec = fspec; } @Override public String toString() { return "_list:" + _list + " _fspec:" + _fspec; } @Override public BrowseFacet getFacet(String value) { if (_closed) { throw new IllegalStateException("This instance of count collector was already closed"); } int sum = -1; String foundValue = null; if (_list != null) { for (FacetAccessible facetAccessor : _list) { BrowseFacet facet = facetAccessor.getFacet(value); if (facet != null) { foundValue = facet.getValue(); if (sum == -1) sum = facet.getFacetValueHitCount(); else sum += facet.getFacetValueHitCount(); } } } if (sum == -1) return null; return new BrowseFacet(foundValue, sum); } public int getCappedFacetCount(Object value, int cap) { if (_closed) { throw new IllegalStateException("This instance of count collector was already closed"); } int sum = 0; if (_list != null) { for (FacetAccessible facetAccessor : _list) { sum += facetAccessor.getFacetHitsCount(value); if (sum >= cap) return cap; } } return sum; } @Override public int getFacetHitsCount(Object value) { if (_closed) { throw new IllegalStateException("This instance of count collector was already closed"); } int sum = 0; if (_list != null) { for (FacetAccessible facetAccessor : _list) { sum += facetAccessor.getFacetHitsCount(value); } } return sum; } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public List<BrowseFacet> getFacets() { if (_closed) { throw new IllegalStateException("This instance of count collector was already closed"); } int maxCnt = _fspec.getMaxCount(); if (maxCnt <= 0) maxCnt = Integer.MAX_VALUE; int minHits = _fspec.getMinHitCount(); LinkedList<BrowseFacet> list = new LinkedList<BrowseFacet>(); int cnt = 0; Comparable<?> facet = null; FacetIterator iter = this.iterator(); Comparator<BrowseFacet> comparator; if (FacetSortSpec.OrderValueAsc.equals(_fspec.getOrderBy())) { while ((facet = iter.next(minHits)) != null) { // find the next facet whose combined hit count obeys minHits list.add(new BrowseFacet(String.valueOf(facet), iter.count)); if (++cnt >= maxCnt) break; } } else if (FacetSortSpec.OrderHitsDesc.equals(_fspec.getOrderBy())) { comparator = new Comparator<BrowseFacet>() { @Override public int compare(BrowseFacet f1, BrowseFacet f2) { int val = f2.getFacetValueHitCount() - f1.getFacetValueHitCount(); if (val == 0) { val = (f1.getValue().compareTo(f2.getValue())); } return val; } }; if (maxCnt != Integer.MAX_VALUE) { // we will maintain a min heap of size maxCnt // Order by hits in descending order and max count is supplied PriorityQueue queue = createPQ(maxCnt, comparator); int qsize = 0; while ((qsize < maxCnt) && ((facet = iter.next(minHits)) != null)) { queue.add(new BrowseFacet(String.valueOf(facet), iter.count)); qsize++; } if (facet != null) { BrowseFacet rootFacet = (BrowseFacet) queue.top(); minHits = rootFacet.getFacetValueHitCount() + 1; // facet count less than top of min heap, it will never be added while (((facet = iter.next(minHits)) != null)) { rootFacet.setValue(String.valueOf(facet)); rootFacet.setFacetValueHitCount(iter.count); rootFacet = (BrowseFacet) queue.updateTop(); minHits = rootFacet.getFacetValueHitCount() + 1; } } // at this point, queue contains top maxCnt facets that have hitcount >= minHits while (qsize-- > 0) { // append each entry to the beginning of the facet list to order facets by hits descending list.addFirst((BrowseFacet) queue.pop()); } } else { // no maxCnt specified. So fetch all facets according to minHits and sort them later while ((facet = iter.next(minHits)) != null) list.add(new BrowseFacet(String.valueOf(facet), iter.count)); Collections.sort(list, comparator); } } else // FacetSortSpec.OrderByCustom.equals(_fspec.getOrderBy() { comparator = _fspec.getCustomComparatorFactory().newComparator(); if (maxCnt != Integer.MAX_VALUE) { PriorityQueue queue = createPQ(maxCnt, comparator); BrowseFacet browseFacet = new BrowseFacet(); int qsize = 0; while ((qsize < maxCnt) && ((facet = iter.next(minHits)) != null)) { queue.add(new BrowseFacet(String.valueOf(facet), iter.count)); qsize++; } if (facet != null) { while ((facet = iter.next(minHits)) != null) { // check with the top of min heap browseFacet.setFacetValueHitCount(iter.count); browseFacet.setValue(String.valueOf(facet)); browseFacet = (BrowseFacet) queue.insertWithOverflow(browseFacet); } } // remove from queue and add to the list while (qsize-- > 0) list.addFirst((BrowseFacet) queue.pop()); } else { // order by custom but no max count supplied while ((facet = iter.next(minHits)) != null) list.add(new BrowseFacet(String.valueOf(facet), iter.count)); Collections.sort(list, comparator); } } return list; } private PriorityQueue<?> createPQ(final int max, final Comparator<BrowseFacet> comparator) { @SuppressWarnings("rawtypes") PriorityQueue<?> queue = new PriorityQueue(max) { @Override protected boolean lessThan(Object arg0, Object arg1) { BrowseFacet o1 = (BrowseFacet) arg0; BrowseFacet o2 = (BrowseFacet) arg1; return comparator.compare(o1, o2) > 0; } }; return queue; } @Override public void close() { if (_closed) { log.warn("This instance of count collector was already closed. This operation is no-op."); return; } _closed = true; if (_list != null) { for (FacetAccessible fa : _list) { fa.close(); } _list.clear(); } } @Override public FacetIterator iterator() { if (_closed) { throw new IllegalStateException("This instance of count collector was already closed"); } ArrayList<FacetIterator> iterList = new ArrayList<FacetIterator>(_list.size()); FacetIterator iter; for (FacetAccessible facetAccessor : _list) { iter = facetAccessor.iterator(); if (iter != null) iterList.add(iter); } if (iterList.get(0) instanceof IntFacetIterator) { ArrayList<IntFacetIterator> il = new ArrayList<IntFacetIterator>(); for (FacetAccessible facetAccessor : _list) { iter = facetAccessor.iterator(); if (iter != null) il.add((IntFacetIterator) iter); } return new CombinedIntFacetIterator(il, _fspec.getMinHitCount()); } if (iterList.get(0) instanceof LongFacetIterator) { ArrayList<LongFacetIterator> il = new ArrayList<LongFacetIterator>(); for (FacetAccessible facetAccessor : _list) { iter = facetAccessor.iterator(); if (iter != null) il.add((LongFacetIterator) iter); } return new CombinedLongFacetIterator(il, _fspec.getMinHitCount()); } if (iterList.get(0) instanceof ShortFacetIterator) { ArrayList<ShortFacetIterator> il = new ArrayList<ShortFacetIterator>(); for (FacetAccessible facetAccessor : _list) { iter = facetAccessor.iterator(); if (iter != null) il.add((ShortFacetIterator) iter); } return new CombinedShortFacetIterator(il, _fspec.getMinHitCount()); } if (iterList.get(0) instanceof FloatFacetIterator) { ArrayList<FloatFacetIterator> il = new ArrayList<FloatFacetIterator>(); for (FacetAccessible facetAccessor : _list) { iter = facetAccessor.iterator(); if (iter != null) il.add((FloatFacetIterator) iter); } return new CombinedFloatFacetIterator(il, _fspec.getMinHitCount()); } if (iterList.get(0) instanceof DoubleFacetIterator) { ArrayList<DoubleFacetIterator> il = new ArrayList<DoubleFacetIterator>(); for (FacetAccessible facetAccessor : _list) { iter = facetAccessor.iterator(); if (iter != null) il.add((DoubleFacetIterator) iter); } return new CombinedDoubleFacetIterator(il, _fspec.getMinHitCount()); } return new CombinedFacetIterator(iterList); } }