LUCENE-4236: commit current state

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene4236@1596373 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Robert Muir 2014-05-20 19:24:55 +00:00
parent 8e223af28c
commit 650c569b10
11 changed files with 340 additions and 393 deletions

View File

@ -19,6 +19,7 @@ package org.apache.lucene.search;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@ -341,6 +342,11 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
@Override
public Scorer scorer(AtomicReaderContext context, Bits acceptDocs)
throws IOException {
// initially the user provided value,
// but if minNrShouldMatch == optional.size(),
// we will optimize and move these to required, making this 0
int minShouldMatch = minNrShouldMatch;
List<Scorer> required = new ArrayList<>();
List<Scorer> prohibited = new ArrayList<>();
List<Scorer> optional = new ArrayList<>();
@ -360,53 +366,152 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
optional.add(subScorer);
}
}
if (required.size() == 0 && optional.size() == 0) {
// scorer simplifications:
if (required.isEmpty() && optional.isEmpty()) {
// no required and optional clauses.
return null;
} else if (optional.size() < minNrShouldMatch) {
} else if (optional.size() < minShouldMatch) {
// either >1 req scorer, or there are 0 req scorers and at least 1
// optional scorer. Therefore if there are not enough optional scorers
// no documents will be matched by the query
return null;
} else if (optional.size() == minShouldMatch) {
// either we have no optional clauses, or they are all required
// nocommit: what if required is empty too?
required.addAll(optional);
optional.clear();
minShouldMatch = 0;
}
// simple conjunction
if (optional.size() == 0 && prohibited.size() == 0) {
float coord = disableCoord ? 1.0f : coord(required.size(), maxCoord);
return new ConjunctionScorer(this, required.toArray(new Scorer[required.size()]), coord);
// pure conjunction
if (optional.isEmpty()) {
return excl(req(required, disableCoord), prohibited);
}
// simple disjunction
if (required.size() == 0 && prohibited.size() == 0 && minNrShouldMatch <= 1 && optional.size() > 1) {
float coord[] = new float[optional.size()+1];
for (int i = 0; i < coord.length; i++) {
coord[i] = disableCoord ? 1.0f : coord(i, maxCoord);
// pure disjunction
if (required.isEmpty()) {
return excl(opt(optional, minShouldMatch, disableCoord), prohibited);
}
// conjunction-disjunction mix
Scorer req = excl(req(required, true), prohibited);
Scorer opt = opt(optional, minShouldMatch, true);
// TODO: clean this up: its horrible
if (disableCoord) {
if (minShouldMatch > 0) {
return new ConjunctionScorer(this, new Scorer[] { req, opt }, 1F);
} else {
return new ReqOptSumScorer(req, opt);
}
} else if (optional.size() == 1) {
if (minShouldMatch > 0) {
return new ConjunctionScorer(this, new Scorer[] { req, opt }, coord(required.size()+1, maxCoord));
} else {
float coordReq = coord(required.size(), maxCoord);
float coordBoth = coord(required.size() + 1, maxCoord);
return new ReqOptSumScorer.ReqSingleOptScorer(req, opt, coordReq, coordBoth);
}
} else {
if (minShouldMatch > 0) {
return new ConjunctionScorer.CoordinatingConjunctionScorer(this, coords(), req, required.size(), opt);
} else {
return new ReqOptSumScorer.ReqMultiOptScorer(req, opt, required.size(), coords());
}
return new DisjunctionSumScorer(this, optional.toArray(new Scorer[optional.size()]), coord);
}
// Return a BooleanScorer2
return new BooleanScorer2(this, disableCoord, minNrShouldMatch, required, prohibited, optional, maxCoord);
}
// nocommit: double-check all this
@Override
public boolean scoresDocsOutOfOrder() {
if (minNrShouldMatch > 1) {
// BS2 (in-order) will be used by scorer()
return false;
}
int optionalCount = 0;
for (BooleanClause c : clauses) {
if (c.isRequired()) {
// BS2 (in-order) will be used by scorer()
return false;
} else if (!c.isProhibited()) {
optionalCount++;
}
}
// nocommit: maybe dont do this optionalCount stuff and just check minNR >= 1 instead?
// we do fancy things in BS2 here anyway
if (optionalCount == minNrShouldMatch) {
return false; // BS2 (in-order) will be used, as this means we have mandatory clauses
}
// scorer() will return an out-of-order scorer if requested.
return true;
}
private Scorer req(List<Scorer> required, boolean disableCoord) {
Scorer req;
if (required.size() == 1) {
req = required.get(0);
if (!disableCoord && maxCoord > 1) {
req = new BoostedScorer(req, coord(required.size(), maxCoord));
}
} else {
req = new ConjunctionScorer(this,
required.toArray(new Scorer[required.size()]),
disableCoord ? 1.0F : coord(required.size(), maxCoord));
}
return req;
}
private Scorer excl(Scorer main, List<Scorer> prohibited) throws IOException {
if (prohibited.isEmpty()) {
return main;
} else if (prohibited.size() == 1) {
return new ReqExclScorer(main, prohibited.get(0));
} else {
// TODO: this scores the required clauses (which is stupid). but we always did this.
float coords[] = new float[prohibited.size()+1];
Arrays.fill(coords, 1F);
return new ReqExclScorer(main, new DisjunctionSumScorer(this,
prohibited.toArray(new Scorer[prohibited.size()]),
coords));
}
}
private Scorer opt(List<Scorer> optional, int minShouldMatch, boolean disableCoord) throws IOException {
Scorer opt;
if (optional.size() == 1) {
opt = optional.get(0);
if (!disableCoord && maxCoord > 1) {
opt = new BoostedScorer(opt, coord(optional.size(), maxCoord));
}
} else {
float coords[];
if (disableCoord) {
coords = new float[optional.size()+1];
Arrays.fill(coords, 1F);
} else {
coords = coords();
}
if (minShouldMatch > 1) {
return new MinShouldMatchSumScorer(this, optional, minShouldMatch, coords);
}
opt = new DisjunctionSumScorer(this,
optional.toArray(new Scorer[optional.size()]),
coords);
}
return opt;
}
private float[] coords() {
float[] coords = new float[maxCoord+1];
coords[0] = 0F;
for (int i = 1; i < coords.length; i++) {
coords[i] = coord(i, maxCoord);
}
return coords;
}
}
@Override

View File

@ -1,328 +0,0 @@
package org.apache.lucene.search;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery.BooleanWeight;
import org.apache.lucene.search.similarities.Similarity;
/* See the description in BooleanScorer.java, comparing
* BooleanScorer & BooleanScorer2 */
/** 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, DisjunctionScorer, ReqOptScorer and ReqExclScorer.
*/
class BooleanScorer2 extends Scorer {
private final List<Scorer> requiredScorers;
private final List<Scorer> optionalScorers;
private final List<Scorer> prohibitedScorers;
private class Coordinator {
final float coordFactors[];
Coordinator(int maxCoord, boolean disableCoord) {
coordFactors = new float[optionalScorers.size() + requiredScorers.size() + 1];
for (int i = 0; i < coordFactors.length; i++) {
coordFactors[i] = disableCoord ? 1.0f : ((BooleanWeight)weight).coord(i, maxCoord);
}
}
int nrMatchers; // to be increased by score() of match counting scorers.
}
private final Coordinator coordinator;
/** The scorer to which all scoring will be delegated,
* except for computing and using the coordination factor.
*/
private final Scorer countingSumScorer;
/** The number of optionalScorers that need to match (if there are any) */
private final int minNrShouldMatch;
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 BooleanScorer2(BooleanWeight weight, boolean disableCoord, int minNrShouldMatch,
List<Scorer> required, List<Scorer> prohibited, List<Scorer> optional, int maxCoord) throws IOException {
super(weight);
if (minNrShouldMatch < 0) {
throw new IllegalArgumentException("Minimum number of optional scorers should not be negative");
}
this.minNrShouldMatch = minNrShouldMatch;
optionalScorers = optional;
requiredScorers = required;
prohibitedScorers = prohibited;
coordinator = new Coordinator(maxCoord, disableCoord);
countingSumScorer = makeCountingSumScorer(disableCoord);
}
/** 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;
SingleMatchScorer(Scorer scorer) {
super(scorer.weight);
this.scorer = scorer;
}
@Override
public float score() throws IOException {
int doc = docID();
if (doc >= lastScoredDoc) {
if (doc > lastScoredDoc) {
lastDocScore = scorer.score();
lastScoredDoc = doc;
}
coordinator.nrMatchers++;
}
return lastDocScore;
}
@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 scorer.cost();
}
}
private Scorer countingDisjunctionSumScorer(final List<Scorer> scorers,
int minNrShouldMatch) throws IOException {
// each scorer from the list counted as a single matcher
if (minNrShouldMatch > 1) {
return new MinShouldMatchSumScorer(weight, scorers, minNrShouldMatch) {
@Override
public float score() throws IOException {
coordinator.nrMatchers += super.nrMatchers;
return super.score();
}
};
} else {
// we pass null for coord[] since we coordinate ourselves and override score()
return new DisjunctionSumScorer(weight, scorers.toArray(new Scorer[scorers.size()]), null) {
@Override
public float score() throws IOException {
coordinator.nrMatchers += super.nrMatchers;
return (float) super.score;
}
};
}
}
private Scorer countingConjunctionSumScorer(boolean disableCoord,
List<Scorer> requiredScorers) throws IOException {
// each scorer from the list counted as a single matcher
final int requiredNrMatchers = requiredScorers.size();
return new ConjunctionScorer(weight, requiredScorers.toArray(new Scorer[requiredScorers.size()])) {
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) {
if (doc > lastScoredDoc) {
lastDocScore = super.score();
lastScoredDoc = doc;
}
coordinator.nrMatchers += requiredNrMatchers;
}
// All scorers match, so defaultSimilarity super.score() always has 1 as
// the coordination factor.
// Therefore the sum of the scores of the requiredScorers
// is used as score.
return lastDocScore;
}
};
}
private Scorer dualConjunctionSumScorer(boolean disableCoord,
Scorer req1, Scorer req2) throws IOException { // non counting.
return new ConjunctionScorer(weight, new Scorer[] { req1, req2 });
// All scorers match, so defaultSimilarity always has 1 as
// the coordination factor.
// Therefore the sum of the scores of two scorers
// is used as score.
}
/** Returns the scorer to be used for match counting and score summing.
* Uses requiredScorers, optionalScorers and prohibitedScorers.
*/
private Scorer makeCountingSumScorer(boolean disableCoord) throws IOException { // each scorer counted as a single matcher
return (requiredScorers.size() == 0)
? makeCountingSumScorerNoReq(disableCoord)
: makeCountingSumScorerSomeReq(disableCoord);
}
private Scorer makeCountingSumScorerNoReq(boolean disableCoord) throws IOException { // No required scorers
// minNrShouldMatch optional scorers are required, but at least 1
int nrOptRequired = (minNrShouldMatch < 1) ? 1 : minNrShouldMatch;
Scorer requiredCountingSumScorer;
if (optionalScorers.size() > nrOptRequired)
requiredCountingSumScorer = countingDisjunctionSumScorer(optionalScorers, nrOptRequired);
else if (optionalScorers.size() == 1)
requiredCountingSumScorer = new SingleMatchScorer(optionalScorers.get(0));
else {
requiredCountingSumScorer = countingConjunctionSumScorer(disableCoord, optionalScorers);
}
return addProhibitedScorers(requiredCountingSumScorer);
}
private Scorer makeCountingSumScorerSomeReq(boolean disableCoord) throws IOException { // At least one required scorer.
if (optionalScorers.size() == minNrShouldMatch) { // all optional scorers also required.
ArrayList<Scorer> allReq = new ArrayList<>(requiredScorers);
allReq.addAll(optionalScorers);
return addProhibitedScorers(countingConjunctionSumScorer(disableCoord, allReq));
} else { // optionalScorers.size() > minNrShouldMatch, and at least one required scorer
Scorer requiredCountingSumScorer =
requiredScorers.size() == 1
? new SingleMatchScorer(requiredScorers.get(0))
: countingConjunctionSumScorer(disableCoord, requiredScorers);
if (minNrShouldMatch > 0) { // use a required disjunction scorer over the optional scorers
return addProhibitedScorers(
dualConjunctionSumScorer( // non counting
disableCoord,
requiredCountingSumScorer,
countingDisjunctionSumScorer(
optionalScorers,
minNrShouldMatch)));
} else { // minNrShouldMatch == 0
return new ReqOptSumScorer(
addProhibitedScorers(requiredCountingSumScorer),
optionalScorers.size() == 1
? new SingleMatchScorer(optionalScorers.get(0))
// require 1 in combined, optional scorer.
: countingDisjunctionSumScorer(optionalScorers, 1));
}
}
}
/** Returns the scorer to be used for match counting and score summing.
* Uses the given required scorer and the prohibitedScorers.
* @param requiredCountingSumScorer A required scorer already built.
*/
private Scorer addProhibitedScorers(Scorer requiredCountingSumScorer) throws IOException
{
return (prohibitedScorers.size() == 0)
? requiredCountingSumScorer // no prohibited
: new ReqExclScorer(requiredCountingSumScorer,
((prohibitedScorers.size() == 1)
? prohibitedScorers.get(0)
: new MinShouldMatchSumScorer(weight, prohibitedScorers)));
}
@Override
public int docID() {
return doc;
}
@Override
public int nextDoc() throws IOException {
return doc = countingSumScorer.nextDoc();
}
@Override
public float score() throws IOException {
coordinator.nrMatchers = 0;
float sum = countingSumScorer.score();
return sum * coordinator.coordFactors[coordinator.nrMatchers];
}
@Override
public int freq() throws IOException {
return countingSumScorer.freq();
}
@Override
public int advance(int target) throws IOException {
return doc = countingSumScorer.advance(target);
}
@Override
public long cost() {
return countingSumScorer.cost();
}
@Override
public Collection<ChildScorer> getChildren() {
ArrayList<ChildScorer> children = new ArrayList<>();
for (Scorer s : optionalScorers) {
children.add(new ChildScorer(s, "SHOULD"));
}
for (Scorer s : prohibitedScorers) {
children.add(new ChildScorer(s, "MUST_NOT"));
}
for (Scorer s : requiredScorers) {
children.add(new ChildScorer(s, "MUST"));
}
return children;
}
}

View File

@ -0,0 +1,35 @@
package org.apache.lucene.search;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
import java.io.IOException;
// nocommit: WTF?
final class BoostedScorer extends FilterScorer {
private final float boost;
BoostedScorer(Scorer in, float boost) {
super(in);
this.boost = boost;
}
@Override
public float score() throws IOException {
return in.score() * boost;
}
}

View File

@ -139,4 +139,25 @@ class ConjunctionScorer extends Scorer {
this.cost = scorer.cost();
}
}
// nocommit: WTF?
static class CoordinatingConjunctionScorer extends ConjunctionScorer {
private final float coords[];
private final int reqCount;
private final Scorer req;
private final Scorer opt;
CoordinatingConjunctionScorer(Weight weight, float coords[], Scorer req, int reqCount, Scorer opt) {
super(weight, new Scorer[] { req, opt });
this.coords = coords;
this.req = req;
this.reqCount = reqCount;
this.opt = opt;
}
@Override
public float score() throws IOException {
return (req.score() + opt.score()) * coords[reqCount + opt.freq()];
}
}
}

View File

@ -42,7 +42,7 @@ abstract class DisjunctionScorer extends Scorer {
* Organize subScorers into a min heap with scorers generating the earliest document on top.
*/
protected final void heapify() {
for (int i = (numScorers >> 1) - 1; i >= 0; i--) {
for (int i = (numScorers >>> 1) - 1; i >= 0; i--) {
heapAdjust(i);
}
}
@ -55,7 +55,7 @@ abstract class DisjunctionScorer extends Scorer {
Scorer scorer = subScorers[root];
int doc = scorer.docID();
int i = root;
while (i <= (numScorers >> 1) - 1) {
while (i <= (numScorers >>> 1) - 1) {
int lchild = (i << 1) + 1;
Scorer lscorer = subScorers[lchild];
int ldoc = lscorer.docID();

View File

@ -0,0 +1,82 @@
package org.apache.lucene.search;
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
import java.io.IOException;
import java.util.Collection;
import org.apache.lucene.util.AttributeSource;
/**
* A {@code FilterScorer} contains another {@code Scorer}, which it
* uses as its basic source of data, possibly transforming the data along the
* way or providing additional functionality. The class
* {@code FilterScorer} itself simply implements all abstract methods
* of {@code Scorer} with versions that pass all requests to the
* contained scorer. Subclasses of {@code FilterScorer} may
* further override some of these methods and may also provide additional
* methods and fields.
*/
public abstract class FilterScorer extends Scorer {
protected final Scorer in;
public FilterScorer(Scorer in) {
super(in.weight);
this.in = in;
}
@Override
public float score() throws IOException {
return in.score();
}
@Override
public int freq() throws IOException {
return in.freq();
}
@Override
public int docID() {
return in.docID();
}
@Override
public int nextDoc() throws IOException {
return in.nextDoc();
}
@Override
public int advance(int target) throws IOException {
return in.advance(target);
}
@Override
public long cost() {
return in.cost();
}
@Override
public Collection<ChildScorer> getChildren() {
return in.getChildren();
}
@Override
public AttributeSource attributes() {
return in.attributes();
}
}

View File

@ -59,6 +59,8 @@ class MinShouldMatchSumScorer extends Scorer {
/** The number of subscorers that provide the current match. */
protected int nrMatchers = -1;
private double score = Float.NaN;
private final float coord[];
/**
* Construct a <code>MinShouldMatchSumScorer</code>.
@ -72,7 +74,7 @@ class MinShouldMatchSumScorer extends Scorer {
* <br>When minimumNrMatchers equals the number of subScorers,
* it is more efficient to use <code>ConjunctionScorer</code>.
*/
public MinShouldMatchSumScorer(Weight weight, List<Scorer> subScorers, int minimumNrMatchers) throws IOException {
public MinShouldMatchSumScorer(Weight weight, List<Scorer> subScorers, int minimumNrMatchers, float coord[]) throws IOException {
super(weight);
this.nrInHeap = this.numScorers = subScorers.size();
@ -105,17 +107,10 @@ class MinShouldMatchSumScorer extends Scorer {
for (int i = 0; i < nrInHeap; i++) {
this.subScorers[i] = this.sortedSubScorers[mm-1+i];
}
this.coord = coord;
minheapHeapify();
assert minheapCheck();
}
/**
* Construct a <code>DisjunctionScorer</code>, using one as the minimum number
* of matching subscorers.
*/
public MinShouldMatchSumScorer(Weight weight, List<Scorer> subScorers) throws IOException {
this(weight, subScorers, 1);
}
@Override
public final Collection<ChildScorer> getChildren() {
@ -223,7 +218,7 @@ class MinShouldMatchSumScorer extends Scorer {
*/
@Override
public float score() throws IOException {
return (float) score;
return coord[nrMatchers] * (float) score;
}
@Override

View File

@ -111,7 +111,7 @@ class ReqExclScorer extends Scorer {
@Override
public Collection<ChildScorer> getChildren() {
return Collections.singleton(new ChildScorer(reqScorer, "FILTERED"));
return Collections.singleton(new ChildScorer(reqScorer, "MUST"));
}
@Override

View File

@ -29,8 +29,8 @@ class ReqOptSumScorer extends Scorer {
/** The scorers passed from the constructor.
* These are set to null as soon as their next() or skipTo() returns false.
*/
private Scorer reqScorer;
private Scorer optScorer;
protected Scorer reqScorer;
protected Scorer optScorer;
/** Construct a <code>ReqOptScorer</code>.
* @param reqScorer The required scorer. This must match.
@ -104,5 +104,67 @@ class ReqOptSumScorer extends Scorer {
public long cost() {
return reqScorer.cost();
}
// nocommit: WTF?
static class ReqSingleOptScorer extends ReqOptSumScorer {
// coord factor if just the required part matches
private final float coordReq;
// coord factor if both required and optional part matches
private final float coordBoth;
public ReqSingleOptScorer(Scorer reqScorer, Scorer optScorer, float coordReq, float coordBoth) {
super(reqScorer, optScorer);
this.coordReq = coordReq;
this.coordBoth = coordBoth;
}
@Override
public float score() throws IOException {
int curDoc = reqScorer.docID();
float reqScore = reqScorer.score();
if (optScorer == null) {
return reqScore * coordReq;
}
int optScorerDoc = optScorer.docID();
if (optScorerDoc < curDoc && (optScorerDoc = optScorer.advance(curDoc)) == NO_MORE_DOCS) {
optScorer = null;
return reqScore * coordReq;
}
return optScorerDoc == curDoc ? (reqScore + optScorer.score()) * coordBoth : reqScore * coordReq;
}
}
// nocommit: WTF?
static class ReqMultiOptScorer extends ReqOptSumScorer {
private final int requiredCount;
private final float coords[];
private final Scorer disjunction;
public ReqMultiOptScorer(Scorer reqScorer, Scorer optScorer, int requiredCount, float coords[]) {
super(reqScorer, optScorer);
this.requiredCount = requiredCount;
this.coords = coords;
this.disjunction = optScorer;
}
@Override
public float score() throws IOException {
int curDoc = reqScorer.docID();
float reqScore = reqScorer.score();
if (optScorer == null) {
return reqScore * coords[requiredCount];
}
int optScorerDoc = optScorer.docID();
if (optScorerDoc < curDoc && (optScorerDoc = optScorer.advance(curDoc)) == NO_MORE_DOCS) {
optScorer = null;
return reqScore * coords[requiredCount];
}
return optScorerDoc == curDoc ? (reqScore + optScorer.score()) * coords[requiredCount + disjunction.freq()] : reqScore * coords[requiredCount];
}
}
}

View File

@ -32,56 +32,29 @@ import java.util.Collections;
* several places, however all they have in hand is a {@link Scorer} object, and
* might end up computing the score of a document more than once.
*/
public class ScoreCachingWrappingScorer extends Scorer {
public class ScoreCachingWrappingScorer extends FilterScorer {
private final Scorer scorer;
private int curDoc = -1;
private float curScore;
/** Creates a new instance by wrapping the given scorer. */
public ScoreCachingWrappingScorer(Scorer scorer) {
super(scorer.weight);
this.scorer = scorer;
super(scorer);
}
@Override
public float score() throws IOException {
int doc = scorer.docID();
int doc = in.docID();
if (doc != curDoc) {
curScore = scorer.score();
curScore = in.score();
curDoc = doc;
}
return curScore;
}
@Override
public int freq() throws IOException {
return scorer.freq();
}
@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 Collection<ChildScorer> getChildren() {
return Collections.singleton(new ChildScorer(scorer, "CACHED"));
}
@Override
public long cost() {
return scorer.cost();
return Collections.singleton(new ChildScorer(in, "CACHED"));
}
}

View File

@ -171,7 +171,8 @@ public class TestSubScorerFreqs extends LuceneTestCase {
boolean includeOptional = occur.contains("SHOULD");
for (int i = 0; i < maxDocs; i++) {
Map<Query, Float> doc0 = c.docCounts.get(i);
assertEquals(includeOptional ? 5 : 4, doc0.size());
// Y doesnt exist in the index, so its not in the scorer tree
assertEquals(4, doc0.size());
assertEquals(1.0F, doc0.get(aQuery), FLOAT_TOLERANCE);
assertEquals(4.0F, doc0.get(dQuery), FLOAT_TOLERANCE);
if (includeOptional) {
@ -179,7 +180,8 @@ public class TestSubScorerFreqs extends LuceneTestCase {
}
Map<Query, Float> doc1 = c.docCounts.get(++i);
assertEquals(includeOptional ? 5 : 4, doc1.size());
// Y doesnt exist in the index, so its not in the scorer tree
assertEquals(4, doc1.size());
assertEquals(1.0F, doc1.get(aQuery), FLOAT_TOLERANCE);
assertEquals(1.0F, doc1.get(dQuery), FLOAT_TOLERANCE);
if (includeOptional) {