mirror of https://github.com/apache/lucene.git
LUCENE-1483: switch to newly added MultiReaderHitCollector for all core collectors, that is aware of segment transitions during searching, to improve performance of searching and warming
git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@738219 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
f1ab1aee64
commit
96e0aaf49e
12
CHANGES.txt
12
CHANGES.txt
|
@ -46,6 +46,11 @@ API Changes
|
|||
newly added NoSuchDirectoryException if the directory does not
|
||||
exist. (Marcel Reutegger, Mike McCandless)
|
||||
|
||||
7. LUCENE-1483: Added new MultiReaderHitCollector which enables faster
|
||||
hit collection by notifying the collector for each sub-reader
|
||||
that's visited. All core collectors now use this API. (Mark
|
||||
Miller, Mike McCandless)
|
||||
|
||||
Bug fixes
|
||||
|
||||
1. LUCENE-1415: MultiPhraseQuery has incorrect hashCode() and equals()
|
||||
|
@ -152,6 +157,13 @@ Optimizations
|
|||
environment. (Todd Feak, Yonik Seeley, Jason Rutherglen via Mike
|
||||
McCandless)
|
||||
|
||||
6. LUCENE-1483: When searching over multiple segments we now visit
|
||||
each sub-reader one at a time. This speeds up warming, since
|
||||
FieldCache entries (if required) can be shared across reopens for
|
||||
those segments that did not change, and also speeds up searches
|
||||
that sort by relevance or by field values. (Mark Miller, Mike
|
||||
McCandless)
|
||||
|
||||
Documentation
|
||||
|
||||
Build
|
||||
|
|
|
@ -111,7 +111,7 @@
|
|||
<mkdir dir="${tags.dir}/${tag}"/>
|
||||
<exec dir="${tags.dir}/${tag}" executable="${svn.exe}"
|
||||
failifexecutionfails="false">
|
||||
<arg line="checkout http://svn.apache.org/repos/asf/lucene/java/${tag}/src/test/"/>
|
||||
<arg line="checkout https://svn.apache.org/repos/asf/lucene/java/${tag}/src/test/"/>
|
||||
</exec>
|
||||
</sequential>
|
||||
</target>
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
<property name="name" value="${ant.project.name}"/>
|
||||
<property name="Name" value="Lucene"/>
|
||||
<property name="version" value="2.9-dev"/>
|
||||
<property name="compatibility.tag" value="tags/lucene_2_4_back_compat_tests_20090123"/>
|
||||
<property name="compatibility.tag" value="tags/lucene_2_4_back_compat_tests_20090127"/>
|
||||
<property name="spec.version" value="${version}"/>
|
||||
<property name="year" value="2000-${current.year}"/>
|
||||
<property name="final.name" value="lucene-${name}-${version}"/>
|
||||
|
|
|
@ -6,20 +6,44 @@ import java.util.Random;
|
|||
import org.apache.lucene.benchmark.byTask.utils.Config;
|
||||
|
||||
/**
|
||||
* Adds fields appropriate for sorting.
|
||||
* Adds fields appropriate for sorting: country,
|
||||
* random_string and sort_field (int).
|
||||
*
|
||||
*/
|
||||
public class SortableSimpleDocMaker extends SimpleDocMaker {
|
||||
private int sortRange;
|
||||
|
||||
private static String[] COUNTRIES = new String[] {"European Union", "United States", "Japan", "Germany", "China (PRC)", "United Kingdom", "France", "Italy", "Spain", "Canada", "Brazil", "Russia", "India", "South Korea", "Australia", "Mexico", "Netherlands", "Turkey", "Sweden", "Belgium", "Indonesia", "Switzerland", "Poland", "Norway", "Republic of China", "Saudi Arabia", "Austria", "Greece", "Denmark", "Iran", "South Africa", "Argentina", "Ireland", "Thailand", "Finland", "Venezuela", "Portugal", "Hong Kong", "United Arab Emirates", "Malaysia", "Czech Republic", "Colombia", "Nigeria", "Romania", "Chile", "Israel", "Singapore", "Philippines", "Pakistan", "Ukraine", "Hungary", "Algeria", "New Zealand", "Egypt", "Kuwait", "Peru", "Kazakhstan", "Slovakia", "Morocco", "Bangladesh", "Vietnam", "Qatar", "Angola", "Libya", "Iraq", "Croatia", "Luxembourg", "Sudan", "Slovenia", "Cuba", "Belarus", "Ecuador", "Serbia", "Oman", "Bulgaria", "Lithuania", "Syria", "Dominican Republic", "Tunisia", "Guatemala", "Azerbaijan", "Sri Lanka", "Kenya", "Latvia", "Turkmenistan", "Costa Rica", "Lebanon", "Uruguay", "Uzbekistan", "Yemen", "Cyprus", "Estonia", "Trinidad and Tobago", "Cameroon", "El Salvador", "Iceland", "Panama", "Bahrain", "Ivory Coast", "Ethiopia", "Tanzania", "Jordan", "Ghana", "Bosnia and Herzegovina", "Macau", "Burma", "Bolivia", "Brunei", "Botswana", "Honduras", "Gabon", "Uganda", "Jamaica", "Zambia", "Senegal", "Paraguay", "Albania", "Equatorial Guinea", "Georgia", "Democratic Republic of the Congo", "Nepal", "Afghanistan", "Cambodia", "Armenia", "Republic of the Congo", "Mozambique", "Republic of Macedonia", "Malta", "Namibia", "Madagascar", "Chad", "Burkina Faso", "Mauritius", "Mali", "The Bahamas", "Papua New Guinea", "Nicaragua", "Haiti", "Benin", "alestinian flag West Bank and Gaza", "Jersey", "Fiji", "Guinea", "Moldova", "Niger", "Laos", "Mongolia", "French Polynesia", "Kyrgyzstan", "Barbados", "Tajikistan", "Malawi", "Liechtenstein", "New Caledonia", "Kosovo", "Rwanda", "Montenegro", "Swaziland", "Guam", "Mauritania", "Guernsey", "Isle of Man", "Togo", "Somalia", "Suriname", "Aruba", "North Korea", "Zimbabwe", "Central African Republic", "Faroe Islands", "Greenland", "Sierra Leone", "Lesotho", "Cape Verde", "Eritrea", "Bhutan", "Belize", "Antigua and Barbuda", "Gibraltar", "Maldives", "San Marino", "Guyana", "Burundi", "Saint Lucia", "Djibouti", "British Virgin Islands", "Liberia", "Seychelles", "The Gambia", "Northern Mariana Islands", "Grenada", "Saint Vincent and the Grenadines", "Saint Kitts and Nevis", "East Timor", "Vanuatu", "Comoros", "Samoa", "Solomon Islands", "Guinea-Bissau", "American Samoa", "Dominica", "Micronesia", "Tonga", "Cook Islands", "Palau", "Marshall Islands", "S<EFBFBD>o Tom<6F> and Pr<50>ncipe", "Anguilla", "Kiribati", "Tuvalu", "Niue"};
|
||||
|
||||
protected DocData getNextDocData() throws NoMoreDataException {
|
||||
Random r = new Random();
|
||||
DocData doc = super.getNextDocData();
|
||||
Properties props = new Properties();
|
||||
props.put("sort_field", Integer.toString(getRandomNumber(0, sortRange)));
|
||||
|
||||
// random int
|
||||
props.put("sort_field", Integer.toString(nextInt(r, sortRange)));
|
||||
|
||||
// random string
|
||||
int len = nextInt(r, 2, 20);
|
||||
char[] buffer = new char[len];
|
||||
for(int i=0;i<len;i++)
|
||||
buffer[i] = (char) nextInt(r, 0x80);
|
||||
props.put("random_string", new String(buffer));
|
||||
|
||||
// random country
|
||||
props.put("country", COUNTRIES[nextInt(r, COUNTRIES.length)]);
|
||||
doc.setProps(props);
|
||||
return doc;
|
||||
}
|
||||
|
||||
private int nextInt(Random r, int lim) {
|
||||
return r.nextInt(lim);
|
||||
}
|
||||
|
||||
private int nextInt(Random r, int start, int end) {
|
||||
return start + r.nextInt(end-start);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
|
@ -29,10 +53,4 @@ public class SortableSimpleDocMaker extends SimpleDocMaker {
|
|||
super.setConfig(config);
|
||||
sortRange = config.get("sort.rng", 20000);
|
||||
}
|
||||
|
||||
private int getRandomNumber(final int low, final int high) {
|
||||
Random r = new Random();
|
||||
int randInt = (Math.abs(r.nextInt()) % (high - low)) + low;
|
||||
return randInt;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,18 +45,24 @@ public class SearchWithSortTask extends ReadTask {
|
|||
SortField[] sortFields = new SortField[fields.length];
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
String field = fields[i];
|
||||
int index = field.lastIndexOf(":");
|
||||
String fieldName;
|
||||
String typeString;
|
||||
if (index != -1) {
|
||||
fieldName = field.substring(0, index);
|
||||
typeString = field.substring(1+index, field.length());
|
||||
SortField sortField0;
|
||||
if (field.equals("doc")) {
|
||||
sortField0 = SortField.FIELD_DOC;
|
||||
} else {
|
||||
typeString = "auto";
|
||||
fieldName = field;
|
||||
int index = field.lastIndexOf(":");
|
||||
String fieldName;
|
||||
String typeString;
|
||||
if (index != -1) {
|
||||
fieldName = field.substring(0, index);
|
||||
typeString = field.substring(1+index, field.length());
|
||||
} else {
|
||||
typeString = "auto";
|
||||
fieldName = field;
|
||||
}
|
||||
int type = getType(typeString);
|
||||
sortField0 = new SortField(fieldName, type);
|
||||
}
|
||||
int type = getType(typeString);
|
||||
sortFields[i] = new SortField(fieldName, type);
|
||||
sortFields[i] = sortField0;
|
||||
}
|
||||
this.sort = new Sort(sortFields);
|
||||
}
|
||||
|
@ -69,6 +75,8 @@ public class SearchWithSortTask extends ReadTask {
|
|||
type = SortField.INT;
|
||||
} else if (typeString.equals("string")) {
|
||||
type = SortField.STRING;
|
||||
} else if (typeString.equals("string_val")) {
|
||||
type = SortField.STRING_VAL;
|
||||
} else {
|
||||
type = SortField.AUTO;
|
||||
}
|
||||
|
|
|
@ -22,14 +22,11 @@ import java.util.Arrays;
|
|||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.MultiReaderHitCollector;
|
||||
import org.apache.lucene.search.Similarity;
|
||||
import org.apache.lucene.search.DefaultSimilarity;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.search.HitCollector;
|
||||
import org.apache.lucene.store.RAMDirectory;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.analysis.SimpleAnalyzer;
|
||||
|
@ -126,9 +123,13 @@ public class TestFieldNormModifier extends TestCase {
|
|||
float lastScore = 0.0f;
|
||||
|
||||
// default similarity should put docs with shorter length first
|
||||
searcher.search(new TermQuery(new Term("field", "word")), new HitCollector() {
|
||||
searcher.search(new TermQuery(new Term("field", "word")), new MultiReaderHitCollector() {
|
||||
private int docBase = -1;
|
||||
public final void collect(int doc, float score) {
|
||||
scores[doc] = score;
|
||||
scores[doc + docBase] = score;
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
this.docBase = docBase;
|
||||
}
|
||||
});
|
||||
searcher.close();
|
||||
|
@ -146,9 +147,13 @@ public class TestFieldNormModifier extends TestCase {
|
|||
|
||||
// new norm (with default similarity) should put longer docs first
|
||||
searcher = new IndexSearcher(store);
|
||||
searcher.search(new TermQuery(new Term("field", "word")), new HitCollector() {
|
||||
searcher.search(new TermQuery(new Term("field", "word")), new MultiReaderHitCollector() {
|
||||
private int docBase = -1;
|
||||
public final void collect(int doc, float score) {
|
||||
scores[doc] = score;
|
||||
scores[doc + docBase] = score;
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
this.docBase = docBase;
|
||||
}
|
||||
});
|
||||
searcher.close();
|
||||
|
@ -183,9 +188,14 @@ public class TestFieldNormModifier extends TestCase {
|
|||
float lastScore = 0.0f;
|
||||
|
||||
// default similarity should return the same score for all documents for this query
|
||||
searcher.search(new TermQuery(new Term("untokfield", "20061212")), new HitCollector() {
|
||||
searcher.search(new TermQuery(new Term("untokfield", "20061212")), new MultiReaderHitCollector() {
|
||||
private int docBase = -1;
|
||||
private int lastMax;
|
||||
public final void collect(int doc, float score) {
|
||||
scores[doc] = score;
|
||||
scores[doc + docBase] = score;
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
this.docBase = docBase;
|
||||
}
|
||||
});
|
||||
searcher.close();
|
||||
|
|
|
@ -23,10 +23,10 @@ import org.apache.lucene.index.Term;
|
|||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.MultiReaderHitCollector;
|
||||
import org.apache.lucene.search.Similarity;
|
||||
import org.apache.lucene.search.DefaultSimilarity;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.search.HitCollector;
|
||||
import org.apache.lucene.store.RAMDirectory;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.analysis.SimpleAnalyzer;
|
||||
|
@ -60,8 +60,6 @@ public class TestLengthNormModifier extends TestCase {
|
|||
IndexWriter writer = new
|
||||
IndexWriter(store, new SimpleAnalyzer(), true);
|
||||
|
||||
IndexSearcher searcher;
|
||||
|
||||
for (int i = 0; i < NUM_DOCS; i++) {
|
||||
Document d = new Document();
|
||||
d.add(new Field("field", "word",
|
||||
|
@ -130,15 +128,17 @@ public class TestLengthNormModifier extends TestCase {
|
|||
float lastScore = 0.0f;
|
||||
|
||||
// default similarity should put docs with shorter length first
|
||||
searcher = new IndexSearcher(store);
|
||||
searcher.search
|
||||
(new TermQuery(new Term("field", "word")),
|
||||
new HitCollector() {
|
||||
public final void collect(int doc, float score) {
|
||||
scores[doc] = score;
|
||||
}
|
||||
});
|
||||
searcher.close();
|
||||
searcher = new IndexSearcher(store);
|
||||
searcher.search(new TermQuery(new Term("field", "word")), new MultiReaderHitCollector() {
|
||||
private int docBase = -1;
|
||||
public final void collect(int doc, float score) {
|
||||
scores[doc + docBase] = score;
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
this.docBase = docBase;
|
||||
}
|
||||
});
|
||||
searcher.close();
|
||||
|
||||
lastScore = Float.MAX_VALUE;
|
||||
for (int i = 0; i < NUM_DOCS; i++) {
|
||||
|
@ -159,14 +159,16 @@ public class TestLengthNormModifier extends TestCase {
|
|||
|
||||
// new norm (with default similarity) should put longer docs first
|
||||
searcher = new IndexSearcher(store);
|
||||
searcher.search
|
||||
(new TermQuery(new Term("field", "word")),
|
||||
new HitCollector() {
|
||||
public final void collect(int doc, float score) {
|
||||
scores[doc] = score;
|
||||
}
|
||||
});
|
||||
searcher.close();
|
||||
searcher.search(new TermQuery(new Term("field", "word")), new MultiReaderHitCollector() {
|
||||
private int docBase = -1;
|
||||
public final void collect(int doc, float score) {
|
||||
scores[doc + docBase] = score;
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
this.docBase = docBase;
|
||||
}
|
||||
});
|
||||
searcher.close();
|
||||
|
||||
lastScore = 0.0f;
|
||||
for (int i = 0; i < NUM_DOCS; i++) {
|
||||
|
|
|
@ -34,7 +34,7 @@ import org.apache.lucene.search.IndexSearcher;
|
|||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.ScoreDoc;
|
||||
import org.apache.lucene.search.Searcher;
|
||||
import org.apache.lucene.search.TopDocCollector;
|
||||
import org.apache.lucene.search.TopScoreDocCollector;
|
||||
|
||||
/** Simple command-line based search demo. */
|
||||
public class SearchFiles {
|
||||
|
@ -193,7 +193,7 @@ public class SearchFiles {
|
|||
int hitsPerPage, boolean raw, boolean interactive) throws IOException {
|
||||
|
||||
// Collect enough docs to show 5 pages
|
||||
TopDocCollector collector = new TopDocCollector(5 * hitsPerPage);
|
||||
TopScoreDocCollector collector = new TopScoreDocCollector(5 * hitsPerPage);
|
||||
searcher.search(query, collector);
|
||||
ScoreDoc[] hits = collector.topDocs().scoreDocs;
|
||||
|
||||
|
@ -212,7 +212,7 @@ public class SearchFiles {
|
|||
break;
|
||||
}
|
||||
|
||||
collector = new TopDocCollector(numTotalHits);
|
||||
collector = new TopScoreDocCollector(numTotalHits);
|
||||
searcher.search(query, collector);
|
||||
hits = collector.topDocs().scoreDocs;
|
||||
}
|
||||
|
|
|
@ -232,4 +232,8 @@ public class FilterIndexReader extends IndexReader {
|
|||
ensureOpen();
|
||||
return in.isOptimized();
|
||||
}
|
||||
|
||||
public IndexReader[] getSequentialSubReaders() {
|
||||
return in.getSequentialSubReaders();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1136,4 +1136,15 @@ public abstract class IndexReader {
|
|||
public static Collection listCommits(Directory dir) throws IOException {
|
||||
return DirectoryIndexReader.listCommits(dir);
|
||||
}
|
||||
|
||||
/** Returns the sequential sub readers that this reader is
|
||||
* logically composed of. IndexSearcher uses this API to
|
||||
* drive searching by one sub reader at a time. If this
|
||||
* reader is not composed of sequential child readers, it
|
||||
* should return null. If this method returns an empty
|
||||
* array, that means this reader is a null reader (for
|
||||
* example a MultiReader that has no sub readers).*/
|
||||
public IndexReader[] getSequentialSubReaders() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -364,8 +364,7 @@ public class MultiReader extends IndexReader {
|
|||
throw new UnsupportedOperationException("MultiReader does not support this method.");
|
||||
}
|
||||
|
||||
// for testing
|
||||
IndexReader[] getSubReaders() {
|
||||
public IndexReader[] getSequentialSubReaders() {
|
||||
return subReaders;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -431,8 +431,7 @@ class MultiSegmentReader extends DirectoryIndexReader {
|
|||
return fieldSet;
|
||||
}
|
||||
|
||||
// for testing
|
||||
SegmentReader[] getSubReaders() {
|
||||
public IndexReader[] getSequentialSubReaders() {
|
||||
return subReaders;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.apache.lucene.search;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
/* Description from Doug Cutting (excerpted from
|
||||
* LUCENE-1483):
|
||||
*
|
||||
|
@ -79,11 +80,11 @@ final class BooleanScorer extends Scorer {
|
|||
public boolean done;
|
||||
public boolean required = false;
|
||||
public boolean prohibited = false;
|
||||
public HitCollector collector;
|
||||
public MultiReaderHitCollector collector;
|
||||
public SubScorer next;
|
||||
|
||||
public SubScorer(Scorer scorer, boolean required, boolean prohibited,
|
||||
HitCollector collector, SubScorer next)
|
||||
MultiReaderHitCollector collector, SubScorer next)
|
||||
throws IOException {
|
||||
this.scorer = scorer;
|
||||
this.done = !scorer.next();
|
||||
|
@ -248,12 +249,12 @@ final class BooleanScorer extends Scorer {
|
|||
|
||||
public final int size() { return SIZE; }
|
||||
|
||||
public HitCollector newCollector(int mask) {
|
||||
public MultiReaderHitCollector newCollector(int mask) {
|
||||
return new Collector(mask, this);
|
||||
}
|
||||
}
|
||||
|
||||
static final class Collector extends HitCollector {
|
||||
static final class Collector extends MultiReaderHitCollector {
|
||||
private BucketTable bucketTable;
|
||||
private int mask;
|
||||
public Collector(int mask, BucketTable bucketTable) {
|
||||
|
@ -281,6 +282,9 @@ final class BooleanScorer extends Scorer {
|
|||
bucket.coord++; // increment coord
|
||||
}
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
// not needed by this implementation
|
||||
}
|
||||
}
|
||||
|
||||
public boolean skipTo(int target) {
|
||||
|
|
|
@ -0,0 +1,869 @@
|
|||
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.text.Collator;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.search.ExtendedFieldCache.DoubleParser;
|
||||
import org.apache.lucene.search.ExtendedFieldCache.LongParser;
|
||||
import org.apache.lucene.search.FieldCache.ByteParser;
|
||||
import org.apache.lucene.search.FieldCache.FloatParser;
|
||||
import org.apache.lucene.search.FieldCache.IntParser;
|
||||
import org.apache.lucene.search.FieldCache.ShortParser;
|
||||
import org.apache.lucene.search.FieldCache.StringIndex;
|
||||
|
||||
/**
|
||||
* A FieldComparator compares hits across multiple IndexReaders.
|
||||
*
|
||||
* A comparator can compare a hit at hit 'slot a' with hit 'slot b',
|
||||
* compare a hit on 'doc i' with hit 'slot a', or copy a hit at 'doc i'
|
||||
* to 'slot a'. Each slot refers to a hit while each doc refers to the
|
||||
* current IndexReader.
|
||||
*
|
||||
* <b>NOTE:</b> This API is experimental and might change in
|
||||
* incompatible ways in the next release.
|
||||
*/
|
||||
public abstract class FieldComparator {
|
||||
|
||||
/** Parses field's values as byte (using {@link
|
||||
* FieldCache#getBytes} and sorts by ascending value */
|
||||
public static final class ByteComparator extends FieldComparator {
|
||||
private final byte[] values;
|
||||
private byte[] currentReaderValues;
|
||||
private final String field;
|
||||
private ByteParser parser;
|
||||
private byte bottom;
|
||||
|
||||
ByteComparator(int numHits, String field, FieldCache.Parser parser) {
|
||||
values = new byte[numHits];
|
||||
this.field = field;
|
||||
this.parser = (ByteParser) parser;
|
||||
}
|
||||
|
||||
public int compare(int slot1, int slot2) {
|
||||
return values[slot1] - values[slot2];
|
||||
}
|
||||
|
||||
public int compareBottom(int doc, float score) {
|
||||
return bottom - currentReaderValues[doc];
|
||||
}
|
||||
|
||||
public void copy(int slot, int doc, float score) {
|
||||
values[slot] = currentReaderValues[doc];
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException {
|
||||
currentReaderValues = parser != null ? ExtendedFieldCache.EXT_DEFAULT
|
||||
.getBytes(reader, field, parser) : ExtendedFieldCache.EXT_DEFAULT
|
||||
.getBytes(reader, field);
|
||||
}
|
||||
|
||||
public void setBottom(final int bottom) {
|
||||
this.bottom = values[bottom];
|
||||
}
|
||||
|
||||
public int sortType() {
|
||||
return SortField.BYTE;
|
||||
}
|
||||
|
||||
public Comparable value(int slot) {
|
||||
return new Byte(values[slot]);
|
||||
}
|
||||
};
|
||||
|
||||
/** Sorts by ascending docID */
|
||||
public static final class DocComparator extends FieldComparator {
|
||||
private final int[] docIDs;
|
||||
private int docBase;
|
||||
private int bottom;
|
||||
|
||||
DocComparator(int numHits) {
|
||||
docIDs = new int[numHits];
|
||||
}
|
||||
|
||||
public int compare(int slot1, int slot2) {
|
||||
// No overflow risk because docIDs are non-negative
|
||||
return docIDs[slot1] - docIDs[slot2];
|
||||
}
|
||||
|
||||
public int compareBottom(int doc, float score) {
|
||||
// No overflow risk because docIDs are non-negative
|
||||
return bottom - (docBase + doc);
|
||||
}
|
||||
|
||||
public void copy(int slot, int doc, float score) {
|
||||
docIDs[slot] = docBase + doc;
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) {
|
||||
// TODO: can we "map" our docIDs to the current
|
||||
// reader? saves having to then subtract on every
|
||||
// compare call
|
||||
this.docBase = docBase;
|
||||
}
|
||||
|
||||
public void setBottom(final int bottom) {
|
||||
this.bottom = docIDs[bottom];
|
||||
}
|
||||
|
||||
public int sortType() {
|
||||
return SortField.DOC;
|
||||
}
|
||||
|
||||
public Comparable value(int slot) {
|
||||
return new Integer(docIDs[slot]);
|
||||
}
|
||||
};
|
||||
|
||||
/** Parses field's values as double (using {@link
|
||||
* ExtendedFieldCache#getDoubles} and sorts by ascending value */
|
||||
public static final class DoubleComparator extends FieldComparator {
|
||||
private final double[] values;
|
||||
private double[] currentReaderValues;
|
||||
private final String field;
|
||||
private DoubleParser parser;
|
||||
private double bottom;
|
||||
|
||||
DoubleComparator(int numHits, String field, FieldCache.Parser parser) {
|
||||
values = new double[numHits];
|
||||
this.field = field;
|
||||
this.parser = (DoubleParser) parser;
|
||||
}
|
||||
|
||||
public int compare(int slot1, int slot2) {
|
||||
final double v1 = values[slot1];
|
||||
final double v2 = values[slot2];
|
||||
if (v1 > v2) {
|
||||
return 1;
|
||||
} else if (v1 < v2) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int compareBottom(int doc, float score) {
|
||||
final double v2 = currentReaderValues[doc];
|
||||
if (bottom > v2) {
|
||||
return 1;
|
||||
} else if (bottom < v2) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void copy(int slot, int doc, float score) {
|
||||
values[slot] = currentReaderValues[doc];
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException {
|
||||
currentReaderValues = parser != null ? ExtendedFieldCache.EXT_DEFAULT
|
||||
.getDoubles(reader, field, parser) : ExtendedFieldCache.EXT_DEFAULT
|
||||
.getDoubles(reader, field);
|
||||
}
|
||||
|
||||
public void setBottom(final int bottom) {
|
||||
this.bottom = values[bottom];
|
||||
}
|
||||
|
||||
public int sortType() {
|
||||
return SortField.DOUBLE;
|
||||
}
|
||||
|
||||
public Comparable value(int slot) {
|
||||
return new Double(values[slot]);
|
||||
}
|
||||
};
|
||||
|
||||
/** Parses field's values as float (using {@link
|
||||
* FieldCache#getFloats} and sorts by ascending value */
|
||||
public static final class FloatComparator extends FieldComparator {
|
||||
private final float[] values;
|
||||
private float[] currentReaderValues;
|
||||
private final String field;
|
||||
private FloatParser parser;
|
||||
private float bottom;
|
||||
|
||||
FloatComparator(int numHits, String field, FieldCache.Parser parser) {
|
||||
values = new float[numHits];
|
||||
this.field = field;
|
||||
this.parser = (FloatParser) parser;
|
||||
}
|
||||
|
||||
public int compare(int slot1, int slot2) {
|
||||
// TODO: are there sneaky non-branch ways to compute
|
||||
// sign of float?
|
||||
final float v1 = values[slot1];
|
||||
final float v2 = values[slot2];
|
||||
if (v1 > v2) {
|
||||
return 1;
|
||||
} else if (v1 < v2) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int compareBottom(int doc, float score) {
|
||||
// TODO: are there sneaky non-branch ways to compute
|
||||
// sign of float?
|
||||
final float v2 = currentReaderValues[doc];
|
||||
if (bottom > v2) {
|
||||
return 1;
|
||||
} else if (bottom < v2) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void copy(int slot, int doc, float score) {
|
||||
values[slot] = currentReaderValues[doc];
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException {
|
||||
currentReaderValues = parser != null ? FieldCache.DEFAULT.getFloats(
|
||||
reader, field, parser) : FieldCache.DEFAULT.getFloats(reader, field);
|
||||
}
|
||||
|
||||
public void setBottom(final int bottom) {
|
||||
this.bottom = values[bottom];
|
||||
}
|
||||
|
||||
public int sortType() {
|
||||
return SortField.FLOAT;
|
||||
}
|
||||
|
||||
public Comparable value(int slot) {
|
||||
return new Float(values[slot]);
|
||||
}
|
||||
};
|
||||
|
||||
/** Parses field's values as int (using {@link
|
||||
* FieldCache#getInts} and sorts by ascending value */
|
||||
public static final class IntComparator extends FieldComparator {
|
||||
private final int[] values;
|
||||
private int[] currentReaderValues;
|
||||
private final String field;
|
||||
private IntParser parser;
|
||||
private int bottom; // Value of bottom of queue
|
||||
|
||||
IntComparator(int numHits, String field, FieldCache.Parser parser) {
|
||||
values = new int[numHits];
|
||||
this.field = field;
|
||||
this.parser = (IntParser) parser;
|
||||
}
|
||||
|
||||
public int compare(int slot1, int slot2) {
|
||||
// TODO: there are sneaky non-branch ways to compute
|
||||
// -1/+1/0 sign
|
||||
// Cannot return values[slot1] - values[slot2] because that
|
||||
// may overflow
|
||||
final int v1 = values[slot1];
|
||||
final int v2 = values[slot2];
|
||||
if (v1 > v2) {
|
||||
return 1;
|
||||
} else if (v1 < v2) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int compareBottom(int doc, float score) {
|
||||
// TODO: there are sneaky non-branch ways to compute
|
||||
// -1/+1/0 sign
|
||||
// Cannot return bottom - values[slot2] because that
|
||||
// may overflow
|
||||
final int v2 = currentReaderValues[doc];
|
||||
if (bottom > v2) {
|
||||
return 1;
|
||||
} else if (bottom < v2) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void copy(int slot, int doc, float score) {
|
||||
values[slot] = currentReaderValues[doc];
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException {
|
||||
currentReaderValues = parser != null ? FieldCache.DEFAULT.getInts(reader,
|
||||
field, parser) : FieldCache.DEFAULT.getInts(reader, field);
|
||||
}
|
||||
|
||||
public void setBottom(final int bottom) {
|
||||
this.bottom = values[bottom];
|
||||
}
|
||||
|
||||
public int sortType() {
|
||||
return SortField.INT;
|
||||
}
|
||||
|
||||
public Comparable value(int slot) {
|
||||
return new Integer(values[slot]);
|
||||
}
|
||||
};
|
||||
|
||||
/** Parses field's values as long (using {@link
|
||||
* ExtendedFieldCache#getLongs} and sorts by ascending value */
|
||||
public static final class LongComparator extends FieldComparator {
|
||||
private final long[] values;
|
||||
private long[] currentReaderValues;
|
||||
private final String field;
|
||||
private LongParser parser;
|
||||
private long bottom;
|
||||
|
||||
LongComparator(int numHits, String field, FieldCache.Parser parser) {
|
||||
values = new long[numHits];
|
||||
this.field = field;
|
||||
this.parser = (LongParser) parser;
|
||||
}
|
||||
|
||||
public int compare(int slot1, int slot2) {
|
||||
// TODO: there are sneaky non-branch ways to compute
|
||||
// -1/+1/0 sign
|
||||
final long v1 = values[slot1];
|
||||
final long v2 = values[slot2];
|
||||
if (v1 > v2) {
|
||||
return 1;
|
||||
} else if (v1 < v2) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int compareBottom(int doc, float score) {
|
||||
// TODO: there are sneaky non-branch ways to compute
|
||||
// -1/+1/0 sign
|
||||
final long v2 = currentReaderValues[doc];
|
||||
if (bottom > v2) {
|
||||
return 1;
|
||||
} else if (bottom < v2) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void copy(int slot, int doc, float score) {
|
||||
values[slot] = currentReaderValues[doc];
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException {
|
||||
currentReaderValues = parser != null ? ExtendedFieldCache.EXT_DEFAULT
|
||||
.getLongs(reader, field, parser) : ExtendedFieldCache.EXT_DEFAULT
|
||||
.getLongs(reader, field);
|
||||
}
|
||||
|
||||
public void setBottom(final int bottom) {
|
||||
this.bottom = values[bottom];
|
||||
}
|
||||
|
||||
public int sortType() {
|
||||
return SortField.LONG;
|
||||
}
|
||||
|
||||
public Comparable value(int slot) {
|
||||
return new Long(values[slot]);
|
||||
}
|
||||
};
|
||||
|
||||
/** Sorts by descending relevance. NOTE: if you are
|
||||
* sorting only by descending relevance and then
|
||||
* secondarily by ascending docID, peformance is faster
|
||||
* using {@link TopScoreDocCollector} directly (which {@link
|
||||
* IndexSearcher#search} uses when no {@link Sort} is
|
||||
* specified). */
|
||||
public static final class RelevanceComparator extends FieldComparator {
|
||||
private final float[] scores;
|
||||
private float bottom;
|
||||
|
||||
RelevanceComparator(int numHits) {
|
||||
scores = new float[numHits];
|
||||
}
|
||||
|
||||
public int compare(int slot1, int slot2) {
|
||||
final float score1 = scores[slot1];
|
||||
final float score2 = scores[slot2];
|
||||
if (score1 > score2) {
|
||||
return -1;
|
||||
} else if (score1 < score2) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int compareBottom(int doc, float score) {
|
||||
if (bottom > score) {
|
||||
return -1;
|
||||
} else if (bottom < score) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void copy(int slot, int doc, float score) {
|
||||
scores[slot] = score;
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) {
|
||||
}
|
||||
|
||||
public void setBottom(final int bottom) {
|
||||
this.bottom = scores[bottom];
|
||||
}
|
||||
|
||||
public int sortType() {
|
||||
return SortField.SCORE;
|
||||
}
|
||||
|
||||
public Comparable value(int slot) {
|
||||
return new Float(scores[slot]);
|
||||
}
|
||||
};
|
||||
|
||||
/** Parses field's values as short (using {@link
|
||||
* FieldCache#getShorts} and sorts by ascending value */
|
||||
public static final class ShortComparator extends FieldComparator {
|
||||
private final short[] values;
|
||||
private short[] currentReaderValues;
|
||||
private final String field;
|
||||
private ShortParser parser;
|
||||
private short bottom;
|
||||
|
||||
ShortComparator(int numHits, String field, FieldCache.Parser parser) {
|
||||
values = new short[numHits];
|
||||
this.field = field;
|
||||
this.parser = (ShortParser) parser;
|
||||
}
|
||||
|
||||
public int compare(int slot1, int slot2) {
|
||||
return values[slot1] - values[slot2];
|
||||
}
|
||||
|
||||
public int compareBottom(int doc, float score) {
|
||||
return bottom - currentReaderValues[doc];
|
||||
}
|
||||
|
||||
public void copy(int slot, int doc, float score) {
|
||||
values[slot] = currentReaderValues[doc];
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException {
|
||||
currentReaderValues = parser != null ? ExtendedFieldCache.EXT_DEFAULT
|
||||
.getShorts(reader, field, parser) : ExtendedFieldCache.EXT_DEFAULT
|
||||
.getShorts(reader, field);
|
||||
}
|
||||
|
||||
public void setBottom(final int bottom) {
|
||||
this.bottom = values[bottom];
|
||||
}
|
||||
|
||||
public int sortType() {
|
||||
return SortField.BYTE;
|
||||
}
|
||||
|
||||
public Comparable value(int slot) {
|
||||
return new Short(values[slot]);
|
||||
}
|
||||
};
|
||||
|
||||
/** Sorts by a field's value using the Collator for a
|
||||
* given Locale.*/
|
||||
public static final class StringComparatorLocale extends FieldComparator {
|
||||
|
||||
private final String[] values;
|
||||
private String[] currentReaderValues;
|
||||
private final String field;
|
||||
final Collator collator;
|
||||
private String bottom;
|
||||
|
||||
StringComparatorLocale(int numHits, String field, Locale locale) {
|
||||
values = new String[numHits];
|
||||
this.field = field;
|
||||
collator = Collator.getInstance(locale);
|
||||
}
|
||||
|
||||
public int compare(int slot1, int slot2) {
|
||||
final String val1 = values[slot1];
|
||||
final String val2 = values[slot2];
|
||||
if (val1 == null) {
|
||||
if (val2 == null) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
} else if (val2 == null) {
|
||||
return 1;
|
||||
}
|
||||
return collator.compare(val1, val2);
|
||||
}
|
||||
|
||||
public int compareBottom(int doc, float score) {
|
||||
final String val2 = currentReaderValues[doc];
|
||||
if (bottom == null) {
|
||||
if (val2 == null) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
} else if (val2 == null) {
|
||||
return 1;
|
||||
}
|
||||
return collator.compare(bottom, val2);
|
||||
}
|
||||
|
||||
public void copy(int slot, int doc, float score) {
|
||||
values[slot] = currentReaderValues[doc];
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException {
|
||||
currentReaderValues = ExtendedFieldCache.EXT_DEFAULT.getStrings(reader,
|
||||
field);
|
||||
}
|
||||
|
||||
public void setBottom(final int bottom) {
|
||||
this.bottom = values[bottom];
|
||||
}
|
||||
|
||||
public int sortType() {
|
||||
return SortField.STRING;
|
||||
}
|
||||
|
||||
public Comparable value(int slot) {
|
||||
return values[slot];
|
||||
}
|
||||
};
|
||||
|
||||
// NOTE: there were a number of other interesting String
|
||||
// comparators explored, but this one seemed to perform
|
||||
// best all around. See LUCENE-1483 for details.
|
||||
public static final class StringOrdValComparator extends FieldComparator {
|
||||
|
||||
private final int[] ords;
|
||||
private final String[] values;
|
||||
private final int[] readerGen;
|
||||
|
||||
private int currentReaderGen = -1;
|
||||
private String[] lookup;
|
||||
private int[] order;
|
||||
private final String field;
|
||||
|
||||
private int bottomSlot = -1;
|
||||
private int bottomOrd;
|
||||
private String bottomValue;
|
||||
private final boolean reversed;
|
||||
private final int sortPos;
|
||||
|
||||
public StringOrdValComparator(int numHits, String field, int sortPos, boolean reversed) {
|
||||
ords = new int[numHits];
|
||||
values = new String[numHits];
|
||||
readerGen = new int[numHits];
|
||||
this.sortPos = sortPos;
|
||||
this.reversed = reversed;
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public int compare(int slot1, int slot2) {
|
||||
if (readerGen[slot1] == readerGen[slot2]) {
|
||||
int cmp = ords[slot1] - ords[slot2];
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
}
|
||||
|
||||
final String val1 = values[slot1];
|
||||
final String val2 = values[slot2];
|
||||
if (val1 == null) {
|
||||
if (val2 == null) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
} else if (val2 == null) {
|
||||
return 1;
|
||||
}
|
||||
return val1.compareTo(val2);
|
||||
}
|
||||
|
||||
public int compareBottom(int doc, float score) {
|
||||
assert bottomSlot != -1;
|
||||
int order = this.order[doc];
|
||||
final int cmp = bottomOrd - order;
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
|
||||
final String val2 = lookup[order];
|
||||
if (bottomValue == null) {
|
||||
if (val2 == null) {
|
||||
return 0;
|
||||
}
|
||||
// bottom wins
|
||||
return -1;
|
||||
} else if (val2 == null) {
|
||||
// doc wins
|
||||
return 1;
|
||||
}
|
||||
return bottomValue.compareTo(val2);
|
||||
}
|
||||
|
||||
private void convert(int slot) {
|
||||
readerGen[slot] = currentReaderGen;
|
||||
int index = 0;
|
||||
String value = values[slot];
|
||||
if (value == null) {
|
||||
ords[slot] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (sortPos == 0 && bottomSlot != -1 && bottomSlot != slot) {
|
||||
// Since we are the primary sort, the entries in the
|
||||
// queue are bounded by bottomOrd:
|
||||
assert bottomOrd < lookup.length;
|
||||
if (reversed) {
|
||||
index = binarySearch(lookup, value, bottomOrd, lookup.length-1);
|
||||
} else {
|
||||
index = binarySearch(lookup, value, 0, bottomOrd);
|
||||
}
|
||||
} else {
|
||||
// Full binary search
|
||||
index = binarySearch(lookup, value);
|
||||
}
|
||||
|
||||
if (index < 0) {
|
||||
index = -index - 2;
|
||||
}
|
||||
ords[slot] = index;
|
||||
}
|
||||
|
||||
public void copy(int slot, int doc, float score) {
|
||||
final int ord = order[doc];
|
||||
ords[slot] = ord;
|
||||
assert ord >= 0;
|
||||
values[slot] = lookup[ord];
|
||||
readerGen[slot] = currentReaderGen;
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException {
|
||||
StringIndex currentReaderValues = ExtendedFieldCache.EXT_DEFAULT.getStringIndex(reader, field);
|
||||
currentReaderGen++;
|
||||
order = currentReaderValues.order;
|
||||
lookup = currentReaderValues.lookup;
|
||||
assert lookup.length > 0;
|
||||
if (bottomSlot != -1) {
|
||||
convert(bottomSlot);
|
||||
bottomOrd = ords[bottomSlot];
|
||||
}
|
||||
}
|
||||
|
||||
public void setBottom(final int bottom) {
|
||||
bottomSlot = bottom;
|
||||
if (readerGen[bottom] != currentReaderGen) {
|
||||
convert(bottomSlot);
|
||||
}
|
||||
bottomOrd = ords[bottom];
|
||||
assert bottomOrd >= 0;
|
||||
assert bottomOrd < lookup.length;
|
||||
bottomValue = values[bottom];
|
||||
}
|
||||
|
||||
public int sortType() {
|
||||
return SortField.STRING;
|
||||
}
|
||||
|
||||
public Comparable value(int slot) {
|
||||
return values[slot];
|
||||
}
|
||||
|
||||
public String[] getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public int getBottomSlot() {
|
||||
return bottomSlot;
|
||||
}
|
||||
|
||||
public String getField() {
|
||||
return field;
|
||||
}
|
||||
};
|
||||
|
||||
/** Sorts by field's natural String sort order. All
|
||||
* comparisons are done using String.compareTo, which is
|
||||
* slow for medium to large result sets but possibly
|
||||
* very fast for very small results sets. */
|
||||
public static final class StringValComparator extends FieldComparator {
|
||||
|
||||
private String[] values;
|
||||
private String[] currentReaderValues;
|
||||
private final String field;
|
||||
private String bottom;
|
||||
|
||||
StringValComparator(int numHits, String field) {
|
||||
values = new String[numHits];
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public int compare(int slot1, int slot2) {
|
||||
final String val1 = values[slot1];
|
||||
final String val2 = values[slot2];
|
||||
if (val1 == null) {
|
||||
if (val2 == null) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
} else if (val2 == null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return val1.compareTo(val2);
|
||||
}
|
||||
|
||||
public int compareBottom(int doc, float score) {
|
||||
final String val2 = currentReaderValues[doc];
|
||||
if (bottom == null) {
|
||||
if (val2 == null) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
} else if (val2 == null) {
|
||||
return 1;
|
||||
}
|
||||
return bottom.compareTo(val2);
|
||||
}
|
||||
|
||||
public void copy(int slot, int doc, float score) {
|
||||
values[slot] = currentReaderValues[doc];
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException {
|
||||
currentReaderValues = ExtendedFieldCache.EXT_DEFAULT.getStrings(reader,
|
||||
field);
|
||||
}
|
||||
|
||||
public void setBottom(final int bottom) {
|
||||
this.bottom = values[bottom];
|
||||
}
|
||||
|
||||
public int sortType() {
|
||||
return SortField.STRING_VAL;
|
||||
}
|
||||
|
||||
public Comparable value(int slot) {
|
||||
return values[slot];
|
||||
}
|
||||
};
|
||||
|
||||
final protected static int binarySearch(String[] a, String key) {
|
||||
return binarySearch(a, key, 0, a.length-1);
|
||||
};
|
||||
|
||||
final protected static int binarySearch(String[] a, String key, int low, int high) {
|
||||
|
||||
while (low <= high) {
|
||||
int mid = (low + high) >>> 1;
|
||||
String midVal = a[mid];
|
||||
int cmp;
|
||||
if (midVal != null) {
|
||||
cmp = midVal.compareTo(key);
|
||||
} else {
|
||||
cmp = -1;
|
||||
}
|
||||
|
||||
if (cmp < 0)
|
||||
low = mid + 1;
|
||||
else if (cmp > 0)
|
||||
high = mid - 1;
|
||||
else
|
||||
return mid;
|
||||
}
|
||||
return -(low + 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Compare hit at slot1 with hit at slot2. Return
|
||||
*
|
||||
* @param slot1 first slot to compare
|
||||
* @param slot2 second slot to compare
|
||||
* @return any N < 0 if slot2's value is sorted after
|
||||
* slot1, any N > 0 if the slot2's value is sorted before
|
||||
* slot1 and 0 if they are equal
|
||||
*/
|
||||
public abstract int compare(int slot1, int slot2);
|
||||
|
||||
/**
|
||||
* Set the bottom queue slot, ie the "weakest" (sorted
|
||||
* last) entry in the queue.
|
||||
*
|
||||
* @param slot the currently weakest (sorted lost) slot in the queue
|
||||
*/
|
||||
public abstract void setBottom(final int slot);
|
||||
|
||||
/**
|
||||
* Compare the bottom of the queue with doc. This will
|
||||
* only invoked after setBottom has been called.
|
||||
*
|
||||
* @param doc that was hit
|
||||
* @param score of the hit
|
||||
* @return any N < 0 if the doc's value is sorted after
|
||||
* the bottom entry (not competitive), any N > 0 if the
|
||||
* doc's value is sorted before the bottom entry and 0 if
|
||||
* they are equal.
|
||||
*/
|
||||
public abstract int compareBottom(int doc, float score);
|
||||
|
||||
/**
|
||||
* Copy hit (doc,score) to hit slot.
|
||||
*
|
||||
* @param slot which slot to copy the hit to
|
||||
* @param doc docID relative to current reader
|
||||
* @param score hit score
|
||||
*/
|
||||
public abstract void copy(int slot, int doc, float score);
|
||||
|
||||
/**
|
||||
* Set a new Reader. All doc correspond to the current Reader.
|
||||
*
|
||||
* @param reader current reader
|
||||
* @param docBase docBase of this reader
|
||||
* @throws IOException
|
||||
* @throws IOException
|
||||
*/
|
||||
public abstract void setNextReader(IndexReader reader, int docBase, int numSlotsFull) throws IOException;
|
||||
|
||||
/**
|
||||
* @return SortField.TYPE
|
||||
*/
|
||||
public abstract int sortType();
|
||||
|
||||
/**
|
||||
* Return the actual value at slot.
|
||||
*
|
||||
* @param slot the value
|
||||
* @return value in this slot upgraded to Comparable
|
||||
*/
|
||||
public abstract Comparable value(int slot);
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
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 org.apache.lucene.index.IndexReader;
|
||||
|
||||
/**
|
||||
* Provides a {@link FieldComparator} for custom field sorting.
|
||||
*
|
||||
* <b>NOTE:</b> This API is experimental and might change in
|
||||
* incompatible ways in the next release.
|
||||
*
|
||||
*/
|
||||
public abstract class FieldComparatorSource {
|
||||
|
||||
/**
|
||||
* Creates a comparator for the field in the given index.
|
||||
*
|
||||
* @param fieldname
|
||||
* Name of the field to create comparator for.
|
||||
* @return FieldComparator.
|
||||
* @throws IOException
|
||||
* If an error occurs reading the index.
|
||||
*/
|
||||
abstract FieldComparator newComparator(String fieldname, IndexReader[] subReaders, int numHits, int sortPos, boolean reversed)
|
||||
throws IOException;
|
||||
}
|
|
@ -34,6 +34,7 @@ import java.util.Locale;
|
|||
* @version $Id$
|
||||
* @see Searcher#search(Query,Filter,int,Sort)
|
||||
* @see FieldCache
|
||||
* @deprecated see {@link FieldValueHitQueue}
|
||||
*/
|
||||
public class FieldSortedHitQueue
|
||||
extends PriorityQueue {
|
||||
|
@ -52,18 +53,10 @@ extends PriorityQueue {
|
|||
this.fields = new SortField[n];
|
||||
for (int i=0; i<n; ++i) {
|
||||
String fieldname = fields[i].getField();
|
||||
// AUTO is resolved before we are called
|
||||
assert fields[i].getType() != SortField.AUTO;
|
||||
comparators[i] = getCachedComparator (reader, fieldname, fields[i].getType(), fields[i].getParser(), fields[i].getLocale(), fields[i].getFactory());
|
||||
// new SortField instances must only be created when auto-detection is in use
|
||||
if (fields[i].getType() == SortField.AUTO) {
|
||||
if (comparators[i].sortType() == SortField.STRING) {
|
||||
this.fields[i] = new SortField (fieldname, fields[i].getLocale(), fields[i].getReverse());
|
||||
} else {
|
||||
this.fields[i] = new SortField (fieldname, comparators[i].sortType(), fields[i].getReverse());
|
||||
}
|
||||
} else {
|
||||
assert comparators[i].sortType() == fields[i].getType();
|
||||
this.fields[i] = fields[i];
|
||||
}
|
||||
this.fields[i] = fields[i];
|
||||
}
|
||||
initialize (size);
|
||||
}
|
||||
|
@ -194,9 +187,6 @@ extends PriorityQueue {
|
|||
}
|
||||
ScoreDocComparator comparator;
|
||||
switch (type) {
|
||||
case SortField.AUTO:
|
||||
comparator = comparatorAuto (reader, fieldname);
|
||||
break;
|
||||
case SortField.INT:
|
||||
comparator = comparatorInt (reader, fieldname, (FieldCache.IntParser)parser);
|
||||
break;
|
||||
|
@ -222,6 +212,8 @@ extends PriorityQueue {
|
|||
case SortField.CUSTOM:
|
||||
comparator = factory.newComparator (reader, fieldname);
|
||||
break;
|
||||
case SortField.AUTO:
|
||||
throw new IllegalStateException("Auto should be resolved before now");
|
||||
default:
|
||||
throw new RuntimeException ("unknown field type: "+type);
|
||||
}
|
||||
|
@ -496,33 +488,4 @@ extends PriorityQueue {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a comparator for sorting hits according to values in the given field.
|
||||
* The terms in the field are looked at to determine whether they contain integers,
|
||||
* floats or strings. Once the type is determined, one of the other static methods
|
||||
* in this class is called to get the comparator.
|
||||
* @param reader Index to use.
|
||||
* @param fieldname Fieldable containg values.
|
||||
* @return Comparator for sorting hits.
|
||||
* @throws IOException If an error occurs reading the index.
|
||||
*/
|
||||
static ScoreDocComparator comparatorAuto (final IndexReader reader, final String fieldname)
|
||||
throws IOException {
|
||||
final String field = fieldname.intern();
|
||||
Object lookupArray = ExtendedFieldCache.EXT_DEFAULT.getAuto (reader, field);
|
||||
if (lookupArray instanceof FieldCache.StringIndex) {
|
||||
return comparatorString (reader, field);
|
||||
} else if (lookupArray instanceof int[]) {
|
||||
return comparatorInt (reader, field, null);
|
||||
} else if (lookupArray instanceof long[]) {
|
||||
return comparatorLong (reader, field, null);
|
||||
} else if (lookupArray instanceof float[]) {
|
||||
return comparatorFloat (reader, field, null);
|
||||
} else if (lookupArray instanceof String[]) {
|
||||
return comparatorString (reader, field);
|
||||
} else {
|
||||
throw new RuntimeException ("unknown data type in field '"+field+"'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,229 @@
|
|||
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 org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.index.TermEnum;
|
||||
import org.apache.lucene.util.PriorityQueue;
|
||||
|
||||
import java.io.IOException;;
|
||||
|
||||
/**
|
||||
* Expert: A hit queue for sorting by hits by terms in more than one field.
|
||||
* Uses <code>FieldCache.DEFAULT</code> for maintaining
|
||||
* internal term lookup tables.
|
||||
*
|
||||
* <b>NOTE:</b> This API is experimental and might change in
|
||||
* incompatible ways in the next release.
|
||||
*
|
||||
* @since lucene 2.9
|
||||
* @version $Id:
|
||||
* @see Searcher#search(Query,Filter,int,Sort)
|
||||
* @see FieldCache
|
||||
*/
|
||||
public class FieldValueHitQueue extends PriorityQueue {
|
||||
|
||||
final static class Entry {
|
||||
int slot;
|
||||
int docID;
|
||||
float score;
|
||||
|
||||
Entry(int slot, int docID, float score) {
|
||||
this.slot = slot;
|
||||
this.docID = docID;
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "slot:" + slot + " docID:" + docID;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a hit queue sorted by the given list of fields.
|
||||
* @param fields SortField array we are sorting by in
|
||||
* priority order (highest priority first); cannot be <code>null</code> or empty
|
||||
* @param size The number of hits to retain. Must be
|
||||
* greater than zero.
|
||||
* @param subReaders Array of IndexReaders we will search,
|
||||
* in order that they will be searched
|
||||
* @throws IOException
|
||||
*/
|
||||
public FieldValueHitQueue(SortField[] fields, int size, IndexReader[] subReaders) throws IOException {
|
||||
numComparators = fields.length;
|
||||
comparators = new FieldComparator[numComparators];
|
||||
reverseMul = new int[numComparators];
|
||||
|
||||
if (fields.length == 0) {
|
||||
throw new IllegalArgumentException("Sort must contain at least one field");
|
||||
}
|
||||
|
||||
this.fields = fields;
|
||||
for (int i=0; i<numComparators; ++i) {
|
||||
SortField field = fields[i];
|
||||
|
||||
// AUTO is resolved before we are called
|
||||
assert field.getType() != SortField.AUTO;
|
||||
|
||||
reverseMul[i] = field.reverse ? -1 : 1;
|
||||
comparators[i] = field.getComparator(subReaders, size, i, field.reverse);
|
||||
}
|
||||
|
||||
if (numComparators == 1) {
|
||||
comparator1 = comparators[0];
|
||||
reverseMul1 = reverseMul[0];
|
||||
} else {
|
||||
comparator1 = null;
|
||||
reverseMul1 = 0;
|
||||
}
|
||||
|
||||
initialize(size);
|
||||
}
|
||||
|
||||
/** Stores a comparator corresponding to each field being sorted by */
|
||||
private final FieldComparator[] comparators;
|
||||
private final FieldComparator comparator1;
|
||||
private final int numComparators;
|
||||
private final int[] reverseMul;
|
||||
private final int reverseMul1;
|
||||
|
||||
FieldComparator[] getComparators() {
|
||||
return comparators;
|
||||
}
|
||||
|
||||
int[] getReverseMul() {
|
||||
return reverseMul;
|
||||
}
|
||||
|
||||
/** Stores the sort criteria being used. */
|
||||
private final SortField[] fields;
|
||||
|
||||
/**
|
||||
* Returns whether <code>a</code> is less relevant than <code>b</code>.
|
||||
* @param a ScoreDoc
|
||||
* @param b ScoreDoc
|
||||
* @return <code>true</code> if document <code>a</code> should be sorted after document <code>b</code>.
|
||||
*/
|
||||
protected boolean lessThan (final Object a, final Object b) {
|
||||
final Entry hitA = (Entry) a;
|
||||
final Entry hitB = (Entry) b;
|
||||
|
||||
assert hitA != hitB;
|
||||
assert hitA.slot != hitB.slot;
|
||||
|
||||
if (numComparators == 1) {
|
||||
// Common case
|
||||
final int c = reverseMul1 * comparator1.compare(hitA.slot, hitB.slot);
|
||||
if (c != 0) {
|
||||
return c > 0;
|
||||
}
|
||||
} else {
|
||||
// run comparators
|
||||
for (int i=0; i<numComparators; ++i) {
|
||||
final int c = reverseMul[i] * comparators[i].compare(hitA.slot, hitB.slot);
|
||||
if (c != 0) {
|
||||
// Short circuit
|
||||
return c > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// avoid random sort order that could lead to duplicates (bug #31241):
|
||||
return hitA.docID > hitB.docID;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Given a FieldDoc object, stores the values used
|
||||
* to sort the given document. These values are not the raw
|
||||
* values out of the index, but the internal representation
|
||||
* of them. This is so the given search hit can be collated
|
||||
* by a MultiSearcher with other search hits.
|
||||
* @param doc The FieldDoc to store sort values into.
|
||||
* @return The same FieldDoc passed in.
|
||||
* @see Searchable#search(Weight,Filter,int,Sort)
|
||||
*/
|
||||
FieldDoc fillFields (final Entry entry) {
|
||||
final int n = comparators.length;
|
||||
final Comparable[] fields = new Comparable[n];
|
||||
for (int i=0; i<n; ++i)
|
||||
fields[i] = comparators[i].value(entry.slot);
|
||||
//if (maxscore > 1.0f) doc.score /= maxscore; // normalize scores
|
||||
return new FieldDoc(entry.docID,
|
||||
entry.score,
|
||||
fields);
|
||||
}
|
||||
|
||||
|
||||
/** Returns the SortFields being used by this hit queue. */
|
||||
SortField[] getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to detect the given field type for an IndexReader.
|
||||
*/
|
||||
static int detectFieldType(IndexReader reader, String fieldKey) throws IOException {
|
||||
String field = ((String)fieldKey).intern();
|
||||
TermEnum enumerator = reader.terms (new Term (field));
|
||||
try {
|
||||
Term term = enumerator.term();
|
||||
if (term == null) {
|
||||
throw new RuntimeException ("no terms in field " + field + " - cannot determine sort type");
|
||||
}
|
||||
int ret = 0;
|
||||
if (term.field() == field) {
|
||||
String termtext = term.text().trim();
|
||||
|
||||
/**
|
||||
* Java 1.4 level code:
|
||||
|
||||
if (pIntegers.matcher(termtext).matches())
|
||||
return IntegerSortedHitQueue.comparator (reader, enumerator, field);
|
||||
|
||||
else if (pFloats.matcher(termtext).matches())
|
||||
return FloatSortedHitQueue.comparator (reader, enumerator, field);
|
||||
*/
|
||||
|
||||
// Java 1.3 level code:
|
||||
try {
|
||||
Integer.parseInt (termtext);
|
||||
ret = SortField.INT;
|
||||
} catch (NumberFormatException nfe1) {
|
||||
try {
|
||||
Long.parseLong(termtext);
|
||||
ret = SortField.LONG;
|
||||
} catch (NumberFormatException nfe2) {
|
||||
try {
|
||||
Float.parseFloat (termtext);
|
||||
ret = SortField.FLOAT;
|
||||
} catch (NumberFormatException nfe3) {
|
||||
ret = SortField.STRING;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException ("field \"" + field + "\" does not appear to be indexed");
|
||||
}
|
||||
return ret;
|
||||
} finally {
|
||||
enumerator.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ import org.apache.lucene.index.CorruptIndexException;
|
|||
* Wrapper used by {@link HitIterator} to provide a lazily loaded hit
|
||||
* from {@link Hits}.
|
||||
*
|
||||
* @deprecated Hits will be removed in Lucene 3.0. Use {@link TopDocCollector} and {@link TopDocs} instead.
|
||||
* @deprecated Hits will be removed in Lucene 3.0. Use {@link TopScoreDocCollector} and {@link TopDocs} instead.
|
||||
*/
|
||||
public class Hit implements java.io.Serializable {
|
||||
|
||||
|
|
|
@ -19,7 +19,9 @@ package org.apache.lucene.search;
|
|||
|
||||
/** Lower-level search API.
|
||||
* <br>HitCollectors are primarily meant to be used to implement queries,
|
||||
* sorting and filtering.
|
||||
* sorting and filtering. See {@link
|
||||
* MultiReaderHitCollector} for a lower level and
|
||||
* higher performance (on a multi-segment index) API.
|
||||
* @see Searcher#search(Query,HitCollector)
|
||||
* @version $Id$
|
||||
*/
|
||||
|
|
|
@ -25,7 +25,7 @@ import java.util.NoSuchElementException;
|
|||
* {@link Hits#iterator()} returns an instance of this class. Calls to {@link #next()}
|
||||
* return a {@link Hit} instance.
|
||||
*
|
||||
* @deprecated Hits will be removed in Lucene 3.0. Use {@link TopDocCollector} and {@link TopDocs} instead.
|
||||
* @deprecated Hits will be removed in Lucene 3.0. Use {@link TopScoreDocCollector} and {@link TopDocs} instead.
|
||||
*/
|
||||
public class HitIterator implements Iterator {
|
||||
private Hits hits;
|
||||
|
|
|
@ -40,9 +40,9 @@ import org.apache.lucene.index.CorruptIndexException;
|
|||
* (but <code>n</code> < {@link #length()}_at_start).
|
||||
*
|
||||
* @deprecated Hits will be removed in Lucene 3.0. <p>
|
||||
* Instead e. g. {@link TopDocCollector} and {@link TopDocs} can be used:<br>
|
||||
* Instead e. g. {@link TopScoreDocCollector} and {@link TopDocs} can be used:<br>
|
||||
* <pre>
|
||||
* TopDocCollector collector = new TopDocCollector(hitsPerPage);
|
||||
* TopScoreDocCollector collector = new TopScoreDocCollector(hitsPerPage);
|
||||
* searcher.search(query, collector);
|
||||
* ScoreDoc[] hits = collector.topDocs().scoreDocs;
|
||||
* for (int i = 0; i < hits.length; i++) {
|
||||
|
|
|
@ -17,6 +17,10 @@ package org.apache.lucene.search;
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import org.apache.lucene.util.SorterTemplate;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.FieldSelector;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
|
@ -24,8 +28,6 @@ import org.apache.lucene.index.IndexReader;
|
|||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.store.Directory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/** Implements search over a single IndexReader.
|
||||
*
|
||||
* <p>Applications usually need only call the inherited {@link #search(Query)}
|
||||
|
@ -38,6 +40,8 @@ import java.io.IOException;
|
|||
public class IndexSearcher extends Searcher {
|
||||
IndexReader reader;
|
||||
private boolean closeReader;
|
||||
private IndexReader[] sortedSubReaders;
|
||||
private int[] sortedStarts;
|
||||
|
||||
/** Creates a searcher searching the index in the named directory.
|
||||
* @throws CorruptIndexException if the index is corrupt
|
||||
|
@ -63,8 +67,60 @@ public class IndexSearcher extends Searcher {
|
|||
private IndexSearcher(IndexReader r, boolean closeReader) {
|
||||
reader = r;
|
||||
this.closeReader = closeReader;
|
||||
sortSubReaders();
|
||||
}
|
||||
|
||||
protected void gatherSubReaders(List allSubReaders, IndexReader r) {
|
||||
IndexReader[] subReaders = r.getSequentialSubReaders();
|
||||
if (subReaders == null) {
|
||||
// Add the reader itself, and do not recurse
|
||||
allSubReaders.add(r);
|
||||
} else {
|
||||
for(int i=0;i<subReaders.length;i++) {
|
||||
gatherSubReaders(allSubReaders, subReaders[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static private final IndexReader[] indexReaderZeroArray = new IndexReader[0];
|
||||
|
||||
protected void sortSubReaders() {
|
||||
|
||||
List subReadersList = new ArrayList();
|
||||
gatherSubReaders(subReadersList, reader);
|
||||
sortedSubReaders = (IndexReader[]) subReadersList.toArray(indexReaderZeroArray);
|
||||
final int length = sortedSubReaders.length;
|
||||
sortedStarts = new int[length];
|
||||
int maxDoc = 0;
|
||||
for (int i = 0; i < sortedSubReaders.length; i++) {
|
||||
sortedStarts[i] = maxDoc;
|
||||
maxDoc += sortedSubReaders[i].maxDoc(); // compute maxDocs
|
||||
}
|
||||
|
||||
// sort readers and starts
|
||||
SorterTemplate sorter = new SorterTemplate() {
|
||||
protected int compare(int i, int j) {
|
||||
int num1 = sortedSubReaders[i].numDocs();
|
||||
int num2 = sortedSubReaders[j].numDocs();
|
||||
if (num1 > num2)
|
||||
return -1;
|
||||
if (num1 < num2)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
protected void swap(int i, int j) {
|
||||
IndexReader temp = sortedSubReaders[i];
|
||||
sortedSubReaders[i] = sortedSubReaders[j];
|
||||
sortedSubReaders[j] = temp;
|
||||
|
||||
int tempInt = sortedStarts[i];
|
||||
sortedStarts[i] = sortedStarts[j];
|
||||
sortedStarts[j] = tempInt;
|
||||
}
|
||||
};
|
||||
sorter.quickSort(0, length - 1);
|
||||
}
|
||||
|
||||
/** Return the {@link IndexReader} this searches. */
|
||||
public IndexReader getIndexReader() {
|
||||
return reader;
|
||||
|
@ -108,7 +164,7 @@ public class IndexSearcher extends Searcher {
|
|||
if (nDocs <= 0) // null might be returned from hq.top() below.
|
||||
throw new IllegalArgumentException("nDocs must be > 0");
|
||||
|
||||
TopDocCollector collector = new TopDocCollector(nDocs);
|
||||
TopScoreDocCollector collector = new TopScoreDocCollector(nDocs);
|
||||
search(weight, filter, collector);
|
||||
return collector.topDocs();
|
||||
}
|
||||
|
@ -117,16 +173,73 @@ public class IndexSearcher extends Searcher {
|
|||
public TopFieldDocs search(Weight weight, Filter filter, final int nDocs,
|
||||
Sort sort)
|
||||
throws IOException {
|
||||
return search(weight, filter, nDocs, sort, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Just like {@link #search(Weight, Filter, int, Sort)},
|
||||
* but you choose whether or not the fields in the
|
||||
* returned {@link FieldDoc} instances should be set by
|
||||
* specifying fillFields.
|
||||
*/
|
||||
public TopFieldDocs search(Weight weight, Filter filter, final int nDocs,
|
||||
Sort sort, boolean fillFields)
|
||||
throws IOException {
|
||||
|
||||
SortField[] fields = sort.fields;
|
||||
boolean legacy = false;
|
||||
for(int i = 0; i < fields.length; i++) {
|
||||
SortField field = fields[i];
|
||||
String fieldname = field.getField();
|
||||
int type = field.getType();
|
||||
// Resolve AUTO into its true type
|
||||
if (type == SortField.AUTO) {
|
||||
int autotype = FieldValueHitQueue.detectFieldType(reader, fieldname);
|
||||
if (autotype == SortField.STRING) {
|
||||
fields[i] = new SortField (fieldname, field.getLocale(), field.getReverse());
|
||||
} else {
|
||||
fields[i] = new SortField (fieldname, autotype, field.getReverse());
|
||||
}
|
||||
}
|
||||
|
||||
TopFieldDocCollector collector =
|
||||
new TopFieldDocCollector(reader, sort, nDocs);
|
||||
search(weight, filter, collector);
|
||||
return (TopFieldDocs)collector.topDocs();
|
||||
if (field.getUseLegacySearch()) {
|
||||
legacy = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (legacy) {
|
||||
// Search the single top-level reader
|
||||
TopScoreDocCollector collector = new TopFieldDocCollector(reader, sort, nDocs);
|
||||
collector.setNextReader(reader, 0);
|
||||
doSearch(reader, weight, filter, collector);
|
||||
return (TopFieldDocs) collector.topDocs();
|
||||
} else {
|
||||
// Search each sub-reader
|
||||
TopFieldCollector collector = new TopFieldCollector(sort, nDocs, sortedSubReaders, fillFields);
|
||||
search(weight, filter, collector);
|
||||
return (TopFieldDocs) collector.topDocs();
|
||||
}
|
||||
}
|
||||
|
||||
// inherit javadoc
|
||||
public void search(Weight weight, Filter filter,
|
||||
final HitCollector results) throws IOException {
|
||||
public void search(Weight weight, Filter filter, HitCollector results)
|
||||
throws IOException {
|
||||
|
||||
final MultiReaderHitCollector collector;
|
||||
if (results instanceof MultiReaderHitCollector) {
|
||||
collector = (MultiReaderHitCollector) results;
|
||||
} else {
|
||||
collector = new MultiReaderCollectorWrapper(results);
|
||||
}
|
||||
|
||||
for (int i = 0; i < sortedSubReaders.length; i++) { // search each subreader
|
||||
collector.setNextReader(sortedSubReaders[i], sortedStarts[i]);
|
||||
doSearch(sortedSubReaders[i], weight, filter, collector);
|
||||
}
|
||||
}
|
||||
|
||||
private void doSearch(IndexReader reader, Weight weight, Filter filter,
|
||||
final HitCollector results) throws IOException {
|
||||
|
||||
Scorer scorer = weight.scorer(reader);
|
||||
if (scorer == null)
|
||||
|
@ -169,4 +282,26 @@ public class IndexSearcher extends Searcher {
|
|||
public Explanation explain(Weight weight, int doc) throws IOException {
|
||||
return weight.explain(reader, doc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for non expert ({@link HitCollector})
|
||||
* implementations, which simply re-bases the incoming
|
||||
* docID before calling {@link HitCollector#collect}.
|
||||
*/
|
||||
static class MultiReaderCollectorWrapper extends MultiReaderHitCollector {
|
||||
private HitCollector collector;
|
||||
private int base = -1;
|
||||
|
||||
public MultiReaderCollectorWrapper(HitCollector collector) {
|
||||
this.collector = collector;
|
||||
}
|
||||
|
||||
public void collect(int doc, float score) {
|
||||
collector.collect(doc + base, score);
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
base = docBase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
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 org.apache.lucene.index.IndexReader;
|
||||
|
||||
/**
|
||||
* Expert: A HitCollector that can be used to collect hits
|
||||
* across sequential IndexReaders. For a Multi*Reader, this
|
||||
* collector advances through each of the sub readers, in an
|
||||
* arbitrary order. This results in a higher performance
|
||||
* means of collection.
|
||||
*
|
||||
* <b>NOTE:</b> The doc that is passed to the collect method
|
||||
* is relative to the current reader. You must re-base the
|
||||
* doc, by recording the docBase from the last setNextReader
|
||||
* call, to map it to the docID space of the
|
||||
* Multi*Reader.
|
||||
*
|
||||
* <b>NOTE:</b> This API is experimental and might change in
|
||||
* incompatible ways in the next release.
|
||||
*/
|
||||
public abstract class MultiReaderHitCollector extends HitCollector {
|
||||
/**
|
||||
* Called before collecting from each IndexReader. All doc
|
||||
* ids in {@link #collect(int, float)} will correspond to reader.
|
||||
*
|
||||
* Add docBase to the current IndexReaders internal document id to
|
||||
* re-base ids in {@link #collect(int, float)}.
|
||||
*
|
||||
* @param reader next IndexReader
|
||||
* @param docBase
|
||||
* @throws IOException
|
||||
*/
|
||||
public abstract void setNextReader(IndexReader reader, int docBase) throws IOException;
|
||||
}
|
|
@ -21,6 +21,7 @@ import org.apache.lucene.document.Document;
|
|||
import org.apache.lucene.document.FieldSelector;
|
||||
import org.apache.lucene.index.CorruptIndexException;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
@ -258,12 +259,34 @@ public class MultiSearcher extends Searcher {
|
|||
|
||||
final int start = starts[i];
|
||||
|
||||
searchables[i].search(weight, filter, new HitCollector() {
|
||||
public void collect(int doc, float score) {
|
||||
results.collect(doc + start, score);
|
||||
}
|
||||
});
|
||||
final MultiReaderHitCollector hc;
|
||||
if (results instanceof MultiReaderHitCollector) {
|
||||
// results can shift
|
||||
final MultiReaderHitCollector resultsMulti = (MultiReaderHitCollector) results;
|
||||
hc = new MultiReaderHitCollector() {
|
||||
public void collect(int doc, float score) {
|
||||
resultsMulti.collect(doc, score);
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase) throws IOException {
|
||||
resultsMulti.setNextReader(reader, start+docBase);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// We must shift the docIDs
|
||||
hc = new MultiReaderHitCollector() {
|
||||
private int docBase;
|
||||
public void collect(int doc, float score) {
|
||||
results.collect(doc + docBase + start, score);
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
this.docBase = docBase;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
searchables[i].search(weight, filter, hc);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.apache.lucene.search;
|
|||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.util.PriorityQueue;
|
||||
|
||||
/** Implements parallel search over a set of <code>Searchables</code>.
|
||||
|
@ -170,12 +171,34 @@ public class ParallelMultiSearcher extends MultiSearcher {
|
|||
|
||||
final int start = starts[i];
|
||||
|
||||
searchables[i].search(weight, filter, new HitCollector() {
|
||||
public void collect(int doc, float score) {
|
||||
results.collect(doc + start, score);
|
||||
}
|
||||
});
|
||||
final MultiReaderHitCollector hc;
|
||||
if (results instanceof MultiReaderHitCollector) {
|
||||
// results can shift
|
||||
final MultiReaderHitCollector resultsMulti = (MultiReaderHitCollector) results;
|
||||
hc = new MultiReaderHitCollector() {
|
||||
public void collect(int doc, float score) {
|
||||
resultsMulti.collect(doc, score);
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase) throws IOException {
|
||||
resultsMulti.setNextReader(reader, start+docBase);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// We must shift the docIDs
|
||||
hc = new MultiReaderHitCollector() {
|
||||
private int docBase;
|
||||
public void collect(int doc, float score) {
|
||||
results.collect(doc + docBase + start, score);
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
this.docBase = docBase;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
searchables[i].search(weight, filter, hc);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ import java.io.IOException;
|
|||
import java.util.BitSet;
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.util.OpenBitSet;
|
||||
|
||||
/**
|
||||
* Constrains search results to only match those which also match a provided
|
||||
|
@ -51,9 +50,13 @@ public class QueryWrapperFilter extends Filter {
|
|||
public BitSet bits(IndexReader reader) throws IOException {
|
||||
final BitSet bits = new BitSet(reader.maxDoc());
|
||||
|
||||
new IndexSearcher(reader).search(query, new HitCollector() {
|
||||
new IndexSearcher(reader).search(query, new MultiReaderHitCollector() {
|
||||
private int base = -1;
|
||||
public final void collect(int doc, float score) {
|
||||
bits.set(doc); // set bit for hit
|
||||
bits.set(doc + base); // set bit for hit
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
base = docBase;
|
||||
}
|
||||
});
|
||||
return bits;
|
||||
|
|
|
@ -25,6 +25,7 @@ package org.apache.lucene.search;
|
|||
*
|
||||
* @since lucene 1.4
|
||||
* @version $Id$
|
||||
* @deprecated use {@link FieldComparator}
|
||||
*/
|
||||
public interface ScoreDocComparator {
|
||||
|
||||
|
|
|
@ -17,9 +17,12 @@ package org.apache.lucene.search;
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
|
||||
/**
|
||||
* Stores information about how to sort documents by terms in an individual
|
||||
* field. Fields must be indexed in order to sort by them.
|
||||
|
@ -67,33 +70,34 @@ implements Serializable {
|
|||
* lower values are at the front. */
|
||||
public static final int DOUBLE = 7;
|
||||
|
||||
/**
|
||||
* Sort using term values as encoded Shorts. Sort values are shorts and lower values are at the front
|
||||
*/
|
||||
/** Sort using term values as encoded Shorts. Sort values are Short and
|
||||
* lower values are at the front. */
|
||||
public static final int SHORT = 8;
|
||||
|
||||
|
||||
/** Sort using a custom Comparator. Sort values are any Comparable and
|
||||
* sorting is done according to natural order. */
|
||||
public static final int CUSTOM = 9;
|
||||
/**
|
||||
* Sort using term values as encoded bytes. Sort values are bytes and lower values are at the front
|
||||
*/
|
||||
|
||||
/** Sort using term values as encoded Bytes. Sort values are Byte and
|
||||
* lower values are at the front. */
|
||||
public static final int BYTE = 10;
|
||||
|
||||
|
||||
|
||||
/** Sort using term values as Strings, but comparing by
|
||||
* value (using String.compareTo) for all comparisons.
|
||||
* This is typically slower than {@link #STRING}, which
|
||||
* uses ordinals to do the sorting. */
|
||||
public static final int STRING_VAL = 11;
|
||||
|
||||
// IMPLEMENTATION NOTE: the FieldCache.STRING_INDEX is in the same "namespace"
|
||||
// as the above static int values. Any new values must not have the same value
|
||||
// as FieldCache.STRING_INDEX.
|
||||
|
||||
|
||||
/** Represents sorting by document score (relevancy). */
|
||||
public static final SortField FIELD_SCORE = new SortField (null, SCORE);
|
||||
|
||||
/** Represents sorting by document number (index order). */
|
||||
public static final SortField FIELD_DOC = new SortField (null, DOC);
|
||||
|
||||
|
||||
private String field;
|
||||
private int type = AUTO; // defaults to determining type dynamically
|
||||
private Locale locale; // defaults to "natural order" (no Locale)
|
||||
|
@ -101,6 +105,11 @@ implements Serializable {
|
|||
private SortComparatorSource factory;
|
||||
private FieldCache.Parser parser;
|
||||
|
||||
// Used for CUSTOM sort
|
||||
private FieldComparatorSource comparatorSource;
|
||||
|
||||
private boolean useLegacy = false; // remove in Lucene 3.0
|
||||
|
||||
/** Creates a sort by terms in the given field where the type of term value
|
||||
* is determined dynamically ({@link #AUTO AUTO}).
|
||||
* @param field Name of field to sort by, cannot be <code>null</code>.
|
||||
|
@ -206,22 +215,46 @@ implements Serializable {
|
|||
/** Creates a sort with a custom comparison function.
|
||||
* @param field Name of field to sort by; cannot be <code>null</code>.
|
||||
* @param comparator Returns a comparator for sorting hits.
|
||||
* @deprecated use SortField (String field, FieldComparatorSource comparator)
|
||||
*/
|
||||
public SortField (String field, SortComparatorSource comparator) {
|
||||
initFieldType(field, CUSTOM);
|
||||
setUseLegacySearch(true);
|
||||
this.factory = comparator;
|
||||
}
|
||||
|
||||
/** Creates a sort with a custom comparison function.
|
||||
* @param field Name of field to sort by; cannot be <code>null</code>.
|
||||
* @param comparator Returns a comparator for sorting hits.
|
||||
*/
|
||||
public SortField (String field, FieldComparatorSource comparator) {
|
||||
initFieldType(field, CUSTOM);
|
||||
this.comparatorSource = comparator;
|
||||
}
|
||||
|
||||
/** Creates a sort, possibly in reverse, with a custom comparison function.
|
||||
* @param field Name of field to sort by; cannot be <code>null</code>.
|
||||
* @param comparator Returns a comparator for sorting hits.
|
||||
* @param reverse True if natural order should be reversed.
|
||||
* @deprecated use SortField (String field, FieldComparatorSource comparator, boolean reverse)
|
||||
*/
|
||||
public SortField (String field, SortComparatorSource comparator, boolean reverse) {
|
||||
initFieldType(field, CUSTOM);
|
||||
setUseLegacySearch(true);
|
||||
this.reverse = reverse;
|
||||
this.factory = comparator;
|
||||
}
|
||||
|
||||
/** Creates a sort, possibly in reverse, with a custom comparison function.
|
||||
* @param field Name of field to sort by; cannot be <code>null</code>.
|
||||
* @param comparator Returns a comparator for sorting hits.
|
||||
* @param reverse True if natural order should be reversed.
|
||||
*/
|
||||
public SortField (String field, FieldComparatorSource comparator, boolean reverse) {
|
||||
initFieldType(field, CUSTOM);
|
||||
this.reverse = reverse;
|
||||
this.comparatorSource = comparator;
|
||||
}
|
||||
|
||||
// Sets field & type, and ensures field is not NULL unless
|
||||
// type is SCORE or DOC
|
||||
|
@ -273,26 +306,91 @@ implements Serializable {
|
|||
return reverse;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #getComparatorSource()}
|
||||
*/
|
||||
public SortComparatorSource getFactory() {
|
||||
return factory;
|
||||
}
|
||||
|
||||
public FieldComparatorSource getComparatorSource() {
|
||||
return comparatorSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use legacy IndexSearch implementation: search with a MultiSegmentReader rather
|
||||
* than passing a single hit collector to multiple SegmentReaders.
|
||||
*
|
||||
* @param legacy true for legacy behavior
|
||||
* @deprecated will be removed in Lucene 3.0.
|
||||
*/
|
||||
public void setUseLegacySearch(boolean legacy) {
|
||||
this.useLegacy = legacy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if true, IndexSearch will use legacy sorting search implementation.
|
||||
* eg. multiple Priority Queues.
|
||||
* @deprecated will be removed in Lucene 3.0.
|
||||
*/
|
||||
public boolean getUseLegacySearch() {
|
||||
return this.useLegacy;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
switch (type) {
|
||||
case SCORE: buffer.append("<score>");
|
||||
break;
|
||||
case SCORE:
|
||||
buffer.append("<score>");
|
||||
break;
|
||||
|
||||
case DOC: buffer.append("<doc>");
|
||||
break;
|
||||
case DOC:
|
||||
buffer.append("<doc>");
|
||||
break;
|
||||
|
||||
case AUTO:
|
||||
buffer.append("<auto: \"").append(field).append("\">");
|
||||
break;
|
||||
|
||||
case STRING:
|
||||
buffer.append("<string: \"").append(field).append("\">");
|
||||
break;
|
||||
|
||||
case STRING_VAL:
|
||||
buffer.append("<string_val: \"").append(field).append("\">");
|
||||
break;
|
||||
|
||||
case BYTE:
|
||||
buffer.append("<byte: \"").append(field).append("\">");
|
||||
break;
|
||||
|
||||
case SHORT:
|
||||
buffer.append("<short: \"").append(field).append("\">");
|
||||
break;
|
||||
|
||||
case INT:
|
||||
buffer.append("<int: \"").append(field).append("\">");
|
||||
break;
|
||||
|
||||
case LONG:
|
||||
buffer.append("<long: \"").append(field).append("\">");
|
||||
break;
|
||||
|
||||
case FLOAT:
|
||||
buffer.append("<float: \"").append(field).append("\">");
|
||||
break;
|
||||
|
||||
case DOUBLE:
|
||||
buffer.append("<double: \"").append(field).append("\">");
|
||||
break;
|
||||
|
||||
case CUSTOM:
|
||||
buffer.append("<custom:\"").append(field).append("\": ").append(factory).append('>');
|
||||
break;
|
||||
buffer.append("<custom:\"").append(field).append("\": ").append(factory).append('>');
|
||||
break;
|
||||
|
||||
default:
|
||||
buffer.append('\"').append(field).append('\"');
|
||||
break;
|
||||
buffer.append("<???: \"").append(field).append("\">");
|
||||
break;
|
||||
}
|
||||
|
||||
if (locale != null) buffer.append('(').append(locale).append(')');
|
||||
|
@ -333,4 +431,65 @@ implements Serializable {
|
|||
if (parser != null) hash += parser.hashCode()^0x3aaf56ff;
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
/** Returns the {@link FieldComparator} to use for sorting.
|
||||
* @param subReaders array of {@link IndexReader} search
|
||||
* will step through
|
||||
* @param numHits number of top hits the queue will store
|
||||
* @param sortPos position of this SortField within {@link
|
||||
* Sort}. The comparator is primary if sortPos==0,
|
||||
* secondary if sortPos==1, etc. Some comparators can
|
||||
* optimize themselves when they are the primary sort.
|
||||
* @param reversed True if the SortField is reversed
|
||||
* @return {@link FieldComparator} to use when sorting
|
||||
*/
|
||||
protected FieldComparator getComparator(final IndexReader[] subReaders, final int numHits, final int sortPos, final boolean reversed) throws IOException {
|
||||
|
||||
if (locale != null) {
|
||||
// TODO: it'd be nice to allow FieldCache.getStringIndex
|
||||
// to optionally accept a Locale so sorting could then use
|
||||
// the faster StringComparator impls
|
||||
return new FieldComparator.StringComparatorLocale(numHits, field, locale);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SortField.SCORE:
|
||||
return new FieldComparator.RelevanceComparator(numHits);
|
||||
|
||||
case SortField.DOC:
|
||||
return new FieldComparator.DocComparator(numHits);
|
||||
|
||||
case SortField.INT:
|
||||
return new FieldComparator.IntComparator(numHits, field, parser);
|
||||
|
||||
case SortField.FLOAT:
|
||||
return new FieldComparator.FloatComparator(numHits, field, parser);
|
||||
|
||||
case SortField.LONG:
|
||||
return new FieldComparator.LongComparator(numHits, field, parser);
|
||||
|
||||
case SortField.DOUBLE:
|
||||
return new FieldComparator.DoubleComparator(numHits, field, parser);
|
||||
|
||||
case SortField.BYTE:
|
||||
return new FieldComparator.ByteComparator(numHits, field, parser);
|
||||
|
||||
case SortField.SHORT:
|
||||
return new FieldComparator.ShortComparator(numHits, field, parser);
|
||||
|
||||
case SortField.CUSTOM:
|
||||
assert factory == null && comparatorSource != null;
|
||||
return comparatorSource.newComparator(field, subReaders, numHits, sortPos, reversed);
|
||||
|
||||
case SortField.STRING:
|
||||
return new FieldComparator.StringOrdValComparator(numHits, field, sortPos, reversed);
|
||||
|
||||
case SortField.STRING_VAL:
|
||||
return new FieldComparator.StringValComparator(numHits, field);
|
||||
|
||||
default:
|
||||
throw new IllegalStateException("Illegal sort type: " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.apache.lucene.search;
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
|
@ -17,13 +19,16 @@ package org.apache.lucene.search;
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* <p>The TimeLimitedCollector is used to timeout search requests that
|
||||
* take longer than the maximum allowed search time limit. After this
|
||||
* time is exceeded, the search thread is stopped by throwing a
|
||||
* TimeExceeded Exception.</p>
|
||||
*
|
||||
*/
|
||||
public class TimeLimitedCollector extends HitCollector {
|
||||
public class TimeLimitedCollector extends MultiReaderHitCollector {
|
||||
|
||||
/**
|
||||
* Default timer resolution.
|
||||
|
@ -138,7 +143,7 @@ public class TimeLimitedCollector extends HitCollector {
|
|||
|
||||
private final long t0;
|
||||
private final long timeout;
|
||||
private final HitCollector hc;
|
||||
private final MultiReaderHitCollector hc;
|
||||
|
||||
/**
|
||||
* Create a TimeLimitedCollector wrapper over another HitCollector with a specified timeout.
|
||||
|
@ -146,7 +151,11 @@ public class TimeLimitedCollector extends HitCollector {
|
|||
* @param timeAllowed max time allowed for collecting hits after which {@link TimeExceededException} is thrown
|
||||
*/
|
||||
public TimeLimitedCollector( final HitCollector hc, final long timeAllowed ) {
|
||||
this.hc = hc;
|
||||
if (hc instanceof MultiReaderHitCollector) {
|
||||
this.hc = (MultiReaderHitCollector) hc;
|
||||
} else {
|
||||
this.hc = new IndexSearcher.MultiReaderCollectorWrapper(hc);
|
||||
}
|
||||
t0 = TIMER_THREAD.getMilliseconds();
|
||||
this.timeout = t0 + timeAllowed;
|
||||
}
|
||||
|
@ -216,4 +225,8 @@ public class TimeLimitedCollector extends HitCollector {
|
|||
public void setGreedy(boolean greedy) {
|
||||
this.greedy = greedy;
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int base) throws IOException {
|
||||
hc.setNextReader(reader, base);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ import org.apache.lucene.util.PriorityQueue;
|
|||
* <p>This may be extended, overriding the collect method to, e.g.,
|
||||
* conditionally invoke <code>super()</code> in order to filter which
|
||||
* documents are collected.
|
||||
*
|
||||
* @deprecated Please use {@link TopScoreDocCollector}
|
||||
* instead, which has better performance.
|
||||
**/
|
||||
public class TopDocCollector extends HitCollector {
|
||||
|
||||
|
|
|
@ -0,0 +1,221 @@
|
|||
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 org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.search.FieldValueHitQueue.Entry;
|
||||
|
||||
/**
|
||||
* A {@link HitCollector} that sorts by {@link SortField} using
|
||||
* {@link FieldComparator}s.
|
||||
*
|
||||
* <b>NOTE:</b> This API is experimental and might change in
|
||||
* incompatible ways in the next release.
|
||||
*/
|
||||
public final class TopFieldCollector extends MultiReaderHitCollector {
|
||||
|
||||
private final FieldValueHitQueue queue;
|
||||
|
||||
private final FieldComparator[] comparators;
|
||||
private FieldComparator comparator1;
|
||||
private final int numComparators;
|
||||
private int[] reverseMul;
|
||||
private int reverseMul1 = 0;
|
||||
|
||||
private final int numHits;
|
||||
private int totalHits;
|
||||
private FieldValueHitQueue.Entry bottom = null;
|
||||
|
||||
/** Stores the maximum score value encountered, needed for normalizing. */
|
||||
private float maxScore = Float.NEGATIVE_INFINITY;
|
||||
|
||||
private boolean queueFull;
|
||||
|
||||
private boolean fillFields;
|
||||
|
||||
public TopFieldCollector(Sort sort, int numHits, IndexReader[] subReaders, boolean fillFields)
|
||||
throws IOException {
|
||||
|
||||
if (sort.fields.length == 0) {
|
||||
throw new IllegalArgumentException("Sort must contain at least one field");
|
||||
}
|
||||
|
||||
queue = new FieldValueHitQueue(sort.fields, numHits, subReaders);
|
||||
comparators = queue.getComparators();
|
||||
reverseMul = queue.getReverseMul();
|
||||
numComparators = comparators.length;
|
||||
|
||||
if (numComparators == 1) {
|
||||
comparator1 = comparators[0];
|
||||
reverseMul1 = reverseMul[0];
|
||||
} else {
|
||||
comparator1 = null;
|
||||
reverseMul1 = 0;
|
||||
}
|
||||
this.numHits = numHits;
|
||||
this.fillFields = fillFields;
|
||||
}
|
||||
|
||||
int currentDocBase;
|
||||
|
||||
// javadoc inherited
|
||||
public void setNextReader(IndexReader reader, int docBase) throws IOException {
|
||||
final int numSlotsFull;
|
||||
if (queueFull)
|
||||
numSlotsFull = numHits;
|
||||
else
|
||||
numSlotsFull = totalHits;
|
||||
|
||||
currentDocBase = docBase;
|
||||
|
||||
for (int i = 0; i < numComparators; i++) {
|
||||
comparators[i].setNextReader(reader, docBase, numSlotsFull);
|
||||
}
|
||||
}
|
||||
|
||||
private final void updateBottom(int doc, float score) {
|
||||
bottom.docID = currentDocBase + doc;
|
||||
bottom.score = score;
|
||||
queue.adjustTop();
|
||||
bottom = (FieldValueHitQueue.Entry) queue.top();
|
||||
}
|
||||
|
||||
private final void add(int slot, int doc, float score) {
|
||||
queue.put(new FieldValueHitQueue.Entry(slot, currentDocBase+doc, score));
|
||||
bottom = (FieldValueHitQueue.Entry) queue.top();
|
||||
queueFull = totalHits == numHits;
|
||||
}
|
||||
|
||||
// javadoc inherited
|
||||
public void collect(int doc, float score) {
|
||||
if (score > 0.0f) {
|
||||
|
||||
maxScore = Math.max(maxScore, score);
|
||||
totalHits++;
|
||||
|
||||
// TODO: one optimization we could do is to pre-fill
|
||||
// the queue with sentinel value that guaranteed to
|
||||
// always compare lower than a real hit; this would
|
||||
// save having to check queueFull on each insert
|
||||
|
||||
if (queueFull) {
|
||||
|
||||
if (numComparators == 1) {
|
||||
// Common case
|
||||
|
||||
// Fastmatch: return if this hit is not competitive
|
||||
final int cmp = reverseMul1 * comparator1.compareBottom(doc, score);
|
||||
if (cmp < 0) {
|
||||
// Definitely not competitive
|
||||
return;
|
||||
} else if (cmp == 0 && doc + currentDocBase > bottom.docID) {
|
||||
// Definitely not competitive
|
||||
return;
|
||||
}
|
||||
|
||||
// This hit is competitive -- replace bottom
|
||||
// element in queue & adjustTop
|
||||
comparator1.copy(bottom.slot, doc, score);
|
||||
|
||||
updateBottom(doc, score);
|
||||
|
||||
comparator1.setBottom(bottom.slot);
|
||||
|
||||
} else {
|
||||
|
||||
// Fastmatch: return if this hit is not competitive
|
||||
for(int i=0;;i++) {
|
||||
final int c = reverseMul[i] * comparators[i].compareBottom(doc, score);
|
||||
if (c < 0) {
|
||||
// Definitely not competitive
|
||||
return;
|
||||
} else if (c > 0) {
|
||||
// Definitely competitive
|
||||
break;
|
||||
} else if (i == numComparators-1) {
|
||||
// This is the equals case.
|
||||
if (doc + currentDocBase > bottom.docID) {
|
||||
// Definitely not competitive
|
||||
return;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This hit is competitive -- replace bottom
|
||||
// element in queue & adjustTop
|
||||
for (int i = 0; i < numComparators; i++) {
|
||||
comparators[i].copy(bottom.slot, doc, score);
|
||||
}
|
||||
|
||||
updateBottom(doc, score);
|
||||
|
||||
for(int i=0;i<numComparators;i++) {
|
||||
comparators[i].setBottom(bottom.slot);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Startup transient: queue hasn't gathered numHits
|
||||
// yet
|
||||
|
||||
final int slot = totalHits-1;
|
||||
// Copy hit into queue
|
||||
if (numComparators == 1) {
|
||||
// Common case
|
||||
comparator1.copy(slot, doc, score);
|
||||
add(slot, doc, score);
|
||||
if (queueFull) {
|
||||
comparator1.setBottom(bottom.slot);
|
||||
}
|
||||
|
||||
} else {
|
||||
for (int i = 0; i < numComparators; i++) {
|
||||
comparators[i].copy(slot, doc, score);
|
||||
}
|
||||
add(slot, doc, score);
|
||||
if (queueFull) {
|
||||
for(int i=0;i<numComparators;i++) {
|
||||
comparators[i].setBottom(bottom.slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// javadoc inherited
|
||||
public TopDocs topDocs() {
|
||||
ScoreDoc[] scoreDocs = new ScoreDoc[queue.size()];
|
||||
if (fillFields) {
|
||||
for (int i = queue.size() - 1; i >= 0; i--) {
|
||||
scoreDocs[i] = queue.fillFields((FieldValueHitQueue.Entry) queue.pop());
|
||||
}
|
||||
} else {
|
||||
Entry entry = (FieldValueHitQueue.Entry) queue.pop();
|
||||
for (int i = queue.size() - 1; i >= 0; i--) {
|
||||
scoreDocs[i] = new FieldDoc(entry.docID,
|
||||
entry.score);
|
||||
}
|
||||
}
|
||||
|
||||
return new TopFieldDocs(totalHits, scoreDocs, queue.getFields(), maxScore);
|
||||
}
|
||||
}
|
|
@ -28,8 +28,10 @@ import org.apache.lucene.index.IndexReader;
|
|||
* <p>This may be extended, overriding the collect method to, e.g.,
|
||||
* conditionally invoke <code>super()</code> in order to filter which
|
||||
* documents are collected.
|
||||
*
|
||||
* @deprecated Please use {@link TopFieldCollector} instead.
|
||||
**/
|
||||
public class TopFieldDocCollector extends TopDocCollector {
|
||||
public class TopFieldDocCollector extends TopScoreDocCollector {
|
||||
|
||||
private FieldDoc reusableFD;
|
||||
|
||||
|
@ -48,15 +50,15 @@ public class TopFieldDocCollector extends TopDocCollector {
|
|||
if (score > 0.0f) {
|
||||
totalHits++;
|
||||
if (reusableFD == null)
|
||||
reusableFD = new FieldDoc(doc, score);
|
||||
reusableFD = new FieldDoc(doc + docBase, score);
|
||||
else {
|
||||
// Whereas TopDocCollector can skip this if the
|
||||
// Whereas TopScoreDocCollector can skip this if the
|
||||
// score is not competitive, we cannot because the
|
||||
// comparators in the FieldSortedHitQueue.lessThan
|
||||
// aren't in general congruent with "higher score
|
||||
// wins"
|
||||
reusableFD.score = score;
|
||||
reusableFD.doc = doc;
|
||||
reusableFD.doc = doc + docBase;
|
||||
}
|
||||
reusableFD = (FieldDoc) hq.insertWithOverflow(reusableFD);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
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 org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.util.PriorityQueue;
|
||||
|
||||
/** A {@link MultiReaderHitCollector} implementation that
|
||||
* collects the top-scoring documents, returning them as a
|
||||
* {@link TopDocs}. This is used by {@link IndexSearcher}
|
||||
* to implement {@link TopDocs}-based search.
|
||||
*
|
||||
* <p>This may be extended, overriding the {@link
|
||||
* MultiReaderHitCollector#collect} method to, e.g.,
|
||||
* conditionally invoke <code>super()</code> in order to
|
||||
* filter which documents are collected, but sure you
|
||||
* either take docBase into account, or also override
|
||||
* {@link MultiReaderHitCollector#setNextReader} method. */
|
||||
public class TopScoreDocCollector extends MultiReaderHitCollector {
|
||||
|
||||
private ScoreDoc reusableSD;
|
||||
|
||||
/** The total number of hits the collector encountered. */
|
||||
protected int totalHits;
|
||||
|
||||
/** The priority queue which holds the top-scoring documents. */
|
||||
protected PriorityQueue hq;
|
||||
|
||||
protected int docBase = 0;
|
||||
|
||||
/** Construct to collect a given number of hits.
|
||||
* @param numHits the maximum number of hits to collect
|
||||
*/
|
||||
public TopScoreDocCollector(int numHits) {
|
||||
this(new HitQueue(numHits));
|
||||
}
|
||||
|
||||
/** Constructor to collect the top-scoring documents by using the given PQ.
|
||||
* @param hq the PQ to use by this instance.
|
||||
*/
|
||||
protected TopScoreDocCollector(PriorityQueue hq) {
|
||||
this.hq = hq;
|
||||
}
|
||||
|
||||
// javadoc inherited
|
||||
public void collect(int doc, float score) {
|
||||
if (score > 0.0f) {
|
||||
totalHits++;
|
||||
if (reusableSD == null) {
|
||||
reusableSD = new ScoreDoc(doc + docBase, score);
|
||||
} else if (score >= reusableSD.score) {
|
||||
// reusableSD holds the last "rejected" entry, so, if
|
||||
// this new score is not better than that, there's no
|
||||
// need to try inserting it
|
||||
reusableSD.doc = doc + docBase;
|
||||
reusableSD.score = score;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
reusableSD = (ScoreDoc) hq.insertWithOverflow(reusableSD);
|
||||
}
|
||||
}
|
||||
|
||||
/** The total number of documents that matched this query. */
|
||||
public int getTotalHits() {
|
||||
return totalHits;
|
||||
}
|
||||
|
||||
/** The top-scoring hits. */
|
||||
public TopDocs topDocs() {
|
||||
ScoreDoc[] scoreDocs = new ScoreDoc[hq.size()];
|
||||
for (int i = hq.size()-1; i >= 0; i--) { // put docs in array
|
||||
scoreDocs[i] = (ScoreDoc) hq.pop();
|
||||
}
|
||||
|
||||
float maxScore = (totalHits==0)
|
||||
? Float.NEGATIVE_INFINITY
|
||||
: scoreDocs[0].score;
|
||||
|
||||
return new TopDocs(totalHits, scoreDocs, maxScore);
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int base) {
|
||||
docBase = base;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
package org.apache.lucene.util;
|
||||
|
||||
/*
|
||||
* Copyright 2003 The Apache Software Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Borrowed from Cglib. Allows custom swap so that two arrays can be sorted
|
||||
* at the same time.
|
||||
*/
|
||||
public abstract class SorterTemplate {
|
||||
private static final int MERGESORT_THRESHOLD = 12;
|
||||
private static final int QUICKSORT_THRESHOLD = 7;
|
||||
|
||||
abstract protected void swap(int i, int j);
|
||||
abstract protected int compare(int i, int j);
|
||||
|
||||
public void quickSort(int lo, int hi) {
|
||||
quickSortHelper(lo, hi);
|
||||
insertionSort(lo, hi);
|
||||
}
|
||||
|
||||
private void quickSortHelper(int lo, int hi) {
|
||||
for (;;) {
|
||||
int diff = hi - lo;
|
||||
if (diff <= QUICKSORT_THRESHOLD) {
|
||||
break;
|
||||
}
|
||||
int i = (hi + lo) / 2;
|
||||
if (compare(lo, i) > 0) {
|
||||
swap(lo, i);
|
||||
}
|
||||
if (compare(lo, hi) > 0) {
|
||||
swap(lo, hi);
|
||||
}
|
||||
if (compare(i, hi) > 0) {
|
||||
swap(i, hi);
|
||||
}
|
||||
int j = hi - 1;
|
||||
swap(i, j);
|
||||
i = lo;
|
||||
int v = j;
|
||||
for (;;) {
|
||||
while (compare(++i, v) < 0) {
|
||||
/* nothing */;
|
||||
}
|
||||
while (compare(--j, v) > 0) {
|
||||
/* nothing */;
|
||||
}
|
||||
if (j < i) {
|
||||
break;
|
||||
}
|
||||
swap(i, j);
|
||||
}
|
||||
swap(i, hi - 1);
|
||||
if (j - lo <= hi - i + 1) {
|
||||
quickSortHelper(lo, j);
|
||||
lo = i + 1;
|
||||
} else {
|
||||
quickSortHelper(i + 1, hi);
|
||||
hi = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void insertionSort(int lo, int hi) {
|
||||
for (int i = lo + 1 ; i <= hi; i++) {
|
||||
for (int j = i; j > lo; j--) {
|
||||
if (compare(j - 1, j) > 0) {
|
||||
swap(j - 1, j);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void mergeSort(int lo, int hi) {
|
||||
int diff = hi - lo;
|
||||
if (diff <= MERGESORT_THRESHOLD) {
|
||||
insertionSort(lo, hi);
|
||||
return;
|
||||
}
|
||||
int mid = lo + diff / 2;
|
||||
mergeSort(lo, mid);
|
||||
mergeSort(mid, hi);
|
||||
merge(lo, mid, hi, mid - lo, hi - mid);
|
||||
}
|
||||
|
||||
private void merge(int lo, int pivot, int hi, int len1, int len2) {
|
||||
if (len1 == 0 || len2 == 0) {
|
||||
return;
|
||||
}
|
||||
if (len1 + len2 == 2) {
|
||||
if (compare(pivot, lo) < 0) {
|
||||
swap(pivot, lo);
|
||||
}
|
||||
return;
|
||||
}
|
||||
int first_cut, second_cut;
|
||||
int len11, len22;
|
||||
if (len1 > len2) {
|
||||
len11 = len1 / 2;
|
||||
first_cut = lo + len11;
|
||||
second_cut = lower(pivot, hi, first_cut);
|
||||
len22 = second_cut - pivot;
|
||||
} else {
|
||||
len22 = len2 / 2;
|
||||
second_cut = pivot + len22;
|
||||
first_cut = upper(lo, pivot, second_cut);
|
||||
len11 = first_cut - lo;
|
||||
}
|
||||
rotate(first_cut, pivot, second_cut);
|
||||
int new_mid = first_cut + len22;
|
||||
merge(lo, first_cut, new_mid, len11, len22);
|
||||
merge(new_mid, second_cut, hi, len1 - len11, len2 - len22);
|
||||
}
|
||||
|
||||
private void rotate(int lo, int mid, int hi) {
|
||||
int lot = lo;
|
||||
int hit = mid - 1;
|
||||
while (lot < hit) {
|
||||
swap(lot++, hit--);
|
||||
}
|
||||
lot = mid; hit = hi - 1;
|
||||
while (lot < hit) {
|
||||
swap(lot++, hit--);
|
||||
}
|
||||
lot = lo; hit = hi - 1;
|
||||
while (lot < hit) {
|
||||
swap(lot++, hit--);
|
||||
}
|
||||
}
|
||||
|
||||
private int lower(int lo, int hi, int val) {
|
||||
int len = hi - lo;
|
||||
while (len > 0) {
|
||||
int half = len / 2;
|
||||
int mid= lo + half;
|
||||
if (compare(mid, val) < 0) {
|
||||
lo = mid + 1;
|
||||
len = len - half -1;
|
||||
} else {
|
||||
len = half;
|
||||
}
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
|
||||
private int upper(int lo, int hi, int val) {
|
||||
int len = hi - lo;
|
||||
while (len > 0) {
|
||||
int half = len / 2;
|
||||
int mid = lo + half;
|
||||
if (compare(val, mid) < 0) {
|
||||
len = half;
|
||||
} else {
|
||||
lo = mid + 1;
|
||||
len = len - half -1;
|
||||
}
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
}
|
|
@ -320,7 +320,7 @@ public class TestIndexReaderReopen extends LuceneTestCase {
|
|||
assertRefCountEquals(1, reader0);
|
||||
|
||||
assertTrue(reader0 instanceof MultiSegmentReader);
|
||||
SegmentReader[] subReaders0 = ((MultiSegmentReader) reader0).getSubReaders();
|
||||
IndexReader[] subReaders0 = reader0.getSequentialSubReaders();
|
||||
for (int i = 0; i < subReaders0.length; i++) {
|
||||
assertRefCountEquals(1, subReaders0[i]);
|
||||
}
|
||||
|
@ -332,7 +332,7 @@ public class TestIndexReaderReopen extends LuceneTestCase {
|
|||
|
||||
IndexReader reader1 = refreshReader(reader0, true).refreshedReader;
|
||||
assertTrue(reader1 instanceof MultiSegmentReader);
|
||||
SegmentReader[] subReaders1 = ((MultiSegmentReader) reader1).getSubReaders();
|
||||
IndexReader[] subReaders1 = reader1.getSequentialSubReaders();
|
||||
assertEquals(subReaders0.length, subReaders1.length);
|
||||
|
||||
for (int i = 0; i < subReaders0.length; i++) {
|
||||
|
@ -349,7 +349,7 @@ public class TestIndexReaderReopen extends LuceneTestCase {
|
|||
|
||||
IndexReader reader2 = refreshReader(reader1, true).refreshedReader;
|
||||
assertTrue(reader2 instanceof MultiSegmentReader);
|
||||
SegmentReader[] subReaders2 = ((MultiSegmentReader) reader2).getSubReaders();
|
||||
IndexReader[] subReaders2 = reader2.getSequentialSubReaders();
|
||||
assertEquals(subReaders1.length, subReaders2.length);
|
||||
|
||||
for (int i = 0; i < subReaders2.length; i++) {
|
||||
|
@ -373,7 +373,7 @@ public class TestIndexReaderReopen extends LuceneTestCase {
|
|||
|
||||
IndexReader reader3 = refreshReader(reader0, true).refreshedReader;
|
||||
assertTrue(reader3 instanceof MultiSegmentReader);
|
||||
SegmentReader[] subReaders3 = ((MultiSegmentReader) reader3).getSubReaders();
|
||||
IndexReader[] subReaders3 = reader3.getSequentialSubReaders();
|
||||
assertEquals(subReaders3.length, subReaders0.length);
|
||||
|
||||
// try some permutations
|
||||
|
@ -945,14 +945,14 @@ public class TestIndexReaderReopen extends LuceneTestCase {
|
|||
|
||||
if (checkSubReaders) {
|
||||
if (reader instanceof MultiSegmentReader) {
|
||||
SegmentReader[] subReaders = ((MultiSegmentReader) reader).getSubReaders();
|
||||
IndexReader[] subReaders = reader.getSequentialSubReaders();
|
||||
for (int i = 0; i < subReaders.length; i++) {
|
||||
assertReaderClosed(subReaders[i], checkSubReaders, checkNormsClosed);
|
||||
}
|
||||
}
|
||||
|
||||
if (reader instanceof MultiReader) {
|
||||
IndexReader[] subReaders = ((MultiReader) reader).getSubReaders();
|
||||
IndexReader[] subReaders = reader.getSequentialSubReaders();
|
||||
for (int i = 0; i < subReaders.length; i++) {
|
||||
assertReaderClosed(subReaders[i], checkSubReaders, checkNormsClosed);
|
||||
}
|
||||
|
@ -971,7 +971,7 @@ public class TestIndexReaderReopen extends LuceneTestCase {
|
|||
reader.ensureOpen();
|
||||
|
||||
if (reader instanceof MultiSegmentReader) {
|
||||
SegmentReader[] subReaders = ((MultiSegmentReader) reader).getSubReaders();
|
||||
IndexReader[] subReaders = reader.getSequentialSubReaders();
|
||||
for (int i = 0; i < subReaders.length; i++) {
|
||||
assertReaderOpen(subReaders[i]);
|
||||
}
|
||||
|
|
|
@ -26,8 +26,8 @@ import org.apache.lucene.analysis.standard.StandardAnalyzer;
|
|||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.HitCollector;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.MultiReaderHitCollector;
|
||||
import org.apache.lucene.search.Searcher;
|
||||
import org.apache.lucene.search.Similarity;
|
||||
import org.apache.lucene.search.TermQuery;
|
||||
|
@ -348,16 +348,21 @@ public class TestOmitTf extends LuceneTestCase {
|
|||
dir.close();
|
||||
}
|
||||
|
||||
public static class CountingHitCollector extends HitCollector {
|
||||
public static class CountingHitCollector extends MultiReaderHitCollector {
|
||||
static int count=0;
|
||||
static int sum=0;
|
||||
private int docBase = -1;
|
||||
CountingHitCollector(){count=0;sum=0;}
|
||||
public void collect(int doc, float score) {
|
||||
count++;
|
||||
sum += doc; // use it to avoid any possibility of being optimized away
|
||||
sum += doc + docBase; // use it to avoid any possibility of being optimized away
|
||||
}
|
||||
|
||||
public static int getCount() { return count; }
|
||||
public static int getSum() { return sum; }
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
this.docBase = docBase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,9 +89,14 @@ public class CheckHits {
|
|||
}
|
||||
|
||||
final Set actual = new TreeSet();
|
||||
searcher.search(query, new HitCollector() {
|
||||
searcher.search(query, new MultiReaderHitCollector() {
|
||||
private int base = -1;
|
||||
public void collect(int doc, float score) {
|
||||
actual.add(new Integer(doc));
|
||||
actual.add(new Integer(doc + base));
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
base = docBase;
|
||||
}
|
||||
});
|
||||
TestCase.assertEquals(query.toString(defaultFieldName), correct, actual);
|
||||
|
@ -143,7 +148,7 @@ public class CheckHits {
|
|||
/** Tests that a Hits has an expected order of documents */
|
||||
public static void checkDocIds(String mes, int[] results, ScoreDoc[] hits)
|
||||
throws IOException {
|
||||
TestCase.assertEquals(mes + " nr of hits", results.length, hits.length);
|
||||
TestCase.assertEquals(mes + " nr of hits", hits.length, results.length);
|
||||
for (int i = 0; i < results.length; i++) {
|
||||
TestCase.assertEquals(mes + " doc nrs for hit " + i, results[i], hits[i].doc);
|
||||
}
|
||||
|
@ -390,7 +395,7 @@ public class CheckHits {
|
|||
super.search(query,results);
|
||||
}
|
||||
public void search(Query query, Filter filter,
|
||||
HitCollector results) throws IOException {
|
||||
HitCollector results) throws IOException {
|
||||
checkExplanations(query);
|
||||
super.search(query,filter, results);
|
||||
}
|
||||
|
@ -411,7 +416,7 @@ public class CheckHits {
|
|||
*
|
||||
* @see CheckHits#verifyExplanation
|
||||
*/
|
||||
public static class ExplanationAsserter extends HitCollector {
|
||||
public static class ExplanationAsserter extends MultiReaderHitCollector {
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
|
@ -423,7 +428,9 @@ public class CheckHits {
|
|||
Searcher s;
|
||||
String d;
|
||||
boolean deep;
|
||||
|
||||
|
||||
private int base = -1;
|
||||
|
||||
/** Constructs an instance which does shallow tests on the Explanation */
|
||||
public ExplanationAsserter(Query q, String defaultFieldName, Searcher s) {
|
||||
this(q,defaultFieldName,s,false);
|
||||
|
@ -437,7 +444,7 @@ public class CheckHits {
|
|||
|
||||
public void collect(int doc, float score) {
|
||||
Explanation exp = null;
|
||||
|
||||
doc = doc + base;
|
||||
try {
|
||||
exp = s.explain(q, doc);
|
||||
} catch (IOException e) {
|
||||
|
@ -449,6 +456,9 @@ public class CheckHits {
|
|||
exp);
|
||||
verifyExplanation(d,doc,score,deep,exp);
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
base = docBase;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ import java.io.IOException;
|
|||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
|
||||
/**
|
||||
* Copyright 2005 Apache Software Foundation
|
||||
*
|
||||
|
@ -151,8 +153,10 @@ public class QueryUtils {
|
|||
|
||||
final int[] sdoc = new int[] {-1};
|
||||
final float maxDiff = 1e-5f;
|
||||
s.search(q,new HitCollector() {
|
||||
s.search(q,new MultiReaderHitCollector() {
|
||||
private int base = -1;
|
||||
public void collect(int doc, float score) {
|
||||
doc = doc + base;
|
||||
try {
|
||||
int op = order[(opidx[0]++)%order.length];
|
||||
//System.out.println(op==skip_op ? "skip("+(sdoc[0]+1)+")":"next()");
|
||||
|
@ -183,6 +187,9 @@ public class QueryUtils {
|
|||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
base = docBase;
|
||||
}
|
||||
});
|
||||
|
||||
// make sure next call to scorer is false.
|
||||
|
@ -198,9 +205,11 @@ public class QueryUtils {
|
|||
//System.out.println("checkFirstSkipTo: "+q);
|
||||
final float maxDiff = 1e-5f;
|
||||
final int lastDoc[] = {-1};
|
||||
s.search(q,new HitCollector() {
|
||||
s.search(q,new MultiReaderHitCollector() {
|
||||
private int base = -1;
|
||||
public void collect(int doc, float score) {
|
||||
//System.out.println("doc="+doc);
|
||||
doc = doc + base;
|
||||
try {
|
||||
for (int i=lastDoc[0]+1; i<=doc; i++) {
|
||||
Weight w = q.weight(s);
|
||||
|
@ -216,6 +225,9 @@ public class QueryUtils {
|
|||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
base = docBase;
|
||||
}
|
||||
});
|
||||
Weight w = q.weight(s);
|
||||
Scorer scorer = w.scorer(s.getIndexReader());
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.apache.lucene.search;
|
|||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.lucene.analysis.SimpleAnalyzer;
|
||||
import org.apache.lucene.document.*;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.store.RAMDirectory;
|
||||
|
@ -65,9 +66,13 @@ public class TestDocBoost extends LuceneTestCase {
|
|||
|
||||
new IndexSearcher(store).search
|
||||
(new TermQuery(new Term("field", "word")),
|
||||
new HitCollector() {
|
||||
new MultiReaderHitCollector() {
|
||||
private int base = -1;
|
||||
public final void collect(int doc, float score) {
|
||||
scores[doc] = score;
|
||||
scores[doc + base] = score;
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
base = docBase;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -168,9 +168,13 @@ public class TestMultiTermConstantScore extends BaseTestRangeFilter {
|
|||
// must use a non score normalizing method for this.
|
||||
Query q = csrq("data", "1", "6", T, T);
|
||||
q.setBoost(100);
|
||||
search.search(q, null, new HitCollector() {
|
||||
search.search(q, null, new MultiReaderHitCollector() {
|
||||
private int base = -1;
|
||||
public void collect(int doc, float score) {
|
||||
assertEquals("score for doc " + doc + " was not correct", 1.0f, score);
|
||||
assertEquals("score for doc " + (doc + base) + " was not correct", 1.0f, score);
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
base = docBase;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -588,7 +592,6 @@ public class TestMultiTermConstantScore extends BaseTestRangeFilter {
|
|||
|
||||
IndexReader reader = IndexReader.open(danishIndex);
|
||||
IndexSearcher search = new IndexSearcher(reader);
|
||||
Query q = new TermQuery(new Term("body","body"));
|
||||
|
||||
Collator c = Collator.getInstance(new Locale("da", "dk"));
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import org.apache.lucene.util.LuceneTestCase;
|
|||
|
||||
import java.util.Random;
|
||||
import java.util.BitSet;
|
||||
import java.util.Set;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
|
@ -51,6 +50,7 @@ public class TestScorerPerf extends LuceneTestCase {
|
|||
// This could possibly fail if Lucene starts checking for docid ranges...
|
||||
RAMDirectory rd = new RAMDirectory();
|
||||
IndexWriter iw = new IndexWriter(rd,new WhitespaceAnalyzer(), true, IndexWriter.MaxFieldLength.LIMITED);
|
||||
iw.addDocument(new Document());
|
||||
iw.close();
|
||||
s = new IndexSearcher(rd);
|
||||
}
|
||||
|
@ -96,17 +96,22 @@ public class TestScorerPerf extends LuceneTestCase {
|
|||
return sets;
|
||||
}
|
||||
|
||||
public static class CountingHitCollector extends HitCollector {
|
||||
public static class CountingHitCollector extends MultiReaderHitCollector {
|
||||
int count=0;
|
||||
int sum=0;
|
||||
protected int docBase = -1;
|
||||
|
||||
public void collect(int doc, float score) {
|
||||
count++;
|
||||
sum += doc; // use it to avoid any possibility of being optimized away
|
||||
sum += docBase+doc; // use it to avoid any possibility of being optimized away
|
||||
}
|
||||
|
||||
public int getCount() { return count; }
|
||||
public int getSum() { return sum; }
|
||||
|
||||
public void setNextReader(IndexReader reader, int base) {
|
||||
docBase = base;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -119,8 +124,8 @@ public class TestScorerPerf extends LuceneTestCase {
|
|||
|
||||
public void collect(int doc, float score) {
|
||||
pos = answer.nextSetBit(pos+1);
|
||||
if (pos != doc) {
|
||||
throw new RuntimeException("Expected doc " + pos + " but got " + doc);
|
||||
if (pos != doc + docBase) {
|
||||
throw new RuntimeException("Expected doc " + pos + " but got " + doc + docBase);
|
||||
}
|
||||
super.collect(doc,score);
|
||||
}
|
||||
|
@ -158,6 +163,7 @@ public class TestScorerPerf extends LuceneTestCase {
|
|||
: new CountingHitCollector();
|
||||
s.search(bq, hc);
|
||||
ret += hc.getSum();
|
||||
|
||||
if (validate) assertEquals(result.cardinality(), hc.getCount());
|
||||
// System.out.println(hc.getCount());
|
||||
}
|
||||
|
|
|
@ -62,9 +62,13 @@ public class TestSetNorm extends LuceneTestCase {
|
|||
|
||||
new IndexSearcher(store).search
|
||||
(new TermQuery(new Term("field", "word")),
|
||||
new HitCollector() {
|
||||
new MultiReaderHitCollector() {
|
||||
private int base = -1;
|
||||
public final void collect(int doc, float score) {
|
||||
scores[doc] = score;
|
||||
scores[doc + base] = score;
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
base = docBase;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ import org.apache.lucene.util.LuceneTestCase;
|
|||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.store.RAMDirectory;
|
||||
import org.apache.lucene.analysis.SimpleAnalyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
|
@ -75,10 +75,11 @@ public class TestSimilarity extends LuceneTestCase {
|
|||
|
||||
searcher.search
|
||||
(new TermQuery(b),
|
||||
new HitCollector() {
|
||||
new MultiReaderHitCollector() {
|
||||
public final void collect(int doc, float score) {
|
||||
assertTrue(score == 1.0f);
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {}
|
||||
});
|
||||
|
||||
BooleanQuery bq = new BooleanQuery();
|
||||
|
@ -87,10 +88,14 @@ public class TestSimilarity extends LuceneTestCase {
|
|||
//System.out.println(bq.toString("field"));
|
||||
searcher.search
|
||||
(bq,
|
||||
new HitCollector() {
|
||||
new MultiReaderHitCollector() {
|
||||
private int base = -1;
|
||||
public final void collect(int doc, float score) {
|
||||
//System.out.println("Doc=" + doc + " score=" + score);
|
||||
assertTrue(score == (float)doc+1);
|
||||
assertTrue(score == (float)doc+base+1);
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
base = docBase;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -100,22 +105,24 @@ public class TestSimilarity extends LuceneTestCase {
|
|||
//System.out.println(pq.toString("field"));
|
||||
searcher.search
|
||||
(pq,
|
||||
new HitCollector() {
|
||||
new MultiReaderHitCollector() {
|
||||
public final void collect(int doc, float score) {
|
||||
//System.out.println("Doc=" + doc + " score=" + score);
|
||||
assertTrue(score == 1.0f);
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {}
|
||||
});
|
||||
|
||||
pq.setSlop(2);
|
||||
//System.out.println(pq.toString("field"));
|
||||
searcher.search
|
||||
(pq,
|
||||
new HitCollector() {
|
||||
new MultiReaderHitCollector() {
|
||||
public final void collect(int doc, float score) {
|
||||
//System.out.println("Doc=" + doc + " score=" + score);
|
||||
assertTrue(score == 2.0f);
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,354 @@
|
|||
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 org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.store.MockRAMDirectory;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.analysis.standard.StandardAnalyzer;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class TestStressSort extends LuceneTestCase {
|
||||
|
||||
private final static int NUM_DOCS = 5000;
|
||||
// NOTE: put seed in here to make failures
|
||||
// deterministic, but do not commit with a seed (to
|
||||
// better test):
|
||||
private final Random r = new Random();
|
||||
private Directory dir, dir2, dir3;
|
||||
private IndexSearcher searcherMultiSegment;
|
||||
private IndexSearcher searcherFewSegment;
|
||||
private IndexSearcher searcherSingleSegment;
|
||||
|
||||
private static final boolean VERBOSE = false;
|
||||
|
||||
// min..max
|
||||
private int nextInt(int min, int max) {
|
||||
return min + r.nextInt(max-min+1);
|
||||
}
|
||||
|
||||
// 0..(lim-1)
|
||||
private int nextInt(int lim) {
|
||||
return r.nextInt(lim);
|
||||
}
|
||||
|
||||
final char[] buffer = new char[20];
|
||||
private String randomString(int size) {
|
||||
assert size < 20;
|
||||
for(int i=0;i<size;i++) {
|
||||
buffer[i] = (char) nextInt(48, 122);
|
||||
}
|
||||
return new String(buffer, 0, size);
|
||||
}
|
||||
|
||||
private void create() throws Throwable {
|
||||
|
||||
// NOTE: put seed in here to make failures
|
||||
// deterministic, but do not commit with a seed (to
|
||||
// better test):
|
||||
final Random r = new Random();
|
||||
dir = new MockRAMDirectory();
|
||||
IndexWriter writer = new IndexWriter(dir, new StandardAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
|
||||
writer.setMaxBufferedDocs(17);
|
||||
|
||||
final Document doc = new Document();
|
||||
final Document doc2 = new Document();
|
||||
|
||||
final Field id = new Field("id", "", Field.Store.YES, Field.Index.NO);
|
||||
doc.add(id);
|
||||
doc2.add(id);
|
||||
|
||||
final Field contents = new Field("contents", "", Field.Store.NO, Field.Index.ANALYZED);
|
||||
doc.add(contents);
|
||||
doc2.add(contents);
|
||||
|
||||
final Field byteField = new Field("byte", "", Field.Store.NO, Field.Index.NOT_ANALYZED);
|
||||
doc.add(byteField);
|
||||
doc2.add(byteField);
|
||||
|
||||
final Field shortField = new Field("short", "", Field.Store.NO, Field.Index.NOT_ANALYZED);
|
||||
doc.add(shortField);
|
||||
doc2.add(shortField);
|
||||
|
||||
final Field intField = new Field("int", "", Field.Store.NO, Field.Index.NOT_ANALYZED);
|
||||
doc.add(intField);
|
||||
doc2.add(intField);
|
||||
|
||||
final Field longField = new Field("long", "", Field.Store.NO, Field.Index.NOT_ANALYZED);
|
||||
doc.add(longField);
|
||||
doc2.add(longField);
|
||||
|
||||
final Field floatField = new Field("float", "", Field.Store.NO, Field.Index.NOT_ANALYZED);
|
||||
doc.add(floatField);
|
||||
doc2.add(floatField);
|
||||
|
||||
final Field doubleField = new Field("double", "", Field.Store.NO, Field.Index.NOT_ANALYZED);
|
||||
doc.add(doubleField);
|
||||
doc2.add(doubleField);
|
||||
|
||||
final Field stringField = new Field("string", "", Field.Store.NO, Field.Index.NOT_ANALYZED);
|
||||
doc.add(stringField);
|
||||
// doc2 doesn't have stringField, so we get nulls
|
||||
|
||||
for(int i=0;i<NUM_DOCS;i++) {
|
||||
id.setValue(""+i);
|
||||
if (i % 1000 == 0) {
|
||||
contents.setValue("a b c z");
|
||||
} else if (i % 100 == 0) {
|
||||
contents.setValue("a b c y");
|
||||
} else if (i % 10 == 0) {
|
||||
contents.setValue("a b c x");
|
||||
} else {
|
||||
contents.setValue("a b c");
|
||||
}
|
||||
byteField.setValue(""+nextInt(Byte.MIN_VALUE, Byte.MAX_VALUE));
|
||||
if (nextInt(10) == 3) {
|
||||
shortField.setValue(""+Short.MIN_VALUE);
|
||||
} else if (nextInt(10) == 7) {
|
||||
shortField.setValue(""+Short.MAX_VALUE);
|
||||
} else {
|
||||
shortField.setValue(""+nextInt(Short.MIN_VALUE, Short.MAX_VALUE));
|
||||
}
|
||||
|
||||
if (nextInt(10) == 3) {
|
||||
intField.setValue(""+Integer.MIN_VALUE);
|
||||
} else if (nextInt(10) == 7) {
|
||||
intField.setValue(""+Integer.MAX_VALUE);
|
||||
} else {
|
||||
intField.setValue(""+r.nextInt());
|
||||
}
|
||||
|
||||
if (nextInt(10) == 3) {
|
||||
longField.setValue(""+Long.MIN_VALUE);
|
||||
} else if (nextInt(10) == 7) {
|
||||
longField.setValue(""+Long.MAX_VALUE);
|
||||
} else {
|
||||
longField.setValue(""+r.nextLong());
|
||||
}
|
||||
floatField.setValue(""+r.nextFloat());
|
||||
doubleField.setValue(""+r.nextDouble());
|
||||
if (i % 197 == 0) {
|
||||
writer.addDocument(doc2);
|
||||
} else {
|
||||
stringField.setValue(randomString(nextInt(20)));
|
||||
writer.addDocument(doc);
|
||||
}
|
||||
}
|
||||
writer.close();
|
||||
searcherMultiSegment = new IndexSearcher(dir);
|
||||
|
||||
dir2 = new MockRAMDirectory(dir);
|
||||
writer = new IndexWriter(dir2, new StandardAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
|
||||
writer.optimize();
|
||||
writer.close();
|
||||
searcherSingleSegment = new IndexSearcher(dir2);
|
||||
|
||||
dir3 = new MockRAMDirectory(dir);
|
||||
writer = new IndexWriter(dir3, new StandardAnalyzer(), IndexWriter.MaxFieldLength.LIMITED);
|
||||
writer.optimize(3);
|
||||
writer.close();
|
||||
searcherFewSegment = new IndexSearcher(dir3);
|
||||
}
|
||||
|
||||
private void close() throws Throwable {
|
||||
searcherMultiSegment.close();
|
||||
searcherFewSegment.close();
|
||||
searcherSingleSegment.close();
|
||||
dir.close();
|
||||
dir2.close();
|
||||
}
|
||||
|
||||
public void testSort() throws Throwable {
|
||||
|
||||
// reverse & not
|
||||
// all types
|
||||
// restrictive & non restrictive searches (on contents)
|
||||
|
||||
create();
|
||||
|
||||
Sort[] sorts = new Sort[50];
|
||||
int sortCount = 0;
|
||||
|
||||
for(int r=0;r<2;r++) {
|
||||
Sort sort;
|
||||
boolean reverse = 1 == r;
|
||||
|
||||
sorts[sortCount++] = sort = new Sort();
|
||||
sort.setSort(new SortField[] {new SortField("byte", SortField.BYTE, reverse)});
|
||||
|
||||
sorts[sortCount++] = sort = new Sort();
|
||||
sort.setSort(new SortField[] {new SortField("short", SortField.SHORT, reverse)});
|
||||
|
||||
sorts[sortCount++] = sort = new Sort();
|
||||
sort.setSort(new SortField[] {new SortField("int", SortField.INT, reverse)});
|
||||
|
||||
sorts[sortCount++] = sort = new Sort();
|
||||
sort.setSort(new SortField[] {new SortField("long", SortField.LONG, reverse)});
|
||||
|
||||
sorts[sortCount++] = sort = new Sort();
|
||||
sort.setSort(new SortField[] {new SortField("float", SortField.FLOAT, reverse)});
|
||||
|
||||
sorts[sortCount++] = sort = new Sort();
|
||||
sort.setSort(new SortField[] {new SortField("double", SortField.DOUBLE, reverse)});
|
||||
|
||||
sorts[sortCount++] = sort = new Sort();
|
||||
sort.setSort(new SortField[] {new SortField("string", SortField.STRING_VAL, reverse)});
|
||||
|
||||
sorts[sortCount++] = sort = new Sort();
|
||||
sort.setSort(new SortField[] {new SortField("string", SortField.STRING, reverse)});
|
||||
|
||||
//sorts[sortCount++] = sort = new Sort();
|
||||
//sort.setSort(new SortField[] {new SortField("string", SortField.STRING_ORD, reverse)});
|
||||
|
||||
//sorts[sortCount++] = sort = new Sort();
|
||||
//sort.setSort(new SortField[] {new SortField("string", SortField.STRING_ORD_VAL, reverse)});
|
||||
|
||||
//sorts[sortCount++] = sort = new Sort();
|
||||
//sort.setSort(new SortField[] {new SortField("string", SortField.STRING_ORD_VAL_DEM, reverse)});
|
||||
|
||||
//sorts[sortCount++] = sort = new Sort();
|
||||
//sort.setSort(new SortField[] {new SortField("string", SortField.STRING_ORD_VAL_DEM2, reverse)});
|
||||
|
||||
sorts[sortCount++] = sort = new Sort();
|
||||
sort.setSort(new SortField[] {new SortField(null, SortField.SCORE, reverse)});
|
||||
|
||||
sorts[sortCount++] = sort = new Sort();
|
||||
sort.setSort(new SortField[] {new SortField(null, SortField.DOC, reverse)});
|
||||
}
|
||||
|
||||
Query[] queries = new Query[4];
|
||||
queries[0] = new MatchAllDocsQuery();
|
||||
queries[1] = new TermQuery(new Term("contents", "x")); // matches every 10th doc
|
||||
queries[2] = new TermQuery(new Term("contents", "y")); // matches every 100th doc
|
||||
queries[3] = new TermQuery(new Term("contents", "z")); // matches every 1000th doc
|
||||
|
||||
for(int sx=0;sx<3;sx++) {
|
||||
final IndexSearcher searcher;
|
||||
if (sx == 0) {
|
||||
searcher = searcherSingleSegment;
|
||||
} else if (sx == 1) {
|
||||
searcher = searcherFewSegment;
|
||||
} else {
|
||||
searcher = searcherMultiSegment;
|
||||
}
|
||||
|
||||
for(int qx=0;qx<queries.length;qx++) {
|
||||
final Query query = queries[qx];
|
||||
|
||||
for(int q=0;q<3;q++) {
|
||||
|
||||
final int queueSize;
|
||||
if (q == 0) {
|
||||
queueSize = 10;
|
||||
} else if (q == 1) {
|
||||
queueSize = 100;
|
||||
} else {
|
||||
queueSize = 1000;
|
||||
}
|
||||
|
||||
for(int s=0;s<sortCount;s++) {
|
||||
Sort sort1 = sorts[s];
|
||||
|
||||
for(int s2=-1;s2<sortCount;s2++) {
|
||||
Sort sort;
|
||||
if (s2 == -1) {
|
||||
// Single field sort
|
||||
sort = sort1;
|
||||
} else {
|
||||
sort = new Sort(new SortField[] {sort1.getSort()[0], sorts[s2].getSort()[0]});
|
||||
}
|
||||
|
||||
// Old
|
||||
Sort oldSort = getOldSort(sort);
|
||||
|
||||
if (VERBOSE) {
|
||||
System.out.println("query=" + query);
|
||||
if (sx == 0) {
|
||||
System.out.println(" single-segment index");
|
||||
} else if (sx == 1) {
|
||||
System.out.println(" few-segment index");
|
||||
} else {
|
||||
System.out.println(" many-segment index");
|
||||
}
|
||||
System.out.println(" numHit=" + queueSize);
|
||||
System.out.println(" old=" + oldSort);
|
||||
System.out.println(" new=" + sort);
|
||||
}
|
||||
|
||||
TopDocs newHits = searcher.search(query, null, queueSize, sort);
|
||||
TopDocs oldHits = searcher.search(query, null, queueSize, oldSort);
|
||||
|
||||
compare(oldHits, newHits);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
private Sort getOldSort(Sort sort) {
|
||||
SortField[] fields = sort.getSort();
|
||||
SortField[] oldFields = new SortField[fields.length];
|
||||
for(int i=0;i<fields.length;i++) {
|
||||
int sortType;
|
||||
if (fields[i].getField() != null && fields[i].getField().equals("string")) {
|
||||
sortType = SortField.STRING;
|
||||
} else {
|
||||
sortType = fields[i].getType();
|
||||
}
|
||||
oldFields[i] = new SortField(fields[i].getField(),
|
||||
sortType,
|
||||
fields[i].getReverse());
|
||||
oldFields[i].setUseLegacySearch(true);
|
||||
}
|
||||
return new Sort(oldFields);
|
||||
}
|
||||
|
||||
private void compare(TopDocs oldHits, TopDocs newHits) {
|
||||
assertEquals(oldHits.totalHits, newHits.totalHits);
|
||||
assertEquals(oldHits.scoreDocs.length, newHits.scoreDocs.length);
|
||||
final ScoreDoc[] oldDocs = oldHits.scoreDocs;
|
||||
final ScoreDoc[] newDocs = newHits.scoreDocs;
|
||||
|
||||
for(int i=0;i<oldDocs.length;i++) {
|
||||
if (oldDocs[i] instanceof FieldDoc) {
|
||||
assert newDocs[i] instanceof FieldDoc;
|
||||
FieldDoc oldHit = (FieldDoc) oldDocs[i];
|
||||
FieldDoc newHit = (FieldDoc) newDocs[i];
|
||||
assertEquals("hit " + i + " of " + oldDocs.length + " differs: oldDoc=" + oldHit.doc + " vs newDoc=" + newHit.doc + " oldFields=" + Arrays.toString(oldHit.fields) + " newFields=" + Arrays.toString(newHit.fields),
|
||||
oldHit.doc, newHit.doc);
|
||||
assertEquals(oldHit.score, newHit.score, 0.00001);
|
||||
assertTrue(Arrays.equals(oldHit.fields, newHit.fields));
|
||||
} else {
|
||||
ScoreDoc oldHit = oldDocs[i];
|
||||
ScoreDoc newHit = newDocs[i];
|
||||
assertEquals(oldHit.doc, newHit.doc);
|
||||
assertEquals(oldHit.score, newHit.score, 0.00001);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -82,15 +82,19 @@ public class TestTermScorer extends LuceneTestCase
|
|||
//must call next first
|
||||
|
||||
|
||||
ts.score(new HitCollector()
|
||||
ts.score(new MultiReaderHitCollector()
|
||||
{
|
||||
private int base = -1;
|
||||
public void collect(int doc, float score)
|
||||
{
|
||||
docs.add(new TestHit(doc, score));
|
||||
docs.add(new TestHit(doc + base, score));
|
||||
assertTrue("score " + score + " is not greater than 0", score > 0);
|
||||
assertTrue("Doc: " + doc + " does not equal: " + 0 +
|
||||
" or doc does not equaal: " + 5, doc == 0 || doc == 5);
|
||||
}
|
||||
public void setNextReader(IndexReader reader, int docBase) {
|
||||
base = docBase;
|
||||
}
|
||||
});
|
||||
assertTrue("docs Size: " + docs.size() + " is not: " + 2, docs.size() == 2);
|
||||
TestHit doc0 = (TestHit) docs.get(0);
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.apache.lucene.search;
|
|||
import org.apache.lucene.analysis.WhitespaceAnalyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
|
||||
import org.apache.lucene.queryParser.QueryParser;
|
||||
|
@ -125,6 +126,7 @@ public class TestTimeLimitedCollector extends LuceneTestCase {
|
|||
search(tlCollector);
|
||||
totalTLCResults = myHc.hitCount();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
assertTrue("Unexpected exception: "+e, false); //==fail
|
||||
}
|
||||
assertEquals( "Wrong number of results!", totalResults, totalTLCResults );
|
||||
|
@ -293,11 +295,12 @@ public class TestTimeLimitedCollector extends LuceneTestCase {
|
|||
}
|
||||
|
||||
// counting hit collector that can slow down at collect().
|
||||
private class MyHitCollector extends HitCollector
|
||||
private class MyHitCollector extends MultiReaderHitCollector
|
||||
{
|
||||
private final BitSet bits = new BitSet();
|
||||
private int slowdown = 0;
|
||||
private int lastDocCollected = -1;
|
||||
private int docBase = -1;
|
||||
|
||||
/**
|
||||
* amount of time to wait on each collect to simulate a long iteration
|
||||
|
@ -307,6 +310,7 @@ public class TestTimeLimitedCollector extends LuceneTestCase {
|
|||
}
|
||||
|
||||
public void collect( final int doc, final float score ) {
|
||||
int docId = doc + docBase;
|
||||
if( slowdown > 0 ) {
|
||||
try {
|
||||
Thread.sleep(slowdown);
|
||||
|
@ -315,8 +319,9 @@ public class TestTimeLimitedCollector extends LuceneTestCase {
|
|||
System.out.println("caught " + x);
|
||||
}
|
||||
}
|
||||
bits.set( doc );
|
||||
lastDocCollected = doc;
|
||||
assert docId >= 0: " base=" + docBase + " doc=" + doc;
|
||||
bits.set( docId );
|
||||
lastDocCollected = docId;
|
||||
}
|
||||
|
||||
public int hitCount() {
|
||||
|
@ -326,6 +331,10 @@ public class TestTimeLimitedCollector extends LuceneTestCase {
|
|||
public int getLastDocCollected() {
|
||||
return lastDocCollected;
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader reader, int base) {
|
||||
docBase = base;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue