| Index: lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesReader.java |
| =================================================================== |
| --- lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesReader.java (revision 1591679) |
| +++ lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesReader.java (working copy) |
| @@ -197,6 +197,10 @@ |
| throw new RuntimeException(e); |
| } |
| } |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| |
| @Override |
| public int length() { |
| @@ -254,6 +258,11 @@ |
| |
| return new Bits() { |
| @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| + |
| + @Override |
| public boolean get(int index) { |
| try { |
| in.seek(field.dataStartFilePointer + (9+field.pattern.length() + field.maxLength+2)*index); |
| Index: lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextLiveDocsFormat.java |
| =================================================================== |
| --- lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextLiveDocsFormat.java (revision 1591679) |
| +++ lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextLiveDocsFormat.java (working copy) |
| @@ -161,8 +161,11 @@ |
| public boolean get(int index) { |
| return bits.get(index); |
| } |
| - |
| @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| + @Override |
| public int length() { |
| return size; |
| } |
| Index: lucene/core/src/java/org/apache/lucene/codecs/lucene40/BitVector.java |
| =================================================================== |
| --- lucene/core/src/java/org/apache/lucene/codecs/lucene40/BitVector.java (revision 1591679) |
| +++ lucene/core/src/java/org/apache/lucene/codecs/lucene40/BitVector.java (working copy) |
| @@ -51,7 +51,10 @@ |
| private int size; |
| private int count; |
| private int version; |
| - |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| /** Constructs a vector capable of holding <code>n</code> bits. */ |
| public BitVector(int n) { |
| size = n; |
| Index: lucene/core/src/java/org/apache/lucene/codecs/lucene45/Lucene45DocValuesProducer.java |
| =================================================================== |
| --- lucene/core/src/java/org/apache/lucene/codecs/lucene45/Lucene45DocValuesProducer.java (revision 1591679) |
| +++ lucene/core/src/java/org/apache/lucene/codecs/lucene45/Lucene45DocValuesProducer.java (working copy) |
| @@ -624,6 +624,10 @@ |
| throw new RuntimeException(e); |
| } |
| } |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| |
| @Override |
| public int length() { |
| Index: lucene/core/src/java/org/apache/lucene/index/BitsSlice.java |
| =================================================================== |
| --- lucene/core/src/java/org/apache/lucene/index/BitsSlice.java (revision 1591679) |
| +++ lucene/core/src/java/org/apache/lucene/index/BitsSlice.java (working copy) |
| @@ -28,7 +28,10 @@ |
| private final Bits parent; |
| private final int start; |
| private final int length; |
| - |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| // start is inclusive; end is exclusive (length = end-start) |
| public BitsSlice(Bits parent, ReaderSlice slice) { |
| this.parent = parent; |
| Index: lucene/core/src/java/org/apache/lucene/index/DocValues.java |
| =================================================================== |
| --- lucene/core/src/java/org/apache/lucene/index/DocValues.java (revision 1591679) |
| +++ lucene/core/src/java/org/apache/lucene/index/DocValues.java (working copy) |
| @@ -139,6 +139,11 @@ |
| public int length() { |
| return maxDoc; |
| } |
| + |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| }; |
| } |
| |
| @@ -157,6 +162,11 @@ |
| public int length() { |
| return maxDoc; |
| } |
| + |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| }; |
| } |
| } |
| Index: lucene/core/src/java/org/apache/lucene/index/MultiBits.java |
| =================================================================== |
| --- lucene/core/src/java/org/apache/lucene/index/MultiBits.java (revision 1591679) |
| +++ lucene/core/src/java/org/apache/lucene/index/MultiBits.java (working copy) |
| @@ -47,8 +47,11 @@ |
| assert doc - starts[reader] < length: "doc=" + doc + " reader=" + reader + " starts[reader]=" + starts[reader] + " length=" + length; |
| return true; |
| } |
| - |
| @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| + @Override |
| public boolean get(int doc) { |
| final int reader = ReaderUtil.subIndex(doc, starts); |
| assert reader != -1; |
| Index: lucene/core/src/java/org/apache/lucene/search/FieldCacheDocIdSet.java |
| =================================================================== |
| --- lucene/core/src/java/org/apache/lucene/search/FieldCacheDocIdSet.java (revision 1591679) |
| +++ lucene/core/src/java/org/apache/lucene/search/FieldCacheDocIdSet.java (working copy) |
| @@ -17,6 +17,7 @@ |
| */ |
| |
| import java.io.IOException; |
| + |
| import org.apache.lucene.util.Bits; |
| import org.apache.lucene.util.FixedBitSet; |
| import org.apache.lucene.util.OpenBitSet; |
| @@ -61,8 +62,11 @@ |
| public boolean get(int docid) { |
| return matchDoc(docid); |
| } |
| - |
| @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| + @Override |
| public int length() { |
| return maxDoc; |
| } |
| @@ -71,8 +75,11 @@ |
| public boolean get(int docid) { |
| return matchDoc(docid) && acceptDocs.get(docid); |
| } |
| - |
| @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| + @Override |
| public int length() { |
| return maxDoc; |
| } |
| Index: lucene/core/src/java/org/apache/lucene/search/FieldCacheImpl.java |
| =================================================================== |
| --- lucene/core/src/java/org/apache/lucene/search/FieldCacheImpl.java (revision 1591679) |
| +++ lucene/core/src/java/org/apache/lucene/search/FieldCacheImpl.java (working copy) |
| @@ -1388,6 +1388,10 @@ |
| public boolean get(int index) { |
| return offsetReader.get(index) != 0; |
| } |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| |
| @Override |
| public int length() { |
| Index: lucene/core/src/java/org/apache/lucene/search/FilteredDocIdSet.java |
| =================================================================== |
| --- lucene/core/src/java/org/apache/lucene/search/FilteredDocIdSet.java (revision 1591679) |
| +++ lucene/core/src/java/org/apache/lucene/search/FilteredDocIdSet.java (working copy) |
| @@ -18,6 +18,7 @@ |
| */ |
| |
| import java.io.IOException; |
| + |
| import org.apache.lucene.util.Bits; |
| |
| /** |
| @@ -64,8 +65,11 @@ |
| public boolean get(int docid) { |
| return bits.get(docid) && FilteredDocIdSet.this.match(docid); |
| } |
| - |
| @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| + @Override |
| public int length() { |
| return bits.length(); |
| } |
| Index: lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java |
| =================================================================== |
| --- lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java (revision 1591679) |
| +++ lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java (working copy) |
| @@ -543,8 +543,7 @@ |
| } else { |
| assert firstFilterDoc > -1; |
| // we are gonna advance() this scorer, so we set inorder=true/toplevel=false |
| - // we pass null as acceptDocs, as our filter has already respected acceptDocs, no need to do twice |
| - final Scorer scorer = weight.scorer(context, null); |
| + final Scorer scorer = weight.scorer(context, filterAcceptDocs); |
| // TODO once we have way to figure out if we use RA or LeapFrog we can remove this scorer |
| return (scorer == null) ? null : new PrimaryAdvancedLeapFrogScorer(weight, firstFilterDoc, filterIter, scorer); |
| } |
| @@ -564,7 +563,7 @@ |
| */ |
| protected boolean useRandomAccess(Bits bits, int firstFilterDoc) { |
| //TODO once we have a cost API on filters and scorers we should rethink this heuristic |
| - return firstFilterDoc < 100; |
| + return firstFilterDoc < 100 && bits.providesConstantTimeAccess(); |
| } |
| } |
| |
| Index: lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java |
| =================================================================== |
| --- lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java (revision 1591679) |
| +++ lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java (working copy) |
| @@ -18,6 +18,7 @@ |
| */ |
| |
| import java.io.IOException; |
| +import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import java.util.List; |
| @@ -603,11 +604,18 @@ |
| */ |
| protected void search(List<AtomicReaderContext> leaves, Weight weight, Collector collector) |
| throws IOException { |
| - |
| + |
| // TODO: should we make this |
| // threaded...? the Collector could be sync'd? |
| // always use single thread: |
| - for (AtomicReaderContext ctx : leaves) { // search each subreader |
| + ArrayList<BulkScorer> scorers = new ArrayList<BulkScorer>(); |
| + for (AtomicReaderContext ctx : leaves) { // search each subreader |
| + BulkScorer scorer = weight.bulkScorer(ctx, !collector.acceptsDocsOutOfOrder(), ctx.reader().getLiveDocs()); |
| + scorers.add(scorer); |
| + } |
| + for(int i = 0; i < leaves.size(); i++) { |
| + BulkScorer scorer = scorers.get(i); |
| + AtomicReaderContext ctx = leaves.get(i); |
| try { |
| collector.setNextReader(ctx); |
| } catch (CollectionTerminatedException e) { |
| @@ -615,7 +623,6 @@ |
| // continue with the following leaf |
| continue; |
| } |
| - BulkScorer scorer = weight.bulkScorer(ctx, !collector.acceptsDocsOutOfOrder(), ctx.reader().getLiveDocs()); |
| if (scorer != null) { |
| try { |
| scorer.score(collector); |
| @@ -624,7 +631,7 @@ |
| // continue with the following leaf |
| } |
| } |
| - } |
| + } |
| } |
| |
| /** Expert: called to re-write queries into primitive queries. |
| Index: lucene/core/src/java/org/apache/lucene/util/Bits.java |
| =================================================================== |
| --- lucene/core/src/java/org/apache/lucene/util/Bits.java (revision 1591679) |
| +++ lucene/core/src/java/org/apache/lucene/util/Bits.java (working copy) |
| @@ -32,6 +32,9 @@ |
| */ |
| public boolean get(int index); |
| |
| + /** Returns whether or not these bits provide constant-time access a.k.a. O(1) */ |
| + public boolean providesConstantTimeAccess(); |
| + |
| /** Returns the number of bits in this set */ |
| public int length(); |
| |
| @@ -56,6 +59,11 @@ |
| public int length() { |
| return len; |
| } |
| + |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| } |
| |
| /** |
| @@ -63,7 +71,10 @@ |
| */ |
| public static class MatchNoBits implements Bits { |
| final int len; |
| - |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| public MatchNoBits( int len ) { |
| this.len = len; |
| } |
| Index: lucene/core/src/java/org/apache/lucene/util/DocIdBitSet.java |
| =================================================================== |
| --- lucene/core/src/java/org/apache/lucene/util/DocIdBitSet.java (revision 1591679) |
| +++ lucene/core/src/java/org/apache/lucene/util/DocIdBitSet.java (working copy) |
| @@ -40,7 +40,10 @@ |
| public Bits bits() { |
| return this; |
| } |
| - |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| /** This DocIdSet implementation is cacheable. */ |
| @Override |
| public boolean isCacheable() { |
| Index: lucene/core/src/java/org/apache/lucene/util/FixedBitSet.java |
| =================================================================== |
| --- lucene/core/src/java/org/apache/lucene/util/FixedBitSet.java (revision 1591679) |
| +++ lucene/core/src/java/org/apache/lucene/util/FixedBitSet.java (working copy) |
| @@ -606,4 +606,9 @@ |
| // empty sets from returning 0, which is too common. |
| return (int) ((h>>32) ^ h) + 0x98761234; |
| } |
| + |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| } |
| Index: lucene/core/src/java/org/apache/lucene/util/OpenBitSet.java |
| =================================================================== |
| --- lucene/core/src/java/org/apache/lucene/util/OpenBitSet.java (revision 1591679) |
| +++ lucene/core/src/java/org/apache/lucene/util/OpenBitSet.java (working copy) |
| @@ -119,8 +119,11 @@ |
| public DocIdSetIterator iterator() { |
| return new OpenBitSetIterator(bits, wlen); |
| } |
| - |
| @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| + @Override |
| public Bits bits() { |
| return this; |
| } |
| Index: lucene/core/src/test/org/apache/lucene/codecs/lucene41/TestBlockPostingsFormat3.java |
| =================================================================== |
| --- lucene/core/src/test/org/apache/lucene/codecs/lucene41/TestBlockPostingsFormat3.java (revision 1591679) |
| +++ lucene/core/src/test/org/apache/lucene/codecs/lucene41/TestBlockPostingsFormat3.java (working copy) |
| @@ -513,8 +513,11 @@ |
| public boolean get(int index) { |
| return bits.get(index); |
| } |
| - |
| @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| + @Override |
| public int length() { |
| return bits.length(); |
| } |
| Index: lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java |
| =================================================================== |
| --- lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java (revision 1591679) |
| +++ lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java (working copy) |
| @@ -449,8 +449,11 @@ |
| bitSet.get(index)); |
| return bitSet.get(index); |
| } |
| - |
| @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| + @Override |
| public int length() { |
| return bitSet.length(); |
| } |
| Index: lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java |
| =================================================================== |
| --- lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java (revision 1591679) |
| +++ lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java (working copy) |
| @@ -151,6 +151,10 @@ |
| } |
| return accept(values.doubleVal(docID)); |
| } |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| |
| @Override |
| public int length() { |
| Index: lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java |
| =================================================================== |
| --- lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java (revision 1591679) |
| +++ lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java (working copy) |
| @@ -143,6 +143,10 @@ |
| } |
| return accept(values.longVal(docID)); |
| } |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| |
| @Override |
| public int length() { |
| Index: lucene/misc/src/java/org/apache/lucene/index/sorter/SortingAtomicReader.java |
| =================================================================== |
| --- lucene/misc/src/java/org/apache/lucene/index/sorter/SortingAtomicReader.java (revision 1591679) |
| +++ lucene/misc/src/java/org/apache/lucene/index/sorter/SortingAtomicReader.java (working copy) |
| @@ -133,6 +133,10 @@ |
| public boolean get(int index) { |
| return liveDocs.get(docMap.oldToNew(index)); |
| } |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| |
| @Override |
| public int length() { |
| @@ -236,6 +240,10 @@ |
| public boolean get(int index) { |
| return in.get(docMap.newToOld(index)); |
| } |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| |
| @Override |
| public int length() { |
| Index: lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java |
| =================================================================== |
| --- lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java (revision 1591679) |
| +++ lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java (working copy) |
| @@ -23,6 +23,7 @@ |
| import org.apache.lucene.search.*; |
| import org.apache.lucene.index.MultiFields; |
| import org.apache.lucene.util.Bits; |
| +import org.apache.lucene.util.LazyIteratorBackedBits; |
| |
| import java.io.IOException; |
| import java.util.Set; |
| @@ -116,6 +117,7 @@ |
| this.reader = context.reader(); |
| this.maxDoc = reader.maxDoc(); |
| this.acceptDocs = acceptDocs; |
| + func.setBitsForFiltering(acceptDocs); |
| vals = func.getValues(weight.context, context); |
| } |
| |
| @@ -135,7 +137,7 @@ |
| if (doc>=maxDoc) { |
| return doc=NO_MORE_DOCS; |
| } |
| - if (acceptDocs != null && !acceptDocs.get(doc)) continue; |
| + if (acceptDocs != null && acceptDocs.providesConstantTimeAccess() && !acceptDocs.get(doc)) continue; |
| return doc; |
| } |
| } |
| Index: lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java |
| =================================================================== |
| --- lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java (revision 1591679) |
| +++ lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java (working copy) |
| @@ -22,6 +22,7 @@ |
| import org.apache.lucene.search.FieldComparatorSource; |
| import org.apache.lucene.search.IndexSearcher; |
| import org.apache.lucene.search.SortField; |
| +import org.apache.lucene.util.Bits; |
| |
| import java.io.IOException; |
| import java.util.IdentityHashMap; |
| @@ -42,6 +43,9 @@ |
| */ |
| public abstract FunctionValues getValues(Map context, AtomicReaderContext readerContext) throws IOException; |
| |
| + public void setBitsForFiltering(Bits acceptedDocs) { |
| + } |
| + |
| @Override |
| public abstract boolean equals(Object o); |
| |
| Index: lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/ScaleFloatFunction.java |
| =================================================================== |
| --- lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/ScaleFloatFunction.java (revision 1591679) |
| +++ lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/ScaleFloatFunction.java (working copy) |
| @@ -17,121 +17,176 @@ |
| |
| package org.apache.lucene.queries.function.valuesource; |
| |
| +import java.io.IOException; |
| +import java.util.ArrayList; |
| +import java.util.HashMap; |
| +import java.util.List; |
| +import java.util.Map; |
| + |
| import org.apache.lucene.index.AtomicReaderContext; |
| +import org.apache.lucene.index.IndexReaderContext; |
| import org.apache.lucene.index.ReaderUtil; |
| +import org.apache.lucene.index.SlowCompositeReaderWrapper; |
| import org.apache.lucene.queries.function.FunctionValues; |
| import org.apache.lucene.queries.function.ValueSource; |
| import org.apache.lucene.queries.function.docvalues.FloatDocValues; |
| import org.apache.lucene.search.IndexSearcher; |
| +import org.apache.lucene.util.Bits; |
| |
| -import java.io.IOException; |
| -import java.util.List; |
| -import java.util.Map; |
| - |
| /** |
| * Scales values to be between min and max. |
| - * <p>This implementation currently traverses all of the source values to obtain |
| - * their min and max. |
| - * <p>This implementation currently cannot distinguish when documents have been |
| - * deleted or documents that have no value, and 0.0 values will be used for |
| - * these cases. This means that if values are normally all greater than 0.0, one can |
| - * still end up with 0.0 as the min value to map from. In these cases, an |
| - * appropriate map() function could be used as a workaround to change 0.0 |
| - * to a value in the real range. |
| */ |
| -public class ScaleFloatFunction extends ValueSource { |
| +public class ScaleFloatFunction extends ValueSource{ |
| protected final ValueSource source; |
| protected final float min; |
| protected final float max; |
| - |
| + private Bits acceptedDocuments = null; |
| + |
| public ScaleFloatFunction(ValueSource source, float min, float max) { |
| this.source = source; |
| this.min = min; |
| this.max = max; |
| } |
| - |
| + |
| @Override |
| public String description() { |
| return "scale(" + source.description() + "," + min + "," + max + ")"; |
| } |
| - |
| + |
| + @Override |
| + public void setBitsForFiltering(Bits acceptedDocs) { |
| + this.acceptedDocuments = acceptedDocs; |
| + } |
| + |
| private static class ScaleInfo { |
| - float minVal; |
| - float maxVal; |
| + public float minimumValueToScaleTo; |
| + public float maximumValueToScaleTo; |
| + private float maxObservedValue = Float.NEGATIVE_INFINITY; |
| + private float minObservedValue = Float.POSITIVE_INFINITY; |
| + private float scaleConstant = 0; |
| + |
| + public float getMaxObservedValue() { |
| + return maxObservedValue; |
| + } |
| + public float getMinObservedValue() { |
| + return minObservedValue; |
| + } |
| + public void setMinObservedValue(float min) { |
| + this.minObservedValue = min; |
| + recalculateScaleConstant(); |
| + } |
| + public void setMaxObservedValue(float max) { |
| + this.maxObservedValue = max; |
| + recalculateScaleConstant(); |
| + } |
| + private void recalculateScaleConstant() { |
| + this.scaleConstant = (this.maxObservedValue - this.minObservedValue ==0) ? 0 : (this.maximumValueToScaleTo-this.minimumValueToScaleTo)/(this.maxObservedValue-this.minObservedValue); |
| + } |
| } |
| - |
| - private ScaleInfo createScaleInfo(Map context, AtomicReaderContext readerContext) throws IOException { |
| - final List<AtomicReaderContext> leaves = ReaderUtil.getTopLevelContext(readerContext).leaves(); |
| - |
| - float minVal = Float.POSITIVE_INFINITY; |
| - float maxVal = Float.NEGATIVE_INFINITY; |
| - |
| - for (AtomicReaderContext leaf : leaves) { |
| - int maxDoc = leaf.reader().maxDoc(); |
| - FunctionValues vals = source.getValues(context, leaf); |
| - for (int i=0; i<maxDoc; i++) { |
| - |
| - float val = vals.floatVal(i); |
| - if ((Float.floatToRawIntBits(val) & (0xff<<23)) == 0xff<<23) { |
| - // if the exponent in the float is all ones, then this is +Inf, -Inf or NaN |
| - // which don't make sense to factor into the scale function |
| - continue; |
| + |
| + private ScaleInfo updateScaleInfo(Map context, AtomicReaderContext readerContext) throws IOException { |
| + ScaleInfo scaleInfo = this.retrieveScaleInfoFromContext(context); |
| + float minVal = scaleInfo.getMinObservedValue(); |
| + float maxVal = scaleInfo.getMaxObservedValue(); |
| + |
| + //find the first legitimate value to initialize our min/max and avoid having to check values against both later on |
| + if(leafHasDocuments(readerContext) && minVal == Float.POSITIVE_INFINITY && maxVal == Float.NEGATIVE_INFINITY) { |
| + FunctionValues vals = source.getValues(context, readerContext); |
| + |
| + float firstLegitimateValue = Float.POSITIVE_INFINITY; |
| + for(int i =0; i < readerContext.reader().maxDoc(); i++) { |
| + if(docIsInBits(this.acceptedDocuments, i)) { |
| + float currentValue = vals.floatVal(i); |
| + if(!(this.valueIsPositiveOrNegativeInfinityOrNotANumber(currentValue))) { |
| + firstLegitimateValue = currentValue; |
| + break; |
| + } |
| + } |
| } |
| - if (val < minVal) { |
| - minVal = val; |
| + if(!(this.valueIsPositiveOrNegativeInfinityOrNotANumber(firstLegitimateValue))) { |
| + minVal = maxVal = firstLegitimateValue; |
| } |
| - if (val > maxVal) { |
| - maxVal = val; |
| + } |
| + |
| + if(leafHasDocuments(readerContext)) { |
| + int maxDoc = readerContext.reader().maxDoc(); |
| + FunctionValues vals = source.getValues(context, readerContext); |
| + for (int i=0; i<maxDoc; i++) { |
| + if(docIsInBits(this.acceptedDocuments, i)) { |
| + float val = vals.floatVal(i); |
| + if (val < minVal && !(valueIsPositiveOrNegativeInfinityOrNotANumber(val))) { // if the exponent in the float is all ones, then this is +Inf, -Inf or NaN which don't make sense to factor into the scale function |
| + minVal = val; |
| + } |
| + else if (val > maxVal && !(valueIsPositiveOrNegativeInfinityOrNotANumber(val))) { // if the exponent in the float is all ones, then this is +Inf, -Inf or NaN which don't make sense to factor into the scale function |
| + maxVal = val; |
| + } |
| + } |
| } |
| } |
| - } |
| - |
| - if (minVal == Float.POSITIVE_INFINITY) { |
| - // must have been an empty index |
| + |
| + if (minVal == Float.POSITIVE_INFINITY) { // must have been an empty index |
| minVal = maxVal = 0; |
| } |
| - |
| - ScaleInfo scaleInfo = new ScaleInfo(); |
| - scaleInfo.minVal = minVal; |
| - scaleInfo.maxVal = maxVal; |
| + |
| + scaleInfo.setMinObservedValue(minVal); |
| + scaleInfo.setMaxObservedValue(maxVal); |
| context.put(ScaleFloatFunction.this, scaleInfo); |
| return scaleInfo; |
| } |
| - |
| + |
| + private ScaleInfo retrieveScaleInfoFromContext(Map context) { |
| + ScaleInfo returnValue = (ScaleInfo)context.get(ScaleFloatFunction.this); |
| + if(returnValue == null) { |
| + returnValue = new ScaleInfo(); |
| + returnValue.minimumValueToScaleTo = this.min; |
| + returnValue.maximumValueToScaleTo = this.max; |
| + } |
| + return returnValue; |
| + } |
| + |
| + private boolean docIsInBits(Bits bitsToCheck, int docID) { |
| + if(bitsToCheck == null) return true; |
| + else return bitsToCheck.get(docID); |
| + } |
| + |
| + private boolean leafHasDocuments(AtomicReaderContext leaf) { |
| + return leaf.reader().maxDoc() > 0; |
| + } |
| + |
| + private boolean valueIsPositiveOrNegativeInfinityOrNotANumber(float value) { |
| + // if the exponent in the float is all ones, then this is +Inf, -Inf or NaN which don't make sense to factor into the scale function |
| + return ((Float.floatToRawIntBits(value) & (0xff<<23)) == 0xff<<23); |
| + } |
| + |
| @Override |
| public FunctionValues getValues(Map context, AtomicReaderContext readerContext) throws IOException { |
| - |
| - ScaleInfo scaleInfo = (ScaleInfo)context.get(ScaleFloatFunction.this); |
| - if (scaleInfo == null) { |
| - scaleInfo = createScaleInfo(context, readerContext); |
| - } |
| - |
| - final float scale = (scaleInfo.maxVal-scaleInfo.minVal==0) ? 0 : (max-min)/(scaleInfo.maxVal-scaleInfo.minVal); |
| - final float minSource = scaleInfo.minVal; |
| - final float maxSource = scaleInfo.maxVal; |
| - |
| + |
| + ScaleInfo scaleInfo = updateScaleInfo(context, readerContext); |
| + final ScaleInfo persistedScaleInfo = scaleInfo; |
| final FunctionValues vals = source.getValues(context, readerContext); |
| - |
| + |
| return new FloatDocValues(this) { |
| @Override |
| public float floatVal(int doc) { |
| - return (vals.floatVal(doc) - minSource) * scale + min; |
| + float scale = persistedScaleInfo.scaleConstant; |
| + float docScore = vals.floatVal(doc); |
| + return (docScore - persistedScaleInfo.getMinObservedValue()) * scale + min; |
| } |
| @Override |
| public String toString(int doc) { |
| - return "scale(" + vals.toString(doc) + ",toMin=" + min + ",toMax=" + max |
| - + ",fromMin=" + minSource |
| - + ",fromMax=" + maxSource |
| - + ")"; |
| - } |
| + return "scale(" + Float.toString(vals.floatVal(doc)) + ",toMin=" + min + ",toMax=" + max |
| + + ",fromMin=" + persistedScaleInfo.minObservedValue |
| + + ",fromMax=" + persistedScaleInfo.maxObservedValue |
| + + ")"; |
| + } |
| }; |
| } |
| - |
| + |
| @Override |
| public void createWeight(Map context, IndexSearcher searcher) throws IOException { |
| source.createWeight(context, searcher); |
| } |
| - |
| + |
| @Override |
| public int hashCode() { |
| int h = Float.floatToIntBits(min); |
| @@ -141,13 +196,13 @@ |
| h += source.hashCode(); |
| return h; |
| } |
| - |
| + |
| @Override |
| public boolean equals(Object o) { |
| if (ScaleFloatFunction.class != o.getClass()) return false; |
| ScaleFloatFunction other = (ScaleFloatFunction)o; |
| return this.min == other.min |
| - && this.max == other.max |
| - && this.source.equals(other.source); |
| + && this.max == other.max |
| + && this.source.equals(other.source); |
| } |
| } |
| Index: lucene/spatial/src/java/org/apache/lucene/spatial/prefix/ContainsPrefixTreeFilter.java |
| =================================================================== |
| --- lucene/spatial/src/java/org/apache/lucene/spatial/prefix/ContainsPrefixTreeFilter.java (revision 1591679) |
| +++ lucene/spatial/src/java/org/apache/lucene/spatial/prefix/ContainsPrefixTreeFilter.java (working copy) |
| @@ -19,6 +19,7 @@ |
| |
| import com.spatial4j.core.shape.Shape; |
| import com.spatial4j.core.shape.SpatialRelation; |
| + |
| import org.apache.lucene.index.AtomicReaderContext; |
| import org.apache.lucene.index.DocsEnum; |
| import org.apache.lucene.search.DocIdSet; |
| @@ -188,6 +189,10 @@ |
| /** A hash based mutable set of docIds. If this were Solr code then we might |
| * use a combination of HashDocSet and SortedIntDocSet instead. */ |
| private static class SmallDocSet extends DocIdSet implements Bits { |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| |
| private final SentinelIntSet intSet; |
| private int maxInt = 0; |
| Index: lucene/spatial/src/java/org/apache/lucene/spatial/serialized/SerializedDVStrategy.java |
| =================================================================== |
| --- lucene/spatial/src/java/org/apache/lucene/spatial/serialized/SerializedDVStrategy.java (revision 1591679) |
| +++ lucene/spatial/src/java/org/apache/lucene/spatial/serialized/SerializedDVStrategy.java (working copy) |
| @@ -21,6 +21,7 @@ |
| import com.spatial4j.core.io.BinaryCodec; |
| import com.spatial4j.core.shape.Point; |
| import com.spatial4j.core.shape.Shape; |
| + |
| import org.apache.lucene.document.BinaryDocValuesField; |
| import org.apache.lucene.document.Field; |
| import org.apache.lucene.index.AtomicReaderContext; |
| @@ -163,6 +164,10 @@ |
| return false; |
| return predFuncValues.boolVal(index); |
| } |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| |
| @Override |
| public int length() { |
| Index: lucene/test-framework/src/java/org/apache/lucene/index/AssertingAtomicReader.java |
| =================================================================== |
| --- lucene/test-framework/src/java/org/apache/lucene/index/AssertingAtomicReader.java (revision 1591679) |
| +++ lucene/test-framework/src/java/org/apache/lucene/index/AssertingAtomicReader.java (working copy) |
| @@ -611,7 +611,11 @@ |
| /** Wraps a Bits but with additional asserts */ |
| public static class AssertingBits implements Bits { |
| final Bits in; |
| - |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| + |
| public AssertingBits(Bits in) { |
| this.in = in; |
| } |
| Index: lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java |
| =================================================================== |
| --- lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java (revision 1591679) |
| +++ lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java (working copy) |
| @@ -1675,6 +1675,10 @@ |
| public boolean get(int index) { |
| return bits.get(index); |
| } |
| + @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| |
| @Override |
| public int length() { |
| Index: solr/core/src/java/org/apache/solr/search/BitDocSet.java |
| =================================================================== |
| --- solr/core/src/java/org/apache/solr/search/BitDocSet.java (revision 1591679) |
| +++ solr/core/src/java/org/apache/solr/search/BitDocSet.java (working copy) |
| @@ -304,7 +304,6 @@ |
| return adjustedDoc = (pos >= 0 && pos < max) ? pos - base : NO_MORE_DOCS; |
| } |
| } |
| - |
| @Override |
| public int advance(int target) { |
| if (target == NO_MORE_DOCS) return adjustedDoc = NO_MORE_DOCS; |
| @@ -342,8 +341,11 @@ |
| public boolean get(int index) { |
| return bs.get(index + base); |
| } |
| - |
| @Override |
| + public boolean providesConstantTimeAccess() { |
| + return true; |
| + } |
| + @Override |
| public int length() { |
| return maxDoc; |
| } |
| Index: solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java |
| =================================================================== |
| --- solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java (revision 1591679) |
| +++ solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java (working copy) |
| @@ -88,6 +88,7 @@ |
| import org.apache.lucene.util.Bits; |
| import org.apache.lucene.util.BytesRef; |
| import org.apache.lucene.util.FixedBitSet; |
| +import org.apache.lucene.util.LazyIteratorBackedBits; |
| import org.apache.solr.common.SolrException; |
| import org.apache.solr.common.SolrException.ErrorCode; |
| import org.apache.solr.common.params.ModifiableSolrParams; |
| @@ -2458,6 +2459,8 @@ |
| private class FilterSet extends DocIdSet { |
| DocIdSet docIdSet; |
| AtomicReaderContext context; |
| + private boolean bitsHaveBeenGenerated = false; |
| + private Bits lazyLoadedBits = null; |
| |
| public FilterSet(DocIdSet docIdSet, AtomicReaderContext context) { |
| this.docIdSet = docIdSet; |
| @@ -2485,8 +2488,21 @@ |
| |
| @Override |
| public Bits bits() throws IOException { |
| - return null; // don't use random access |
| + if(bitsHaveBeenGenerated) { |
| + return lazyLoadedBits; |
| + } |
| + else { |
| + lazyLoadedBits = generateBits(); |
| + bitsHaveBeenGenerated = true; |
| + return lazyLoadedBits; |
| + } |
| } |
| + |
| + private Bits generateBits() throws IOException { |
| + DocIdSetIterator iter = this.iterator(); |
| + LazyIteratorBackedBits myBits = new LazyIteratorBackedBits(65536,iter); |
| + return myBits; |
| + } |
| } |
| |
| private static class FilterIterator extends DocIdSetIterator { |