package com.browseengine.bobo.facets.filter;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import java.io.IOException;
import org.apache.lucene.search.DocIdSetIterator;
import com.browseengine.bobo.api.BoboSegmentReader;
import com.browseengine.bobo.docidset.EmptyDocIdSet;
import com.browseengine.bobo.docidset.RandomAccessDocIdSet;
import com.browseengine.bobo.facets.FacetHandler;
import com.browseengine.bobo.facets.data.FacetDataCache;
import com.browseengine.bobo.facets.data.MultiValueFacetDataCache;
import com.browseengine.bobo.util.BigNestedIntArray;
import com.browseengine.bobo.util.BigSegmentedArray;
public final class FacetRangeFilter extends RandomAccessFilter {
private final FacetHandler<FacetDataCache<?>> _facetHandler;
private final String _rangeString;
public FacetRangeFilter(FacetHandler<FacetDataCache<?>> facetHandler, String rangeString) {
_facetHandler = facetHandler;
_rangeString = rangeString;
}
@Override
public double getFacetSelectivity(BoboSegmentReader reader) {
double selectivity = 0;
FacetDataCache<?> dataCache = _facetHandler.getFacetData(reader);
int[] range = parse(dataCache, _rangeString);
if (range != null) {
int accumFreq = 0;
for (int idx = range[0]; idx <= range[1]; ++idx) {
accumFreq += dataCache.freqs[idx];
}
int total = reader.maxDoc();
selectivity = (double) accumFreq / (double) total;
}
if (selectivity > 0.999) {
selectivity = 1.0;
}
return selectivity;
}
private final static class FacetRangeDocIdSetIterator extends DocIdSetIterator {
private int _doc = -1;
private int _minID = Integer.MAX_VALUE;
private int _maxID = -1;
private final int _start;
private final int _end;
private final BigSegmentedArray _orderArray;
FacetRangeDocIdSetIterator(int start, int end, FacetDataCache<?> dataCache) {
_start = start;
_end = end;
for (int i = start; i <= end; ++i) {
_minID = Math.min(_minID, dataCache.minIDs[i]);
_maxID = Math.max(_maxID, dataCache.maxIDs[i]);
}
_doc = Math.max(-1, _minID - 1);
_orderArray = dataCache.orderArray;
}
@Override
final public int docID() {
return _doc;
}
@Override
final public int nextDoc() throws IOException {
return (_doc = (_doc < _maxID ? _orderArray.findValueRange(_start, _end, (_doc + 1), _maxID)
: NO_MORE_DOCS));
}
@Override
final public int advance(int id) throws IOException {
if (_doc < id) {
return (_doc = (id <= _maxID ? _orderArray.findValueRange(_start, _end, id, _maxID)
: NO_MORE_DOCS));
}
return nextDoc();
}
@Override
public long cost() {
// TODO Auto-generated method stub
return 0;
}
}
private final static class MultiFacetRangeDocIdSetIterator extends DocIdSetIterator {
private int _doc = -1;
private int _minID = Integer.MAX_VALUE;
private int _maxID = -1;
private final int _start;
private final int _end;
private final BigNestedIntArray nestedArray;
MultiFacetRangeDocIdSetIterator(int start, int end, MultiValueFacetDataCache<?> dataCache) {
_start = start;
_end = end;
for (int i = start; i <= end; ++i) {
_minID = Math.min(_minID, dataCache.minIDs[i]);
_maxID = Math.max(_maxID, dataCache.maxIDs[i]);
}
_doc = Math.max(-1, _minID - 1);
nestedArray = dataCache._nestedArray;
}
@Override
final public int docID() {
return _doc;
}
@Override
final public int nextDoc() throws IOException {
return (_doc = (_doc < _maxID ? nestedArray.findValuesInRange(_start, _end, (_doc + 1),
_maxID) : NO_MORE_DOCS));
}
@Override
final public int advance(int id) throws IOException {
if (_doc < id) {
return (_doc = (id <= _maxID ? nestedArray.findValuesInRange(_start, _end, id, _maxID)
: NO_MORE_DOCS));
}
return nextDoc();
}
@Override
public long cost() {
// TODO Auto-generated method stub
return 0;
}
}
public static class FacetRangeValueConverter implements FacetValueConverter {
public static FacetRangeValueConverter instance = new FacetRangeValueConverter();
private FacetRangeValueConverter() {
}
@Override
public int[] convert(FacetDataCache<String> dataCache, String[] vals) {
return convertIndexes(dataCache, vals);
}
}
public static int[] convertIndexes(FacetDataCache<?> dataCache, String[] vals) {
IntList list = new IntArrayList();
for (String val : vals) {
int[] range = parse(dataCache, val);
if (range != null) {
for (int i = range[0]; i <= range[1]; ++i) {
list.add(i);
}
}
}
return list.toIntArray();
}
public static int[] parse(FacetDataCache<?> dataCache, String rangeString) {
String[] ranges = getRangeStrings(rangeString);
String lower = ranges[0];
String upper = ranges[1];
String includeLower = ranges[2];
String includeUpper = ranges[3];
boolean incLower = true, incUpper = true;
if ("false".equals(includeLower)) incLower = false;
if ("false".equals(includeUpper)) incUpper = false;
if ("*".equals(lower)) {
lower = null;
}
if ("*".equals(upper)) {
upper = null;
}
int start, end;
if (lower == null) {
start = 1;
} else {
start = dataCache.valArray.indexOf(lower);
if (start < 0) {
start = -(start + 1);
} else {
// when the lower value is in the list, we need to consider if we want this lower value
// included or not;
if (incLower == false) {
start++;
}
}
}
if (upper == null) {
end = dataCache.valArray.size() - 1;
} else {
end = dataCache.valArray.indexOf(upper);
if (end < 0) {
end = -(end + 1) - 1;
} else {
// when the lower value is in the list, we need to consider if we want this lower value
// included or not;
if (incUpper == false) {
end--;
}
}
}
return new int[] { start, end };
}
public static String[] getRangeStrings(String rangeString) {
int index2 = rangeString.indexOf(" TO ");
boolean incLower = true, incUpper = true;
if (rangeString.trim().startsWith("(")) incLower = false;
if (rangeString.trim().endsWith(")")) incUpper = false;
int index = -1, index3 = -1;
if (incLower == true) index = rangeString.indexOf('[');
else if (incLower == false) index = rangeString.indexOf('(');
if (incUpper == true) index3 = rangeString.indexOf(']');
else if (incUpper == false) index3 = rangeString.indexOf(')');
String lower, upper;
try {
lower = rangeString.substring(index + 1, index2).trim();
upper = rangeString.substring(index2 + 4, index3).trim();
return new String[] { lower, upper, String.valueOf(incLower), String.valueOf(incUpper) };
} catch (RuntimeException re) {
throw re;
}
}
@Override
public RandomAccessDocIdSet getRandomAccessDocIdSet(final BoboSegmentReader reader)
throws IOException {
final FacetDataCache<?> dataCache = _facetHandler.getFacetData(reader);
final boolean multi = dataCache instanceof MultiValueFacetDataCache;
final BigNestedIntArray nestedArray = multi ? ((MultiValueFacetDataCache<?>) dataCache)._nestedArray
: null;
final int[] range = parse(dataCache, _rangeString);
if (range == null) {
return null;
}
if (range[0] > range[1]) {
return EmptyDocIdSet.getInstance();
}
return new RandomAccessDocIdSet() {
int _start = range[0];
int _end = range[1];
@Override
final public boolean get(int docId) {
if (multi) {
nestedArray.containsValueInRange(docId, _start, _end);
}
int index = dataCache.orderArray.get(docId);
return index >= _start && index <= _end;
}
@Override
public DocIdSetIterator iterator() {
if (multi) {
return new MultiFacetRangeDocIdSetIterator(_start, _end,
(MultiValueFacetDataCache<?>) dataCache);
} else {
return new FacetRangeDocIdSetIterator(_start, _end, dataCache);
}
}
};
}
}