package doser.lucene.query;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.similarities.Similarity;
import doser.lucene.query.LearnToRankQuery.LearnToRankWeight;
/**
* An alternative to BooleanScorer that also allows a minimum number of optional
* scorers that should match. <br>
* Implements skipTo(), and has no limitations on the numbers of added scorers. <br>
* Uses ConjunctionScorer, AbstractDisjunctionScorer, ReqOptScorer and
* ReqExclScorer.
*/
class LearnToRankScorer extends Scorer { // NOPMD by quh on 28.02.14 10:44
/** Count a scorer as a single match. */
private class SingleMatchScorer extends Scorer {
private final LearnToRankClause clause;
// Save the score of lastScoredDoc, so that we don't compute it more
// than
// once in score().
private float lastDocScore = Float.NaN;
private int lastScoredDoc = -1;
private final Scorer scorer;
SingleMatchScorer(final Scorer scorer, final LearnToRankClause clause) {
super(scorer.getWeight());
this.scorer = scorer;
this.clause = clause;
}
@Override
public int advance(final int target) throws IOException {
return scorer.advance(target);
}
@Override
public long cost() {
return 0;
}
@Override
public int docID() {
return scorer.docID();
}
@Override
public int freq() throws IOException {
return 1;
}
@Override
public int nextDoc() throws IOException {
return scorer.nextDoc();
}
@Override
public float score() throws IOException {
final int doc = docID();
if (doc > lastScoredDoc) {
lastDocScore = scorer.score();
lastScoredDoc = doc;
}
final float val = lastDocScore * clause.getWeight();
clause.addFeatureValue(docBase, doc, val);
return val;
}
}
/**
* The scorer to which all scoring will be delegated, except for computing
* and using the coordination factor.
*/
private final Scorer countingSumScorer;
private int doc = -1;
private final int docBase;
private final LearnToRankClause[] optionalClauses;
private final List<Scorer> optionalScorers;
private final LearnToRankClause[] requiredClauses;
private final List<Scorer> requiredScorers;
/**
* Creates a {@link Scorer} with the given similarity and lists of required,
* prohibited and optional scorers. In no required scorers are added, at
* least one of the optional scorers will have to match during the search.
*
* @param weight
* The BooleanWeight to be used.
* @param disableCoord
* If this parameter is true, coordination level matching (
* {@link Similarity#coord(int, int)}) is not used.
* @param minNrShouldMatch
* The minimum number of optional added scorers that should match
* during the search. In case no required scorers are added, at
* least one of the optional scorers will have to match during
* the search.
* @param required
* the list of required scorers.
* @param prohibited
* the list of prohibited scorers.
* @param optional
* the list of optional scorers.
*/
public LearnToRankScorer(final LearnToRankWeight weight,
final List<Scorer> optional, final List<Scorer> required,
final LearnToRankClause[] optionalClauses,
final LearnToRankClause[] requiredClauses, final int docBase)
throws IOException {
super(weight);
this.docBase = docBase;
optionalScorers = optional;
requiredScorers = required;
this.optionalClauses = optionalClauses;
this.requiredClauses = requiredClauses;
countingSumScorer = createSumScorer();
}
@Override
public int advance(final int target) throws IOException {
return doc = countingSumScorer.advance(target);
}
@Override
public long cost() {
// TODO Auto-genedrated method stub
return 0;
}
private Scorer countingConjunctionSumScorer(
final List<Scorer> requiredScorers,
final LearnToRankClause[] requiredClauses) throws IOException {
// each scorer from the list counted as a single matcher
final Scorer[] sco = new Scorer[requiredScorers.size()];
for (int i = 0; i < sco.length; i++) {
sco[i] = requiredScorers.get(i);
}
return new ConjunctionScorer(weight, sco, requiredClauses, docBase) {
private float lastDocScore = Float.NaN;
private int lastScoredDoc = -1;
@Override
public float score() throws IOException {
final int doc = docID();
if (doc >= lastScoredDoc && doc > lastScoredDoc) {
lastDocScore = super.score();
lastScoredDoc = doc;
}
return lastDocScore;
}
};
}
private Scorer countingDisjunctionSumScorer(final List<Scorer> scorers,
final int minNrShouldMatch) throws IOException {
// each scorer from the list counted as a single matcher
return new DisjunctionSumScorer(weight, scorers, optionalClauses,
docBase) {
// Save the score of lastScoredDoc, so that we don't compute it more
// than
// once in score().
private float lastDocScore = Float.NaN;
private int lastScoredDoc = -1;
@Override
public float score() throws IOException {
final int doc = docID();
if (doc > lastScoredDoc) {
lastDocScore = super.score();
lastScoredDoc = doc;
}
return lastDocScore;
}
};
}
private Scorer createSumScorer() throws IOException {
return (requiredScorers.size() == 0) ? makeCountingSumScorerNoReq()
: makeCountingSumScorerSomeReq();
}
@Override
public int docID() {
return doc;
}
@Override
public int freq() throws IOException {
return countingSumScorer.freq();
}
@Override
public Collection<ChildScorer> getChildren() {
final ArrayList<ChildScorer> children = new ArrayList<ChildScorer>();
for (final Scorer scorer : optionalScorers) {
children.add(new ChildScorer(scorer, "SHOULD"));
}
return children;
}
/**
* Returns the scorer to be used for match counting and score summing. Uses
* requiredScorers, optionalScorers.
*/
private Scorer makeCountingSumScorerNoReq() throws IOException { // No //
// required
// scorers
// minNrShouldMatch optional scorers are required, but at least 1
final int nrOptRequired = 1;
Scorer reqCountSumScorer;
if (optionalScorers.size() > nrOptRequired) {
reqCountSumScorer = countingDisjunctionSumScorer(optionalScorers,
nrOptRequired);
} else {
reqCountSumScorer = new SingleMatchScorer(optionalScorers.get(0),
optionalClauses[0]);
}
return reqCountSumScorer;
}
private Scorer makeCountingSumScorerSomeReq() throws IOException {
final Scorer reqCountSumScorer = requiredScorers.size() == 1 ? new SingleMatchScorer(
requiredScorers.get(0), requiredClauses[0])
: countingConjunctionSumScorer(requiredScorers, requiredClauses);
if (optionalScorers.size() == 0) {
return reqCountSumScorer;
} else {
return new ReqOptSumScorer(reqCountSumScorer,
optionalScorers.size() == 1 ? new SingleMatchScorer(
optionalScorers.get(0), optionalClauses[0])
: countingDisjunctionSumScorer(optionalScorers, 1));
}
}
@Override
public int nextDoc() throws IOException {
return doc = countingSumScorer.nextDoc();
}
@Override
public float score() throws IOException {
return countingSumScorer.score();
}
/**
* Scores and collects all matching documents.
*
* @param collector
* The collector to which all matching documents are passed
* through.
*/
// @Override
// public void score(final Collector collector) throws IOException {
// collector.setScorer(this);
// while ((doc = countingSumScorer.nextDoc()) != NO_MORE_DOCS) {
// collector.collect(doc);
// }
// }
//
// @Override
// public boolean score(final Collector collector, final int max,
// final int firstDocID) throws IOException {
// doc = firstDocID;
// collector.setScorer(this);
// while (doc < max) {
// collector.collect(doc);
// doc = countingSumScorer.nextDoc();
// }
// return doc != NO_MORE_DOCS;
// }
}
//
// import java.io.IOException;
// import java.util.ArrayList;
// import java.util.Collection;
// import java.util.List;
// import org.apache.lucene.search.Collector;
// import org.apache.lucene.search.Scorer;
// import org.apache.lucene.search.similarities.Similarity;
//
// import
// de.uop.code.disambiguation.ltr.lucene.query.LearnToRankQuery.LearnToRankWeight;
//
// /**
// * An alternative to BooleanScorer that also allows a minimum number of
// optional
// * scorers that should match. <br>
// * Implements skipTo(), and has no limitations on the numbers of added
// scorers. <br>
// * Uses ConjunctionScorer, AbstractDisjunctionScorer, ReqOptScorer and
// ReqExclScorer.
// */
// class LearnToRankScorer extends Scorer {
//
// private final List<Scorer> requiredScorers;
//
// private final List<Scorer> optionalScorers;
//
// private final LearnToRankClause[] optionalClauses;
//
// private final LearnToRankClause[] requiredClauses;
//
// private final int docBase;
//
// /**
// * The scorer to which all scoring will be delegated, except for computing
// * and using the coordination factor.
// */
// private final Scorer countingSumScorer;
//
// private int doc = -1;
//
// /**
// * Creates a {@link Scorer} with the given similarity and lists of required,
// * prohibited and optional scorers. In no required scorers are added, at
// * least one of the optional scorers will have to match during the search.
// *
// * @param weight
// * The BooleanWeight to be used.
// * @param disableCoord
// * If this parameter is true, coordination level matching (
// * {@link Similarity#coord(int, int)}) is not used.
// * @param minNrShouldMatch
// * The minimum number of optional added scorers that should match
// * during the search. In case no required scorers are added, at
// * least one of the optional scorers will have to match during
// * the search.
// * @param required
// * the list of required scorers.
// * @param prohibited
// * the list of prohibited scorers.
// * @param optional
// * the list of optional scorers.
// */
// public LearnToRankScorer(LearnToRankWeight weight, List<Scorer> optional,
// List<Scorer> required, LearnToRankClause[] optionalClauses,
// LearnToRankClause[] requiredClauses, int docBase)
// throws IOException {
// super(weight);
// this.docBase = docBase;
// this.optionalScorers = optional;
// this.requiredScorers = required;
// this.optionalClauses = optionalClauses;
// this.requiredClauses = requiredClauses;
// countingSumScorer = createSumScorer();
// }
//
//
// /** Count a scorer as a single match. */
// private class SingleMatchScorer extends Scorer {
// private Scorer scorer;
// private int lastScoredDoc = -1;
// // Save the score of lastScoredDoc, so that we don't compute it more
// // than
// // once in score().
// private float lastDocScore = Float.NaN;
//
// private LearnToRankClause clause;
//
// SingleMatchScorer(Scorer scorer, LearnToRankClause clause) {
// super(scorer.getWeight());
// this.scorer = scorer;
// this.clause = clause;
// }
//
// @Override
// public float score() throws IOException {
// int doc = docID();
// if (doc > lastScoredDoc) {
// lastDocScore = scorer.score();
// lastScoredDoc = doc;
// }
// float val = lastDocScore * clause.getWeight();
// clause.addFeatureValue(docBase, doc, val);
// return val;
// }
//
// @Override
// public int freq() throws IOException {
// return 1;
// }
//
// @Override
// public int docID() {
// return scorer.docID();
// }
//
// @Override
// public int nextDoc() throws IOException {
// return scorer.nextDoc();
// }
//
// @Override
// public int advance(int target) throws IOException {
// return scorer.advance(target);
// }
//
// @Override
// public long cost() {
// return 0;
// }
// }
//
// private Scorer createSumScorer() throws IOException {
// return (requiredScorers.size() == 0) ? makeCountingSumScorerNoReq()
// : makeCountingSumScorerSomeReq();
// }
//
// private Scorer countingDisjunctionSumScorer(final List<Scorer> scorers,
// int minNrShouldMatch) throws IOException {
// // each scorer from the list counted as a single matcher
// Scorer[] sco = new Scorer[scorers.size()];
// for (int i = 0; i < sco.length; i++) {
// sco[i] = scorers.get(i);
// }
//
// return new DisjunctionSumScorer(weight, sco,
// optionalClauses, docBase) {
// private int lastScoredDoc = -1;
// // Save the score of lastScoredDoc, so that we don't compute it more
// // than
// // once in score().
// private float lastDocScore = Float.NaN;
//
// @Override
// public float score() throws IOException {
// int doc = docID();
// if (doc > lastScoredDoc) {
// lastDocScore = super.score();
// lastScoredDoc = doc;
// }
// return lastDocScore;
// }
// };
// }
//
// private Scorer countingConjunctionSumScorer(List<Scorer> requiredScorers,
// LearnToRankClause[] requiredClauses) throws IOException {
// // each scorer from the list counted as a single matcher
// Scorer[] sco = new Scorer[requiredScorers.size()];
// for (int i = 0; i < sco.length; i++) {
// sco[i] = requiredScorers.get(i);
// }
// return new ConjunctionScorer(weight, sco,
// requiredClauses, docBase) {
// private int lastScoredDoc = -1;
// private float lastDocScore = Float.NaN;
//
// @Override
// public float score() throws IOException {
// int doc = docID();
// if (doc >= lastScoredDoc) {
// if (doc > lastScoredDoc) {
// lastDocScore = super.score();
// lastScoredDoc = doc;
// }
// }
// return lastDocScore;
// }
// };
// }
//
// /**
// * Returns the scorer to be used for match counting and score summing. Uses
// * requiredScorers, optionalScorers.
// */
//
// private Scorer makeCountingSumScorerNoReq() throws IOException { // No //
// required
// // scorers
// // minNrShouldMatch optional scorers are required, but at least 1
// int nrOptRequired = 1;
// Scorer requiredCountingSumScorer;
// if (optionalScorers.size() > nrOptRequired) {
// requiredCountingSumScorer = countingDisjunctionSumScorer(
// optionalScorers, nrOptRequired);
// } else {
// requiredCountingSumScorer = new SingleMatchScorer(
// optionalScorers.get(0), optionalClauses[0]);
// }
// return requiredCountingSumScorer;
// }
//
// private Scorer makeCountingSumScorerSomeReq() throws IOException {
// Scorer requiredCountingSumScorer = requiredScorers.size() == 1 ? new
// SingleMatchScorer(
// requiredScorers.get(0), requiredClauses[0])
// : countingConjunctionSumScorer(requiredScorers, requiredClauses);
//
// if (optionalScorers.size() == 0) {
// return requiredCountingSumScorer;
// } else {
// return new ReqOptSumScorer(requiredCountingSumScorer,
// optionalScorers.size() == 1 ? new SingleMatchScorer(
// optionalScorers.get(0), optionalClauses[0])
// : countingDisjunctionSumScorer(optionalScorers, 1));
// }
//
// }
//
// /**
// * 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 ((doc = countingSumScorer.nextDoc()) != NO_MORE_DOCS) {
// collector.collect(doc);
// }
// }
//
// @Override
// public boolean score(Collector collector, int max, int firstDocID)
// throws IOException {
// doc = firstDocID;
// collector.setScorer(this);
// while (doc < max) {
// collector.collect(doc);
// doc = countingSumScorer.nextDoc();
// }
// return doc != NO_MORE_DOCS;
// }
//
// @Override
// public int docID() {
// return doc;
// }
//
// @Override
// public int nextDoc() throws IOException {
// return doc = countingSumScorer.nextDoc();
// }
//
// @Override
// public float score() throws IOException {
// float sum = countingSumScorer.score();
// return sum;
// }
//
// @Override
// public int freq() throws IOException {
// return countingSumScorer.freq();
// }
//
// @Override
// public int advance(int target) throws IOException {
// return doc = countingSumScorer.advance(target);
// }
//
// @Override
// public Collection<ChildScorer> getChildren() {
// ArrayList<ChildScorer> children = new ArrayList<ChildScorer>();
// for (Scorer s : optionalScorers) {
// children.add(new ChildScorer(s, "SHOULD"));
// }
// return children;
// }
//
// @Override
// public long cost() {
// // TODO Auto-generated method stub
// return 0;
// }