diff --git a/src/java/org/apache/lucene/search/MultiFieldSortedHitQueue.java b/src/java/org/apache/lucene/search/MultiFieldSortedHitQueue.java index 9a0fb4dc2e2..2436f653207 100644 --- a/src/java/org/apache/lucene/search/MultiFieldSortedHitQueue.java +++ b/src/java/org/apache/lucene/search/MultiFieldSortedHitQueue.java @@ -64,6 +64,11 @@ extends PriorityQueue { /** Stores the sort criteria being used. */ protected SortField[] fields; + /** Stores the maximum score value encountered, for normalizing. + * we only care about scores greater than 1.0 - if all the scores + * are less than 1.0, we don't have to normalize. */ + protected float maxscore = 1.0f; + /** * Returns whether a is less relevant than b. @@ -74,6 +79,12 @@ extends PriorityQueue { protected final boolean lessThan (final Object a, final Object b) { final ScoreDoc docA = (ScoreDoc) a; final ScoreDoc docB = (ScoreDoc) b; + + // keep track of maximum score + if (docA.score > maxscore) maxscore = docA.score; + if (docB.score > maxscore) maxscore = docB.score; + + // run comparators final int n = comparators.length; int c = 0; for (int i=0; i 1.0f) doc.score /= maxscore; // normalize scores return doc; } @@ -108,4 +120,5 @@ extends PriorityQueue { SortField[] getFields() { return fields; } + } diff --git a/src/test/org/apache/lucene/search/TestSort.java b/src/test/org/apache/lucene/search/TestSort.java index dd767521b39..a86c15a5a1a 100644 --- a/src/test/org/apache/lucene/search/TestSort.java +++ b/src/test/org/apache/lucene/search/TestSort.java @@ -28,6 +28,8 @@ import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.io.IOException; import java.util.regex.Pattern; +import java.util.HashMap; +import java.util.Iterator; import junit.framework.TestCase; import junit.framework.Test; @@ -241,6 +243,115 @@ extends TestCase { runMultiSorts (multi); } + // test that the relevancy scores are the same even if + // hits are sorted + public void testNormalizedScores() throws Exception { + + // capture relevancy scores + HashMap scoresX = getScores (full.search (queryX)); + HashMap scoresY = getScores (full.search (queryY)); + HashMap scoresA = getScores (full.search (queryA)); + + // we'll test searching locally, remote and multi + // note: the multi test depends on each separate index containing + // the same documents as our local index, so the computed normalization + // will be the same. so we make a multi searcher over two equal document + // sets - not realistic, but necessary for testing. + MultiSearcher remote = new MultiSearcher (new Searchable[] { getRemote() }); + MultiSearcher multi = new MultiSearcher (new Searchable[] { full, full }); + + // change sorting and make sure relevancy stays the same + + sort = new Sort(); + assertSameValues (scoresX, getScores(full.search(queryX,sort))); + assertSameValues (scoresX, getScores(remote.search(queryX,sort))); + assertSameValues (scoresX, getScores(multi.search(queryX,sort))); + assertSameValues (scoresY, getScores(full.search(queryY,sort))); + assertSameValues (scoresY, getScores(remote.search(queryY,sort))); + assertSameValues (scoresY, getScores(multi.search(queryY,sort))); + assertSameValues (scoresA, getScores(full.search(queryA,sort))); + assertSameValues (scoresA, getScores(remote.search(queryA,sort))); + assertSameValues (scoresA, getScores(multi.search(queryA,sort))); + + sort.setSort(SortField.FIELD_DOC); + assertSameValues (scoresX, getScores(full.search(queryX,sort))); + assertSameValues (scoresX, getScores(remote.search(queryX,sort))); + assertSameValues (scoresX, getScores(multi.search(queryX,sort))); + assertSameValues (scoresY, getScores(full.search(queryY,sort))); + assertSameValues (scoresY, getScores(remote.search(queryY,sort))); + assertSameValues (scoresY, getScores(multi.search(queryY,sort))); + assertSameValues (scoresA, getScores(full.search(queryA,sort))); + assertSameValues (scoresA, getScores(remote.search(queryA,sort))); + assertSameValues (scoresA, getScores(multi.search(queryA,sort))); + + sort.setSort ("int"); + assertSameValues (scoresX, getScores(full.search(queryX,sort))); + assertSameValues (scoresX, getScores(remote.search(queryX,sort))); + assertSameValues (scoresX, getScores(multi.search(queryX,sort))); + assertSameValues (scoresY, getScores(full.search(queryY,sort))); + assertSameValues (scoresY, getScores(remote.search(queryY,sort))); + assertSameValues (scoresY, getScores(multi.search(queryY,sort))); + assertSameValues (scoresA, getScores(full.search(queryA,sort))); + assertSameValues (scoresA, getScores(remote.search(queryA,sort))); + assertSameValues (scoresA, getScores(multi.search(queryA,sort))); + + sort.setSort ("float"); + assertSameValues (scoresX, getScores(full.search(queryX,sort))); + assertSameValues (scoresX, getScores(remote.search(queryX,sort))); + assertSameValues (scoresX, getScores(multi.search(queryX,sort))); + assertSameValues (scoresY, getScores(full.search(queryY,sort))); + assertSameValues (scoresY, getScores(remote.search(queryY,sort))); + assertSameValues (scoresY, getScores(multi.search(queryY,sort))); + assertSameValues (scoresA, getScores(full.search(queryA,sort))); + assertSameValues (scoresA, getScores(remote.search(queryA,sort))); + assertSameValues (scoresA, getScores(multi.search(queryA,sort))); + + sort.setSort ("string"); + assertSameValues (scoresX, getScores(full.search(queryX,sort))); + assertSameValues (scoresX, getScores(remote.search(queryX,sort))); + assertSameValues (scoresX, getScores(multi.search(queryX,sort))); + assertSameValues (scoresY, getScores(full.search(queryY,sort))); + assertSameValues (scoresY, getScores(remote.search(queryY,sort))); + assertSameValues (scoresY, getScores(multi.search(queryY,sort))); + assertSameValues (scoresA, getScores(full.search(queryA,sort))); + assertSameValues (scoresA, getScores(remote.search(queryA,sort))); + assertSameValues (scoresA, getScores(multi.search(queryA,sort))); + + sort.setSort (new String[] {"int","float"}); + assertSameValues (scoresX, getScores(full.search(queryX,sort))); + assertSameValues (scoresX, getScores(remote.search(queryX,sort))); + assertSameValues (scoresX, getScores(multi.search(queryX,sort))); + assertSameValues (scoresY, getScores(full.search(queryY,sort))); + assertSameValues (scoresY, getScores(remote.search(queryY,sort))); + assertSameValues (scoresY, getScores(multi.search(queryY,sort))); + assertSameValues (scoresA, getScores(full.search(queryA,sort))); + assertSameValues (scoresA, getScores(remote.search(queryA,sort))); + assertSameValues (scoresA, getScores(multi.search(queryA,sort))); + + sort.setSort (new SortField[] { new SortField ("int", true), new SortField (null, SortField.DOC, true) }); + assertSameValues (scoresX, getScores(full.search(queryX,sort))); + assertSameValues (scoresX, getScores(remote.search(queryX,sort))); + assertSameValues (scoresX, getScores(multi.search(queryX,sort))); + assertSameValues (scoresY, getScores(full.search(queryY,sort))); + assertSameValues (scoresY, getScores(remote.search(queryY,sort))); + assertSameValues (scoresY, getScores(multi.search(queryY,sort))); + assertSameValues (scoresA, getScores(full.search(queryA,sort))); + assertSameValues (scoresA, getScores(remote.search(queryA,sort))); + assertSameValues (scoresA, getScores(multi.search(queryA,sort))); + + sort.setSort (new String[] {"float","string"}); + assertSameValues (scoresX, getScores(full.search(queryX,sort))); + assertSameValues (scoresX, getScores(remote.search(queryX,sort))); + assertSameValues (scoresX, getScores(multi.search(queryX,sort))); + assertSameValues (scoresY, getScores(full.search(queryY,sort))); + assertSameValues (scoresY, getScores(remote.search(queryY,sort))); + assertSameValues (scoresY, getScores(multi.search(queryY,sort))); + assertSameValues (scoresA, getScores(full.search(queryA,sort))); + assertSameValues (scoresA, getScores(remote.search(queryA,sort))); + assertSameValues (scoresA, getScores(multi.search(queryA,sort))); + + } + // runs a variety of sorts useful for multisearchers private void runMultiSorts (Searcher multi) throws Exception { sort.setSort (SortField.FIELD_DOC); @@ -313,6 +424,30 @@ extends TestCase { assertTrue (Pattern.compile(pattern).matcher(buff.toString()).matches()); } + private HashMap getScores (Hits hits) + throws IOException { + HashMap scoreMap = new HashMap(); + int n = hits.length(); + for (int i=0; i