/**
* Copyright 2014 National University of Ireland, Galway.
*
* This file is part of the SIREn project. Project and contact information:
*
* https://github.com/rdelbru/SIREn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sindice.siren.search.node;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import org.apache.lucene.search.Weight;
import org.apache.lucene.util.IntsRef;
/**
* A {@link NodeScorer} for OR like queries within a node, counterpart of
* {@link NodeConjunctionScorer}.
*
* <p>
*
* Code taken from {@link DisjunctionSumScorer} and adapted for the Siren
* use case.
*/
class NodeDisjunctionScorer extends NodeScorer {
/** The number of subscorers. */
private final int nrScorers;
/** The scorers. */
protected final Collection<NodeScorer> scorers;
/**
* The scorerNodeQueue contains all subscorers ordered by their current
* docID(), with the minimum at the top. <br>
* The scorerNodeQueue is initialized the first time nextDoc() or advance() is
* called. <br>
* An exhausted scorer is immediately removed from the scorerDocQueue. <br>
* If less than the minimumNrMatchers scorers remain in the scorerDocQueue
* nextDoc() and advance() return false.
* <p>
* After each to call to nextDoc() or advance() <code>currentScore</code> is
* the total score of the current matching doc, <code>nrMatchers</code> is the
* number of matching scorers, and all scorers are after the matching doc, or
* are exhausted.
*/
private NodeDisjunctionScorerQueue nodeScorerQueue = null;
/** The document number of the current match. */
private int currentDoc = -1;
private IntsRef currentNode = new IntsRef(new int[] { -1 }, 0, 1);
/**
* Construct a {@link NodeDisjunctionScorer}.
*
* @param subScorers
* A collection of at least two primitives scorers.
* @throws IOException
*/
public NodeDisjunctionScorer(final Weight weight,
final List<NodeScorer> scorers)
throws IOException {
super(weight);
nrScorers = scorers.size();
if (nrScorers <= 1) {
throw new IllegalArgumentException("There must be at least 2 subScorers");
}
this.scorers = scorers;
nodeScorerQueue = this.initNodeScorerQueue();
}
/**
* Initialize the {@link NodeDisjunctionScorerQueue}.
*/
private NodeDisjunctionScorerQueue initNodeScorerQueue() throws IOException {
final NodeDisjunctionScorerQueue nodeQueue = new NodeDisjunctionScorerQueue(nrScorers);
for (final NodeScorer s : scorers) {
nodeQueue.put(s);
}
return nodeQueue;
}
@Override
public float freqInNode() throws IOException {
if (currentDoc == -1) { // if nextCandidateDocument not called for the first time
return 0;
}
// return the number of matchers in the node
return this.nrMatchers();
}
@Override
public float scoreInNode() throws IOException {
if (currentDoc == -1) { // if nextCandidateDocument not called for the first time
return 0;
}
nodeScorerQueue.countAndSumMatchers();
return nodeScorerQueue.scoreInNode();
}
@Override
public boolean nextCandidateDocument() throws IOException {
boolean more = true;
// The first time nextCandidateDocument is called, we must not advance the
// underlying scorers as they have been already advanced during the queue init
if (currentDoc != -1) { // if not called for the first time
more = nodeScorerQueue.nextCandidateDocumentAndAdjustElsePop();
}
currentDoc = nodeScorerQueue.doc();
currentNode = nodeScorerQueue.node();
return more;
}
@Override
public boolean nextNode() throws IOException {
final boolean more = nodeScorerQueue.nextNodeAndAdjust();
currentNode = nodeScorerQueue.node();
return more;
}
/**
* Returns the number of subscorers matching the current node. Initially
* invalid, until {@link #nextCandidateDocument()} is called the first time.
* @throws IOException
*/
public int nrMatchers() throws IOException {
// update the number of matched scorers
nodeScorerQueue.countAndSumMatchers();
return nodeScorerQueue.nrMatchersInNode();
}
@Override
public boolean skipToCandidate(final int target) throws IOException {
final boolean more = nodeScorerQueue.skipToCandidateAndAdjustElsePop(target);
currentDoc = nodeScorerQueue.doc();
currentNode = nodeScorerQueue.node();
return more;
}
@Override
public int doc() {
return currentDoc;
}
@Override
public IntsRef node() {
return currentNode;
}
@Override
public String toString() {
return "NodeDisjunctionScorer(" + weight + "," + this.doc() + "," +
this.node() + ")";
}
}