ConjunctionScorer.
* This Scorer implements {@link Scorer#skipTo(int)} and uses skipTo() on the given Scorers.
*/
class DisjunctionSumScorer extends Scorer {
/** The number of subscorers. */
private final int nrScorers;
/** The subscorers. */
protected final List
* After each to call to next() or skipTo()
* currentSumScore is the total score of the current matching doc,
* nrMatchers is the number of matching scorers,
* and all scorers are after the matching doc, or are exhausted.
*/
private ScorerDocQueue scorerDocQueue;
/** The document number of the current match. */
private int currentDoc = -1;
/** The number of subscorers that provide the current match. */
protected int nrMatchers = -1;
private float currentScore = Float.NaN;
/** Construct a DisjunctionScorer.
* @param subScorers A collection of at least two subscorers.
* @param minimumNrMatchers The positive minimum number of subscorers that should
* match to match this query.
*
When minimumNrMatchers is bigger than
* the number of subScorers,
* no matches will be produced.
*
When minimumNrMatchers equals the number of subScorers,
* it more efficient to use ConjunctionScorer.
*/
public DisjunctionSumScorer( ListDisjunctionScorer, using one as the minimum number
* of matching subscorers.
*/
public DisjunctionSumScorer(ListscorerDocQueue.
*/
private void initScorerDocQueue() throws IOException {
scorerDocQueue = new ScorerDocQueue(nrScorers);
for (Scorer se : subScorers) {
if (se.nextDoc() != NO_MORE_DOCS) {
scorerDocQueue.insert(se);
}
}
}
/** Scores and collects all matching documents.
* @param collector The collector to which all matching documents are passed through.
*/
@Override
public void score(Collector collector) throws IOException {
collector.setScorer(this);
while (nextDoc() != NO_MORE_DOCS) {
collector.collect(currentDoc);
}
}
/** Expert: Collects matching documents in a range. Hook for optimization.
* Note that {@link #next()} must be called once before this method is called
* for the first time.
* @param collector The collector to which all matching documents are passed through.
* @param max Do not score documents past this.
* @return true if more matching documents may remain.
*/
@Override
protected boolean score(Collector collector, int max, int firstDocID) throws IOException {
// firstDocID is ignored since nextDoc() sets 'currentDoc'
collector.setScorer(this);
while (currentDoc < max) {
collector.collect(currentDoc);
if (nextDoc() == NO_MORE_DOCS) {
return false;
}
}
return true;
}
@Override
public int nextDoc() throws IOException {
if (scorerDocQueue.size() < minimumNrMatchers || !advanceAfterCurrent()) {
currentDoc = NO_MORE_DOCS;
}
return currentDoc;
}
/** Advance all subscorers after the current document determined by the
* top of the scorerDocQueue.
* Repeat until at least the minimum number of subscorers match on the same
* document and all subscorers are after that document or are exhausted.
*
On entry the scorerDocQueue has at least minimumNrMatchers
* available. At least the scorer with the minimum document number will be advanced.
* @return true iff there is a match.
*
In case there is a match, currentDoc, currentSumScore,
* and nrMatchers describe the match.
*
* TODO: Investigate whether it is possible to use skipTo() when
* the minimum number of matchers is bigger than one, ie. try and use the
* character of ConjunctionScorer for the minimum number of matchers.
* Also delay calling score() on the sub scorers until the minimum number of
* matchers is reached.
*
For this, a Scorer array with minimumNrMatchers elements might
* hold Scorers at currentDoc that are temporarily popped from scorerQueue.
*/
protected boolean advanceAfterCurrent() throws IOException {
do { // repeat until minimum nr of matchers
currentDoc = scorerDocQueue.topDoc();
currentScore = scorerDocQueue.topScore();
nrMatchers = 1;
do { // Until all subscorers are after currentDoc
if (!scorerDocQueue.topNextAndAdjustElsePop()) {
if (scorerDocQueue.size() == 0) {
break; // nothing more to advance, check for last match.
}
}
if (scorerDocQueue.topDoc() != currentDoc) {
break; // All remaining subscorers are after currentDoc.
}
currentScore += scorerDocQueue.topScore();
nrMatchers++;
} while (true);
if (nrMatchers >= minimumNrMatchers) {
return true;
} else if (scorerDocQueue.size() < minimumNrMatchers) {
return false;
}
} while (true);
}
/** Returns the score of the current document matching the query.
* Initially invalid, until {@link #next()} is called the first time.
*/
@Override
public float score() throws IOException { return currentScore; }
@Override
public int docID() {
return currentDoc;
}
/** Returns the number of subscorers matching the current document.
* Initially invalid, until {@link #next()} is called the first time.
*/
public int nrMatchers() {
return nrMatchers;
}
/**
* Advances to the first match beyond the current whose document number is
* greater than or equal to a given target.
* The implementation uses the skipTo() method on the subscorers.
*
* @param target
* The target document number.
* @return the document whose number is greater than or equal to the given
* target, or -1 if none exist.
*/
@Override
public int advance(int target) throws IOException {
if (scorerDocQueue.size() < minimumNrMatchers) {
return currentDoc = NO_MORE_DOCS;
}
if (target <= currentDoc) {
return currentDoc;
}
do {
if (scorerDocQueue.topDoc() >= target) {
return advanceAfterCurrent() ? currentDoc : (currentDoc = NO_MORE_DOCS);
} else if (!scorerDocQueue.topSkipToAndAdjustElsePop(target)) {
if (scorerDocQueue.size() < minimumNrMatchers) {
return currentDoc = NO_MORE_DOCS;
}
}
} while (true);
}
}