LUCENE-5487: merge trunk
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene5487@1576473 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java b/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
index e6dfa3b..23154f9 100644
--- a/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
@@ -242,7 +242,7 @@
for (Iterator<Weight> wIter = weights.iterator(); wIter.hasNext();) {
Weight w = wIter.next();
BooleanClause c = cIter.next();
- if (w.scorer(context, true, true, context.reader().getLiveDocs()) == null) {
+ if (w.scorer(context, context.reader().getLiveDocs()) == null) {
if (c.isRequired()) {
fail = true;
Explanation r = new Explanation(0.0f, "no match on required clause (" + c.getQuery().toString() + ")");
@@ -305,8 +305,43 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs)
+ public BulkScorer bulkScorer(AtomicReaderContext context, boolean scoreDocsInOrder,
+ Bits acceptDocs) throws IOException {
+
+ if (scoreDocsInOrder || minNrShouldMatch > 1) {
+ // TODO: (LUCENE-4872) in some cases BooleanScorer may be faster for minNrShouldMatch
+ // but the same is even true of pure conjunctions...
+ return super.bulkScorer(context, scoreDocsInOrder, acceptDocs);
+ }
+
+ List<BulkScorer> prohibited = new ArrayList<BulkScorer>();
+ List<BulkScorer> optional = new ArrayList<BulkScorer>();
+ Iterator<BooleanClause> cIter = clauses.iterator();
+ for (Weight w : weights) {
+ BooleanClause c = cIter.next();
+ BulkScorer subScorer = w.bulkScorer(context, false, acceptDocs);
+ if (subScorer == null) {
+ if (c.isRequired()) {
+ return null;
+ }
+ } else if (c.isRequired()) {
+ // TODO: there are some cases where BooleanScorer
+ // would handle conjunctions faster than
+ // BooleanScorer2...
+ return super.bulkScorer(context, scoreDocsInOrder, acceptDocs);
+ } else if (c.isProhibited()) {
+ prohibited.add(subScorer);
+ } else {
+ optional.add(subScorer);
+ }
+ }
+
+ // Check if we can and should return a BooleanScorer
+ return new BooleanScorer(this, disableCoord, minNrShouldMatch, optional, prohibited, maxCoord);
+ }
+
+ @Override
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs)
throws IOException {
List<Scorer> required = new ArrayList<Scorer>();
List<Scorer> prohibited = new ArrayList<Scorer>();
@@ -314,7 +349,7 @@
Iterator<BooleanClause> cIter = clauses.iterator();
for (Weight w : weights) {
BooleanClause c = cIter.next();
- Scorer subScorer = w.scorer(context, true, false, acceptDocs);
+ Scorer subScorer = w.scorer(context, acceptDocs);
if (subScorer == null) {
if (c.isRequired()) {
return null;
@@ -328,20 +363,6 @@
}
}
- // NOTE: we could also use BooleanScorer, if we knew
- // this BooleanQuery was embedded in another
- // BooleanQuery that was also using BooleanScorer (ie,
- // BooleanScorer can nest). But this is hard to
- // detect and we never do so today... (ie, we only
- // return BooleanScorer for topScorer):
-
- // Check if we can and should return a BooleanScorer
- // TODO: (LUCENE-4872) in some cases BooleanScorer may be faster for minNrShouldMatch
- // but the same is even true of pure conjunctions...
- if (!scoreDocsInOrder && topScorer && required.size() == 0 && minNrShouldMatch <= 1) {
- return new BooleanScorer(this, disableCoord, minNrShouldMatch, optional, prohibited, maxCoord);
- }
-
if (required.size() == 0 && optional.size() == 0) {
// no required and optional clauses.
return null;
@@ -373,9 +394,14 @@
@Override
public boolean scoresDocsOutOfOrder() {
+ if (minNrShouldMatch > 1) {
+ // BS2 (in-order) will be used by scorer()
+ return false;
+ }
for (BooleanClause c : clauses) {
if (c.isRequired()) {
- return false; // BS2 (in-order) will be used by scorer()
+ // BS2 (in-order) will be used by scorer()
+ return false;
}
}
diff --git a/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java b/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java
index c470290..7c2b6aa 100644
--- a/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java
@@ -23,6 +23,7 @@
import java.util.List;
import org.apache.lucene.index.AtomicReaderContext;
+import org.apache.lucene.index.DocsEnum;
import org.apache.lucene.search.BooleanQuery.BooleanWeight;
/* Description from Doug Cutting (excerpted from
@@ -58,7 +59,7 @@
* conjunction can reduce the number of priority queue
* updates for the optional terms. */
-final class BooleanScorer extends Scorer {
+final class BooleanScorer extends BulkScorer {
private static final class BooleanScorerCollector extends Collector {
private BucketTable bucketTable;
@@ -108,38 +109,6 @@
}
- // An internal class which is used in score(Collector, int) for setting the
- // current score. This is required since Collector exposes a setScorer method
- // and implementations that need the score will call scorer.score().
- // Therefore the only methods that are implemented are score() and doc().
- private static final class BucketScorer extends Scorer {
-
- double score;
- int doc = NO_MORE_DOCS;
- int freq;
-
- public BucketScorer(Weight weight) { super(weight); }
-
- @Override
- public int advance(int target) { return NO_MORE_DOCS; }
-
- @Override
- public int docID() { return doc; }
-
- @Override
- public int freq() { return freq; }
-
- @Override
- public int nextDoc() { return NO_MORE_DOCS; }
-
- @Override
- public float score() { return (float)score; }
-
- @Override
- public long cost() { return 1; }
-
- }
-
static final class Bucket {
int doc = -1; // tells if bucket is valid
double score; // incremental score
@@ -175,19 +144,21 @@
}
static final class SubScorer {
- public Scorer scorer;
+ public BulkScorer scorer;
// TODO: re-enable this if BQ ever sends us required clauses
//public boolean required = false;
public boolean prohibited;
public Collector collector;
public SubScorer next;
+ public boolean more;
- public SubScorer(Scorer scorer, boolean required, boolean prohibited,
+ public SubScorer(BulkScorer scorer, boolean required, boolean prohibited,
Collector collector, SubScorer next) {
if (required) {
throw new IllegalArgumentException("this scorer cannot handle required=true");
}
this.scorer = scorer;
+ this.more = true;
// TODO: re-enable this if BQ ever sends us required clauses
//this.required = required;
this.prohibited = prohibited;
@@ -206,26 +177,20 @@
private Bucket current;
// Any time a prohibited clause matches we set bit 0:
private static final int PROHIBITED_MASK = 1;
-
- BooleanScorer(BooleanWeight weight, boolean disableCoord, int minNrShouldMatch,
- List<Scorer> optionalScorers, List<Scorer> prohibitedScorers, int maxCoord) throws IOException {
- super(weight);
- this.minNrShouldMatch = minNrShouldMatch;
- if (optionalScorers != null && optionalScorers.size() > 0) {
- for (Scorer scorer : optionalScorers) {
- if (scorer.nextDoc() != NO_MORE_DOCS) {
- scorers = new SubScorer(scorer, false, false, bucketTable.newCollector(0), scorers);
- }
- }
+ private final Weight weight;
+
+ BooleanScorer(BooleanWeight weight, boolean disableCoord, int minNrShouldMatch,
+ List<BulkScorer> optionalScorers, List<BulkScorer> prohibitedScorers, int maxCoord) throws IOException {
+ this.minNrShouldMatch = minNrShouldMatch;
+ this.weight = weight;
+
+ for (BulkScorer scorer : optionalScorers) {
+ scorers = new SubScorer(scorer, false, false, bucketTable.newCollector(0), scorers);
}
- if (prohibitedScorers != null && prohibitedScorers.size() > 0) {
- for (Scorer scorer : prohibitedScorers) {
- if (scorer.nextDoc() != NO_MORE_DOCS) {
- scorers = new SubScorer(scorer, false, true, bucketTable.newCollector(PROHIBITED_MASK), scorers);
- }
- }
+ for (BulkScorer scorer : prohibitedScorers) {
+ scorers = new SubScorer(scorer, false, true, bucketTable.newCollector(PROHIBITED_MASK), scorers);
}
coordFactors = new float[optionalScorers.size() + 1];
@@ -234,17 +199,15 @@
}
}
- // firstDocID is ignored since nextDoc() initializes 'current'
@Override
- public boolean score(Collector collector, int max, int firstDocID) throws IOException {
- // Make sure it's only BooleanScorer that calls us:
- assert firstDocID == -1;
+ public boolean score(Collector collector, int max) throws IOException {
+
boolean more;
Bucket tmp;
- BucketScorer bs = new BucketScorer(weight);
+ FakeScorer fs = new FakeScorer();
// The internal loop will set the score and doc before calling collect.
- collector.setScorer(bs);
+ collector.setScorer(fs);
do {
bucketTable.first = null;
@@ -263,7 +226,7 @@
// that should work)... but in theory an outside
// app could pass a different max so we must check
// it:
- if (current.doc >= max){
+ if (current.doc >= max) {
tmp = current;
current = current.next;
tmp.next = bucketTable.first;
@@ -272,9 +235,9 @@
}
if (current.coord >= minNrShouldMatch) {
- bs.score = current.score * coordFactors[current.coord];
- bs.doc = current.doc;
- bs.freq = current.coord;
+ fs.score = (float) (current.score * coordFactors[current.coord]);
+ fs.doc = current.doc;
+ fs.freq = current.coord;
collector.collect(current.doc);
}
}
@@ -292,9 +255,9 @@
more = false;
end += BucketTable.SIZE;
for (SubScorer sub = scorers; sub != null; sub = sub.next) {
- int subScorerDocID = sub.scorer.docID();
- if (subScorerDocID != NO_MORE_DOCS) {
- more |= sub.scorer.score(sub.collector, end, subScorerDocID);
+ if (sub.more) {
+ sub.more = sub.scorer.score(sub.collector, end);
+ more |= sub.more;
}
}
current = bucketTable.first;
@@ -303,43 +266,8 @@
return false;
}
-
- @Override
- public int advance(int target) {
- throw new UnsupportedOperationException();
- }
@Override
- public int docID() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int nextDoc() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public float score() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int freq() throws IOException {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public long cost() {
- return Integer.MAX_VALUE;
- }
-
- @Override
- public void score(Collector collector) throws IOException {
- score(collector, Integer.MAX_VALUE, -1);
- }
-
- @Override
public String toString() {
StringBuilder buffer = new StringBuilder();
buffer.append("boolean(");
@@ -350,9 +278,4 @@
buffer.append(")");
return buffer.toString();
}
-
- @Override
- public Collection<ChildScorer> getChildren() {
- throw new UnsupportedOperationException();
- }
}
diff --git a/lucene/core/src/java/org/apache/lucene/search/BooleanScorer2.java b/lucene/core/src/java/org/apache/lucene/search/BooleanScorer2.java
index 85fa403..ff2d870 100644
--- a/lucene/core/src/java/org/apache/lucene/search/BooleanScorer2.java
+++ b/lucene/core/src/java/org/apache/lucene/search/BooleanScorer2.java
@@ -279,28 +279,6 @@
: new MinShouldMatchSumScorer(weight, prohibitedScorers)));
}
- /** 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;
diff --git a/lucene/core/src/java/org/apache/lucene/search/BulkScorer.java b/lucene/core/src/java/org/apache/lucene/search/BulkScorer.java
new file mode 100644
index 0000000..2331cae
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/BulkScorer.java
@@ -0,0 +1,46 @@
+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;
+
+/** This class is used to score a range of documents at
+ * once, and is returned by {@link Weight#bulkScorer}. Only
+ * queries that have a more optimized means of scoring
+ * across a range of documents need to override this.
+ * Otherwise, a default implementation is wrapped around
+ * the {@link Scorer} returned by {@link Weight#scorer}. */
+
+public abstract class BulkScorer {
+
+ /** Scores and collects all matching documents.
+ * @param collector The collector to which all matching documents are passed.
+ */
+ public void score(Collector collector) throws IOException {
+ score(collector, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Collects matching documents in a range.
+ *
+ * @param collector The collector to which all matching documents are passed.
+ * @param max Score up to, but not including, this doc
+ * @return true if more matching documents may remain.
+ */
+ public abstract boolean score(Collector collector, int max) throws IOException;
+}
diff --git a/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java b/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
index ea4b809..a917a0c 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
@@ -134,8 +134,23 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, final Bits acceptDocs) throws IOException {
+ public BulkScorer bulkScorer(AtomicReaderContext context, boolean scoreDocsInOrder, Bits acceptDocs) throws IOException {
+ final DocIdSetIterator disi;
+ if (filter != null) {
+ assert query == null;
+ return super.bulkScorer(context, scoreDocsInOrder, acceptDocs);
+ } else {
+ assert query != null && innerWeight != null;
+ BulkScorer bulkScorer = innerWeight.bulkScorer(context, scoreDocsInOrder, acceptDocs);
+ if (bulkScorer == null) {
+ return null;
+ }
+ return new ConstantBulkScorer(bulkScorer, this, queryWeight);
+ }
+ }
+
+ @Override
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
final DocIdSetIterator disi;
if (filter != null) {
assert query == null;
@@ -146,7 +161,7 @@
disi = dis.iterator();
} else {
assert query != null && innerWeight != null;
- disi = innerWeight.scorer(context, scoreDocsInOrder, topScorer, acceptDocs);
+ disi = innerWeight.scorer(context, acceptDocs);
}
if (disi == null) {
@@ -154,7 +169,7 @@
}
return new ConstantScorer(disi, this, queryWeight);
}
-
+
@Override
public boolean scoresDocsOutOfOrder() {
return (innerWeight != null) ? innerWeight.scoresDocsOutOfOrder() : false;
@@ -162,7 +177,7 @@
@Override
public Explanation explain(AtomicReaderContext context, int doc) throws IOException {
- final Scorer cs = scorer(context, true, false, context.reader().getLiveDocs());
+ final Scorer cs = scorer(context, context.reader().getLiveDocs());
final boolean exists = (cs != null && cs.advance(doc) == doc);
final ComplexExplanation result = new ComplexExplanation();
@@ -181,6 +196,52 @@
}
}
+ /** We return this as our {@link BulkScorer} so that if the CSQ
+ * wraps a query with its own optimized top-level
+ * scorer (e.g. BooleanScorer) we can use that
+ * top-level scorer. */
+ protected class ConstantBulkScorer extends BulkScorer {
+ final BulkScorer bulkScorer;
+ final Weight weight;
+ final float theScore;
+
+ public ConstantBulkScorer(BulkScorer bulkScorer, Weight weight, float theScore) {
+ this.bulkScorer = bulkScorer;
+ this.weight = weight;
+ this.theScore = theScore;
+ }
+
+ @Override
+ public boolean score(Collector collector, int max) throws IOException {
+ return bulkScorer.score(wrapCollector(collector), max);
+ }
+
+ private Collector wrapCollector(final Collector collector) {
+ return new Collector() {
+ @Override
+ public void setScorer(Scorer scorer) throws IOException {
+ // we must wrap again here, but using the scorer passed in as parameter:
+ collector.setScorer(new ConstantScorer(scorer, weight, theScore));
+ }
+
+ @Override
+ public void collect(int doc) throws IOException {
+ collector.collect(doc);
+ }
+
+ @Override
+ public void setNextReader(AtomicReaderContext context) throws IOException {
+ collector.setNextReader(context);
+ }
+
+ @Override
+ public boolean acceptsDocsOutOfOrder() {
+ return collector.acceptsDocsOutOfOrder();
+ }
+ };
+ }
+ }
+
protected class ConstantScorer extends Scorer {
final DocIdSetIterator docIdSetIterator;
final float theScore;
@@ -222,57 +283,13 @@
return docIdSetIterator.cost();
}
- private Collector wrapCollector(final Collector collector) {
- return new Collector() {
- @Override
- public void setScorer(Scorer scorer) throws IOException {
- // we must wrap again here, but using the scorer passed in as parameter:
- collector.setScorer(new ConstantScorer(scorer, ConstantScorer.this.weight, ConstantScorer.this.theScore));
- }
-
- @Override
- public void collect(int doc) throws IOException {
- collector.collect(doc);
- }
-
- @Override
- public void setNextReader(AtomicReaderContext context) throws IOException {
- collector.setNextReader(context);
- }
-
- @Override
- public boolean acceptsDocsOutOfOrder() {
- return collector.acceptsDocsOutOfOrder();
- }
- };
- }
-
- // this optimization allows out of order scoring as top scorer!
- @Override
- public void score(Collector collector) throws IOException {
- if (query != null) {
- ((Scorer) docIdSetIterator).score(wrapCollector(collector));
- } else {
- super.score(collector);
- }
- }
-
- // this optimization allows out of order scoring as top scorer,
- @Override
- public boolean score(Collector collector, int max, int firstDocID) throws IOException {
- if (query != null) {
- return ((Scorer) docIdSetIterator).score(wrapCollector(collector), max, firstDocID);
- } else {
- return super.score(collector, max, firstDocID);
- }
- }
-
@Override
public Collection<ChildScorer> getChildren() {
- if (query != null)
+ if (query != null) {
return Collections.singletonList(new ChildScorer((Scorer) docIdSetIterator, "constant"));
- else
+ } else {
return Collections.emptyList();
+ }
}
}
diff --git a/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java b/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java
index f61a54f..2e49ca1 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java
@@ -153,12 +153,11 @@
/** Create the scorer used to score our associated DisjunctionMaxQuery */
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
List<Scorer> scorers = new ArrayList<Scorer>();
for (Weight w : weights) {
// we will advance() subscorers
- Scorer subScorer = w.scorer(context, true, false, acceptDocs);
+ Scorer subScorer = w.scorer(context, acceptDocs);
if (subScorer != null) {
scorers.add(subScorer);
diff --git a/lucene/core/src/java/org/apache/lucene/search/FakeScorer.java b/lucene/core/src/java/org/apache/lucene/search/FakeScorer.java
new file mode 100644
index 0000000..89b92a5
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/FakeScorer.java
@@ -0,0 +1,72 @@
+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.util.Collection;
+
+/** Used by {@link BulkScorer}s that need to pass a {@link
+ * Scorer} to {@link Collector#setScorer}. */
+final class FakeScorer extends Scorer {
+ float score;
+ int doc = -1;
+ int freq = 1;
+
+ public FakeScorer() {
+ super(null);
+ }
+
+ @Override
+ public int advance(int target) {
+ throw new UnsupportedOperationException("FakeScorer doesn't support advance(int)");
+ }
+
+ @Override
+ public int docID() {
+ return doc;
+ }
+
+ @Override
+ public int freq() {
+ return freq;
+ }
+
+ @Override
+ public int nextDoc() {
+ throw new UnsupportedOperationException("FakeScorer doesn't support nextDoc()");
+ }
+
+ @Override
+ public float score() {
+ return score;
+ }
+
+ @Override
+ public long cost() {
+ return 1;
+ }
+
+ @Override
+ public Weight getWeight() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Collection<ChildScorer> getChildren() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java b/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java
index e65561a..f4ef594 100644
--- a/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java
@@ -93,12 +93,12 @@
}
@Override
- public void normalize (float norm, float topLevelBoost) {
+ public void normalize(float norm, float topLevelBoost) {
weight.normalize(norm, topLevelBoost * getBoost()); // incorporate boost
}
@Override
- public Explanation explain (AtomicReaderContext ir, int i) throws IOException {
+ public Explanation explain(AtomicReaderContext ir, int i) throws IOException {
Explanation inner = weight.explain (ir, i);
Filter f = FilteredQuery.this.filter;
DocIdSet docIdSet = f.getDocIdSet(ir, ir.reader().getLiveDocs());
@@ -124,16 +124,30 @@
// return a filtering scorer
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder, boolean topScorer, final Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
assert filter != null;
- final DocIdSet filterDocIdSet = filter.getDocIdSet(context, acceptDocs);
+ DocIdSet filterDocIdSet = filter.getDocIdSet(context, acceptDocs);
if (filterDocIdSet == null) {
// this means the filter does not accept any documents.
return null;
}
- return strategy.filteredScorer(context, scoreDocsInOrder, topScorer, weight, filterDocIdSet);
+ return strategy.filteredScorer(context, weight, filterDocIdSet);
+ }
+
+ // return a filtering top scorer
+ @Override
+ public BulkScorer bulkScorer(AtomicReaderContext context, boolean scoreDocsInOrder, Bits acceptDocs) throws IOException {
+ assert filter != null;
+
+ DocIdSet filterDocIdSet = filter.getDocIdSet(context, acceptDocs);
+ if (filterDocIdSet == null) {
+ // this means the filter does not accept any documents.
+ return null;
+ }
+
+ return strategy.filteredBulkScorer(context, weight, scoreDocsInOrder, filterDocIdSet);
}
};
}
@@ -147,37 +161,20 @@
private static final class QueryFirstScorer extends Scorer {
private final Scorer scorer;
private int scorerDoc = -1;
- private Bits filterbits;
+ private final Bits filterBits;
protected QueryFirstScorer(Weight weight, Bits filterBits, Scorer other) {
super(weight);
this.scorer = other;
- this.filterbits = filterBits;
+ this.filterBits = filterBits;
}
-
- // optimization: we are topScorer and collect directly
- @Override
- public void score(Collector collector) throws IOException {
- // the normalization trick already applies the boost of this query,
- // so we can use the wrapped scorer directly:
- collector.setScorer(scorer);
- for (;;) {
- final int scorerDoc = scorer.nextDoc();
- if (scorerDoc == DocIdSetIterator.NO_MORE_DOCS) {
- break;
- }
- if (filterbits.get(scorerDoc)) {
- collector.collect(scorerDoc);
- }
- }
- }
-
+
@Override
public int nextDoc() throws IOException {
int doc;
for(;;) {
doc = scorer.nextDoc();
- if (doc == Scorer.NO_MORE_DOCS || filterbits.get(doc)) {
+ if (doc == Scorer.NO_MORE_DOCS || filterBits.get(doc)) {
return scorerDoc = doc;
}
}
@@ -186,7 +183,7 @@
@Override
public int advance(int target) throws IOException {
int doc = scorer.advance(target);
- if (doc != Scorer.NO_MORE_DOCS && !filterbits.get(doc)) {
+ if (doc != Scorer.NO_MORE_DOCS && !filterBits.get(doc)) {
return scorerDoc = nextDoc();
} else {
return scorerDoc = doc;
@@ -216,6 +213,40 @@
return scorer.cost();
}
}
+
+ private static class QueryFirstBulkScorer extends BulkScorer {
+
+ private final Scorer scorer;
+ private final Bits filterBits;
+
+ public QueryFirstBulkScorer(Scorer scorer, Bits filterBits) {
+ this.scorer = scorer;
+ this.filterBits = filterBits;
+ }
+
+ @Override
+ public boolean score(Collector collector, int maxDoc) throws IOException {
+ // the normalization trick already applies the boost of this query,
+ // so we can use the wrapped scorer directly:
+ collector.setScorer(scorer);
+ if (scorer.docID() == -1) {
+ scorer.nextDoc();
+ }
+ while (true) {
+ final int scorerDoc = scorer.docID();
+ if (scorerDoc < maxDoc) {
+ if (filterBits.get(scorerDoc)) {
+ collector.collect(scorerDoc);
+ }
+ scorer.nextDoc();
+ } else {
+ break;
+ }
+ }
+
+ return scorer.docID() != Scorer.NO_MORE_DOCS;
+ }
+ }
/**
* A Scorer that uses a "leap-frog" approach (also called "zig-zag join"). The scorer and the filter
@@ -236,32 +267,7 @@
this.secondary = secondary;
this.scorer = scorer;
}
-
- // optimization: we are topScorer and collect directly using short-circuited algo
- @Override
- public final void score(Collector collector) throws IOException {
- // the normalization trick already applies the boost of this query,
- // so we can use the wrapped scorer directly:
- collector.setScorer(scorer);
- int primDoc = primaryNext();
- int secDoc = secondary.advance(primDoc);
- for (;;) {
- if (primDoc == secDoc) {
- // Check if scorer has exhausted, only before collecting.
- if (primDoc == DocIdSetIterator.NO_MORE_DOCS) {
- break;
- }
- collector.collect(primDoc);
- primDoc = primary.nextDoc();
- secDoc = secondary.advance(primDoc);
- } else if (secDoc > primDoc) {
- primDoc = primary.advance(secDoc);
- } else {
- secDoc = secondary.advance(primDoc);
- }
- }
- }
-
+
private final int advanceToNextCommonDoc() throws IOException {
for (;;) {
if (secondaryDoc < primaryDoc) {
@@ -472,18 +478,6 @@
*
* @param context
* the {@link AtomicReaderContext} for which to return the {@link Scorer}.
- * @param scoreDocsInOrder
- * specifies whether in-order scoring of documents is required. Note
- * that if set to false (i.e., out-of-order scoring is required),
- * this method can return whatever scoring mode it supports, as every
- * in-order scorer is also an out-of-order one. However, an
- * out-of-order scorer may not support {@link Scorer#nextDoc()}
- * and/or {@link Scorer#advance(int)}, therefore it is recommended to
- * request an in-order scorer if use of these methods is required.
- * @param topScorer
- * if true, {@link Scorer#score(Collector)} will be called; if false,
- * {@link Scorer#nextDoc()} and/or {@link Scorer#advance(int)} will
- * be called.
* @param weight the {@link FilteredQuery} {@link Weight} to create the filtered scorer.
* @param docIdSet the filter {@link DocIdSet} to apply
* @return a filtered scorer
@@ -491,8 +485,30 @@
* @throws IOException if an {@link IOException} occurs
*/
public abstract Scorer filteredScorer(AtomicReaderContext context,
- boolean scoreDocsInOrder, boolean topScorer, Weight weight,
- DocIdSet docIdSet) throws IOException;
+ Weight weight, DocIdSet docIdSet) throws IOException;
+
+ /**
+ * Returns a filtered {@link BulkScorer} based on this
+ * strategy. This is an optional method: the default
+ * implementation just calls {@link #filteredScorer} and
+ * wraps that into a BulkScorer.
+ *
+ * @param context
+ * the {@link AtomicReaderContext} for which to return the {@link Scorer}.
+ * @param weight the {@link FilteredQuery} {@link Weight} to create the filtered scorer.
+ * @param docIdSet the filter {@link DocIdSet} to apply
+ * @return a filtered top scorer
+ */
+ public BulkScorer filteredBulkScorer(AtomicReaderContext context,
+ Weight weight, boolean scoreDocsInOrder, DocIdSet docIdSet) throws IOException {
+ Scorer scorer = filteredScorer(context, weight, docIdSet);
+ if (scorer == null) {
+ return null;
+ }
+ // This impl always scores docs in order, so we can
+ // ignore scoreDocsInOrder:
+ return new Weight.DefaultBulkScorer(scorer);
+ }
}
/**
@@ -506,7 +522,7 @@
public static class RandomAccessFilterStrategy extends FilterStrategy {
@Override
- public Scorer filteredScorer(AtomicReaderContext context, boolean scoreDocsInOrder, boolean topScorer, Weight weight, DocIdSet docIdSet) throws IOException {
+ public Scorer filteredScorer(AtomicReaderContext context, Weight weight, DocIdSet docIdSet) throws IOException {
final DocIdSetIterator filterIter = docIdSet.iterator();
if (filterIter == null) {
// this means the filter does not accept any documents.
@@ -523,12 +539,12 @@
final boolean useRandomAccess = filterAcceptDocs != null && useRandomAccess(filterAcceptDocs, firstFilterDoc);
if (useRandomAccess) {
// if we are using random access, we return the inner scorer, just with other acceptDocs
- return weight.scorer(context, scoreDocsInOrder, topScorer, filterAcceptDocs);
+ return weight.scorer(context, filterAcceptDocs);
} else {
assert firstFilterDoc > -1;
// we are gonna advance() this scorer, so we set inorder=true/toplevel=false
// we pass null as acceptDocs, as our filter has already respected acceptDocs, no need to do twice
- final Scorer scorer = weight.scorer(context, true, false, null);
+ final Scorer scorer = weight.scorer(context, null);
// TODO once we have way to figure out if we use RA or LeapFrog we can remove this scorer
return (scorer == null) ? null : new PrimaryAdvancedLeapFrogScorer(weight, firstFilterDoc, filterIter, scorer);
}
@@ -559,25 +575,27 @@
private LeapFrogFilterStrategy(boolean scorerFirst) {
this.scorerFirst = scorerFirst;
}
+
@Override
public Scorer filteredScorer(AtomicReaderContext context,
- boolean scoreDocsInOrder, boolean topScorer, Weight weight,
- DocIdSet docIdSet) throws IOException {
+ Weight weight, DocIdSet docIdSet) throws IOException {
final DocIdSetIterator filterIter = docIdSet.iterator();
if (filterIter == null) {
// this means the filter does not accept any documents.
return null;
}
- // we are gonna advance() this scorer, so we set inorder=true/toplevel=false
// we pass null as acceptDocs, as our filter has already respected acceptDocs, no need to do twice
- final Scorer scorer = weight.scorer(context, true, false, null);
+ final Scorer scorer = weight.scorer(context, null);
+ if (scorer == null) {
+ return null;
+ }
+
if (scorerFirst) {
- return (scorer == null) ? null : new LeapFrogScorer(weight, scorer, filterIter, scorer);
+ return new LeapFrogScorer(weight, scorer, filterIter, scorer);
} else {
- return (scorer == null) ? null : new LeapFrogScorer(weight, filterIter, scorer, scorer);
+ return new LeapFrogScorer(weight, filterIter, scorer, scorer);
}
}
-
}
/**
@@ -596,16 +614,33 @@
private static final class QueryFirstFilterStrategy extends FilterStrategy {
@Override
public Scorer filteredScorer(final AtomicReaderContext context,
- boolean scoreDocsInOrder, boolean topScorer, Weight weight,
+ Weight weight,
DocIdSet docIdSet) throws IOException {
Bits filterAcceptDocs = docIdSet.bits();
if (filterAcceptDocs == null) {
- return LEAP_FROG_QUERY_FIRST_STRATEGY.filteredScorer(context, scoreDocsInOrder, topScorer, weight, docIdSet);
+ // Filter does not provide random-access Bits; we
+ // must fallback to leapfrog:
+ return LEAP_FROG_QUERY_FIRST_STRATEGY.filteredScorer(context, weight, docIdSet);
}
- final Scorer scorer = weight.scorer(context, true, false, null);
+ final Scorer scorer = weight.scorer(context, null);
return scorer == null ? null : new QueryFirstScorer(weight,
filterAcceptDocs, scorer);
}
+
+ @Override
+ public BulkScorer filteredBulkScorer(final AtomicReaderContext context,
+ Weight weight,
+ boolean scoreDocsInOrder, // ignored (we always top-score in order)
+ DocIdSet docIdSet) throws IOException {
+ Bits filterAcceptDocs = docIdSet.bits();
+ if (filterAcceptDocs == null) {
+ // Filter does not provide random-access Bits; we
+ // must fallback to leapfrog:
+ return LEAP_FROG_QUERY_FIRST_STRATEGY.filteredBulkScorer(context, weight, scoreDocsInOrder, docIdSet);
+ }
+ final Scorer scorer = weight.scorer(context, null);
+ return scorer == null ? null : new QueryFirstBulkScorer(scorer, filterAcceptDocs);
+ }
}
}
diff --git a/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java b/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
index 266a26e..8a9aec9 100644
--- a/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
+++ b/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
@@ -607,7 +607,7 @@
// continue with the following leaf
continue;
}
- Scorer scorer = weight.scorer(ctx, !collector.acceptsDocsOutOfOrder(), true, ctx.reader().getLiveDocs());
+ BulkScorer scorer = weight.bulkScorer(ctx, !collector.acceptsDocsOutOfOrder(), ctx.reader().getLiveDocs());
if (scorer != null) {
try {
scorer.score(collector);
@@ -768,45 +768,6 @@
this.doMaxScore = doMaxScore;
}
- private final class FakeScorer extends Scorer {
- float score;
- int doc;
-
- public FakeScorer() {
- super(null);
- }
-
- @Override
- public int advance(int target) {
- throw new UnsupportedOperationException("FakeScorer doesn't support advance(int)");
- }
-
- @Override
- public int docID() {
- return doc;
- }
-
- @Override
- public int freq() {
- throw new UnsupportedOperationException("FakeScorer doesn't support freq()");
- }
-
- @Override
- public int nextDoc() {
- throw new UnsupportedOperationException("FakeScorer doesn't support nextDoc()");
- }
-
- @Override
- public float score() {
- return score;
- }
-
- @Override
- public long cost() {
- return 1;
- }
- }
-
private final FakeScorer fakeScorer = new FakeScorer();
@Override
diff --git a/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java b/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
index 0737a7c..8f2edd7 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
@@ -114,8 +114,7 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
return new MatchAllScorer(context.reader(), acceptDocs, this, queryWeight);
}
diff --git a/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java b/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
index b48accc..93fb1b6 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
@@ -179,8 +179,7 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
assert !termArrays.isEmpty();
final AtomicReader reader = context.reader();
final Bits liveDocs = acceptDocs;
@@ -263,7 +262,7 @@
@Override
public Explanation explain(AtomicReaderContext context, int doc) throws IOException {
- Scorer scorer = scorer(context, true, false, context.reader().getLiveDocs());
+ Scorer scorer = scorer(context, context.reader().getLiveDocs());
if (scorer != null) {
int newDoc = scorer.advance(doc);
if (newDoc == doc) {
diff --git a/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java b/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java
index d8de142..cd40a11 100644
--- a/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java
@@ -245,8 +245,7 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
assert !terms.isEmpty();
final AtomicReader reader = context.reader();
final Bits liveDocs = acceptDocs;
@@ -305,7 +304,7 @@
@Override
public Explanation explain(AtomicReaderContext context, int doc) throws IOException {
- Scorer scorer = scorer(context, true, false, context.reader().getLiveDocs());
+ Scorer scorer = scorer(context, context.reader().getLiveDocs());
if (scorer != null) {
int newDoc = scorer.advance(doc);
if (newDoc == doc) {
diff --git a/lucene/core/src/java/org/apache/lucene/search/QueryWrapperFilter.java b/lucene/core/src/java/org/apache/lucene/search/QueryWrapperFilter.java
index 823b47b..1d6c8ff 100644
--- a/lucene/core/src/java/org/apache/lucene/search/QueryWrapperFilter.java
+++ b/lucene/core/src/java/org/apache/lucene/search/QueryWrapperFilter.java
@@ -56,7 +56,7 @@
return new DocIdSet() {
@Override
public DocIdSetIterator iterator() throws IOException {
- return weight.scorer(privateContext, true, false, acceptDocs);
+ return weight.scorer(privateContext, acceptDocs);
}
@Override
public boolean isCacheable() { return false; }
diff --git a/lucene/core/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java b/lucene/core/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java
index f850f53..471dc20 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java
@@ -45,11 +45,6 @@
}
@Override
- public boolean score(Collector collector, int max, int firstDocID) throws IOException {
- return scorer.score(collector, max, firstDocID);
- }
-
- @Override
public float score() throws IOException {
int doc = scorer.docID();
if (doc != curDoc) {
@@ -76,11 +71,6 @@
}
@Override
- public void score(Collector collector) throws IOException {
- scorer.score(collector);
- }
-
- @Override
public int advance(int target) throws IOException {
return scorer.advance(target);
}
diff --git a/lucene/core/src/java/org/apache/lucene/search/Scorer.java b/lucene/core/src/java/org/apache/lucene/search/Scorer.java
index 47fef12..abcbb61 100644
--- a/lucene/core/src/java/org/apache/lucene/search/Scorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/Scorer.java
@@ -54,42 +54,6 @@
this.weight = weight;
}
- /** Scores and collects all matching documents.
- * @param collector The collector to which all matching documents are passed.
- */
- public void score(Collector collector) throws IOException {
- assert docID() == -1; // not started
- collector.setScorer(this);
- int doc;
- while ((doc = nextDoc()) != NO_MORE_DOCS) {
- collector.collect(doc);
- }
- }
-
- /**
- * Expert: Collects matching documents in a range. Hook for optimization.
- * Note, <code>firstDocID</code> is added to ensure that {@link #nextDoc()}
- * was called before this method.
- *
- * @param collector
- * The collector to which all matching documents are passed.
- * @param max
- * Do not score documents past this.
- * @param firstDocID
- * The first document ID (ensures {@link #nextDoc()} is called before
- * this method.
- * @return true if more matching documents may remain.
- */
- public boolean score(Collector collector, int max, int firstDocID) throws IOException {
- assert docID() == firstDocID;
- collector.setScorer(this);
- int doc;
- for (doc = firstDocID; doc < max; doc = nextDoc()) {
- collector.collect(doc);
- }
- return doc != NO_MORE_DOCS;
- }
-
/** Returns the score of the current document matching the query.
* Initially invalid, until {@link #nextDoc()} or {@link #advance(int)}
* is called the first time, or when called from within
diff --git a/lucene/core/src/java/org/apache/lucene/search/TermQuery.java b/lucene/core/src/java/org/apache/lucene/search/TermQuery.java
index b6a1f23..5435ccd 100644
--- a/lucene/core/src/java/org/apache/lucene/search/TermQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/TermQuery.java
@@ -75,8 +75,7 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
assert termStates.topReaderContext == ReaderUtil.getTopLevelContext(context) : "The top-reader used to create Weight (" + termStates.topReaderContext + ") is not the same as the current reader's top-reader (" + ReaderUtil.getTopLevelContext(context);
final TermsEnum termsEnum = getTermsEnum(context);
if (termsEnum == null) {
@@ -111,7 +110,7 @@
@Override
public Explanation explain(AtomicReaderContext context, int doc) throws IOException {
- Scorer scorer = scorer(context, true, false, context.reader().getLiveDocs());
+ Scorer scorer = scorer(context, context.reader().getLiveDocs());
if (scorer != null) {
int newDoc = scorer.advance(doc);
if (newDoc == doc) {
diff --git a/lucene/core/src/java/org/apache/lucene/search/Weight.java b/lucene/core/src/java/org/apache/lucene/search/Weight.java
index 48dd209..696c7ab 100644
--- a/lucene/core/src/java/org/apache/lucene/search/Weight.java
+++ b/lucene/core/src/java/org/apache/lucene/search/Weight.java
@@ -21,6 +21,7 @@
import org.apache.lucene.index.AtomicReader; // javadocs
import org.apache.lucene.index.AtomicReaderContext;
+import org.apache.lucene.index.DocsEnum;
import org.apache.lucene.index.IndexReaderContext; // javadocs
import org.apache.lucene.search.similarities.Similarity;
import org.apache.lucene.util.Bits;
@@ -35,8 +36,7 @@
* {@link AtomicReader} dependent state should reside in the {@link Scorer}.
* <p>
* Since {@link Weight} creates {@link Scorer} instances for a given
- * {@link AtomicReaderContext} ({@link #scorer(AtomicReaderContext,
- * boolean, boolean, Bits)})
+ * {@link AtomicReaderContext} ({@link #scorer(AtomicReaderContext, Bits)})
* callers must maintain the relationship between the searcher's top-level
* {@link IndexReaderContext} and the context used to create a {@link Scorer}.
* <p>
@@ -51,7 +51,7 @@
* <li>The query normalization factor is passed to {@link #normalize(float, float)}. At
* this point the weighting is complete.
* <li>A <code>Scorer</code> is constructed by
- * {@link #scorer(AtomicReaderContext, boolean, boolean, Bits)}.
+ * {@link #scorer(AtomicReaderContext, Bits)}.
* </ol>
*
* @since 2.9
@@ -91,18 +91,6 @@
*
* @param context
* the {@link AtomicReaderContext} for which to return the {@link Scorer}.
- * @param scoreDocsInOrder
- * specifies whether in-order scoring of documents is required. Note
- * that if set to false (i.e., out-of-order scoring is required),
- * this method can return whatever scoring mode it supports, as every
- * in-order scorer is also an out-of-order one. However, an
- * out-of-order scorer may not support {@link Scorer#nextDoc()}
- * and/or {@link Scorer#advance(int)}, therefore it is recommended to
- * request an in-order scorer if use of these methods is required.
- * @param topScorer
- * if true, {@link Scorer#score(Collector)} will be called; if false,
- * {@link Scorer#nextDoc()} and/or {@link Scorer#advance(int)} will
- * be called.
* @param acceptDocs
* Bits that represent the allowable docs to match (typically deleted docs
* but possibly filtering other documents)
@@ -110,19 +98,89 @@
* @return a {@link Scorer} which scores documents in/out-of order.
* @throws IOException if there is a low-level I/O error
*/
- public abstract Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException;
+ public abstract Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException;
+
+ /**
+ * Optional method, to return a {@link BulkScorer} to
+ * score the query and send hits to a {@link Collector}.
+ * Only queries that have a different top-level approach
+ * need to override this; the default implementation
+ * pulls a normal {@link Scorer} and iterates and
+ * collects the resulting hits.
+ *
+ * @param context
+ * the {@link AtomicReaderContext} for which to return the {@link Scorer}.
+ * @param scoreDocsInOrder
+ * specifies whether in-order scoring of documents is required. Note
+ * that if set to false (i.e., out-of-order scoring is required),
+ * this method can return whatever scoring mode it supports, as every
+ * in-order scorer is also an out-of-order one. However, an
+ * out-of-order scorer may not support {@link Scorer#nextDoc()}
+ * and/or {@link Scorer#advance(int)}, therefore it is recommended to
+ * request an in-order scorer if use of these
+ * methods is required.
+ * @param acceptDocs
+ * Bits that represent the allowable docs to match (typically deleted docs
+ * but possibly filtering other documents)
+ *
+ * @return a {@link BulkScorer} which scores documents and
+ * passes them to a collector.
+ * @throws IOException if there is a low-level I/O error
+ */
+ public BulkScorer bulkScorer(AtomicReaderContext context, boolean scoreDocsInOrder, Bits acceptDocs) throws IOException {
+
+ Scorer scorer = scorer(context, acceptDocs);
+ if (scorer == null) {
+ // No docs match
+ return null;
+ }
+
+ // This impl always scores docs in order, so we can
+ // ignore scoreDocsInOrder:
+ return new DefaultBulkScorer(scorer);
+ }
+
+ /** Just wraps a Scorer and performs top scoring using it. */
+ static class DefaultBulkScorer extends BulkScorer {
+ private final Scorer scorer;
+
+ public DefaultBulkScorer(Scorer scorer) {
+ assert scorer != null;
+ this.scorer = scorer;
+ }
+
+ @Override
+ public boolean score(Collector collector, int max) throws IOException {
+ // TODO: this may be sort of weird, when we are
+ // embedded in a BooleanScorer, because we are
+ // called for every chunk of 2048 documents. But,
+ // then, scorer is a FakeScorer in that case, so any
+ // Collector doing something "interesting" in
+ // setScorer will be forced to use BS2 anyways:
+ collector.setScorer(scorer);
+ if (scorer.docID() == -1) {
+ scorer.nextDoc();
+ }
+ int doc;
+ for (doc = scorer.docID(); doc < max; doc = scorer.nextDoc()) {
+ collector.collect(doc);
+ }
+ return doc != DocsEnum.NO_MORE_DOCS;
+ }
+ }
/**
* Returns true iff this implementation scores docs only out of order. This
* method is used in conjunction with {@link Collector}'s
* {@link Collector#acceptsDocsOutOfOrder() acceptsDocsOutOfOrder} and
- * {@link #scorer(AtomicReaderContext, boolean, boolean, Bits)} to
+ * {@link #bulkScorer(AtomicReaderContext, boolean, Bits)} to
* create a matching {@link Scorer} instance for a given {@link Collector}, or
* vice versa.
* <p>
* <b>NOTE:</b> the default implementation returns <code>false</code>, i.e.
* the <code>Scorer</code> scores documents in-order.
*/
- public boolean scoresDocsOutOfOrder() { return false; }
+ public boolean scoresDocsOutOfOrder() {
+ return false;
+ }
}
diff --git a/lucene/core/src/java/org/apache/lucene/search/package.html b/lucene/core/src/java/org/apache/lucene/search/package.html
index 1a9e577..51e199e 100644
--- a/lucene/core/src/java/org/apache/lucene/search/package.html
+++ b/lucene/core/src/java/org/apache/lucene/search/package.html
@@ -53,8 +53,8 @@
<p>
Once a Query has been created and submitted to the {@link org.apache.lucene.search.IndexSearcher IndexSearcher}, the scoring
process begins. After some infrastructure setup, control finally passes to the {@link org.apache.lucene.search.Weight Weight}
-implementation and its {@link org.apache.lucene.search.Scorer Scorer} instances. See the <a href="#algorithm">Algorithm</a>
-section for more notes on the process.
+implementation and its {@link org.apache.lucene.search.Scorer Scorer} or {@link org.apache.lucene.search.BulkScorer BulkScore}
+instances. See the <a href="#algorithm">Algorithm</a> section for more notes on the process.
</p>
<!-- FILL IN MORE HERE -->
<!-- TODO: this page over-links the same things too many times -->
@@ -370,6 +370,12 @@
{@link org.apache.lucene.search.Scorer Scorer} — An abstract class containing common
functionality for scoring. Provides both scoring and
explanation capabilities. This is created per-segment.</li>
+ <li>
+ {@link org.apache.lucene.search.BulkScorer BulkScorer} — An abstract class that scores
+ a range of documents. A default implementation simply iterates through the hits from
+ {@link org.apache.lucene.search.Scorer Scorer}, but some queries such as
+ {@link org.apache.lucene.search.BooleanQuery BooleanQuery} have more efficient
+ implementations.</li>
</ol>
Details on each of these classes, and their children, can be found in the subsections below.
</p>
@@ -430,13 +436,19 @@
that scores via a {@link org.apache.lucene.search.similarities.Similarity Similarity} will just defer to the Similarity's implementation:
{@link org.apache.lucene.search.similarities.Similarity.SimWeight#normalize SimWeight#normalize(float,float)}.</li>
<li>
- {@link org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.AtomicReaderContext, boolean, boolean, org.apache.lucene.util.Bits)
- scorer(AtomicReaderContext context, boolean scoresDocsInOrder, boolean topScorer, Bits acceptDocs)} —
+ {@link org.apache.lucene.search.Weight#scorer(org.apache.lucene.index.AtomicReaderContext, org.apache.lucene.util.Bits)
+ scorer(AtomicReaderContext context, Bits acceptDocs)} —
Construct a new {@link org.apache.lucene.search.Scorer Scorer} for this Weight. See <a href="#scorerClass">The Scorer Class</a>
below for help defining a Scorer. As the name implies, the Scorer is responsible for doing the actual scoring of documents
given the Query.
</li>
<li>
+ {@link org.apache.lucene.search.Weight#bulkScorer(org.apache.lucene.index.AtomicReaderContext, boolean, org.apache.lucene.util.Bits)
+ scorer(AtomicReaderContext context, boolean scoreDocsInOrder, Bits acceptDocs)} —
+ Construct a new {@link org.apache.lucene.search.BulkScorer BulkScorer} for this Weight. See <a href="#bulkScorerClass">The BulkScorer Class</a>
+ below for help defining a BulkScorer. This is an optional method, and most queries do not implement it.
+ </li>
+ <li>
{@link org.apache.lucene.search.Weight#explain(org.apache.lucene.index.AtomicReaderContext, int)
explain(AtomicReaderContext context, int doc)} — Provide a means for explaining why a given document was
scored the way it was.
@@ -489,6 +501,18 @@
</li>
</ol>
</p>
+<a name="bulkScorerClass"></a>
+<h4>The BulkScorer Class</h4>
+ <p>The
+ {@link org.apache.lucene.search.BulkScorer BulkScorer} scores a range of documents. There is only one
+ abstract method:
+ <ol>
+ <li>
+ {@link org.apache.lucene.search.BulkScorer#score(org.apache.lucene.search.Collector,int) score(Collector,int)} —
+ Score all documents up to but not including the specified max document.
+ </li>
+ </ol>
+ </p>
<h4>Why would I want to add my own Query?</h4>
<p>In a nutshell, you want to add your own custom Query implementation when you think that Lucene's
@@ -539,7 +563,7 @@
<p>If a Filter is being used, some initial setup is done to determine which docs to include.
Otherwise, we ask the Weight for a {@link org.apache.lucene.search.Scorer Scorer} for each
{@link org.apache.lucene.index.IndexReader IndexReader} segment and proceed by calling
- {@link org.apache.lucene.search.Scorer#score(org.apache.lucene.search.Collector) Scorer.score()}.
+ {@link org.apache.lucene.search.BulkScorer#score(org.apache.lucene.search.Collector) BulkScorer.score(Collector)}.
</p>
<p>At last, we are actually going to score some documents. The score method takes in the Collector
(most likely the TopScoreDocCollector or TopFieldCollector) and does its business.Of course, here
diff --git a/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java b/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java
index 31034ea..d2e924e 100644
--- a/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadNearQuery.java
@@ -148,15 +148,14 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
return new PayloadNearSpanScorer(query.getSpans(context, acceptDocs, termContexts), this,
similarity, similarity.simScorer(stats, context));
}
@Override
public Explanation explain(AtomicReaderContext context, int doc) throws IOException {
- PayloadNearSpanScorer scorer = (PayloadNearSpanScorer) scorer(context, true, false, context.reader().getLiveDocs());
+ PayloadNearSpanScorer scorer = (PayloadNearSpanScorer) scorer(context, context.reader().getLiveDocs());
if (scorer != null) {
int newDoc = scorer.advance(doc);
if (newDoc == doc) {
diff --git a/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java b/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java
index b263999..04ecd80 100644
--- a/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/payloads/PayloadTermQuery.java
@@ -79,8 +79,7 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
return new PayloadTermSpanScorer((TermSpans) query.getSpans(context, acceptDocs, termContexts),
this, similarity.simScorer(stats, context));
}
@@ -177,7 +176,7 @@
@Override
public Explanation explain(AtomicReaderContext context, int doc) throws IOException {
- PayloadTermSpanScorer scorer = (PayloadTermSpanScorer) scorer(context, true, false, context.reader().getLiveDocs());
+ PayloadTermSpanScorer scorer = (PayloadTermSpanScorer) scorer(context, context.reader().getLiveDocs());
if (scorer != null) {
int newDoc = scorer.advance(doc);
if (newDoc == doc) {
diff --git a/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java b/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java
index fb73721..9acff49 100644
--- a/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java
+++ b/lucene/core/src/java/org/apache/lucene/search/spans/SpanWeight.java
@@ -81,8 +81,7 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
if (stats == null) {
return null;
} else {
@@ -92,7 +91,7 @@
@Override
public Explanation explain(AtomicReaderContext context, int doc) throws IOException {
- SpanScorer scorer = (SpanScorer) scorer(context, true, false, context.reader().getLiveDocs());
+ SpanScorer scorer = (SpanScorer) scorer(context, context.reader().getLiveDocs());
if (scorer != null) {
int newDoc = scorer.advance(doc);
if (newDoc == doc) {
diff --git a/lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java b/lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java
index f34733a..ee47759 100644
--- a/lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java
+++ b/lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java
@@ -17,8 +17,6 @@
* limitations under the License.
*/
-import java.io.IOException;
-
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
@@ -232,11 +230,6 @@
}
@Override
- public boolean score(Collector collector, int max, int firstDocID) {
- throw new UnsupportedOperationException(UNSUPPORTED_MSG);
- }
-
- @Override
public float score() {
throw new UnsupportedOperationException(UNSUPPORTED_MSG);
}
@@ -351,8 +344,7 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) {
throw new UnsupportedOperationException(UNSUPPORTED_MSG);
}
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestBooleanOr.java b/lucene/core/src/test/org/apache/lucene/search/TestBooleanOr.java
index a9fe1da..b1ba0f1 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestBooleanOr.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestBooleanOr.java
@@ -183,7 +183,7 @@
Weight w = s.createNormalizedWeight(bq);
assertEquals(1, s.getIndexReader().leaves().size());
- Scorer scorer = w.scorer(s.getIndexReader().leaves().get(0), false, true, null);
+ BulkScorer scorer = w.bulkScorer(s.getIndexReader().leaves().get(0), false, null);
final FixedBitSet hits = new FixedBitSet(docCount);
final AtomicInteger end = new AtomicInteger();
@@ -211,7 +211,7 @@
while (end.intValue() < docCount) {
final int inc = TestUtil.nextInt(random(), 1, 1000);
end.getAndAdd(inc);
- scorer.score(c, end.intValue(), -1);
+ scorer.score(c, end.intValue());
}
assertEquals(docCount, hits.cardinality());
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestBooleanQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestBooleanQuery.java
index faa779a..4442db3 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestBooleanQuery.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestBooleanQuery.java
@@ -17,6 +17,7 @@
* limitations under the License.
*/
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -29,6 +30,7 @@
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
@@ -43,7 +45,6 @@
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util.NamedThreadFactory;
import org.apache.lucene.util.TestUtil;
-import org.apache.lucene.util.TestUtil;
public class TestBooleanQuery extends LuceneTestCase {
@@ -234,8 +235,7 @@
Weight weight = s.createNormalizedWeight(q);
- Scorer scorer = weight.scorer(s.leafContexts.get(0),
- true, false, null);
+ Scorer scorer = weight.scorer(s.leafContexts.get(0), null);
// First pass: just use .nextDoc() to gather all hits
final List<ScoreDoc> hits = new ArrayList<ScoreDoc>();
@@ -252,8 +252,7 @@
for(int iter2=0;iter2<10;iter2++) {
weight = s.createNormalizedWeight(q);
- scorer = weight.scorer(s.leafContexts.get(0),
- true, false, null);
+ scorer = weight.scorer(s.leafContexts.get(0), null);
if (VERBOSE) {
System.out.println(" iter2=" + iter2);
@@ -327,4 +326,30 @@
directory.close();
}
+ // LUCENE-5487
+ public void testInOrderWithMinShouldMatch() throws Exception {
+ Directory dir = newDirectory();
+ RandomIndexWriter w = new RandomIndexWriter(random(), dir);
+ Document doc = new Document();
+ doc.add(newTextField("field", "some text here", Field.Store.NO));
+ w.addDocument(doc);
+ IndexReader r = w.getReader();
+ w.close();
+ IndexSearcher s = new IndexSearcher(r) {
+ @Override
+ protected void search(List<AtomicReaderContext> leaves, Weight weight, Collector collector) throws IOException {
+ assertEquals(-1, collector.getClass().getSimpleName().indexOf("OutOfOrder"));
+ super.search(leaves, weight, collector);
+ }
+ };
+ BooleanQuery bq = new BooleanQuery();
+ bq.add(new TermQuery(new Term("field", "some")), BooleanClause.Occur.SHOULD);
+ bq.add(new TermQuery(new Term("field", "text")), BooleanClause.Occur.SHOULD);
+ bq.add(new TermQuery(new Term("field", "here")), BooleanClause.Occur.SHOULD);
+ bq.setMinimumNumberShouldMatch(2);
+ s.search(bq, 10);
+ r.close();
+ dir.close();
+ }
+
}
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java b/lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java
index ffeac52..41e5f60 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java
@@ -17,8 +17,10 @@
* limitations under the License.
*/
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import org.apache.lucene.document.Document;
@@ -30,10 +32,10 @@
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanQuery.BooleanWeight;
import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.Bits;
import org.apache.lucene.util.LuceneTestCase;
-public class TestBooleanScorer extends LuceneTestCase
-{
+public class TestBooleanScorer extends LuceneTestCase {
private static final String FIELD = "category";
public void testMethod() throws Exception {
@@ -78,27 +80,23 @@
writer.close();
IndexSearcher searcher = newSearcher(ir);
BooleanWeight weight = (BooleanWeight) new BooleanQuery().createWeight(searcher);
- Scorer[] scorers = new Scorer[] {new Scorer(weight) {
+ BulkScorer[] scorers = new BulkScorer[] {new BulkScorer() {
private int doc = -1;
- @Override public float score() { return 0; }
- @Override public int freq() { return 0; }
- @Override public int docID() { return doc; }
-
- @Override public int nextDoc() {
- return doc = doc == -1 ? 3000 : NO_MORE_DOCS;
- }
- @Override public int advance(int target) {
- return doc = target <= 3000 ? 3000 : NO_MORE_DOCS;
- }
-
@Override
- public long cost() {
- return 1;
+ public boolean score(Collector c, int maxDoc) throws IOException {
+ assert doc == -1;
+ doc = 3000;
+ FakeScorer fs = new FakeScorer();
+ fs.doc = doc;
+ fs.score = 1.0f;
+ c.setScorer(fs);
+ c.collect(3000);
+ return false;
}
}};
- BooleanScorer bs = new BooleanScorer(weight, false, 1, Arrays.asList(scorers), null, scorers.length);
+ BooleanScorer bs = new BooleanScorer(weight, false, 1, Arrays.asList(scorers), Collections.<BulkScorer>emptyList(), scorers.length);
final List<Integer> hits = new ArrayList<Integer>();
bs.score(new Collector() {
@@ -157,7 +155,7 @@
public void setScorer(Scorer scorer) {
// Make sure we got BooleanScorer:
final Class<?> clazz = scorer instanceof AssertingScorer ? ((AssertingScorer) scorer).getIn().getClass() : scorer.getClass();
- assertEquals("Scorer is implemented by wrong class", BooleanScorer.class.getName() + "$BucketScorer", clazz.getName());
+ assertEquals("Scorer is implemented by wrong class", FakeScorer.class.getName(), clazz.getName());
}
@Override
@@ -180,4 +178,80 @@
r.close();
d.close();
}
+
+ /** Throws UOE if Weight.scorer is called */
+ private static class CrazyMustUseBulkScorerQuery extends Query {
+
+ @Override
+ public String toString(String field) {
+ return "MustUseBulkScorerQuery";
+ }
+
+ @Override
+ public Weight createWeight(IndexSearcher searcher) throws IOException {
+ return new Weight() {
+ @Override
+ public Explanation explain(AtomicReaderContext context, int doc) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Query getQuery() {
+ return CrazyMustUseBulkScorerQuery.this;
+ }
+
+ @Override
+ public float getValueForNormalization() {
+ return 1.0f;
+ }
+
+ @Override
+ public void normalize(float norm, float topLevelBoost) {
+ }
+
+ @Override
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public BulkScorer bulkScorer(AtomicReaderContext context, boolean scoreDocsInOrder, Bits acceptDocs) {
+ return new BulkScorer() {
+
+ @Override
+ public boolean score(Collector collector, int max) throws IOException {
+ collector.setScorer(new FakeScorer());
+ collector.collect(0);
+ return false;
+ }
+ };
+ }
+ };
+ }
+ }
+
+ /** Make sure BooleanScorer can embed another
+ * BooleanScorer. */
+ public void testEmbeddedBooleanScorer() throws Exception {
+ Directory dir = newDirectory();
+ RandomIndexWriter w = new RandomIndexWriter(random(), dir);
+ Document doc = new Document();
+ doc.add(newTextField("field", "doctors are people who prescribe medicines of which they know little, to cure diseases of which they know less, in human beings of whom they know nothing", Field.Store.NO));
+ w.addDocument(doc);
+ IndexReader r = w.getReader();
+ w.close();
+
+ IndexSearcher s = newSearcher(r);
+ BooleanQuery q1 = new BooleanQuery();
+ q1.add(new TermQuery(new Term("field", "little")), BooleanClause.Occur.SHOULD);
+ q1.add(new TermQuery(new Term("field", "diseases")), BooleanClause.Occur.SHOULD);
+
+ BooleanQuery q2 = new BooleanQuery();
+ q2.add(q1, BooleanClause.Occur.SHOULD);
+ q2.add(new CrazyMustUseBulkScorerQuery(), BooleanClause.Occur.SHOULD);
+
+ assertEquals(1, s.search(q2, 10).totalHits);
+ r.close();
+ dir.close();
+ }
}
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java
index 1fa8526..21a6a7d 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java
@@ -121,7 +121,7 @@
checkHits(searcher, csq2, csq2.getBoost(), ConstantScoreQuery.ConstantScorer.class.getName(), ConstantScoreQuery.ConstantScorer.class.getName());
// for the combined BQ, the scorer should always be BooleanScorer's BucketScorer, because our scorer supports out-of order collection!
- final String bucketScorerClass = BooleanScorer.class.getName() + "$BucketScorer";
+ final String bucketScorerClass = FakeScorer.class.getName();
checkHits(searcher, bq, csq1.getBoost() + csq2.getBoost(), bucketScorerClass, null);
checkHits(searcher, csqbq, csqbq.getBoost(), ConstantScoreQuery.ConstantScorer.class.getName(), bucketScorerClass);
} finally {
@@ -158,7 +158,7 @@
}
// LUCENE-5307
- // don't reuse the scorer of filters since they have been created with topScorer=false
+ // don't reuse the scorer of filters since they have been created with bulkScorer=false
public void testQueryWrapperFilter() throws IOException {
Directory d = newDirectory();
RandomIndexWriter w = new RandomIndexWriter(random(), d);
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestDisjunctionMaxQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestDisjunctionMaxQuery.java
index dcaf023..4615d39 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestDisjunctionMaxQuery.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestDisjunctionMaxQuery.java
@@ -180,7 +180,7 @@
assertTrue(s.getTopReaderContext() instanceof AtomicReaderContext);
final Weight dw = s.createNormalizedWeight(dq);
AtomicReaderContext context = (AtomicReaderContext)s.getTopReaderContext();
- final Scorer ds = dw.scorer(context, true, false, context.reader().getLiveDocs());
+ final Scorer ds = dw.scorer(context, context.reader().getLiveDocs());
final boolean skipOk = ds.advance(3) != DocIdSetIterator.NO_MORE_DOCS;
if (skipOk) {
fail("firsttime skipTo found a match? ... "
@@ -196,7 +196,7 @@
QueryUtils.check(random(), dq, s);
final Weight dw = s.createNormalizedWeight(dq);
AtomicReaderContext context = (AtomicReaderContext)s.getTopReaderContext();
- final Scorer ds = dw.scorer(context, true, false, context.reader().getLiveDocs());
+ final Scorer ds = dw.scorer(context, context.reader().getLiveDocs());
assertTrue("firsttime skipTo found no match",
ds.advance(3) != DocIdSetIterator.NO_MORE_DOCS);
assertEquals("found wrong docid", "d4", r.document(ds.docID()).get("id"));
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java
index d1ec6f5..de87f96 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java
@@ -385,10 +385,10 @@
private static FilteredQuery.FilterStrategy randomFilterStrategy(Random random, final boolean useRandomAccess) {
if (useRandomAccess) {
- return new FilteredQuery.RandomAccessFilterStrategy() {
+ return new FilteredQuery.RandomAccessFilterStrategy() {
@Override
protected boolean useRandomAccess(Bits bits, int firstFilterDoc) {
- return useRandomAccess;
+ return true;
}
};
}
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestMinShouldMatch2.java b/lucene/core/src/test/org/apache/lucene/search/TestMinShouldMatch2.java
index 4ca3722..694484c 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestMinShouldMatch2.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestMinShouldMatch2.java
@@ -126,7 +126,7 @@
if (slow) {
return new SlowMinShouldMatchScorer(weight, reader, searcher);
} else {
- return weight.scorer(reader.getContext(), true, false, null);
+ return weight.scorer(reader.getContext(), null);
}
}
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestTermScorer.java b/lucene/core/src/test/org/apache/lucene/search/TestTermScorer.java
index 0c4e229..b0ff346 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestTermScorer.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestTermScorer.java
@@ -78,7 +78,7 @@
Weight weight = indexSearcher.createNormalizedWeight(termQuery);
assertTrue(indexSearcher.getTopReaderContext() instanceof AtomicReaderContext);
AtomicReaderContext context = (AtomicReaderContext)indexSearcher.getTopReaderContext();
- Scorer ts = weight.scorer(context, true, true, context.reader().getLiveDocs());
+ BulkScorer ts = weight.bulkScorer(context, true, context.reader().getLiveDocs());
// we have 2 documents with the term all in them, one document for all the
// other values
final List<TestHit> docs = new ArrayList<TestHit>();
@@ -140,7 +140,7 @@
Weight weight = indexSearcher.createNormalizedWeight(termQuery);
assertTrue(indexSearcher.getTopReaderContext() instanceof AtomicReaderContext);
AtomicReaderContext context = (AtomicReaderContext) indexSearcher.getTopReaderContext();
- Scorer ts = weight.scorer(context, true, false, context.reader().getLiveDocs());
+ Scorer ts = weight.scorer(context, context.reader().getLiveDocs());
assertTrue("next did not return a doc",
ts.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
assertTrue("score is not correct", ts.score() == 1.6931472f);
@@ -159,7 +159,7 @@
Weight weight = indexSearcher.createNormalizedWeight(termQuery);
assertTrue(indexSearcher.getTopReaderContext() instanceof AtomicReaderContext);
AtomicReaderContext context = (AtomicReaderContext) indexSearcher.getTopReaderContext();
- Scorer ts = weight.scorer(context, true, false, context.reader().getLiveDocs());
+ Scorer ts = weight.scorer(context, context.reader().getLiveDocs());
assertTrue("Didn't skip", ts.advance(3) != DocIdSetIterator.NO_MORE_DOCS);
// The next doc should be doc 5
assertTrue("doc should be number 5", ts.docID() == 5);
diff --git a/lucene/core/src/test/org/apache/lucene/search/spans/TestNearSpansOrdered.java b/lucene/core/src/test/org/apache/lucene/search/spans/TestNearSpansOrdered.java
index 48a4b4f..e6a6cb6 100644
--- a/lucene/core/src/test/org/apache/lucene/search/spans/TestNearSpansOrdered.java
+++ b/lucene/core/src/test/org/apache/lucene/search/spans/TestNearSpansOrdered.java
@@ -167,7 +167,7 @@
Weight w = searcher.createNormalizedWeight(q);
IndexReaderContext topReaderContext = searcher.getTopReaderContext();
AtomicReaderContext leave = topReaderContext.leaves().get(0);
- Scorer s = w.scorer(leave, true, false, leave.reader().getLiveDocs());
+ Scorer s = w.scorer(leave, leave.reader().getLiveDocs());
assertEquals(1, s.advance(1));
}
diff --git a/lucene/core/src/test/org/apache/lucene/search/spans/TestSpans.java b/lucene/core/src/test/org/apache/lucene/search/spans/TestSpans.java
index 30e8a9e..1f57342 100644
--- a/lucene/core/src/test/org/apache/lucene/search/spans/TestSpans.java
+++ b/lucene/core/src/test/org/apache/lucene/search/spans/TestSpans.java
@@ -429,7 +429,7 @@
slop,
ordered);
- spanScorer = searcher.createNormalizedWeight(snq).scorer(ctx, true, false, ctx.reader().getLiveDocs());
+ spanScorer = searcher.createNormalizedWeight(snq).scorer(ctx, ctx.reader().getLiveDocs());
} finally {
searcher.setSimilarity(oldSim);
}
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java b/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java
index 1c76622..7057e7b 100644
--- a/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java
+++ b/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java
@@ -29,6 +29,7 @@
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.BulkScorer;
import org.apache.lucene.search.Weight;
import org.apache.lucene.util.Bits;
@@ -117,12 +118,17 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
+ // We can only run as a top scorer:
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public BulkScorer bulkScorer(AtomicReaderContext context, boolean scoreDocsInOrder, Bits acceptDocs) throws IOException {
// TODO: it could be better if we take acceptDocs
// into account instead of baseScorer?
- Scorer baseScorer = baseWeight.scorer(context, scoreDocsInOrder, false, acceptDocs);
+ Scorer baseScorer = baseWeight.scorer(context, acceptDocs);
DrillSidewaysScorer.DocsAndCost[] dims = new DrillSidewaysScorer.DocsAndCost[drillDowns.length];
int nullCount = 0;
@@ -167,7 +173,7 @@
dims[dim].disi = disi;
}
} else {
- DocIdSetIterator disi = ((Weight) drillDowns[dim]).scorer(context, true, false, null);
+ DocIdSetIterator disi = ((Weight) drillDowns[dim]).scorer(context, null);
if (disi == null) {
nullCount++;
continue;
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysScorer.java b/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysScorer.java
index 220e649..8764f7e 100644
--- a/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysScorer.java
+++ b/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysScorer.java
@@ -22,14 +22,16 @@
import java.util.Collections;
import org.apache.lucene.index.AtomicReaderContext;
+import org.apache.lucene.index.DocsEnum;
import org.apache.lucene.search.Collector;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.BulkScorer;
import org.apache.lucene.search.Weight;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.FixedBitSet;
-class DrillSidewaysScorer extends Scorer {
+class DrillSidewaysScorer extends BulkScorer {
//private static boolean DEBUG = false;
@@ -52,7 +54,6 @@
DrillSidewaysScorer(Weight w, AtomicReaderContext context, Scorer baseScorer, Collector drillDownCollector,
DocsAndCost[] dims, boolean scoreSubDocsAtOnce) {
- super(w);
this.dims = dims;
this.context = context;
this.baseScorer = baseScorer;
@@ -61,18 +62,22 @@
}
@Override
- public void score(Collector collector) throws IOException {
+ public boolean score(Collector collector, int maxDoc) throws IOException {
+ if (maxDoc != Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("maxDoc must be Integer.MAX_VALUE");
+ }
//if (DEBUG) {
// System.out.println("\nscore: reader=" + context.reader());
//}
//System.out.println("score r=" + context.reader());
- collector.setScorer(this);
+ FakeScorer scorer = new FakeScorer();
+ collector.setScorer(scorer);
if (drillDownCollector != null) {
- drillDownCollector.setScorer(this);
+ drillDownCollector.setScorer(scorer);
drillDownCollector.setNextReader(context);
}
for (DocsAndCost dim : dims) {
- dim.sidewaysCollector.setScorer(this);
+ dim.sidewaysCollector.setScorer(scorer);
dim.sidewaysCollector.setNextReader(context);
}
@@ -140,6 +145,8 @@
//System.out.println("union");
doUnionScoring(collector, disis, sidewaysCollectors);
}
+
+ return false;
}
/** Used when base query is highly constraining vs the
@@ -154,7 +161,7 @@
//}
int docID = baseScorer.docID();
- nextDoc: while (docID != NO_MORE_DOCS) {
+ nextDoc: while (docID != DocsEnum.NO_MORE_DOCS) {
Collector failedCollector = null;
for (int i=0;i<disis.length;i++) {
// TODO: should we sort this 2nd dimension of
@@ -612,39 +619,53 @@
sidewaysCollector.collect(collectDocID);
}
- @Override
- public int docID() {
- return collectDocID;
- }
+ private final class FakeScorer extends Scorer {
+ float score;
+ int doc;
- @Override
- public float score() {
- return collectScore;
- }
+ public FakeScorer() {
+ super(null);
+ }
+
+ @Override
+ public int advance(int target) {
+ throw new UnsupportedOperationException("FakeScorer doesn't support advance(int)");
+ }
- @Override
- public int freq() {
- return 1+dims.length;
- }
+ @Override
+ public int docID() {
+ return collectDocID;
+ }
- @Override
- public int nextDoc() {
- throw new UnsupportedOperationException();
- }
+ @Override
+ public int freq() {
+ return 1+dims.length;
+ }
- @Override
- public int advance(int target) {
- throw new UnsupportedOperationException();
- }
+ @Override
+ public int nextDoc() {
+ throw new UnsupportedOperationException("FakeScorer doesn't support nextDoc()");
+ }
+
+ @Override
+ public float score() {
+ return collectScore;
+ }
- @Override
- public long cost() {
- return baseScorer.cost();
- }
+ @Override
+ public long cost() {
+ return baseScorer.cost();
+ }
- @Override
- public Collection<ChildScorer> getChildren() {
- return Collections.singletonList(new ChildScorer(baseScorer, "MUST"));
+ @Override
+ public Collection<ChildScorer> getChildren() {
+ return Collections.singletonList(new ChildScorer(baseScorer, "MUST"));
+ }
+
+ @Override
+ public Weight getWeight() {
+ throw new UnsupportedOperationException();
+ }
}
static class DocsAndCost implements Comparable<DocsAndCost> {
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyFacetSumValueSource.java b/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyFacetSumValueSource.java
index bb04db1..1342c18 100644
--- a/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyFacetSumValueSource.java
+++ b/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/TaxonomyFacetSumValueSource.java
@@ -18,12 +18,13 @@
*/
import java.io.IOException;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.apache.lucene.facet.FacetsCollector;
import org.apache.lucene.facet.FacetsCollector.MatchingDocs;
+import org.apache.lucene.facet.FacetsCollector;
import org.apache.lucene.facet.FacetsConfig;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.queries.function.FunctionValues;
@@ -31,6 +32,7 @@
import org.apache.lucene.queries.function.docvalues.DoubleDocValues;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Weight;
import org.apache.lucene.util.IntsRef;
/** Aggregates sum of values from {@link
@@ -70,6 +72,16 @@
@Override public int nextDoc() throws IOException { throw new UnsupportedOperationException(); }
@Override public int advance(int target) throws IOException { throw new UnsupportedOperationException(); }
@Override public long cost() { return 0; }
+
+ @Override
+ public Weight getWeight() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Collection<ChildScorer> getChildren() {
+ throw new UnsupportedOperationException();
+ }
}
private final void sumValues(List<MatchingDocs> matchingDocs, boolean keepScores, ValueSource valueSource) throws IOException {
diff --git a/lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java b/lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java
index a39678d..195ec23 100644
--- a/lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java
+++ b/lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java
@@ -18,14 +18,15 @@
*/
+import java.io.IOException;
+import java.util.Collection;
+
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.search.*;
import org.apache.lucene.util.ArrayUtil;
import org.apache.lucene.util.PriorityQueue;
-import java.io.IOException;
-
// TODO: this sentence is too long for the class summary.
/** BlockGroupingCollector performs grouping with a
* single pass collector, as long as you are grouping by a
@@ -90,7 +91,7 @@
int doc;
public FakeScorer() {
- super((Weight) null);
+ super(null);
}
@Override
@@ -122,6 +123,16 @@
public long cost() {
return 1;
}
+
+ @Override
+ public Weight getWeight() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Collection<ChildScorer> getChildren() {
+ throw new UnsupportedOperationException();
+ }
}
private static final class OneGroup {
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/FakeScorer.java b/lucene/join/src/java/org/apache/lucene/search/join/FakeScorer.java
new file mode 100644
index 0000000..d4b02dc
--- /dev/null
+++ b/lucene/join/src/java/org/apache/lucene/search/join/FakeScorer.java
@@ -0,0 +1,75 @@
+package org.apache.lucene.search.join;
+
+/*
+ * 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.util.Collection;
+
+import org.apache.lucene.search.Collector;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Weight;
+
+/** Passed to {@link Collector#setScorer} during join collection. */
+final class FakeScorer extends Scorer {
+ float score;
+ int doc = -1;
+ int freq = 1;
+
+ public FakeScorer() {
+ super(null);
+ }
+
+ @Override
+ public int advance(int target) {
+ throw new UnsupportedOperationException("FakeScorer doesn't support advance(int)");
+ }
+
+ @Override
+ public int docID() {
+ return doc;
+ }
+
+ @Override
+ public int freq() {
+ throw new UnsupportedOperationException("FakeScorer doesn't support freq()");
+ }
+
+ @Override
+ public int nextDoc() {
+ throw new UnsupportedOperationException("FakeScorer doesn't support nextDoc()");
+ }
+
+ @Override
+ public float score() {
+ return score;
+ }
+
+ @Override
+ public long cost() {
+ return 1;
+ }
+
+ @Override
+ public Weight getWeight() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Collection<ChildScorer> getChildren() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java
index fac8ed7..739ef35 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java
@@ -17,6 +17,10 @@
* limitations under the License.
*/
+import java.io.IOException;
+import java.util.Locale;
+import java.util.Set;
+
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.DocsEnum;
import org.apache.lucene.index.IndexReader;
@@ -30,16 +34,13 @@
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.BulkScorer;
import org.apache.lucene.search.Weight;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefHash;
import org.apache.lucene.util.FixedBitSet;
-import java.io.IOException;
-import java.util.Locale;
-import java.util.Set;
-
class TermsIncludingScoreQuery extends Query {
final String field;
@@ -131,11 +132,9 @@
@Override
public Explanation explain(AtomicReaderContext context, int doc) throws IOException {
- SVInnerScorer scorer = (SVInnerScorer) scorer(context, false, false, context.reader().getLiveDocs());
+ SVInnerScorer scorer = (SVInnerScorer) bulkScorer(context, false, null);
if (scorer != null) {
- if (scorer.advanceForExplainOnly(doc) == doc) {
- return scorer.explain();
- }
+ return scorer.explain(doc);
}
return new ComplexExplanation(false, 0.0f, "Not a match");
}
@@ -163,7 +162,7 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder, boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
Terms terms = context.reader().terms(field);
if (terms == null) {
return null;
@@ -173,23 +172,41 @@
final long cost = context.reader().maxDoc() * terms.size();
segmentTermsEnum = terms.iterator(segmentTermsEnum);
- if (scoreDocsInOrder) {
- if (multipleValuesPerDocument) {
- return new MVInOrderScorer(this, acceptDocs, segmentTermsEnum, context.reader().maxDoc(), cost);
- } else {
- return new SVInOrderScorer(this, acceptDocs, segmentTermsEnum, context.reader().maxDoc(), cost);
- }
- } else if (multipleValuesPerDocument) {
- return new MVInnerScorer(this, acceptDocs, segmentTermsEnum, context.reader().maxDoc(), cost);
+ if (multipleValuesPerDocument) {
+ return new MVInOrderScorer(this, acceptDocs, segmentTermsEnum, context.reader().maxDoc(), cost);
} else {
- return new SVInnerScorer(this, acceptDocs, segmentTermsEnum, cost);
+ return new SVInOrderScorer(this, acceptDocs, segmentTermsEnum, context.reader().maxDoc(), cost);
+ }
+ }
+
+ @Override
+ public BulkScorer bulkScorer(AtomicReaderContext context, boolean scoreDocsInOrder, Bits acceptDocs) throws IOException {
+
+ if (scoreDocsInOrder) {
+ return super.bulkScorer(context, scoreDocsInOrder, acceptDocs);
+ } else {
+ Terms terms = context.reader().terms(field);
+ if (terms == null) {
+ return null;
+ }
+ // what is the runtime...seems ok?
+ final long cost = context.reader().maxDoc() * terms.size();
+
+ segmentTermsEnum = terms.iterator(segmentTermsEnum);
+ // Optimized impls that take advantage of docs
+ // being allowed to be out of order:
+ if (multipleValuesPerDocument) {
+ return new MVInnerScorer(this, acceptDocs, segmentTermsEnum, context.reader().maxDoc(), cost);
+ } else {
+ return new SVInnerScorer(this, acceptDocs, segmentTermsEnum, cost);
+ }
}
}
};
}
// This impl assumes that the 'join' values are used uniquely per doc per field. Used for one to many relations.
- class SVInnerScorer extends Scorer {
+ class SVInnerScorer extends BulkScorer {
final BytesRef spare = new BytesRef();
final Bits acceptDocs;
@@ -203,7 +220,6 @@
int doc;
SVInnerScorer(Weight weight, Bits acceptDocs, TermsEnum termsEnum, long cost) {
- super(weight);
this.acceptDocs = acceptDocs;
this.termsEnum = termsEnum;
this.cost = cost;
@@ -211,25 +227,20 @@
}
@Override
- public void score(Collector collector) throws IOException {
- collector.setScorer(this);
- for (int doc = nextDocOutOfOrder(); doc != NO_MORE_DOCS; doc = nextDocOutOfOrder()) {
- collector.collect(doc);
+ public boolean score(Collector collector, int max) throws IOException {
+ FakeScorer fakeScorer = new FakeScorer();
+ collector.setScorer(fakeScorer);
+ if (doc == -1) {
+ doc = nextDocOutOfOrder();
}
- }
+ while(doc < max) {
+ fakeScorer.doc = doc;
+ fakeScorer.score = scores[ords[scoreUpto]];
+ collector.collect(doc);
+ doc = nextDocOutOfOrder();
+ }
- @Override
- public float score() throws IOException {
- return scores[ords[scoreUpto]];
- }
-
- Explanation explain() throws IOException {
- return new ComplexExplanation(true, score(), "Score based on join value " + termsEnum.term().utf8ToString());
- }
-
- @Override
- public int docID() {
- return doc;
+ return doc != DocsEnum.NO_MORE_DOCS;
}
int nextDocOutOfOrder() throws IOException {
@@ -258,17 +269,7 @@
return docsEnum.nextDoc();
}
- @Override
- public int nextDoc() throws IOException {
- throw new UnsupportedOperationException("nextDoc() isn't supported because doc ids are emitted out of order");
- }
-
- @Override
- public int advance(int target) throws IOException {
- throw new UnsupportedOperationException("advance() isn't supported because doc ids are emitted out of order");
- }
-
- private int advanceForExplainOnly(int target) throws IOException {
+ private Explanation explain(int target) throws IOException {
int docId;
do {
docId = nextDocOutOfOrder();
@@ -283,17 +284,8 @@
}
docsEnum = null; // goto the next ord.
} while (docId != DocIdSetIterator.NO_MORE_DOCS);
- return docId;
- }
- @Override
- public int freq() {
- return 1;
- }
-
- @Override
- public long cost() {
- return cost;
+ return new ComplexExplanation(true, scores[ords[scoreUpto]], "Score based on join value " + termsEnum.term().utf8ToString());
}
}
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java
index ffb114d..180d310 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java
@@ -124,11 +124,9 @@
// NOTE: acceptDocs applies (and is checked) only in the
// child document space
@Override
- public Scorer scorer(AtomicReaderContext readerContext, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext readerContext, Bits acceptDocs) throws IOException {
- // Pass scoreDocsInOrder true, topScorer false to our sub:
- final Scorer parentScorer = parentWeight.scorer(readerContext, true, false, null);
+ final Scorer parentScorer = parentWeight.scorer(readerContext, null);
if (parentScorer == null) {
// No matches
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java
index 5e68f5e..9dbb2a9 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java
@@ -325,46 +325,6 @@
}
}
- private final static class FakeScorer extends Scorer {
-
- float score;
- int doc;
-
- public FakeScorer() {
- super((Weight) null);
- }
-
- @Override
- public float score() {
- return score;
- }
-
- @Override
- public int freq() {
- return 1; // TODO: does anything else make sense?... duplicate of grouping's FakeScorer btw?
- }
-
- @Override
- public int docID() {
- return doc;
- }
-
- @Override
- public int advance(int target) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int nextDoc() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public long cost() {
- return 1;
- }
- }
-
private OneGroup[] sortedGroups;
private void sortQueue() {
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
index e5fe46d..f6985e2 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
@@ -17,6 +17,12 @@
* limitations under the License.
*/
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.Set;
+
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
@@ -35,12 +41,6 @@
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.FixedBitSet;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Locale;
-import java.util.Set;
-
/**
* This query requires that you index
* children and parent docs as a single block, using the
@@ -158,12 +158,9 @@
// NOTE: acceptDocs applies (and is checked) only in the
// parent document space
@Override
- public Scorer scorer(AtomicReaderContext readerContext, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext readerContext, Bits acceptDocs) throws IOException {
- // Pass scoreDocsInOrder true, topScorer false to our sub and the live docs:
- final Scorer childScorer = childWeight.scorer(readerContext, true, false, readerContext.reader().getLiveDocs());
-
+ final Scorer childScorer = childWeight.scorer(readerContext, readerContext.reader().getLiveDocs());
if (childScorer == null) {
// No matches
return null;
@@ -195,7 +192,7 @@
@Override
public Explanation explain(AtomicReaderContext context, int doc) throws IOException {
- BlockJoinScorer scorer = (BlockJoinScorer) scorer(context, true, false, context.reader().getLiveDocs());
+ BlockJoinScorer scorer = (BlockJoinScorer) scorer(context, context.reader().getLiveDocs());
if (scorer != null && scorer.advance(doc) == doc) {
return scorer.explain(context.docBase);
}
diff --git a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java
index c7ddcdd..573e3ef 100644
--- a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java
+++ b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java
@@ -1103,7 +1103,6 @@
parentDoc = s.doc(qGroup.groupValue);
assertEquals("Lisa", parentDoc.get("name"));
-
r.close();
dir.close();
}
@@ -1126,7 +1125,7 @@
ToParentBlockJoinQuery q = new ToParentBlockJoinQuery(tq, parentFilter, ScoreMode.Avg);
Weight weight = s.createNormalizedWeight(q);
- DocIdSetIterator disi = weight.scorer(s.getIndexReader().leaves().get(0), true, true, null);
+ DocIdSetIterator disi = weight.scorer(s.getIndexReader().leaves().get(0), null);
assertEquals(1, disi.advance(1));
r.close();
dir.close();
@@ -1160,7 +1159,7 @@
ToParentBlockJoinQuery q = new ToParentBlockJoinQuery(tq, parentFilter, ScoreMode.Avg);
Weight weight = s.createNormalizedWeight(q);
- DocIdSetIterator disi = weight.scorer(s.getIndexReader().leaves().get(0), true, true, null);
+ DocIdSetIterator disi = weight.scorer(s.getIndexReader().leaves().get(0), null);
assertEquals(2, disi.advance(0));
r.close();
dir.close();
@@ -1200,7 +1199,6 @@
ToParentBlockJoinQuery childJoinQuery = new ToParentBlockJoinQuery(childQuery, parentsFilter, ScoreMode.Avg);
ToParentBlockJoinCollector c = new ToParentBlockJoinCollector(Sort.RELEVANCE, 2, true, true);
-
s.search(childJoinQuery, c);
//Get all child documents within groups
@@ -1312,7 +1310,6 @@
GroupDocs<Integer> group = groups.groups[0];
StoredDocument doc = r.document(group.groupValue.intValue());
assertEquals("0", doc.get("parentID"));
- System.out.println("group: " + group);
group = groups.groups[1];
doc = r.document(group.groupValue.intValue());
@@ -1378,7 +1375,6 @@
GroupDocs<Integer> group = groups.groups[0];
StoredDocument doc = r.document(group.groupValue.intValue());
assertEquals("0", doc.get("parentID"));
- System.out.println("group: " + group);
group = groups.groups[1];
doc = r.document(group.groupValue.intValue());
diff --git a/lucene/join/src/test/org/apache/lucene/search/join/TestJoinUtil.java b/lucene/join/src/test/org/apache/lucene/search/join/TestJoinUtil.java
index 94f784a..08c9c8d 100644
--- a/lucene/join/src/test/org/apache/lucene/search/join/TestJoinUtil.java
+++ b/lucene/join/src/test/org/apache/lucene/search/join/TestJoinUtil.java
@@ -17,6 +17,19 @@
* limitations under the License.
*/
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
import org.apache.lucene.analysis.MockAnalyzer;
import org.apache.lucene.analysis.MockTokenizer;
import org.apache.lucene.document.Document;
@@ -34,6 +47,8 @@
import org.apache.lucene.index.Term;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Collector;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Explanation;
@@ -54,19 +69,6 @@
import org.apache.lucene.util.TestUtil;
import org.junit.Test;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
public class TestJoinUtil extends LuceneTestCase {
public void testSimple() throws Exception {
@@ -151,6 +153,104 @@
dir.close();
}
+ /** LUCENE-5487: verify a join query inside a SHOULD BQ
+ * will still use the join query's optimized BulkScorers */
+ public void testInsideBooleanQuery() throws Exception {
+ final String idField = "id";
+ final String toField = "productId";
+
+ Directory dir = newDirectory();
+ RandomIndexWriter w = new RandomIndexWriter(
+ random(),
+ dir,
+ newIndexWriterConfig(TEST_VERSION_CURRENT,
+ new MockAnalyzer(random())).setMergePolicy(newLogMergePolicy()));
+
+ // 0
+ Document doc = new Document();
+ doc.add(new TextField("description", "random text", Field.Store.NO));
+ doc.add(new TextField("name", "name1", Field.Store.NO));
+ doc.add(new TextField(idField, "7", Field.Store.NO));
+ w.addDocument(doc);
+
+ // 1
+ doc = new Document();
+ doc.add(new TextField("price", "10.0", Field.Store.NO));
+ doc.add(new TextField(idField, "2", Field.Store.NO));
+ doc.add(new TextField(toField, "7", Field.Store.NO));
+ w.addDocument(doc);
+
+ // 2
+ doc = new Document();
+ doc.add(new TextField("price", "20.0", Field.Store.NO));
+ doc.add(new TextField(idField, "3", Field.Store.NO));
+ doc.add(new TextField(toField, "7", Field.Store.NO));
+ w.addDocument(doc);
+
+ // 3
+ doc = new Document();
+ doc.add(new TextField("description", "more random text", Field.Store.NO));
+ doc.add(new TextField("name", "name2", Field.Store.NO));
+ doc.add(new TextField(idField, "0", Field.Store.NO));
+ w.addDocument(doc);
+ w.commit();
+
+ // 4
+ doc = new Document();
+ doc.add(new TextField("price", "10.0", Field.Store.NO));
+ doc.add(new TextField(idField, "5", Field.Store.NO));
+ doc.add(new TextField(toField, "0", Field.Store.NO));
+ w.addDocument(doc);
+
+ // 5
+ doc = new Document();
+ doc.add(new TextField("price", "20.0", Field.Store.NO));
+ doc.add(new TextField(idField, "6", Field.Store.NO));
+ doc.add(new TextField(toField, "0", Field.Store.NO));
+ w.addDocument(doc);
+
+ w.forceMerge(1);
+
+ IndexSearcher indexSearcher = new IndexSearcher(w.getReader());
+ w.close();
+
+ // Search for product
+ Query joinQuery =
+ JoinUtil.createJoinQuery(idField, false, toField, new TermQuery(new Term("description", "random")), indexSearcher, ScoreMode.Avg);
+
+ BooleanQuery bq = new BooleanQuery();
+ bq.add(joinQuery, BooleanClause.Occur.SHOULD);
+ bq.add(new TermQuery(new Term("id", "3")), BooleanClause.Occur.SHOULD);
+
+ indexSearcher.search(bq, new Collector() {
+ boolean sawFive;
+ @Override
+ public void setNextReader(AtomicReaderContext context) {
+ }
+ @Override
+ public void collect(int docID) {
+ // Hairy / evil (depends on how BooleanScorer
+ // stores temporarily collected docIDs by
+ // appending to head of linked list):
+ if (docID == 5) {
+ sawFive = true;
+ } else if (docID == 1) {
+ assertFalse("optimized bulkScorer was not used for join query embedded in boolean query!", sawFive);
+ }
+ }
+ @Override
+ public void setScorer(Scorer scorer) {
+ }
+ @Override
+ public boolean acceptsDocsOutOfOrder() {
+ return true;
+ }
+ });
+
+ indexSearcher.getIndexReader().close();
+ dir.close();
+ }
+
public void testSimpleWithScoring() throws Exception {
final String idField = "id";
final String toField = "movieId";
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java
index 602fa8b..9861617 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java
@@ -234,20 +234,14 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
- // Pass true for "scoresDocsInOrder", because we
- // require in-order scoring, even if caller does not,
- // since we call advance on the valSrcScorers. Pass
- // false for "topScorer" because we will not invoke
- // score(Collector) on these scorers:
- Scorer subQueryScorer = subQueryWeight.scorer(context, true, false, acceptDocs);
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
+ Scorer subQueryScorer = subQueryWeight.scorer(context, acceptDocs);
if (subQueryScorer == null) {
return null;
}
Scorer[] valSrcScorers = new Scorer[valSrcWeights.length];
for(int i = 0; i < valSrcScorers.length; i++) {
- valSrcScorers[i] = valSrcWeights[i].scorer(context, true, topScorer, acceptDocs);
+ valSrcScorers[i] = valSrcWeights[i].scorer(context, acceptDocs);
}
return new CustomScorer(CustomScoreQuery.this.getCustomScoreProvider(context), this, queryWeight, subQueryScorer, valSrcScorers);
}
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java
index ba3163e..7ba5f4f 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java
@@ -97,11 +97,9 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
- // we are gonna advance() the subscorer
- Scorer subQueryScorer = qWeight.scorer(context, true, false, acceptDocs);
- if(subQueryScorer == null) {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
+ Scorer subQueryScorer = qWeight.scorer(context, acceptDocs);
+ if (subQueryScorer == null) {
return null;
}
return new BoostedQuery.CustomScorer(context, this, getBoost(), subQueryScorer, boostVal);
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java
index 6c44b05..726b97e 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java
@@ -90,14 +90,13 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
return new AllScorer(context, acceptDocs, this, queryWeight);
}
@Override
public Explanation explain(AtomicReaderContext context, int doc) throws IOException {
- return ((AllScorer)scorer(context, true, true, context.reader().getLiveDocs())).explain(doc);
+ return ((AllScorer)scorer(context, context.reader().getLiveDocs())).explain(doc);
}
}
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/QueryValueSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/QueryValueSource.java
index 3e22e8e..10a5f0d 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/QueryValueSource.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/QueryValueSource.java
@@ -123,7 +123,7 @@
try {
if (doc < lastDocRequested) {
if (noMatches) return defVal;
- scorer = weight.scorer(readerContext, true, false, acceptDocs);
+ scorer = weight.scorer(readerContext, acceptDocs);
if (scorer==null) {
noMatches = true;
return defVal;
@@ -154,7 +154,7 @@
try {
if (doc < lastDocRequested) {
if (noMatches) return false;
- scorer = weight.scorer(readerContext, true, false, acceptDocs);
+ scorer = weight.scorer(readerContext, acceptDocs);
scorerDoc = -1;
if (scorer==null) {
noMatches = true;
@@ -212,7 +212,7 @@
mval.exists = false;
return;
}
- scorer = weight.scorer(readerContext, true, false, acceptDocs);
+ scorer = weight.scorer(readerContext, acceptDocs);
scorerDoc = -1;
if (scorer==null) {
noMatches = true;
diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingBulkOutOfOrderScorer.java b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingBulkOutOfOrderScorer.java
new file mode 100644
index 0000000..0b2fa34
--- /dev/null
+++ b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingBulkOutOfOrderScorer.java
@@ -0,0 +1,110 @@
+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.lang.ref.WeakReference;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Random;
+import java.util.WeakHashMap;
+
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.util.VirtualMethod;
+
+/** A crazy {@link BulkScorer} that wraps a {@link Scorer}
+ * but shuffles the order of the collected documents. */
+public class AssertingBulkOutOfOrderScorer extends BulkScorer {
+
+ final Random random;
+ final Scorer scorer;
+
+ public AssertingBulkOutOfOrderScorer(Random random, Scorer scorer) {
+ this.random = random;
+ this.scorer = scorer;
+ }
+
+ private void shuffle(int[] docIDs, float[] scores, int[] freqs, int size) {
+ for (int i = size - 1; i > 0; --i) {
+ final int other = random.nextInt(i + 1);
+
+ final int tmpDoc = docIDs[i];
+ docIDs[i] = docIDs[other];
+ docIDs[other] = tmpDoc;
+
+ final float tmpScore = scores[i];
+ scores[i] = scores[other];
+ scores[other] = tmpScore;
+
+ final int tmpFreq = freqs[i];
+ freqs[i] = freqs[other];
+ freqs[other] = tmpFreq;
+ }
+ }
+
+ private static void flush(int[] docIDs, float[] scores, int[] freqs, int size,
+ FakeScorer scorer, Collector collector) throws IOException {
+ for (int i = 0; i < size; ++i) {
+ scorer.doc = docIDs[i];
+ scorer.freq = freqs[i];
+ scorer.score = scores[i];
+ collector.collect(scorer.doc);
+ }
+ }
+
+ @Override
+ public boolean score(Collector collector, int max) throws IOException {
+ if (scorer.docID() == -1) {
+ scorer.nextDoc();
+ }
+
+ FakeScorer fake = new FakeScorer();
+ collector.setScorer(fake);
+
+ final int bufferSize = 1 + random.nextInt(100);
+ final int[] docIDs = new int[bufferSize];
+ final float[] scores = new float[bufferSize];
+ final int[] freqs = new int[bufferSize];
+
+ int buffered = 0;
+ int doc = scorer.docID();
+ while (doc < max) {
+ docIDs[buffered] = doc;
+ scores[buffered] = scorer.score();
+ freqs[buffered] = scorer.freq();
+
+ if (++buffered == bufferSize) {
+ shuffle(docIDs, scores, freqs, buffered);
+ flush(docIDs, scores, freqs, buffered, fake, collector);
+ buffered = 0;
+ }
+ doc = scorer.nextDoc();
+ }
+
+ shuffle(docIDs, scores, freqs, buffered);
+ flush(docIDs, scores, freqs, buffered, fake, collector);
+
+ return doc != Scorer.NO_MORE_DOCS;
+ }
+
+ @Override
+ public String toString() {
+ return "AssertingBulkOutOfOrderScorer(" + scorer + ")";
+ }
+}
diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingBulkScorer.java b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingBulkScorer.java
new file mode 100644
index 0000000..995f49a
--- /dev/null
+++ b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingBulkScorer.java
@@ -0,0 +1,84 @@
+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.lang.ref.WeakReference;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Random;
+import java.util.WeakHashMap;
+
+import org.apache.lucene.index.DocsEnum;
+import org.apache.lucene.util.VirtualMethod;
+
+/** Wraps a Scorer with additional checks */
+public class AssertingBulkScorer extends BulkScorer {
+
+ private static final VirtualMethod<BulkScorer> SCORE_COLLECTOR = new VirtualMethod<BulkScorer>(BulkScorer.class, "score", Collector.class);
+ private static final VirtualMethod<BulkScorer> SCORE_COLLECTOR_RANGE = new VirtualMethod<BulkScorer>(BulkScorer.class, "score", Collector.class, int.class);
+
+ public static BulkScorer wrap(Random random, BulkScorer other) {
+ if (other == null || other instanceof AssertingBulkScorer) {
+ return other;
+ }
+ return new AssertingBulkScorer(random, other);
+ }
+
+ public static boolean shouldWrap(BulkScorer inScorer) {
+ return SCORE_COLLECTOR.isOverriddenAsOf(inScorer.getClass()) || SCORE_COLLECTOR_RANGE.isOverriddenAsOf(inScorer.getClass());
+ }
+
+ final Random random;
+ final BulkScorer in;
+
+ private AssertingBulkScorer(Random random, BulkScorer in) {
+ this.random = random;
+ this.in = in;
+ }
+
+ public BulkScorer getIn() {
+ return in;
+ }
+
+ @Override
+ public void score(Collector collector) throws IOException {
+ if (random.nextBoolean()) {
+ try {
+ final boolean remaining = in.score(collector, DocsEnum.NO_MORE_DOCS);
+ assert !remaining;
+ } catch (UnsupportedOperationException e) {
+ in.score(collector);
+ }
+ } else {
+ in.score(collector);
+ }
+ }
+
+ @Override
+ public boolean score(Collector collector, int max) throws IOException {
+ return in.score(collector, max);
+ }
+
+ @Override
+ public String toString() {
+ return "AssertingBulkScorer(" + in + ")";
+ }
+
+}
diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java
index eef42b0..47725db 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingIndexSearcher.java
@@ -90,6 +90,7 @@
@Override
protected void search(List<AtomicReaderContext> leaves, Weight weight, Collector collector) throws IOException {
+ // TODO: shouldn't we AssertingCollector.wrap(collector) here?
super.search(leaves, AssertingWeight.wrap(random, weight), collector);
}
diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingScorer.java b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingScorer.java
index 22a73c1..b55cf63 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingScorer.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingScorer.java
@@ -32,31 +32,20 @@
/** Wraps a Scorer with additional checks */
public class AssertingScorer extends Scorer {
- enum TopScorer {
- YES, NO, UNKNOWN;
- }
-
- private static final VirtualMethod<Scorer> SCORE_COLLECTOR = new VirtualMethod<Scorer>(Scorer.class, "score", Collector.class);
- private static final VirtualMethod<Scorer> SCORE_COLLECTOR_RANGE = new VirtualMethod<Scorer>(Scorer.class, "score", Collector.class, int.class, int.class);
-
// we need to track scorers using a weak hash map because otherwise we
// could loose references because of eg.
// AssertingScorer.score(Collector) which needs to delegate to work correctly
private static Map<Scorer, WeakReference<AssertingScorer>> ASSERTING_INSTANCES = Collections.synchronizedMap(new WeakHashMap<Scorer, WeakReference<AssertingScorer>>());
- private static Scorer wrap(Random random, Scorer other, TopScorer topScorer, boolean inOrder) {
+ public static Scorer wrap(Random random, Scorer other) {
if (other == null || other instanceof AssertingScorer) {
return other;
}
- final AssertingScorer assertScorer = new AssertingScorer(random, other, topScorer, inOrder);
+ final AssertingScorer assertScorer = new AssertingScorer(random, other);
ASSERTING_INSTANCES.put(other, new WeakReference<AssertingScorer>(assertScorer));
return assertScorer;
}
- static Scorer wrap(Random random, Scorer other, boolean topScorer, boolean inOrder) {
- return wrap(random, other, topScorer ? TopScorer.YES : TopScorer.NO, inOrder);
- }
-
static Scorer getAssertingScorer(Random random, Scorer other) {
if (other == null || other instanceof AssertingScorer) {
return other;
@@ -68,7 +57,7 @@
// scorer1.score(collector) calls
// collector.setScorer(scorer2) with scorer1 != scorer2, such as
// BooleanScorer. In that case we can't enable all assertions
- return new AssertingScorer(random, other, TopScorer.UNKNOWN, false);
+ return new AssertingScorer(random, other);
} else {
return assertingScorer;
}
@@ -77,20 +66,12 @@
final Random random;
final Scorer in;
final AssertingAtomicReader.AssertingDocsEnum docsEnumIn;
- final TopScorer topScorer;
- final boolean inOrder;
- final boolean canCallNextDoc;
- private AssertingScorer(Random random, Scorer in, TopScorer topScorer, boolean inOrder) {
+ private AssertingScorer(Random random, Scorer in) {
super(in.weight);
this.random = random;
this.in = in;
- this.topScorer = topScorer;
- this.inOrder = inOrder;
- this.docsEnumIn = new AssertingAtomicReader.AssertingDocsEnum(in, topScorer == TopScorer.NO);
- this.canCallNextDoc = topScorer != TopScorer.YES // not a top scorer
- || !SCORE_COLLECTOR_RANGE.isOverriddenAsOf(in.getClass()) // the default impl relies upon nextDoc()
- || !SCORE_COLLECTOR.isOverriddenAsOf(in.getClass()); // the default impl relies upon nextDoc()
+ this.docsEnumIn = new AssertingAtomicReader.AssertingDocsEnum(in);
}
public Scorer getIn() {
@@ -116,143 +97,6 @@
return score;
}
- private final static class FakeScorer extends Scorer {
-
- float score;
- int doc;
- int freq;
- final long cost;
-
- public FakeScorer(Scorer other) {
- super((Weight) null);
- this.cost = other.cost();
- }
-
- @Override
- public float score() {
- return score;
- }
-
- @Override
- public int freq() {
- return freq;
- }
-
- @Override
- public int docID() {
- return doc;
- }
-
- @Override
- public int advance(int target) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public int nextDoc() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public long cost() {
- return cost;
- }
- }
-
- private void shuffle(int[] docIDs, float[] scores, int[] freqs, int size) {
- for (int i = size - 1; i > 0; --i) {
- final int other = random.nextInt(i + 1);
-
- final int tmpDoc = docIDs[i];
- docIDs[i] = docIDs[other];
- docIDs[other] = tmpDoc;
-
- final float tmpScore = scores[i];
- scores[i] = scores[other];
- scores[other] = tmpScore;
-
- final int tmpFreq = freqs[i];
- freqs[i] = freqs[other];
- freqs[other] = tmpFreq;
- }
- }
-
- private static void flush(int[] docIDs, float[] scores, int[] freqs, int size,
- FakeScorer scorer, Collector collector) throws IOException {
- for (int i = 0; i < size; ++i) {
- scorer.doc = docIDs[i];
- scorer.freq = freqs[i];
- scorer.score = scores[i];
- collector.collect(scorer.doc);
- }
- }
-
- private void scoreInRandomOrder(Collector collector) throws IOException {
- assert docID() == -1; // not started
- FakeScorer fake = new FakeScorer(this);
- collector.setScorer(fake);
-
- final int bufferSize = 1 + random.nextInt(100);
- final int[] docIDs = new int[bufferSize];
- final float[] scores = new float[bufferSize];
- final int[] freqs = new int[bufferSize];
-
- int buffered = 0;
- int doc;
- while ((doc = nextDoc()) != NO_MORE_DOCS) {
- docIDs[buffered] = doc;
- scores[buffered] = score();
- freqs[buffered] = freq();
-
- if (++buffered == bufferSize) {
- shuffle(docIDs, scores, freqs, buffered);
- flush(docIDs, scores, freqs, buffered, fake, collector);
- buffered = 0;
- }
- }
-
- shuffle(docIDs, scores, freqs, buffered);
- flush(docIDs, scores, freqs, buffered, fake, collector);
- }
-
-
- @Override
- public void score(Collector collector) throws IOException {
- assert topScorer != TopScorer.NO;
- if (SCORE_COLLECTOR.isOverriddenAsOf(this.in.getClass())) {
- if (random.nextBoolean()) {
- try {
- final boolean remaining = in.score(collector, DocsEnum.NO_MORE_DOCS, in.nextDoc());
- assert !remaining;
- } catch (UnsupportedOperationException e) {
- in.score(collector);
- }
- } else {
- in.score(collector);
- }
- } else {
- // score(Collector) has not been overridden, use the super method in
- // order to benefit from all assertions
- if (collector.acceptsDocsOutOfOrder() && random.nextBoolean()) {
- scoreInRandomOrder(collector);
- } else {
- super.score(collector);
- }
- }
- }
-
- @Override
- public boolean score(Collector collector, int max, int firstDocID) throws IOException {
- assert topScorer != TopScorer.NO;
- if (SCORE_COLLECTOR_RANGE.isOverriddenAsOf(this.in.getClass())) {
- return in.score(collector, max, firstDocID);
- } else {
- // score(Collector,int,int) has not been overridden, use the super
- // method in order to benefit from all assertions
- return super.score(collector, max, firstDocID);
- }
- }
-
@Override
public Collection<ChildScorer> getChildren() {
// We cannot hide that we hold a single child, else
@@ -275,13 +119,11 @@
@Override
public int nextDoc() throws IOException {
- assert canCallNextDoc : "top scorers should not call nextDoc()";
return docsEnumIn.nextDoc();
}
@Override
public int advance(int target) throws IOException {
- assert canCallNextDoc : "top scorers should not call advance(target)";
return docsEnumIn.advance(target);
}
diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingWeight.java b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingWeight.java
index c712b59..793b396 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingWeight.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingWeight.java
@@ -60,19 +60,46 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
// if the caller asks for in-order scoring or if the weight does not support
// out-of order scoring then collection will have to happen in-order.
- final boolean inOrder = scoreDocsInOrder || !scoresDocsOutOfOrder();
- final Scorer inScorer = in.scorer(context, scoreDocsInOrder, topScorer, acceptDocs);
- return AssertingScorer.wrap(new Random(random.nextLong()), inScorer, topScorer, inOrder);
+ final Scorer inScorer = in.scorer(context, acceptDocs);
+ return AssertingScorer.wrap(new Random(random.nextLong()), inScorer);
+ }
+
+ @Override
+ public BulkScorer bulkScorer(AtomicReaderContext context, boolean scoreDocsInOrder, Bits acceptDocs) throws IOException {
+ // if the caller asks for in-order scoring or if the weight does not support
+ // out-of order scoring then collection will have to happen in-order.
+ BulkScorer inScorer = in.bulkScorer(context, scoreDocsInOrder, acceptDocs);
+ if (inScorer == null) {
+ return null;
+ }
+
+ if (AssertingBulkScorer.shouldWrap(inScorer)) {
+ // The incoming scorer already has a specialized
+ // implementation for BulkScorer, so we should use it:
+ return AssertingBulkScorer.wrap(new Random(random.nextLong()), inScorer);
+ } else if (scoreDocsInOrder == false && random.nextBoolean()) {
+ // The caller claims it can handle out-of-order
+ // docs; let's confirm that by pulling docs and
+ // randomly shuffling them before collection:
+ //Scorer scorer = in.scorer(context, acceptDocs);
+ Scorer scorer = scorer(context, acceptDocs);
+
+ // Scorer should not be null if bulkScorer wasn't:
+ assert scorer != null;
+ return new AssertingBulkOutOfOrderScorer(new Random(random.nextLong()), scorer);
+ } else {
+ // Let super wrap this.scorer instead, so we use
+ // AssertingScorer:
+ return super.bulkScorer(context, scoreDocsInOrder, acceptDocs);
+ }
}
@Override
public boolean scoresDocsOutOfOrder() {
return scoresDocsOutOfOrder;
}
-
}
diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/QueryUtils.java b/lucene/test-framework/src/java/org/apache/lucene/search/QueryUtils.java
index 0679be8..cd00f74 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/search/QueryUtils.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/search/QueryUtils.java
@@ -267,7 +267,7 @@
if (scorer == null) {
Weight w = s.createNormalizedWeight(q);
AtomicReaderContext context = readerContextArray.get(leafPtr);
- scorer = w.scorer(context, true, false, context.reader().getLiveDocs());
+ scorer = w.scorer(context, context.reader().getLiveDocs());
}
int op = order[(opidx[0]++) % order.length];
@@ -314,7 +314,7 @@
indexSearcher.setSimilarity(s.getSimilarity());
Weight w = indexSearcher.createNormalizedWeight(q);
AtomicReaderContext ctx = (AtomicReaderContext)indexSearcher.getTopReaderContext();
- Scorer scorer = w.scorer(ctx, true, false, ctx.reader().getLiveDocs());
+ Scorer scorer = w.scorer(ctx, ctx.reader().getLiveDocs());
if (scorer != null) {
boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS;
Assert.assertFalse("query's last doc was "+ lastDoc[0] +" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more);
@@ -341,7 +341,7 @@
indexSearcher.setSimilarity(s.getSimilarity());
Weight w = indexSearcher.createNormalizedWeight(q);
AtomicReaderContext ctx = previousReader.getContext();
- Scorer scorer = w.scorer(ctx, true, false, ctx.reader().getLiveDocs());
+ Scorer scorer = w.scorer(ctx, ctx.reader().getLiveDocs());
if (scorer != null) {
boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS;
Assert.assertFalse("query's last doc was "+ lastDoc[0] +" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more);
@@ -372,7 +372,7 @@
long startMS = System.currentTimeMillis();
for (int i=lastDoc[0]+1; i<=doc; i++) {
Weight w = s.createNormalizedWeight(q);
- Scorer scorer = w.scorer(context.get(leafPtr), true, false, liveDocs);
+ Scorer scorer = w.scorer(context.get(leafPtr), liveDocs);
Assert.assertTrue("query collected "+doc+" but skipTo("+i+") says no more docs!",scorer.advance(i) != DocIdSetIterator.NO_MORE_DOCS);
Assert.assertEquals("query collected "+doc+" but skipTo("+i+") got to "+scorer.docID(),doc,scorer.docID());
float skipToScore = scorer.score();
@@ -400,7 +400,7 @@
IndexSearcher indexSearcher = LuceneTestCase.newSearcher(previousReader);
indexSearcher.setSimilarity(s.getSimilarity());
Weight w = indexSearcher.createNormalizedWeight(q);
- Scorer scorer = w.scorer((AtomicReaderContext)indexSearcher.getTopReaderContext(), true, false, previousReader.getLiveDocs());
+ Scorer scorer = w.scorer((AtomicReaderContext)indexSearcher.getTopReaderContext(), previousReader.getLiveDocs());
if (scorer != null) {
boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS;
Assert.assertFalse("query's last doc was "+ lastDoc[0] +" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more);
@@ -425,7 +425,7 @@
IndexSearcher indexSearcher = LuceneTestCase.newSearcher(previousReader);
indexSearcher.setSimilarity(s.getSimilarity());
Weight w = indexSearcher.createNormalizedWeight(q);
- Scorer scorer = w.scorer((AtomicReaderContext)indexSearcher.getTopReaderContext(), true, false, previousReader.getLiveDocs());
+ Scorer scorer = w.scorer((AtomicReaderContext)indexSearcher.getTopReaderContext(), previousReader.getLiveDocs());
if (scorer != null) {
boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS;
Assert.assertFalse("query's last doc was "+ lastDoc[0] +" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more);
diff --git a/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java b/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
index e2c5ba8..71f4e45 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
@@ -17,6 +17,21 @@
package org.apache.solr.handler.component;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.IndexReaderContext;
import org.apache.lucene.index.ReaderUtil;
@@ -28,6 +43,7 @@
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.Weight;
import org.apache.lucene.search.grouping.GroupDocs;
import org.apache.lucene.search.grouping.SearchGroup;
import org.apache.lucene.search.grouping.TopGroups;
@@ -83,22 +99,6 @@
import org.apache.solr.search.grouping.endresulttransformer.SimpleEndResultTransformer;
import org.apache.solr.util.SolrPluginUtils;
-import org.apache.commons.lang.StringUtils;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
/**
* TODO!
*
@@ -1227,5 +1227,15 @@
public long cost() {
return 1;
}
+
+ @Override
+ public Weight getWeight() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Collection<ChildScorer> getChildren() {
+ throw new UnsupportedOperationException();
+ }
}
}
diff --git a/solr/core/src/java/org/apache/solr/schema/LatLonType.java b/solr/core/src/java/org/apache/solr/schema/LatLonType.java
index 51b186b..bfc9b48 100644
--- a/solr/core/src/java/org/apache/solr/schema/LatLonType.java
+++ b/solr/core/src/java/org/apache/solr/schema/LatLonType.java
@@ -333,14 +333,13 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
return new SpatialScorer(context, acceptDocs, this, queryWeight);
}
@Override
public Explanation explain(AtomicReaderContext context, int doc) throws IOException {
- return ((SpatialScorer)scorer(context, true, true, context.reader().getLiveDocs())).explain(doc);
+ return ((SpatialScorer)scorer(context, context.reader().getLiveDocs())).explain(doc);
}
}
diff --git a/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java
index 6c451fd..f410fc1 100644
--- a/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java
+++ b/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java
@@ -232,8 +232,7 @@
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
if (filter == null) {
boolean debug = rb != null && rb.isDebug();
long start = debug ? System.currentTimeMillis() : 0;
@@ -502,7 +501,7 @@
@Override
public Explanation explain(AtomicReaderContext context, int doc) throws IOException {
- Scorer scorer = scorer(context, true, false, context.reader().getLiveDocs());
+ Scorer scorer = scorer(context, context.reader().getLiveDocs());
boolean exists = scorer.advance(doc) == doc;
ComplexExplanation result = new ComplexExplanation();
diff --git a/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java b/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java
index 2b6268e..dfdf7ad 100644
--- a/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java
+++ b/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java
@@ -119,8 +119,7 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
- boolean topScorer, Bits acceptDocs) throws IOException {
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
return new ConstantScorer(context, this, queryWeight, acceptDocs);
}
diff --git a/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java b/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
index 22a7360..e7007ef 100644
--- a/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
+++ b/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
@@ -2472,7 +2472,7 @@
iterators.add(iter);
}
for (Weight w : weights) {
- Scorer scorer = w.scorer(context, true, false, context.reader().getLiveDocs());
+ Scorer scorer = w.scorer(context, context.reader().getLiveDocs());
if (scorer == null) return null;
iterators.add(scorer);
}
diff --git a/solr/core/src/java/org/apache/solr/search/join/IgnoreAcceptDocsQuery.java b/solr/core/src/java/org/apache/solr/search/join/IgnoreAcceptDocsQuery.java
index 7e057fe..d61b4ce 100644
--- a/solr/core/src/java/org/apache/solr/search/join/IgnoreAcceptDocsQuery.java
+++ b/solr/core/src/java/org/apache/solr/search/join/IgnoreAcceptDocsQuery.java
@@ -86,8 +86,8 @@
}
@Override
- public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder, boolean topScorer, Bits acceptDocs) throws IOException {
- return w.scorer(context, scoreDocsInOrder, topScorer, null);
+ public Scorer scorer(AtomicReaderContext context, Bits acceptDocs) throws IOException {
+ return w.scorer(context, null);
}
}