The custom sorting function in Lucene is similar to the implementation method of custom sorting in Java collections. Both of them need to implement the comparison interface. In Java, you only need to implement the Comparable interface. However, in Lucene, you need to implement the SortComparatorSource interface and the ScoreDocComparator interface. Let’s take a look at the definitions of these two interfaces before understanding the specific implementation method.
The function of the SortComparatorSource interface is to return a comparator (Expert: returns a comparator for sorting ScoreDocs). This interface only defines one method. As follows:
Java code
/**
* Creates a comparator for the field in the given index.
* @param reader - Index to create comparator for.
* @param fieldname - Field to create comparator for.
* @return Comparator of ScoreDoc objects.
* @throws IOException - If an error occurs reading the index.
*/
public ScoreDocComparator newComparator(IndexReader reader,String fieldname) throws IOException
view plaincopy to clipboardprint?
/**
* Creates a comparator for the field in the given index.
* @param reader - Index to create comparator for.
* @param fieldname - Field to create comparator for.
* @return Comparator of ScoreDoc objects.
* @throws IOException - If an error occurs reading the index.
*/
public ScoreDocComparator newComparator(IndexReader reader,String fieldname) throws IOException
/**
* Creates a comparator for the field in the given index.
* @param reader - Index to create comparator for.
* @param fieldname - Field to create comparator for.
* @return Comparator of ScoreDoc objects.
* @throws IOException - If an error occurs reading the index.
*/
public ScoreDocComparator newComparator(IndexReader reader,String fieldname) throws IOException
This method just creates a ScoreDocComparator instance to implement sorting. So we also need to implement the ScoreDocComparator interface. Let's take a look at the ScoreDocComparator interface. The function is to compare two ScoreDoc objects for sorting. Two static instances of Lucene implementation are defined in the following:
Java code
//Special comparator for sorting hits according to computed relevance (document score).
public static final ScoreDocComparator RELEVANCE;
//Special comparator for sorting hits according to index order (document number).
public static final ScoreDocComparator INDEXORDER;
view plaincopy to clipboardprint?
//Special comparator for sorting hits according to computed relevance (document score).
public static final ScoreDocComparator RELEVANCE;
//Special comparator for sorting hits according to index order (document number).
public static final ScoreDocComparator INDEXORDER;
//Special comparator for sorting hits according to computed relevance (document score).
public static final ScoreDocComparator RELEVANCE;
//Special comparator for sorting hits according to index order (document number).
public static final ScoreDocComparator INDEXORDER;
There are 3 methods related to sorting, which we need to implement as follows:
Java code
/**
* Compares two ScoreDoc objects and returns a result indicating their sort order.
* @param i First ScoreDoc
* @param j Second ScoreDoc
* @return -1 if i should come before j;
* 1 if i should come after j;
* 0 if they are equal
*/
public int compare(ScoreDoc i,ScoreDoc j);
/**
* Returns the value used to sort the given document. The object returned must implement the interface. This is used by multisearchers to determine how to collate results from their searchers.
* @param i Document
* @return Serializable object
*/
public Comparable sortValue(ScoreDoc i);
/**
* Returns the type of sort. Should return , , , , or . It is not valid to return . This is used by multisearchers to determine how to collate results from their searchers.
* @return One of the constants in SortField.
*/
public int sortType();
view plaincopy to clipboardprint?
/**
* Compares two ScoreDoc objects and returns a result indicating their sort order.
* @param i First ScoreDoc
* @param j Second ScoreDoc
* @return -1 if i should come before j;
* 1 if i should come after j;
* 0 if they are equal
*/
public int compare(ScoreDoc i,ScoreDoc j);
/**
* Returns the value used to sort the given document. The object returned must implement the interface. This is used by multisearchers to determine how to collate results from their searchers.
* @param i Document
* @return Serializable object
*/
public Comparable sortValue(ScoreDoc i);
/**
* Returns the type of sort. Should return , , , , or . It is not valid to return . This is used by multisearchers to determine how to collate results from their searchers.
* @return One of the constants in SortField.
*/
public int sortType();
/**
* Compares two ScoreDoc objects and returns a result indicating their sort order.
* @param i First ScoreDoc
* @param j Second ScoreDoc
* @return -1 if i should come before j;
* 1 if i should come after j;
* 0 if they are equal
*/
public int compare(ScoreDoc i,ScoreDoc j);
/**
* Returns the value used to sort the given document. The object returned must implement the interface. This is used by multisearchers to determine how to collate results from their searchers.
* @param i Document
* @return Serializable object
*/
public Comparable sortValue(ScoreDoc i);
/**
* Returns the type of sort. Should return , , , , or . It is not valid to return . This is used by multisearchers to determine how to collate results from their searchers.
* @return One of the constants in SortField.
*/
public int sortType();
Let’s see an example!
This example is an implementation in Lucene in Action, used to search for the name of the restaurant closest to you. Restaurant coordinates are stored in the string "x,y".
Java code
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
//Implement the search for the name of the restaurant closest to you. The restaurant coordinates are stored in the string "x,y"
//DistanceComparatorSource implements the SortComparatorSource interface
public class DistanceComparatorSource implements SortComparatorSource {
private static final long serialVersionUID = 1L;
// x y is used to save coordinate position
private int x;
private int y;
public DistanceComparatorSource(int x, int y) {
= x;
= y;
}
// Return to ScoreDocComparator to implement sorting function
public ScoreDocComparator newComparator(IndexReader reader, String fieldname) throws IOException {
return new DistanceScoreDocLookupComparator(reader, fieldname, x, y);
}
//DistanceScoreDocLookupComparator implements ScoreDocComparator for sorting
private static class DistanceScoreDocLookupComparator implements ScoreDocComparator {
private float[] distances; // Save the distance from each restaurant to a specified point
// Constructor, the constructor completes almost all the preparations here.
public DistanceScoreDocLookupComparator(IndexReader reader, String fieldname, int x, int y) throws IOException {
("fieldName2="+fieldname);
final TermEnum enumerator = (new Term(fieldname, ""));
("maxDoc="+());
distances = new float[()]; // Initialize distances
if ( > 0) {
TermDocs termDocs = ();
try {
if (() == null) {
throw new RuntimeException("no terms in field " + fieldname);
}
int i = 0,j = 0;
do {
("in do-while :" + i ++);
Term term = (); // Take out each Term
if (() != fieldname) // If it does not match the given domain, compare the next one
break;
//Sets this to the data for the current term in a TermEnum.
//This may be optimized in some implementations.
(enumerator); //Reference TermDocs Doc
while (()) {
(" in while :" + j ++);
(" in while ,Term :" + ());
String[] xy = ().split(","); // Where to go x y
int deltax = (xy[0]) - x;
int deltay = (xy[1]) - y;
// Calculate distance
distances[()] = (float) (deltax * deltax + deltay * deltay);
}
}
while (());
} finally {
();
}
}
}
//The above constructor preparation is simpler here
public int compare(ScoreDoc i, ScoreDoc j) {
if (distances[] < distances[])
return -1;
if (distances[] > distances[])
return 1;
return 0;
}
// Return distance
public Comparable sortValue(ScoreDoc i) {
return new Float(distances[]);
}
//Specify SortType
public int sortType() {
return ;
}
}
public String toString() {
return "Distance from (" + x + "," + y + ")";
}
}
view plaincopy to clipboardprint?
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
//Implement the search for the name of the restaurant closest to you. The restaurant coordinates are stored in the string "x,y"
//DistanceComparatorSource implements the SortComparatorSource interface
public class DistanceComparatorSource implements SortComparatorSource {
private static final long serialVersionUID = 1L;
// x y is used to save coordinate position
private int x;
private int y;
public DistanceComparatorSource(int x, int y) {
= x;
= y;
}
// Return to ScoreDocComparator to implement sorting function
public ScoreDocComparator newComparator(IndexReader reader, String fieldname) throws IOException {
return new DistanceScoreDocLookupComparator(reader, fieldname, x, y);
}
//DistanceScoreDocLookupComparator implements ScoreDocComparator for sorting
private static class DistanceScoreDocLookupComparator implements ScoreDocComparator {
private float[] distances; // Save the distance from each restaurant to a specified point
// Constructor, the constructor completes almost all the preparations here.
public DistanceScoreDocLookupComparator(IndexReader reader, String fieldname, int x, int y) throws IOException {
("fieldName2="+fieldname);
final TermEnum enumerator = (new Term(fieldname, ""));
("maxDoc="+());
distances = new float[()]; // Initialize distances
if ( > 0) {
TermDocs termDocs = ();
try {
if (() == null) {
throw new RuntimeException("no terms in field " + fieldname);
}
int i = 0,j = 0;
do {
("in do-while :" + i ++);
Term term = (); // Take out each Term
if (() != fieldname) // If it does not match the given domain, compare the next one
break;
//Sets this to the data for the current term in a TermEnum.
//This may be optimized in some implementations.
(enumerator); //Reference TermDocs Doc
while (()) {
(" in while :" + j ++);
(" in while ,Term :" + ());
String[] xy = ().split(","); // Where to go x y
int deltax = (xy[0]) - x;
int deltay = (xy[1]) - y;
// Calculate distance
distances[()] = (float) (deltax * deltax + deltay * deltay);
}
}
while (());
} finally {
();
}
}
}
//The above constructor preparation is simpler here
public int compare(ScoreDoc i, ScoreDoc j) {
if (distances[] < distances[])
return -1;
if (distances[] > distances[])
return 1;
return 0;
}
// Return distance
public Comparable sortValue(ScoreDoc i) {
return new Float(distances[]);
}
//Specify SortType
public int sortType() {
return ;
}
}
public String toString() {
return "Distance from (" + x + "," + y + ")";
}
}
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
//Implement the search for the name of the restaurant closest to you. The restaurant coordinates are stored in the string "x,y"
//DistanceComparatorSource implements the SortComparatorSource interface
public class DistanceComparatorSource implements SortComparatorSource {
private static final long serialVersionUID = 1L;
// x y is used to save coordinate position
private int x;
private int y;
public DistanceComparatorSource(int x, int y) {
= x;
= y;
}
// Return to ScoreDocComparator to implement sorting function
public ScoreDocComparator newComparator(IndexReader reader, String fieldname) throws IOException {
return new DistanceScoreDocLookupComparator(reader, fieldname, x, y);
}
//DistanceScoreDocLookupComparator implements ScoreDocComparator for sorting
private static class DistanceScoreDocLookupComparator implements ScoreDocComparator {
private float[] distances; // Save the distance from each restaurant to a specified point
// Constructor, the constructor completes almost all the preparations here.
public DistanceScoreDocLookupComparator(IndexReader reader, String fieldname, int x, int y) throws IOException {
("fieldName2="+fieldname);
final TermEnum enumerator = (new Term(fieldname, ""));
("maxDoc="+());
distances = new float[()]; // Initialize distances
if ( > 0) {
TermDocs termDocs = ();
try {
if (() == null) {
throw new RuntimeException("no terms in field " + fieldname);
}
int i = 0,j = 0;
do {
("in do-while :" + i ++);
Term term = (); // Take out each Term
if (() != fieldname) // If it does not match the given domain, compare the next one
break;
//Sets this to the data for the current term in a TermEnum.
//This may be optimized in some implementations.
(enumerator); //Reference TermDocs Doc
while (()) {
(" in while :" + j ++);
(" in while ,Term :" + ());
String[] xy = ().split(","); // Where to go x y
int deltax = (xy[0]) - x;
int deltay = (xy[1]) - y;
// Calculate distance
distances[()] = (float) (deltax * deltax + deltay * deltay);
}
}
while (());
} finally {
();
}
}
}
//The above constructor preparation is relatively simple here
public int compare(ScoreDoc i, ScoreDoc j) {
if (distances[] < distances[])
return -1;
if (distances[] > distances[])
return 1;
return 0;
}
// Return distance
public Comparable sortValue(ScoreDoc i) {
return new Float(distances[]);
}
//Specify SortType
public int sortType() {
return ;
}
}
public String toString() {
return "Distance from (" + x + "," + y + ")";
}
}
These are two classes that implement the above two interfaces, with detailed comments inside, which shows that custom sorting is not difficult. Whether this implementation can be implemented correctly, let's see if the test code can pass.
Java code
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
public class DistanceComparatorSourceTest extends TestCase {
private RAMDirectory directory;
private IndexSearcher searcher;
private Query query;
//Create a test environment
protected void setUp() throws Exception {
directory = new RAMDirectory();
IndexWriter writer = new IndexWriter(directory, new WhitespaceAnalyzer(), true);
addPoint(writer, "El Charro", "restaurant", 1, 2);
addPoint(writer, "Cafe Poca Cosa", "restaurant", 5, 9);
addPoint(writer, "Los Betos", "restaurant", 9, 6);
addPoint(writer, "Nico's Taco Shop", "restaurant", 3, 8);
();
searcher = new IndexSearcher(directory);
query = new TermQuery(new Term("type", "restaurant"));
}
private void addPoint(IndexWriter writer, String name, String type, int x, int y) throws IOException {
Document doc = new Document();
(new Field("name", name, , ));
(new Field("type", type, , ));
(new Field("location", x + "," + y, , .UN_TOKENIZED));
(doc);
}
public void testNearestRestaurantToHome() throws Exception {
//Use DistanceComparatorSource to construct a SortField
Sort sort = new Sort(new SortField("location", new DistanceComparatorSource(0, 0)));
Hits hits = (query, sort); // Search
//test
assertEquals("closest", "El Charro", (0).get("name"));
assertEquals("furthest", "Los Betos", (3).get("name"));
}
public void testNeareastRestaurantToWork() throws Exception {
Sort sort = new Sort(new SortField("location", new DistanceComparatorSource(10, 10))); // Working coordinates 10,10
//The above test implements custom sorting, but cannot access more detailed information about custom sorting.
//TopFieldDocs can further access relevant information
TopFieldDocs docs = (query, null, 3, sort);
assertEquals(4, );
assertEquals(3, );
//Get FieldDoc Use FieldDoc to obtain more detailed information about sorting. Please check FieldDoc Doc
FieldDoc fieldDoc = (FieldDoc) [0];
assertEquals("(10,10) -> (9,6) = sqrt(17)", new Float((17)), [0]);
Document document = ();
assertEquals("Los Betos", ("name"));
dumpDocs(sort, docs); // Show relevant information
}
// Show information about sorting
private void dumpDocs(Sort sort, TopFieldDocs docs) throws IOException {
("Sorted by: " + sort);
ScoreDoc[] scoreDocs = ;
for (int i = 0; i < ; i++) {
FieldDoc fieldDoc = (FieldDoc) scoreDocs[i];
Float distance = (Float) [0];
Document doc = ();
(" " + ("name") + " @ (" + ("location") + ") -> " + distance);
}
}
}
The function of the SortComparatorSource interface is to return a comparator (Expert: returns a comparator for sorting ScoreDocs). This interface only defines one method. As follows:
Java code
/**
* Creates a comparator for the field in the given index.
* @param reader - Index to create comparator for.
* @param fieldname - Field to create comparator for.
* @return Comparator of ScoreDoc objects.
* @throws IOException - If an error occurs reading the index.
*/
public ScoreDocComparator newComparator(IndexReader reader,String fieldname) throws IOException
view plaincopy to clipboardprint?
/**
* Creates a comparator for the field in the given index.
* @param reader - Index to create comparator for.
* @param fieldname - Field to create comparator for.
* @return Comparator of ScoreDoc objects.
* @throws IOException - If an error occurs reading the index.
*/
public ScoreDocComparator newComparator(IndexReader reader,String fieldname) throws IOException
/**
* Creates a comparator for the field in the given index.
* @param reader - Index to create comparator for.
* @param fieldname - Field to create comparator for.
* @return Comparator of ScoreDoc objects.
* @throws IOException - If an error occurs reading the index.
*/
public ScoreDocComparator newComparator(IndexReader reader,String fieldname) throws IOException
This method just creates a ScoreDocComparator instance to implement sorting. So we also need to implement the ScoreDocComparator interface. Let's take a look at the ScoreDocComparator interface. The function is to compare two ScoreDoc objects for sorting. Two static instances of Lucene implementation are defined in the following:
Java code
//Special comparator for sorting hits according to computed relevance (document score).
public static final ScoreDocComparator RELEVANCE;
//Special comparator for sorting hits according to index order (document number).
public static final ScoreDocComparator INDEXORDER;
view plaincopy to clipboardprint?
//Special comparator for sorting hits according to computed relevance (document score).
public static final ScoreDocComparator RELEVANCE;
//Special comparator for sorting hits according to index order (document number).
public static final ScoreDocComparator INDEXORDER;
//Special comparator for sorting hits according to computed relevance (document score).
public static final ScoreDocComparator RELEVANCE;
//Special comparator for sorting hits according to index order (document number).
public static final ScoreDocComparator INDEXORDER;
There are 3 methods related to sorting, which we need to implement as follows:
Java code
/**
* Compares two ScoreDoc objects and returns a result indicating their sort order.
* @param i First ScoreDoc
* @param j Second ScoreDoc
* @return -1 if i should come before j;
* 1 if i should come after j;
* 0 if they are equal
*/
public int compare(ScoreDoc i,ScoreDoc j);
/**
* Returns the value used to sort the given document. The object returned must implement the interface. This is used by multisearchers to determine how to collate results from their searchers.
* @param i Document
* @return Serializable object
*/
public Comparable sortValue(ScoreDoc i);
/**
* Returns the type of sort. Should return , , , , or . It is not valid to return . This is used by multisearchers to determine how to collate results from their searchers.
* @return One of the constants in SortField.
*/
public int sortType();
view plaincopy to clipboardprint?
/**
* Compares two ScoreDoc objects and returns a result indicating their sort order.
* @param i First ScoreDoc
* @param j Second ScoreDoc
* @return -1 if i should come before j;
* 1 if i should come after j;
* 0 if they are equal
*/
public int compare(ScoreDoc i,ScoreDoc j);
/**
* Returns the value used to sort the given document. The object returned must implement the interface. This is used by multisearchers to determine how to collate results from their searchers.
* @param i Document
* @return Serializable object
*/
public Comparable sortValue(ScoreDoc i);
/**
* Returns the type of sort. Should return , , , , or . It is not valid to return . This is used by multisearchers to determine how to collate results from their searchers.
* @return One of the constants in SortField.
*/
public int sortType();
/**
* Compares two ScoreDoc objects and returns a result indicating their sort order.
* @param i First ScoreDoc
* @param j Second ScoreDoc
* @return -1 if i should come before j;
* 1 if i should come after j;
* 0 if they are equal
*/
public int compare(ScoreDoc i,ScoreDoc j);
/**
* Returns the value used to sort the given document. The object returned must implement the interface. This is used by multisearchers to determine how to collate results from their searchers.
* @param i Document
* @return Serializable object
*/
public Comparable sortValue(ScoreDoc i);
/**
* Returns the type of sort. Should return , , , , or . It is not valid to return . This is used by multisearchers to determine how to collate results from their searchers.
* @return One of the constants in SortField.
*/
public int sortType();
Let’s see an example!
This example is an implementation in Lucene in Action, used to search for the name of the restaurant closest to you. Restaurant coordinates are stored in the string "x,y".
Java code
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
//Implement the search for the name of the restaurant closest to you. The restaurant coordinates are stored in the string "x,y"
//DistanceComparatorSource implements the SortComparatorSource interface
public class DistanceComparatorSource implements SortComparatorSource {
private static final long serialVersionUID = 1L;
// x y is used to save coordinate position
private int x;
private int y;
public DistanceComparatorSource(int x, int y) {
= x;
= y;
}
// Return to ScoreDocComparator to implement sorting function
public ScoreDocComparator newComparator(IndexReader reader, String fieldname) throws IOException {
return new DistanceScoreDocLookupComparator(reader, fieldname, x, y);
}
//DistanceScoreDocLookupComparator implements ScoreDocComparator for sorting
private static class DistanceScoreDocLookupComparator implements ScoreDocComparator {
private float[] distances; // Save the distance from each restaurant to a specified point
// Constructor, the constructor completes almost all the preparations here.
public DistanceScoreDocLookupComparator(IndexReader reader, String fieldname, int x, int y) throws IOException {
("fieldName2="+fieldname);
final TermEnum enumerator = (new Term(fieldname, ""));
("maxDoc="+());
distances = new float[()]; // Initialize distances
if ( > 0) {
TermDocs termDocs = ();
try {
if (() == null) {
throw new RuntimeException("no terms in field " + fieldname);
}
int i = 0,j = 0;
do {
("in do-while :" + i ++);
Term term = (); // Take out each Term
if (() != fieldname) // If it does not match the given domain, compare the next one
break;
//Sets this to the data for the current term in a TermEnum.
//This may be optimized in some implementations.
(enumerator); //Reference TermDocs Doc
while (()) {
(" in while :" + j ++);
(" in while ,Term :" + ());
String[] xy = ().split(","); // Where to go x y
int deltax = (xy[0]) - x;
int deltay = (xy[1]) - y;
// Calculate distance
distances[()] = (float) (deltax * deltax + deltay * deltay);
}
}
while (());
} finally {
();
}
}
}
//The above constructor preparation is simpler here
public int compare(ScoreDoc i, ScoreDoc j) {
if (distances[] < distances[])
return -1;
if (distances[] > distances[])
return 1;
return 0;
}
// Return distance
public Comparable sortValue(ScoreDoc i) {
return new Float(distances[]);
}
//Specify SortType
public int sortType() {
return ;
}
}
public String toString() {
return "Distance from (" + x + "," + y + ")";
}
}
view plaincopy to clipboardprint?
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
//Implement the search for the name of the restaurant closest to you. The restaurant coordinates are stored in the string "x,y"
//DistanceComparatorSource implements the SortComparatorSource interface
public class DistanceComparatorSource implements SortComparatorSource {
private static final long serialVersionUID = 1L;
// x y is used to save coordinate position
private int x;
private int y;
public DistanceComparatorSource(int x, int y) {
= x;
= y;
}
// Return to ScoreDocComparator to implement sorting function
public ScoreDocComparator newComparator(IndexReader reader, String fieldname) throws IOException {
return new DistanceScoreDocLookupComparator(reader, fieldname, x, y);
}
//DistanceScoreDocLookupComparator implements ScoreDocComparator for sorting
private static class DistanceScoreDocLookupComparator implements ScoreDocComparator {
private float[] distances; // Save the distance from each restaurant to a specified point
// Constructor, the constructor completes almost all the preparations here.
public DistanceScoreDocLookupComparator(IndexReader reader, String fieldname, int x, int y) throws IOException {
("fieldName2="+fieldname);
final TermEnum enumerator = (new Term(fieldname, ""));
("maxDoc="+());
distances = new float[()]; // Initialize distances
if ( > 0) {
TermDocs termDocs = ();
try {
if (() == null) {
throw new RuntimeException("no terms in field " + fieldname);
}
int i = 0,j = 0;
do {
("in do-while :" + i ++);
Term term = (); // Take out each Term
if (() != fieldname) // If it does not match the given domain, compare the next one
break;
//Sets this to the data for the current term in a TermEnum.
//This may be optimized in some implementations.
(enumerator); //Reference TermDocs Doc
while (()) {
(" in while :" + j ++);
(" in while ,Term :" + ());
String[] xy = ().split(","); // Where to go x y
int deltax = (xy[0]) - x;
int deltay = (xy[1]) - y;
// Calculate distance
distances[()] = (float) (deltax * deltax + deltay * deltay);
}
}
while (());
} finally {
();
}
}
}
//The above constructor preparation is simpler here
public int compare(ScoreDoc i, ScoreDoc j) {
if (distances[] < distances[])
return -1;
if (distances[] > distances[])
return 1;
return 0;
}
// Return distance
public Comparable sortValue(ScoreDoc i) {
return new Float(distances[]);
}
//Specify SortType
public int sortType() {
return ;
}
}
public String toString() {
return "Distance from (" + x + "," + y + ")";
}
}
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
//Implement the search for the name of the restaurant closest to you. The restaurant coordinates are stored in the string "x,y"
//DistanceComparatorSource implements the SortComparatorSource interface
public class DistanceComparatorSource implements SortComparatorSource {
private static final long serialVersionUID = 1L;
// x y is used to save coordinate position
private int x;
private int y;
public DistanceComparatorSource(int x, int y) {
= x;
= y;
}
// Return to ScoreDocComparator to implement sorting function
public ScoreDocComparator newComparator(IndexReader reader, String fieldname) throws IOException {
return new DistanceScoreDocLookupComparator(reader, fieldname, x, y);
}
//DistanceScoreDocLookupComparator implements ScoreDocComparator for sorting
private static class DistanceScoreDocLookupComparator implements ScoreDocComparator {
private float[] distances; // Save the distance from each restaurant to a specified point
// Constructor, the constructor completes almost all the preparations here.
public DistanceScoreDocLookupComparator(IndexReader reader, String fieldname, int x, int y) throws IOException {
("fieldName2="+fieldname);
final TermEnum enumerator = (new Term(fieldname, ""));
("maxDoc="+());
distances = new float[()]; // Initialize distances
if ( > 0) {
TermDocs termDocs = ();
try {
if (() == null) {
throw new RuntimeException("no terms in field " + fieldname);
}
int i = 0,j = 0;
do {
("in do-while :" + i ++);
Term term = (); // Take out each Term
if (() != fieldname) // If it does not match the given domain, compare the next one
break;
//Sets this to the data for the current term in a TermEnum.
//This may be optimized in some implementations.
(enumerator); //Reference TermDocs Doc
while (()) {
(" in while :" + j ++);
(" in while ,Term :" + ());
String[] xy = ().split(","); // Where to go x y
int deltax = (xy[0]) - x;
int deltay = (xy[1]) - y;
// Calculate distance
distances[()] = (float) (deltax * deltax + deltay * deltay);
}
}
while (());
} finally {
();
}
}
}
//The above constructor preparation is relatively simple here
public int compare(ScoreDoc i, ScoreDoc j) {
if (distances[] < distances[])
return -1;
if (distances[] > distances[])
return 1;
return 0;
}
// Return distance
public Comparable sortValue(ScoreDoc i) {
return new Float(distances[]);
}
//Specify SortType
public int sortType() {
return ;
}
}
public String toString() {
return "Distance from (" + x + "," + y + ")";
}
}
These are two classes that implement the above two interfaces, with detailed comments inside, which shows that custom sorting is not difficult. Whether this implementation can be implemented correctly, let's see if the test code can pass.
Java code
package ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
public class DistanceComparatorSourceTest extends TestCase {
private RAMDirectory directory;
private IndexSearcher searcher;
private Query query;
//Create a test environment
protected void setUp() throws Exception {
directory = new RAMDirectory();
IndexWriter writer = new IndexWriter(directory, new WhitespaceAnalyzer(), true);
addPoint(writer, "El Charro", "restaurant", 1, 2);
addPoint(writer, "Cafe Poca Cosa", "restaurant", 5, 9);
addPoint(writer, "Los Betos", "restaurant", 9, 6);
addPoint(writer, "Nico's Taco Shop", "restaurant", 3, 8);
();
searcher = new IndexSearcher(directory);
query = new TermQuery(new Term("type", "restaurant"));
}
private void addPoint(IndexWriter writer, String name, String type, int x, int y) throws IOException {
Document doc = new Document();
(new Field("name", name, , ));
(new Field("type", type, , ));
(new Field("location", x + "," + y, , .UN_TOKENIZED));
(doc);
}
public void testNearestRestaurantToHome() throws Exception {
//Use DistanceComparatorSource to construct a SortField
Sort sort = new Sort(new SortField("location", new DistanceComparatorSource(0, 0)));
Hits hits = (query, sort); // Search
//test
assertEquals("closest", "El Charro", (0).get("name"));
assertEquals("furthest", "Los Betos", (3).get("name"));
}
public void testNeareastRestaurantToWork() throws Exception {
Sort sort = new Sort(new SortField("location", new DistanceComparatorSource(10, 10))); // Working coordinates 10,10
//The above test implements custom sorting, but cannot access more detailed information about custom sorting.
//TopFieldDocs can further access relevant information
TopFieldDocs docs = (query, null, 3, sort);
assertEquals(4, );
assertEquals(3, );
//Get FieldDoc Use FieldDoc to obtain more detailed information about sorting. Please check FieldDoc Doc
FieldDoc fieldDoc = (FieldDoc) [0];
assertEquals("(10,10) -> (9,6) = sqrt(17)", new Float((17)), [0]);
Document document = ();
assertEquals("Los Betos", ("name"));
dumpDocs(sort, docs); // Show relevant information
}
// Show information about sorting
private void dumpDocs(Sort sort, TopFieldDocs docs) throws IOException {
("Sorted by: " + sort);
ScoreDoc[] scoreDocs = ;
for (int i = 0; i < ; i++) {
FieldDoc fieldDoc = (FieldDoc) scoreDocs[i];
Float distance = (Float) [0];
Document doc = ();
(" " + ("name") + " @ (" + ("location") + ") -> " + distance);
}
}
}