package org.apache.lucene.search;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
class ReqExclScorer extends Scorer {
private final Scorer reqScorer;
private final DocIdSetIterator reqApproximation;
private final DocIdSetIterator exclApproximation;
private final TwoPhaseIterator reqTwoPhaseIterator;
private final TwoPhaseIterator exclTwoPhaseIterator;
public ReqExclScorer(Scorer reqScorer, Scorer exclScorer) {
super(reqScorer.weight);
this.reqScorer = reqScorer;
reqTwoPhaseIterator = reqScorer.twoPhaseIterator();
if (reqTwoPhaseIterator == null) {
reqApproximation = reqScorer.iterator();
} else {
reqApproximation = reqTwoPhaseIterator.approximation();
}
exclTwoPhaseIterator = exclScorer.twoPhaseIterator();
if (exclTwoPhaseIterator == null) {
exclApproximation = exclScorer.iterator();
} else {
exclApproximation = exclTwoPhaseIterator.approximation();
}
}
private static boolean matchesOrNull(TwoPhaseIterator it) throws IOException {
return it == null || it.matches();
}
@Override
public DocIdSetIterator iterator() {
return TwoPhaseIterator.asDocIdSetIterator(twoPhaseIterator());
}
@Override
public int docID() {
return reqApproximation.docID();
}
@Override
public float score() throws IOException {
return reqScorer.score();
}
@Override
public int advanceShallow(int target) throws IOException {
return reqScorer.advanceShallow(target);
}
@Override
public float getMaxScore(int upTo) throws IOException {
return reqScorer.getMaxScore(upTo);
}
@Override
public void setMinCompetitiveScore(float score) throws IOException {
reqScorer.setMinCompetitiveScore(score);
}
@Override
public Collection<ChildScorable> getChildren() {
return Collections.singleton(new ChildScorable(reqScorer, "MUST"));
}
private static final int ADVANCE_COST = 10;
private static float matchCost(
DocIdSetIterator reqApproximation,
TwoPhaseIterator reqTwoPhaseIterator,
DocIdSetIterator exclApproximation,
TwoPhaseIterator exclTwoPhaseIterator) {
float matchCost = 2;
if (reqTwoPhaseIterator != null) {
matchCost += reqTwoPhaseIterator.matchCost();
}
final float exclMatchCost = ADVANCE_COST
+ (exclTwoPhaseIterator == null ? 0 : exclTwoPhaseIterator.matchCost());
float ratio;
if (reqApproximation.cost() <= 0) {
ratio = 1f;
} else if (exclApproximation.cost() <= 0) {
ratio = 0f;
} else {
ratio = (float) Math.min(reqApproximation.cost(), exclApproximation.cost()) / reqApproximation.cost();
}
matchCost += ratio * exclMatchCost;
return matchCost;
}
@Override
public TwoPhaseIterator twoPhaseIterator() {
final float matchCost = matchCost(reqApproximation, reqTwoPhaseIterator, exclApproximation, exclTwoPhaseIterator);
if (reqTwoPhaseIterator == null
|| (exclTwoPhaseIterator != null && reqTwoPhaseIterator.matchCost() <= exclTwoPhaseIterator.matchCost())) {
return new TwoPhaseIterator(reqApproximation) {
@Override
public boolean matches() throws IOException {
final int doc = reqApproximation.docID();
int exclDoc = exclApproximation.docID();
if (exclDoc < doc) {
exclDoc = exclApproximation.advance(doc);
}
if (exclDoc != doc) {
return matchesOrNull(reqTwoPhaseIterator);
}
return matchesOrNull(reqTwoPhaseIterator) && !matchesOrNull(exclTwoPhaseIterator);
}
@Override
public float matchCost() {
return matchCost;
}
};
} else {
return new TwoPhaseIterator(reqApproximation) {
@Override
public boolean matches() throws IOException {
final int doc = reqApproximation.docID();
int exclDoc = exclApproximation.docID();
if (exclDoc < doc) {
exclDoc = exclApproximation.advance(doc);
}
if (exclDoc != doc) {
return matchesOrNull(reqTwoPhaseIterator);
}
return !matchesOrNull(exclTwoPhaseIterator) && matchesOrNull(reqTwoPhaseIterator);
}
@Override
public float matchCost() {
return matchCost;
}
};
}
}
}