mirror of https://github.com/apache/lucene.git
Latest patches to hit sorting code, including unit tests, two of which currently fail.
git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@150207 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
12eee6df5a
commit
60f1d192c7
|
@ -33,8 +33,10 @@ import java.io.IOException;
|
||||||
class FieldDocSortedHitQueue
|
class FieldDocSortedHitQueue
|
||||||
extends PriorityQueue {
|
extends PriorityQueue {
|
||||||
|
|
||||||
// this cannot contain AUTO fields
|
// this cannot contain AUTO fields - any AUTO fields should
|
||||||
SortField[] fields;
|
// have been resolved by the time this class is used.
|
||||||
|
volatile SortField[] fields;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a hit queue sorted by the given list of fields.
|
* Creates a hit queue sorted by the given list of fields.
|
||||||
|
@ -51,15 +53,14 @@ extends PriorityQueue {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows redefinition of sort fields if they are <code>null</code>.
|
* Allows redefinition of sort fields if they are <code>null</code>.
|
||||||
* This is to handle the
|
* This is to handle the case using ParallelMultiSearcher where the
|
||||||
* case using ParallelMultiSearcher where the original list
|
* original list contains AUTO and we don't know the actual sort
|
||||||
* contains AUTO and we don't know
|
* type until the values come back. The fields can only be set once.
|
||||||
* the actual sort type until the values come back. This
|
* This method is thread safe.
|
||||||
* method is thread safe.
|
|
||||||
* @param fields
|
* @param fields
|
||||||
*/
|
*/
|
||||||
synchronized void setFields (SortField[] fields) {
|
synchronized void setFields (SortField[] fields) {
|
||||||
if (fields == null) this.fields = fields;
|
if (this.fields == null) this.fields = fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -92,12 +93,16 @@ extends PriorityQueue {
|
||||||
break;
|
break;
|
||||||
case SortField.DOC:
|
case SortField.DOC:
|
||||||
case SortField.INT:
|
case SortField.INT:
|
||||||
case SortField.STRING:
|
|
||||||
int i1 = ((Integer)docA.fields[i]).intValue();
|
int i1 = ((Integer)docA.fields[i]).intValue();
|
||||||
int i2 = ((Integer)docB.fields[i]).intValue();
|
int i2 = ((Integer)docB.fields[i]).intValue();
|
||||||
if (i1 > i2) c = -1;
|
if (i1 > i2) c = -1;
|
||||||
if (i1 < i2) c = 1;
|
if (i1 < i2) c = 1;
|
||||||
break;
|
break;
|
||||||
|
case SortField.STRING:
|
||||||
|
String s1 = (String) docA.fields[i];
|
||||||
|
String s2 = (String) docB.fields[i];
|
||||||
|
c = s2.compareTo(s1);
|
||||||
|
break;
|
||||||
case SortField.FLOAT:
|
case SortField.FLOAT:
|
||||||
float f1 = ((Float)docA.fields[i]).floatValue();
|
float f1 = ((Float)docA.fields[i]).floatValue();
|
||||||
float f2 = ((Float)docB.fields[i]).floatValue();
|
float f2 = ((Float)docB.fields[i]).floatValue();
|
||||||
|
@ -123,12 +128,16 @@ extends PriorityQueue {
|
||||||
break;
|
break;
|
||||||
case SortField.DOC:
|
case SortField.DOC:
|
||||||
case SortField.INT:
|
case SortField.INT:
|
||||||
case SortField.STRING:
|
|
||||||
int i1 = ((Integer)docA.fields[i]).intValue();
|
int i1 = ((Integer)docA.fields[i]).intValue();
|
||||||
int i2 = ((Integer)docB.fields[i]).intValue();
|
int i2 = ((Integer)docB.fields[i]).intValue();
|
||||||
if (i1 < i2) c = -1;
|
if (i1 < i2) c = -1;
|
||||||
if (i1 > i2) c = 1;
|
if (i1 > i2) c = 1;
|
||||||
break;
|
break;
|
||||||
|
case SortField.STRING:
|
||||||
|
String s1 = (String) docA.fields[i];
|
||||||
|
String s2 = (String) docB.fields[i];
|
||||||
|
c = s1.compareTo(s2);
|
||||||
|
break;
|
||||||
case SortField.FLOAT:
|
case SortField.FLOAT:
|
||||||
float f1 = ((Float)docA.fields[i]).floatValue();
|
float f1 = ((Float)docA.fields[i]).floatValue();
|
||||||
float f2 = ((Float)docB.fields[i]).floatValue();
|
float f2 = ((Float)docB.fields[i]).floatValue();
|
||||||
|
@ -148,4 +157,4 @@ extends PriorityQueue {
|
||||||
}
|
}
|
||||||
return c > 0;
|
return c > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,11 +39,16 @@ import java.util.regex.Pattern;
|
||||||
*
|
*
|
||||||
* <p>A static cache is maintained. This cache contains an integer
|
* <p>A static cache is maintained. This cache contains an integer
|
||||||
* or float array of length <code>IndexReader.maxDoc()</code> for each field
|
* or float array of length <code>IndexReader.maxDoc()</code> for each field
|
||||||
* name for which a sort is performed. In other words, the size of
|
* name for which a sort is performed. In other words, the size of the
|
||||||
* the cache in bytes is:
|
* cache in bytes is:
|
||||||
*
|
*
|
||||||
* <p><code>4 * IndexReader.maxDoc() * (# of different fields actually used to sort)</code>
|
* <p><code>4 * IndexReader.maxDoc() * (# of different fields actually used to sort)</code>
|
||||||
*
|
*
|
||||||
|
* <p>For String fields, the cache is larger: in addition to the
|
||||||
|
* above array, the value of every term in the field is kept in memory.
|
||||||
|
* If there are many unique terms in the field, this could
|
||||||
|
* be quite large.
|
||||||
|
*
|
||||||
* <p>Note that the size of the cache is not affected by how many
|
* <p>Note that the size of the cache is not affected by how many
|
||||||
* fields are in the index and <i>might</i> be used to sort - only by
|
* fields are in the index and <i>might</i> be used to sort - only by
|
||||||
* the ones actually used to sort a result set.
|
* the ones actually used to sort a result set.
|
||||||
|
@ -172,7 +177,7 @@ extends PriorityQueue {
|
||||||
*/
|
*/
|
||||||
protected static ScoreDocComparator determineComparator (IndexReader reader, String field)
|
protected static ScoreDocComparator determineComparator (IndexReader reader, String field)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
field = field.intern();
|
||||||
TermEnum enumerator = reader.terms (new Term (field, ""));
|
TermEnum enumerator = reader.terms (new Term (field, ""));
|
||||||
try {
|
try {
|
||||||
Term term = enumerator.term();
|
Term term = enumerator.term();
|
||||||
|
|
|
@ -66,12 +66,13 @@ extends FieldSortedHitQueue {
|
||||||
/**
|
/**
|
||||||
* Returns a comparator for sorting hits according to a field containing floats.
|
* Returns a comparator for sorting hits according to a field containing floats.
|
||||||
* @param reader Index to use.
|
* @param reader Index to use.
|
||||||
* @param field Field containg float values.
|
* @param fieldname Field containg float values.
|
||||||
* @return Comparator for sorting hits.
|
* @return Comparator for sorting hits.
|
||||||
* @throws IOException If an error occurs reading the index.
|
* @throws IOException If an error occurs reading the index.
|
||||||
*/
|
*/
|
||||||
static ScoreDocLookupComparator comparator (final IndexReader reader, final String field)
|
static ScoreDocLookupComparator comparator (final IndexReader reader, final String fieldname)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
final String field = fieldname.intern();
|
||||||
return new ScoreDocLookupComparator () {
|
return new ScoreDocLookupComparator () {
|
||||||
|
|
||||||
protected final float[] fieldOrder = generateSortIndex();
|
protected final float[] fieldOrder = generateSortIndex();
|
||||||
|
@ -140,12 +141,13 @@ extends FieldSortedHitQueue {
|
||||||
* Returns a comparator for sorting hits according to a field containing floats using the given enumerator
|
* Returns a comparator for sorting hits according to a field containing floats using the given enumerator
|
||||||
* to collect term values.
|
* to collect term values.
|
||||||
* @param reader Index to use.
|
* @param reader Index to use.
|
||||||
* @param field Field containg float values.
|
* @param fieldname Field containg float values.
|
||||||
* @return Comparator for sorting hits.
|
* @return Comparator for sorting hits.
|
||||||
* @throws IOException If an error occurs reading the index.
|
* @throws IOException If an error occurs reading the index.
|
||||||
*/
|
*/
|
||||||
static ScoreDocLookupComparator comparator (final IndexReader reader, final TermEnum enumerator, final String field)
|
static ScoreDocLookupComparator comparator (final IndexReader reader, final TermEnum enumerator, final String fieldname)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
final String field = fieldname.intern();
|
||||||
return new ScoreDocLookupComparator () {
|
return new ScoreDocLookupComparator () {
|
||||||
|
|
||||||
protected final float[] fieldOrder = generateSortIndex();
|
protected final float[] fieldOrder = generateSortIndex();
|
||||||
|
@ -202,4 +204,4 @@ extends FieldSortedHitQueue {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,8 @@ import java.io.IOException;
|
||||||
* Expert: A sorted hit queue for fields that contain strictly integer values.
|
* Expert: A sorted hit queue for fields that contain strictly integer values.
|
||||||
* Hits are sorted into the queue by the values in the field and then by document number.
|
* Hits are sorted into the queue by the values in the field and then by document number.
|
||||||
*
|
*
|
||||||
* <p>Created: Jan 30, 2004 3:35:09 PM
|
* <p>Created: Jan 30, 2004 3:35:09 PM
|
||||||
*
|
*
|
||||||
* @author Tim Jones (Nacimiento Software)
|
* @author Tim Jones (Nacimiento Software)
|
||||||
* @since lucene 1.4
|
* @since lucene 1.4
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
|
@ -67,12 +67,13 @@ extends FieldSortedHitQueue {
|
||||||
/**
|
/**
|
||||||
* Returns a comparator for sorting hits according to a field containing integers.
|
* Returns a comparator for sorting hits according to a field containing integers.
|
||||||
* @param reader Index to use.
|
* @param reader Index to use.
|
||||||
* @param field Field containg integer values.
|
* @param fieldname Field containg integer values.
|
||||||
* @return Comparator for sorting hits.
|
* @return Comparator for sorting hits.
|
||||||
* @throws IOException If an error occurs reading the index.
|
* @throws IOException If an error occurs reading the index.
|
||||||
*/
|
*/
|
||||||
static ScoreDocLookupComparator comparator (final IndexReader reader, final String field)
|
static ScoreDocLookupComparator comparator (final IndexReader reader, final String fieldname)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
final String field = fieldname.intern();
|
||||||
return new ScoreDocLookupComparator() {
|
return new ScoreDocLookupComparator() {
|
||||||
|
|
||||||
/** The sort information being used by this instance */
|
/** The sort information being used by this instance */
|
||||||
|
@ -142,12 +143,13 @@ extends FieldSortedHitQueue {
|
||||||
* Returns a comparator for sorting hits according to a field containing integers using the given enumerator
|
* Returns a comparator for sorting hits according to a field containing integers using the given enumerator
|
||||||
* to collect term values.
|
* to collect term values.
|
||||||
* @param reader Index to use.
|
* @param reader Index to use.
|
||||||
* @param field Field containg integer values.
|
* @param fieldname Field containg integer values.
|
||||||
* @return Comparator for sorting hits.
|
* @return Comparator for sorting hits.
|
||||||
* @throws IOException If an error occurs reading the index.
|
* @throws IOException If an error occurs reading the index.
|
||||||
*/
|
*/
|
||||||
static ScoreDocLookupComparator comparator (final IndexReader reader, final TermEnum enumerator, final String field)
|
static ScoreDocLookupComparator comparator (final IndexReader reader, final TermEnum enumerator, final String fieldname)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
final String field = fieldname.intern();
|
||||||
return new ScoreDocLookupComparator() {
|
return new ScoreDocLookupComparator() {
|
||||||
|
|
||||||
protected final int[] fieldOrder = generateSortIndex();
|
protected final int[] fieldOrder = generateSortIndex();
|
||||||
|
@ -204,4 +206,4 @@ extends FieldSortedHitQueue {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,8 @@ import java.io.IOException;
|
||||||
* The type of content in each field could be determined dynamically by
|
* The type of content in each field could be determined dynamically by
|
||||||
* FieldSortedHitQueue.determineComparator().
|
* FieldSortedHitQueue.determineComparator().
|
||||||
*
|
*
|
||||||
* <p>Created: Feb 3, 2004 4:46:55 PM
|
* <p>Created: Feb 3, 2004 4:46:55 PM
|
||||||
*
|
*
|
||||||
* @author Tim Jones (Nacimiento Software)
|
* @author Tim Jones (Nacimiento Software)
|
||||||
* @since lucene 1.4
|
* @since lucene 1.4
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
|
@ -50,8 +50,9 @@ extends PriorityQueue {
|
||||||
comparators = new ScoreDocComparator[n];
|
comparators = new ScoreDocComparator[n];
|
||||||
this.fields = new SortField[n];
|
this.fields = new SortField[n];
|
||||||
for (int i=0; i<n; ++i) {
|
for (int i=0; i<n; ++i) {
|
||||||
comparators[i] = FieldSortedHitQueue.getCachedComparator (reader, fields[i].getField(), fields[i].getType());
|
String fieldname = fields[i].getField();
|
||||||
this.fields[i] = new SortField (fields[i].getField(), comparators[i].sortType(), fields[i].getReverse());
|
comparators[i] = FieldSortedHitQueue.getCachedComparator (reader, fieldname, fields[i].getType());
|
||||||
|
this.fields[i] = new SortField (fieldname, comparators[i].sortType(), fields[i].getReverse());
|
||||||
}
|
}
|
||||||
initialize (size);
|
initialize (size);
|
||||||
}
|
}
|
||||||
|
@ -107,4 +108,4 @@ extends PriorityQueue {
|
||||||
SortField[] getFields() {
|
SortField[] getFields() {
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,62 @@ package org.apache.lucene.search;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates sort criteria for returned hits. The sort criteria can
|
* Encapsulates sort criteria for returned hits.
|
||||||
* be changed between calls to Searcher#search(). This class is thread safe.
|
*
|
||||||
|
* <p>The fields used to determine sort order must be carefully chosen.
|
||||||
|
* Documents must contain a single term in such a field,
|
||||||
|
* and the value of the term should indicate the document's relative position in
|
||||||
|
* a given sort order. The field must be indexed, but should not be tokenized,
|
||||||
|
* and does not need to be stored (unless you happen to want it back with the
|
||||||
|
* rest of your document data). In other words:
|
||||||
|
*
|
||||||
|
* <dl><dd><code>document.add (new Field ("byNumber", Integer.toString(x), false, true, false));</code>
|
||||||
|
* </dd></dl>
|
||||||
|
*
|
||||||
|
* <p><h3>Valid Types of Values</h3>
|
||||||
|
*
|
||||||
|
* <p>There are three possible kinds of term values which may be put into
|
||||||
|
* sorting fields: Integers, Floats, or Strings. Unless
|
||||||
|
* {@link SortField SortField} objects are specified, the type of value
|
||||||
|
* in the field is determined by using a regular expression against the
|
||||||
|
* first term in the field.
|
||||||
|
*
|
||||||
|
* <p>Integer term values should contain only digits and an optional
|
||||||
|
* preceeding negative sign. Values must be base 10 and in the range
|
||||||
|
* <code>Integer.MIN_VALUE</code> and <code>Integer.MAX_VALUE</code> inclusive.
|
||||||
|
* Documents which should appear first in the sort
|
||||||
|
* should have low value integers, later documents high values
|
||||||
|
* (i.e. the documents should be numbered <code>1..n</code> where
|
||||||
|
* <code>1</code> is the first and <code>n</code> the last).
|
||||||
|
*
|
||||||
|
* <p>Float term values should conform to values accepted by
|
||||||
|
* {@link Float Float.valueOf(String)} (except that <code>NaN</code>
|
||||||
|
* and <code>Infinity</code> are not supported).
|
||||||
|
* Documents which should appear first in the sort
|
||||||
|
* should have low values, later documents high values.
|
||||||
|
*
|
||||||
|
* <p>String term values can contain any valid String, but should
|
||||||
|
* not be tokenized. The values are sorted according to their
|
||||||
|
* {@link Comparable natural order}. Note that using this type
|
||||||
|
* of term value has higher memory requirements than the other
|
||||||
|
* two types - see {@link FieldSortedHitQueue FieldSortedHitQueue}.
|
||||||
|
*
|
||||||
|
* <p><h3>Object Reuse</h3>
|
||||||
|
*
|
||||||
|
* <p>One of these objects can be
|
||||||
|
* used multiple times and the sort order changed between usages.
|
||||||
|
*
|
||||||
|
* <p>This class is thread safe.
|
||||||
|
*
|
||||||
|
* <p><h3>Memory Usage</h3>
|
||||||
|
*
|
||||||
|
* See {@link FieldSortedHitQueue FieldSortedHitQueue} for
|
||||||
|
* information on the memory requirements of sorting hits.
|
||||||
|
*
|
||||||
|
* <p>Created: Feb 12, 2004 10:53:57 AM
|
||||||
*
|
*
|
||||||
* <p>Created: Feb 12, 2004 10:53:57 AM
|
|
||||||
*
|
|
||||||
* @author Tim Jones (Nacimiento Software)
|
* @author Tim Jones (Nacimiento Software)
|
||||||
* @since lucene 1.4
|
* @since lucene 1.4
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
|
@ -31,80 +81,95 @@ import java.io.Serializable;
|
||||||
public class Sort
|
public class Sort
|
||||||
implements Serializable {
|
implements Serializable {
|
||||||
|
|
||||||
/** Represents sorting by computed relevance. Using this sort criteria
|
/** Represents sorting by computed relevance. Using this sort criteria
|
||||||
* returns the same results with slightly more overhead as calling
|
* returns the same results with slightly more overhead as calling
|
||||||
* Searcher#search() without a sort criteria. */
|
* Searcher#search() without a sort criteria. */
|
||||||
public static final Sort RELEVANCE =
|
public static final Sort RELEVANCE = new Sort ();
|
||||||
new Sort (new SortField[] { SortField.FIELD_SCORE, SortField.FIELD_DOC });
|
|
||||||
|
|
||||||
/** Represents sorting by index order. */
|
/** Represents sorting by index order. */
|
||||||
public static final Sort INDEXORDER = new Sort (SortField.FIELD_DOC);
|
public static final Sort INDEXORDER = new Sort (SortField.FIELD_DOC);
|
||||||
|
|
||||||
|
// internal representation of the sort criteria
|
||||||
|
SortField[] fields;
|
||||||
|
|
||||||
|
|
||||||
// internal representation of the sort criteria
|
/** Sorts by computed relevance. This is the same sort criteria as
|
||||||
SortField[] fields;
|
* calling Searcher#search() without a sort criteria, only with
|
||||||
|
* slightly more overhead. */
|
||||||
|
public Sort() {
|
||||||
|
this (new SortField[]{SortField.FIELD_SCORE, SortField.FIELD_DOC});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Sorts by the terms in <code>field</code> then by index order (document
|
/** Sorts by the terms in <code>field</code> then by index order (document
|
||||||
* number). */
|
* number). */
|
||||||
public Sort (String field) {
|
public Sort (String field) {
|
||||||
setSort (field, false);
|
setSort (field, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sorts possibly in reverse by the terms in <code>field</code> then by
|
|
||||||
* index order (document number). */
|
|
||||||
public Sort (String field, boolean reverse) {
|
|
||||||
setSort (field, reverse);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sorts in succession by the terms in each field. */
|
/** Sorts possibly in reverse by the terms in <code>field</code> then by
|
||||||
public Sort (String[] fields) {
|
* index order (document number). */
|
||||||
setSort (fields);
|
public Sort (String field, boolean reverse) {
|
||||||
}
|
setSort (field, reverse);
|
||||||
|
}
|
||||||
|
|
||||||
/** Sorts by the criteria in the given SortField. */
|
|
||||||
public Sort (SortField field) {
|
|
||||||
setSort (field);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sorts in succession by the criteria in each SortField. */
|
/** Sorts in succession by the terms in each field. */
|
||||||
public Sort (SortField[] fields) {
|
public Sort (String[] fields) {
|
||||||
setSort (fields);
|
setSort (fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the sort to the terms in <code>field</code> then by index order
|
|
||||||
* (document number). */
|
|
||||||
public final void setSort (String field) {
|
|
||||||
setSort (field, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets the sort to the terms in <code>field</code> possibly in reverse,
|
/** Sorts by the criteria in the given SortField. */
|
||||||
* then by index order (document number). */
|
public Sort (SortField field) {
|
||||||
public void setSort (String field, boolean reverse) {
|
setSort (field);
|
||||||
SortField[] nfields = new SortField[] {
|
}
|
||||||
new SortField (field, SortField.AUTO, reverse),
|
|
||||||
new SortField (field, SortField.DOC)
|
|
||||||
};
|
|
||||||
fields = nfields;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets the sort to the terms in each field in succession. */
|
|
||||||
public void setSort (String[] fieldnames) {
|
|
||||||
final int n = fieldnames.length;
|
|
||||||
SortField[] nfields = new SortField[n];
|
|
||||||
for (int i=0; i<n; ++i) {
|
|
||||||
nfields[i] = new SortField (fieldnames[i], SortField.AUTO);
|
|
||||||
}
|
|
||||||
fields = nfields;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets the sort to the given criteria. */
|
/** Sorts in succession by the criteria in each SortField. */
|
||||||
public void setSort (SortField field) {
|
public Sort (SortField[] fields) {
|
||||||
this.fields = new SortField[] { field };
|
setSort (fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the sort to the given criteria in succession. */
|
|
||||||
public void setSort (SortField[] fields) {
|
/** Sets the sort to the terms in <code>field</code> then by index order
|
||||||
this.fields = fields;
|
* (document number). */
|
||||||
}
|
public final void setSort (String field) {
|
||||||
|
setSort (field, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Sets the sort to the terms in <code>field</code> possibly in reverse,
|
||||||
|
* then by index order (document number). */
|
||||||
|
public void setSort (String field, boolean reverse) {
|
||||||
|
SortField[] nfields = new SortField[]{
|
||||||
|
new SortField (field, SortField.AUTO, reverse),
|
||||||
|
new SortField (field, SortField.DOC)
|
||||||
|
};
|
||||||
|
fields = nfields;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Sets the sort to the terms in each field in succession. */
|
||||||
|
public void setSort (String[] fieldnames) {
|
||||||
|
final int n = fieldnames.length;
|
||||||
|
SortField[] nfields = new SortField[n];
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
nfields[i] = new SortField (fieldnames[i], SortField.AUTO);
|
||||||
|
}
|
||||||
|
fields = nfields;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Sets the sort to the given criteria. */
|
||||||
|
public void setSort (SortField field) {
|
||||||
|
this.fields = new SortField[]{field};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Sets the sort to the given criteria in succession. */
|
||||||
|
public void setSort (SortField[] fields) {
|
||||||
|
this.fields = fields;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ implements Serializable {
|
||||||
* @param field Name of field to sort by, cannot be <code>null</code>.
|
* @param field Name of field to sort by, cannot be <code>null</code>.
|
||||||
*/
|
*/
|
||||||
public SortField (String field) {
|
public SortField (String field) {
|
||||||
this.field = field;
|
this.field = field.intern();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a sort, possibly in reverse, by terms in the given field where
|
/** Creates a sort, possibly in reverse, by terms in the given field where
|
||||||
|
@ -83,7 +83,7 @@ implements Serializable {
|
||||||
* @param reverse True if natural order should be reversed.
|
* @param reverse True if natural order should be reversed.
|
||||||
*/
|
*/
|
||||||
public SortField (String field, boolean reverse) {
|
public SortField (String field, boolean reverse) {
|
||||||
this.field = field;
|
this.field = field.intern();
|
||||||
this.reverse = reverse;
|
this.reverse = reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ implements Serializable {
|
||||||
* @param type Type of values in the terms.
|
* @param type Type of values in the terms.
|
||||||
*/
|
*/
|
||||||
public SortField (String field, int type) {
|
public SortField (String field, int type) {
|
||||||
this.field = field;
|
this.field = (field != null) ? field.intern() : field;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ implements Serializable {
|
||||||
* @param reverse True if natural order should be reversed.
|
* @param reverse True if natural order should be reversed.
|
||||||
*/
|
*/
|
||||||
public SortField (String field, int type, boolean reverse) {
|
public SortField (String field, int type, boolean reverse) {
|
||||||
this.field = field;
|
this.field = (field != null) ? field.intern() : field;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.reverse = reverse;
|
this.reverse = reverse;
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,4 +223,4 @@ extends FieldSortedHitQueue {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,341 @@
|
||||||
|
package org.apache.lucene.search;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright 2004 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.apache.lucene.store.RAMDirectory;
|
||||||
|
import org.apache.lucene.index.IndexWriter;
|
||||||
|
import org.apache.lucene.index.Term;
|
||||||
|
import org.apache.lucene.analysis.SimpleAnalyzer;
|
||||||
|
import org.apache.lucene.document.Document;
|
||||||
|
import org.apache.lucene.document.Field;
|
||||||
|
|
||||||
|
import java.rmi.Naming;
|
||||||
|
import java.rmi.registry.LocateRegistry;
|
||||||
|
import java.rmi.registry.Registry;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import junit.framework.Test;
|
||||||
|
import junit.framework.TestSuite;
|
||||||
|
import junit.textui.TestRunner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for sorting code.
|
||||||
|
*
|
||||||
|
* <p>Created: Feb 17, 2004 4:55:10 PM
|
||||||
|
*
|
||||||
|
* @author Tim Jones (Nacimiento Software)
|
||||||
|
* @since lucene 1.4
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class TestSort
|
||||||
|
extends TestCase {
|
||||||
|
|
||||||
|
private Searcher full;
|
||||||
|
private Searcher searchX;
|
||||||
|
private Searcher searchY;
|
||||||
|
private Query queryX;
|
||||||
|
private Query queryY;
|
||||||
|
private Query queryA;
|
||||||
|
private Sort sort;
|
||||||
|
|
||||||
|
|
||||||
|
public TestSort (String name) {
|
||||||
|
super (name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main (String[] argv) {
|
||||||
|
if (argv == null || argv.length < 1)
|
||||||
|
TestRunner.run (suite());
|
||||||
|
else if ("server".equals (argv[0])) {
|
||||||
|
TestSort test = new TestSort (null);
|
||||||
|
try {
|
||||||
|
test.startServer();
|
||||||
|
Thread.sleep (500000);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println (e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Test suite() {
|
||||||
|
return new TestSuite (TestSort.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// document data:
|
||||||
|
// the tracer field is used to determine which document was hit
|
||||||
|
// the contents field is used to search and sort by relevance
|
||||||
|
// the int field to sort by int
|
||||||
|
// the float field to sort by float
|
||||||
|
// the string field to sort by string
|
||||||
|
private String[][] data = new String[][] {
|
||||||
|
// tracer contents int float string
|
||||||
|
{ "A", "x a", "5", "4f", "c" },
|
||||||
|
{ "B", "y a", "5", "3.4028235E38", "i" },
|
||||||
|
{ "C", "x a b c", "2147483647", "1.0", "j" },
|
||||||
|
{ "D", "y a b c", "-1", "0.0f", "a" },
|
||||||
|
{ "E", "x a b c d", "5", "2f", "h" },
|
||||||
|
{ "F", "y a b c d", "2", "3.14159f", "g" },
|
||||||
|
{ "G", "x a b c d", "3", "-1.0", "f" },
|
||||||
|
{ "H", "y a b c d", "0", "1.4E-45", "e" },
|
||||||
|
{ "I", "x a b c d e f", "-2147483648", "1.0e+0", "d" },
|
||||||
|
{ "J", "y a b c d e f", "4", ".5", "b" },
|
||||||
|
};
|
||||||
|
|
||||||
|
// create an index of all the documents, or just the x, or just the y documents
|
||||||
|
private Searcher getIndex (boolean even, boolean odd)
|
||||||
|
throws IOException {
|
||||||
|
RAMDirectory indexStore = new RAMDirectory ();
|
||||||
|
IndexWriter writer = new IndexWriter (indexStore, new SimpleAnalyzer(), true);
|
||||||
|
for (int i=0; i<data.length; ++i) {
|
||||||
|
if (((i%2)==0 && even) || ((i%2)==1 && odd)) {
|
||||||
|
Document doc = new Document(); // store, index, token
|
||||||
|
doc.add (new Field ("tracer", data[i][0], true, false, false));
|
||||||
|
doc.add (new Field ("contents", data[i][1], false, true, true));
|
||||||
|
doc.add (new Field ("int", data[i][2], false, true, false));
|
||||||
|
doc.add (new Field ("float", data[i][3], false, true, false));
|
||||||
|
doc.add (new Field ("string", data[i][4], false, true, false));
|
||||||
|
writer.addDocument (doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writer.optimize ();
|
||||||
|
writer.close ();
|
||||||
|
return new IndexSearcher (indexStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Searcher getFullIndex()
|
||||||
|
throws IOException {
|
||||||
|
return getIndex (true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Searcher getXIndex()
|
||||||
|
throws IOException {
|
||||||
|
return getIndex (true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Searcher getYIndex()
|
||||||
|
throws IOException {
|
||||||
|
return getIndex (false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
full = getFullIndex();
|
||||||
|
searchX = getXIndex();
|
||||||
|
searchY = getYIndex();
|
||||||
|
queryX = new TermQuery (new Term ("contents", "x"));
|
||||||
|
queryY = new TermQuery (new Term ("contents", "y"));
|
||||||
|
queryA = new TermQuery (new Term ("contents", "a"));
|
||||||
|
sort = new Sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
// test the sorts by score and document number
|
||||||
|
public void testBuiltInSorts() throws Exception {
|
||||||
|
sort = new Sort();
|
||||||
|
assertMatches (full, queryX, sort, "ACEGI");
|
||||||
|
assertMatches (full, queryY, sort, "BDFHJ");
|
||||||
|
|
||||||
|
sort.setSort(SortField.FIELD_DOC);
|
||||||
|
assertMatches (full, queryX, sort, "ACEGI");
|
||||||
|
assertMatches (full, queryY, sort, "BDFHJ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// test sorts where the type of field is specified
|
||||||
|
public void testTypedSort() throws Exception {
|
||||||
|
sort.setSort (new SortField[] { new SortField ("int", SortField.INT), SortField.FIELD_DOC });
|
||||||
|
assertMatches (full, queryX, sort, "IGAEC");
|
||||||
|
assertMatches (full, queryY, sort, "DHFJB");
|
||||||
|
|
||||||
|
sort.setSort (new SortField[] { new SortField ("float", SortField.FLOAT), SortField.FIELD_DOC });
|
||||||
|
assertMatches (full, queryX, sort, "GCIEA");
|
||||||
|
assertMatches (full, queryY, sort, "DHJFB");
|
||||||
|
|
||||||
|
sort.setSort (new SortField[] { new SortField ("string", SortField.STRING), SortField.FIELD_DOC });
|
||||||
|
assertMatches (full, queryX, sort, "AIGEC");
|
||||||
|
assertMatches (full, queryY, sort, "DJHFB");
|
||||||
|
}
|
||||||
|
|
||||||
|
// test sorts where the type of field is determined dynamically
|
||||||
|
public void testAutoSort() throws Exception {
|
||||||
|
sort.setSort("int");
|
||||||
|
assertMatches (full, queryX, sort, "IGAEC");
|
||||||
|
assertMatches (full, queryY, sort, "DHFJB");
|
||||||
|
|
||||||
|
sort.setSort("float");
|
||||||
|
assertMatches (full, queryX, sort, "GCIEA");
|
||||||
|
assertMatches (full, queryY, sort, "DHJFB");
|
||||||
|
|
||||||
|
sort.setSort("string");
|
||||||
|
assertMatches (full, queryX, sort, "AIGEC");
|
||||||
|
assertMatches (full, queryY, sort, "DJHFB");
|
||||||
|
}
|
||||||
|
|
||||||
|
// test sorts in reverse
|
||||||
|
public void testReverseSort() throws Exception {
|
||||||
|
sort.setSort (new SortField[] { new SortField (null, SortField.SCORE, true), SortField.FIELD_DOC });
|
||||||
|
assertMatches (full, queryX, sort, "IEGCA");
|
||||||
|
assertMatches (full, queryY, sort, "JFHDB");
|
||||||
|
|
||||||
|
sort.setSort (new SortField (null, SortField.DOC, true));
|
||||||
|
assertMatches (full, queryX, sort, "IGECA");
|
||||||
|
assertMatches (full, queryY, sort, "JHFDB");
|
||||||
|
|
||||||
|
sort.setSort ("int", true);
|
||||||
|
assertMatches (full, queryX, sort, "CAEGI");
|
||||||
|
assertMatches (full, queryY, sort, "BJFHD");
|
||||||
|
|
||||||
|
sort.setSort ("float", true);
|
||||||
|
assertMatches (full, queryX, sort, "AECIG");
|
||||||
|
assertMatches (full, queryY, sort, "BFJHD");
|
||||||
|
|
||||||
|
sort.setSort ("string", true);
|
||||||
|
assertMatches (full, queryX, sort, "CEGIA");
|
||||||
|
assertMatches (full, queryY, sort, "BFHJD");
|
||||||
|
}
|
||||||
|
|
||||||
|
// test sorts using a series of fields
|
||||||
|
public void testSortCombos() throws Exception {
|
||||||
|
sort.setSort (new String[] {"int","float"});
|
||||||
|
assertMatches (full, queryX, sort, "IGEAC");
|
||||||
|
|
||||||
|
sort.setSort (new SortField[] { new SortField ("int", true), new SortField (null, SortField.DOC, true) });
|
||||||
|
assertMatches (full, queryX, sort, "CEAGI");
|
||||||
|
|
||||||
|
sort.setSort (new String[] {"float","string"});
|
||||||
|
assertMatches (full, queryX, sort, "GICEA");
|
||||||
|
}
|
||||||
|
|
||||||
|
// test a variety of sorts using more than one searcher
|
||||||
|
public void testMultiSort() throws Exception {
|
||||||
|
MultiSearcher searcher = new MultiSearcher (new Searchable[] { searchX, searchY });
|
||||||
|
runMultiSorts (searcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
// test a variety of sorts using a parallel multisearcher
|
||||||
|
public void testParallelMultiSort() throws Exception {
|
||||||
|
Searcher searcher = new ParallelMultiSearcher (new Searchable[] { searchX, searchY });
|
||||||
|
runMultiSorts (searcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
// test a variety of sorts using a remote searcher
|
||||||
|
public void testRemoteSort() throws Exception {
|
||||||
|
Searchable searcher = getRemote();
|
||||||
|
MultiSearcher multi = new MultiSearcher (new Searchable[] { searcher });
|
||||||
|
runMultiSorts (multi);
|
||||||
|
}
|
||||||
|
|
||||||
|
// runs a variety of sorts useful for multisearchers
|
||||||
|
private void runMultiSorts (Searcher multi) throws Exception {
|
||||||
|
sort.setSort (SortField.FIELD_DOC);
|
||||||
|
assertMatchesPattern (multi, queryA, sort, "[AB]{2}[CD]{2}[EF]{2}[GH]{2}[IJ]{2}");
|
||||||
|
|
||||||
|
sort.setSort (new SortField ("int", SortField.INT));
|
||||||
|
assertMatchesPattern (multi, queryA, sort, "IDHFGJ[ABE]{3}C");
|
||||||
|
|
||||||
|
sort.setSort (new SortField[] {new SortField ("int", SortField.INT), SortField.FIELD_DOC});
|
||||||
|
assertMatchesPattern (multi, queryA, sort, "IDHFGJ[AB]{2}EC");
|
||||||
|
|
||||||
|
sort.setSort ("int");
|
||||||
|
assertMatchesPattern (multi, queryA, sort, "IDHFGJ[AB]{2}EC");
|
||||||
|
|
||||||
|
sort.setSort (new SortField[] {new SortField ("float", SortField.FLOAT), SortField.FIELD_DOC});
|
||||||
|
assertMatchesPattern (multi, queryA, sort, "GDHJ[CI]{2}EFAB");
|
||||||
|
|
||||||
|
sort.setSort ("float");
|
||||||
|
assertMatchesPattern (multi, queryA, sort, "GDHJ[CI]{2}EFAB");
|
||||||
|
|
||||||
|
sort.setSort ("string");
|
||||||
|
assertMatches (multi, queryA, sort, "DJAIHGFEBC");
|
||||||
|
|
||||||
|
sort.setSort ("int", true);
|
||||||
|
assertMatchesPattern (multi, queryA, sort, "C[AB]{2}EJGFHDI");
|
||||||
|
|
||||||
|
sort.setSort ("float", true);
|
||||||
|
assertMatchesPattern (multi, queryA, sort, "BAFE[IC]{2}JHDG");
|
||||||
|
|
||||||
|
sort.setSort ("string", true);
|
||||||
|
assertMatches (multi, queryA, sort, "CBEFGHIAJD");
|
||||||
|
|
||||||
|
sort.setSort (new String[] {"int","float"});
|
||||||
|
assertMatches (full, queryA, sort, "IDHFGJEABC");
|
||||||
|
|
||||||
|
sort.setSort (new String[] {"float","string"});
|
||||||
|
assertMatches (full, queryA, sort, "GDHJICEFAB");
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the documents returned by the search match the expected list
|
||||||
|
private void assertMatches (Searcher searcher, Query query, Sort sort, String expectedResult)
|
||||||
|
throws IOException {
|
||||||
|
Hits result = searcher.search (query, sort);
|
||||||
|
StringBuffer buff = new StringBuffer(10);
|
||||||
|
int n = result.length();
|
||||||
|
for (int i=0; i<n; ++i) {
|
||||||
|
Document doc = result.doc(i);
|
||||||
|
String[] v = doc.getValues("tracer");
|
||||||
|
for (int j=0; j<v.length; ++j) {
|
||||||
|
buff.append (v[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals (expectedResult, buff.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the documents returned by the search match the expected list pattern
|
||||||
|
private void assertMatchesPattern (Searcher searcher, Query query, Sort sort, String pattern)
|
||||||
|
throws IOException {
|
||||||
|
Hits result = searcher.search (query, sort);
|
||||||
|
StringBuffer buff = new StringBuffer(10);
|
||||||
|
int n = result.length();
|
||||||
|
for (int i=0; i<n; ++i) {
|
||||||
|
Document doc = result.doc(i);
|
||||||
|
String[] v = doc.getValues("tracer");
|
||||||
|
for (int j=0; j<v.length; ++j) {
|
||||||
|
buff.append (v[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// System.out.println ("matching \""+buff+"\" against pattern \""+pattern+"\"");
|
||||||
|
assertTrue (Pattern.compile(pattern).matcher(buff.toString()).matches());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Searchable getRemote () throws Exception {
|
||||||
|
try {
|
||||||
|
return lookupRemote ();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
startServer ();
|
||||||
|
return lookupRemote ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Searchable lookupRemote () throws Exception {
|
||||||
|
return (Searchable) Naming.lookup ("//localhost/SortedSearchable");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startServer () throws Exception {
|
||||||
|
// construct an index
|
||||||
|
Searcher local = getFullIndex();
|
||||||
|
// local.search (queryA, new Sort());
|
||||||
|
|
||||||
|
// publish it
|
||||||
|
Registry reg = LocateRegistry.createRegistry (1099);
|
||||||
|
RemoteSearchable impl = new RemoteSearchable (local);
|
||||||
|
Naming.rebind ("//localhost/SortedSearchable", impl);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue