diff --git a/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java b/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java index d9541c6e0ec..852173c6eb2 100644 --- a/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java +++ b/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java @@ -30,7 +30,7 @@ import java.util.Comparator; */ class DisjunctionMaxScorer extends Scorer { - /* The scorers for subqueries that have remaining docs, kept sorted by number of next doc. */ + /* The scorers for subqueries that have remaining docs, kept as a min heap by number of next doc. */ private ArrayList subScorers = new ArrayList(); /* Multiplier applied to non-maximum-scoring subqueries for a document as they are summed into the result. */ @@ -39,33 +39,8 @@ class DisjunctionMaxScorer extends Scorer { private boolean more = false; // True iff there is a next document private boolean firstTime = true; // True iff next() has not yet been called - /* Comparator to sort subScorers according to the document number of next document */ - private static class DisjunctionMaxClauseComparator implements Comparator { - - /* Scorers have all been positioned at their next document already */ - public int compare(Object o1, Object o2) { - if (o1 instanceof Scorer && o2 instanceof Scorer) { - Scorer s1 = (Scorer) o1; - Scorer s2 = (Scorer) o2; - - return s1.doc() - s2.doc(); - } - else { - throw new ClassCastException("Objects not of the type 'Scorer'"); - } - } - - /* Compatible equality */ - public boolean equals(Scorer s1, Scorer s2) { - return s1.doc() == s2.doc(); - } - - } - - /* Fixed instance of the comparator to reuse */ - private static DisjunctionMaxClauseComparator subScorerComparator = new DisjunctionMaxClauseComparator(); - /** Creates a new instance of DisjunctionMaxScorer + * @param tieBreakerMultiplier Multiplier applied to non-maximum-scoring subqueries for a document as they are summed into the result. * @param similarity -- not used since our definition involves neither coord nor terms directly */ public DisjunctionMaxScorer(float tieBreakerMultiplier, Similarity similarity) { super(similarity); @@ -76,46 +51,30 @@ class DisjunctionMaxScorer extends Scorer { * @param scorer the scorer of a subquery of our associated DisjunctionMaxQuery */ public void add(Scorer scorer) throws IOException { - if ( scorer.next() ) { // Initialize and retain only if it produces docs + if (scorer.next()) { // Initialize and retain only if it produces docs subScorers.add(scorer); more = true; } } - /* First time initialization. Sort subScorers. */ - private void init() { - sortSubScorers(); - firstTime = false; - } - - /* Sort subScorers in order of document number of next document to be generated */ - private void sortSubScorers() { - Scorer[] sorted = (Scorer[]) subScorers.toArray(new Scorer[subScorers.size()]); - Arrays.sort(sorted, subScorerComparator); - for (int i=0; i ((Scorer) subScorers.get(i)).doc(); i++) - subScorers.set(i-1, subScorers.get(i)); - if ( i!=1 ) subScorers.set(i-1, s); - } else { - subScorers.remove(0); - if ( subScorers.isEmpty() ) return (more = false); + if (((Scorer) subScorers.get(0)).next()) + heapAdjust(0); + else { + heapRemoveRoot(); + if (subScorers.isEmpty()) return (more = false); } } while ( ((Scorer) subScorers.get(0)).doc()==lastdoc ); return true; @@ -132,13 +91,23 @@ class DisjunctionMaxScorer extends Scorer { * @return the score of the current generated document */ public float score() throws IOException { - float max = ((Scorer) subScorers.get(0)).score(), sum = max; - for (int i = 1, doc = ((Scorer) subScorers.get(0)).doc(); i < subScorers.size() && ((Scorer) subScorers.get(i)).doc() == doc; i++) { - float sub = ((Scorer) subScorers.get(i)).score(); - sum += sub; - max = Math.max(max, sub); + int doc = ((Scorer) subScorers.get(0)).doc(); + float[] sum = {((Scorer) subScorers.get(0)).score()}, max = {sum[0]}; + int size = subScorers.size(); + scoreAll(1, size, doc, sum, max); + scoreAll(2, size, doc, sum, max); + return max[0] + (sum[0] - max[0])*tieBreakerMultiplier; + } + + // Recursively iterate all subScorers that generated last doc computing sum and max + private void scoreAll(int root, int size, int doc, float[] sum, float[] max) throws IOException { + if (root0 && ((Scorer)subScorers.get(0)).doc()>1)-1; i>=0; i--) + heapAdjust(i); + } + + /* The subtree of subScorers at root is a min heap except possibly for its root element. + * Bubble the root down as required to make the subtree a heap. + */ + private void heapAdjust(int root) { + Scorer scorer=(Scorer)subScorers.get(root); + int doc=scorer.doc(); + int i=root, size=subScorers.size(); + while (i<=(size>>1)-1) { + int lchild=(i<<1)+1; + Scorer lscorer=(Scorer)subScorers.get(lchild); + int ldoc=lscorer.doc(); + int rdoc=Integer.MAX_VALUE, rchild=(i<<1)+2; + Scorer rscorer=null; + if (rchild