package com.browseengine.bobo.api;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Collector;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.Weight;
import com.browseengine.bobo.facets.CombinedFacetAccessible;
import com.browseengine.bobo.facets.FacetCountCollector;
import com.browseengine.bobo.facets.FacetHandler;
import com.browseengine.bobo.facets.FacetHandlerInitializerParam;
import com.browseengine.bobo.facets.RuntimeFacetHandler;
import com.browseengine.bobo.facets.RuntimeFacetHandlerFactory;
import com.browseengine.bobo.facets.filter.AndFilter;
import com.browseengine.bobo.facets.filter.RandomAccessFilter;
import com.browseengine.bobo.search.BoboSearcher;
import com.browseengine.bobo.search.FacetHitCollector;
import com.browseengine.bobo.sort.SortCollector;
/**
* This class implements the browsing functionality.
*/
public class BoboSubBrowser extends BoboSearcher implements Browsable {
private static Logger logger = Logger.getLogger(BoboSubBrowser.class);
private final BoboSegmentReader _reader;
private final Map<String, RuntimeFacetHandlerFactory<?, ?>> _runtimeFacetHandlerFactoryMap;
private final HashMap<String, FacetHandler<?>> _runtimeFacetHandlerMap;
private HashMap<String, FacetHandler<?>> _allFacetHandlerMap;
private ArrayList<RuntimeFacetHandler<?>> _runtimeFacetHandlers = null;
@Override
public IndexReader getIndexReader() {
return _reader;
}
public BoboSubBrowser(AtomicReaderContext ctx) {
super(ctx);
_reader = (BoboSegmentReader) ctx.reader();
_runtimeFacetHandlerMap = new HashMap<String, FacetHandler<?>>();
_runtimeFacetHandlerFactoryMap = _reader.getRuntimeFacetHandlerFactoryMap();
_allFacetHandlerMap = null;
}
private boolean isNoQueryNoFilter(BrowseRequest req) {
Query q = req.getQuery();
Filter filter = req.getFilter();
return ((q == null || q instanceof MatchAllDocsQuery) && filter == null && !_reader
.hasDeletions());
}
@Override
public Object[] getRawFieldVal(int docid, String fieldname) throws IOException {
FacetHandler<?> facetHandler = getFacetHandler(fieldname);
if (facetHandler == null) {
return getFieldVal(docid, fieldname);
} else {
return facetHandler.getRawFieldValues(_reader, docid);
}
}
/**
* Sets runtime facet handler. If has the same name as a preload handler, for the
* duration of this browser, this one will be used.
*
* @param facetHandler
* Runtime facet handler
*/
@Override
public void setFacetHandler(FacetHandler<?> facetHandler) throws IOException {
Set<String> dependsOn = facetHandler.getDependsOn();
if (dependsOn.size() > 0) {
Iterator<String> iter = dependsOn.iterator();
while (iter.hasNext()) {
String fn = iter.next();
FacetHandler<?> f = _runtimeFacetHandlerMap.get(fn);
if (f == null) {
f = _reader.getFacetHandler(fn);
}
if (f == null) {
throw new IOException("depended on facet handler: " + fn + ", but is not found");
}
facetHandler.putDependedFacetHandler(f);
}
}
facetHandler.loadFacetData(_reader);
_runtimeFacetHandlerMap.put(facetHandler.getName(), facetHandler);
}
/**
* Gets a defined facet handler
*
* @param name
* facet name
* @return a facet handler
*/
@Override
public FacetHandler<?> getFacetHandler(String name) {
return getFacetHandlerMap().get(name);
}
@Override
public Map<String, FacetHandler<?>> getFacetHandlerMap() {
if (_allFacetHandlerMap == null) {
_allFacetHandlerMap = new HashMap<String, FacetHandler<?>>(_reader.getFacetHandlerMap());
}
_allFacetHandlerMap.putAll(_runtimeFacetHandlerMap);
return _allFacetHandlerMap;
}
/**
* Gets a set of facet names
*
* @return set of facet names
*/
@Override
public Set<String> getFacetNames() {
Map<String, FacetHandler<?>> map = getFacetHandlerMap();
return map.keySet();
}
@SuppressWarnings("unchecked")
@Override
public void browse(BrowseRequest req, Weight w, Collector collector,
Map<String, FacetAccessible> facetMap, int start) throws BrowseException {
if (_reader == null) {
return;
}
// initialize all RuntimeFacetHandlers with data supplied by user at run-time.
_runtimeFacetHandlers = new ArrayList<RuntimeFacetHandler<?>>(
_runtimeFacetHandlerFactoryMap.size());
Set<String> runtimeFacetNames = _runtimeFacetHandlerFactoryMap.keySet();
for (String facetName : runtimeFacetNames) {
FacetHandler<?> sfacetHandler = this.getFacetHandler(facetName);
if (sfacetHandler != null) {
logger.warn("attempting to reset facetHandler: " + sfacetHandler);
continue;
}
RuntimeFacetHandlerFactory<FacetHandlerInitializerParam, ?> factory = (RuntimeFacetHandlerFactory<FacetHandlerInitializerParam, ?>) _runtimeFacetHandlerFactoryMap
.get(facetName);
try {
FacetHandlerInitializerParam data = req.getFacethandlerData(facetName);
if (data == null) data = FacetHandlerInitializerParam.EMPTY_PARAM;
if (data != FacetHandlerInitializerParam.EMPTY_PARAM || !factory.isLoadLazily()) {
RuntimeFacetHandler<?> facetHandler = factory.get(data);
if (facetHandler != null) {
_runtimeFacetHandlers.add(facetHandler); // add to a list so we close them after search
this.setFacetHandler(facetHandler);
}
}
} catch (IOException e) {
throw new BrowseException("error trying to set FacetHandler : " + facetName + ":"
+ e.getMessage(), e);
}
}
// done initialize all RuntimeFacetHandlers with data supplied by user at run-time.
Set<String> fields = getFacetNames();
LinkedList<Filter> preFilterList = new LinkedList<Filter>();
List<FacetHitCollector> facetHitCollectorList = new LinkedList<FacetHitCollector>();
Filter baseFilter = req.getFilter();
if (baseFilter != null) {
preFilterList.add(baseFilter);
}
int selCount = req.getSelectionCount();
boolean isNoQueryNoFilter = isNoQueryNoFilter(req);
boolean isDefaultSearch = isNoQueryNoFilter && selCount == 0;
try {
for (String name : fields) {
BrowseSelection sel = req.getSelection(name);
FacetSpec ospec = req.getFacetSpec(name);
FacetHandler<?> handler = getFacetHandler(name);
if (handler == null) {
logger.error("facet handler: " + name + " is not defined, ignored.");
continue;
}
FacetHitCollector facetHitCollector = null;
RandomAccessFilter filter = null;
if (sel != null) {
filter = handler.buildFilter(sel);
}
if (ospec == null) {
if (filter != null) {
preFilterList.add(filter);
}
} else {
FacetSpec fspec = ospec;
facetHitCollector = new FacetHitCollector();
facetHitCollector.facetHandler = handler;
if (isDefaultSearch) {
facetHitCollector._collectAllSource = handler.getFacetCountCollectorSource(sel, fspec);
} else {
facetHitCollector._facetCountCollectorSource = handler.getFacetCountCollectorSource(
sel, fspec);
if (ospec.isExpandSelection()) {
if (isNoQueryNoFilter && sel != null && selCount == 1) {
facetHitCollector._collectAllSource = handler.getFacetCountCollectorSource(sel,
fspec);
if (filter != null) {
preFilterList.add(filter);
}
} else {
if (filter != null) {
facetHitCollector._filter = filter;
}
}
} else {
if (filter != null) {
preFilterList.add(filter);
}
}
}
}
if (facetHitCollector != null) {
facetHitCollectorList.add(facetHitCollector);
}
}
Filter finalFilter = null;
if (preFilterList.size() > 0) {
if (preFilterList.size() == 1) {
finalFilter = preFilterList.getFirst();
} else {
finalFilter = new AndFilter(preFilterList);
}
}
setFacetHitCollectorList(facetHitCollectorList);
try {
search(w, finalFilter, collector, start, req.getMapReduceWrapper());
} finally {
for (FacetHitCollector facetCollector : facetHitCollectorList) {
String name = facetCollector.facetHandler.getName();
LinkedList<FacetCountCollector> resultcollector = null;
resultcollector = facetCollector._countCollectorList;
if (resultcollector == null || resultcollector.size() == 0) {
resultcollector = facetCollector._collectAllCollectorList;
}
if (resultcollector != null) {
FacetSpec fspec = req.getFacetSpec(name);
assert fspec != null;
if (resultcollector.size() == 1) {
facetMap.put(name, resultcollector.get(0));
} else {
ArrayList<FacetAccessible> finalList = new ArrayList<FacetAccessible>(
resultcollector.size());
for (FacetCountCollector fc : resultcollector) {
finalList.add(fc);
}
CombinedFacetAccessible combinedCollector = new CombinedFacetAccessible(fspec,
finalList);
facetMap.put(name, combinedCollector);
}
}
}
}
} catch (IOException ioe) {
throw new BrowseException(ioe.getMessage(), ioe);
}
}
@Override
public SortCollector getSortCollector(SortField[] sort, Query q, int offset, int count,
boolean fetchStoredFields, Set<String> termVectorsToFetch, String[] groupBy, int maxPerGroup,
boolean collectDocIdCache) {
return SortCollector.buildSortCollector(this, q, sort, offset, count, fetchStoredFields,
termVectorsToFetch, groupBy, maxPerGroup, collectDocIdCache);
}
/**
* browses the index.
*
* @param req
* browse request
* @return browse result
*/
@Override
public BrowseResult browse(BrowseRequest req) {
throw new UnsupportedOperationException();
}
public Map<String, FacetHandler<?>> getRuntimeFacetHandlerMap() {
return _runtimeFacetHandlerMap;
}
@Override
public int numDocs() {
return _reader.numDocs();
}
/**
* Returns the field data for a given doc.
*
* @param docid
* doc
* @param fieldname
* name of the field
* @return field data
*/
@Override
public String[] getFieldVal(int docid, final String fieldname) throws IOException {
FacetHandler<?> facetHandler = getFacetHandler(fieldname);
if (facetHandler != null) {
return facetHandler.getFieldValues(_reader, docid);
} else {
logger.warn("facet handler: " + fieldname + " not defined, looking at stored field.");
return _reader.getStoredFieldValue(docid, fieldname);
}
}
@Override
public void doClose() throws IOException {
if (_runtimeFacetHandlers != null) {
for (RuntimeFacetHandler<?> handler : _runtimeFacetHandlers) {
handler.close();
}
}
if (_reader != null) {
_reader.clearRuntimeFacetData();
_reader.clearRuntimeFacetHandler();
}
}
}