diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index 16f02b5137d..a9f01ae2dc6 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -83,6 +83,9 @@ Bug Fixes romaji even for out-of-vocabulary kana cases (e.g. half-width forms). (Robert Muir) +* LUCENE-4504: Fix broken sort comparator in ValueSource.getSortField, + used when sorting by a function query. (Tom Shally via Robert Muir) + Optimizations * LUCENE-4443: Lucene41PostingsFormat no longer writes unnecessary offsets diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java index 91c94cf4f9c..67b41cbd803 100644 --- a/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java +++ b/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java @@ -192,7 +192,7 @@ public abstract class ValueSource { if (docValue < value) { return -1; } else if (docValue > value) { - return -1; + return 1; } else { return 0; } diff --git a/lucene/queries/src/test/org/apache/lucene/queries/function/TestFunctionQuerySort.java b/lucene/queries/src/test/org/apache/lucene/queries/function/TestFunctionQuerySort.java new file mode 100644 index 00000000000..65cf830fa7d --- /dev/null +++ b/lucene/queries/src/test/org/apache/lucene/queries/function/TestFunctionQuerySort.java @@ -0,0 +1,99 @@ +package org.apache.lucene.queries.function; + +/* + * 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.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.StringField; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.queries.function.valuesource.IntFieldSource; +import org.apache.lucene.search.FieldDoc; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.ScoreDoc; +import org.apache.lucene.search.Sort; +import org.apache.lucene.search.SortField; +import org.apache.lucene.search.TopDocs; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.LuceneTestCase; + +/** Test that functionquery's getSortField() actually works */ +public class TestFunctionQuerySort extends LuceneTestCase { + + public void testSearchAfterWhenSortingByFunctionValues() throws IOException { + Directory dir = newDirectory(); + IndexWriterConfig iwc = newIndexWriterConfig(TEST_VERSION_CURRENT, null); + iwc.setMergePolicy(newLogMergePolicy()); // depends on docid order + RandomIndexWriter writer = new RandomIndexWriter(random(), dir, iwc); + + Document doc = new Document(); + Field field = new StringField("value", "", Field.Store.YES); + doc.add(field); + + // Save docs unsorted (decreasing value n, n-1, ...) + final int NUM_VALS = 5; + for (int val = NUM_VALS; val > 0; val--) { + field.setStringValue(Integer.toString(val)); + writer.addDocument(doc); + } + + // Open index + IndexReader reader = writer.getReader(); + writer.close(); + IndexSearcher searcher = new IndexSearcher(reader); + + // Get ValueSource from FieldCache + IntFieldSource src = new IntFieldSource("value"); + // ...and make it a sort criterion + SortField sf = src.getSortField(false).rewrite(searcher); + Sort orderBy = new Sort(sf); + + // Get hits sorted by our FunctionValues (ascending values) + Query q = new MatchAllDocsQuery(); + TopDocs hits = searcher.search(q, Integer.MAX_VALUE, orderBy); + assertEquals(NUM_VALS, hits.scoreDocs.length); + // Verify that sorting works in general + int i = 0; + for (ScoreDoc hit : hits.scoreDocs) { + int valueFromDoc = Integer.parseInt(reader.document(hit.doc).get("value")); + assertEquals(++i, valueFromDoc); + } + + // Now get hits after hit #2 using IS.searchAfter() + int afterIdx = 1; + FieldDoc afterHit = (FieldDoc) hits.scoreDocs[afterIdx]; + hits = searcher.searchAfter(afterHit, q, Integer.MAX_VALUE, orderBy); + + // Expected # of hits: NUM_VALS - 2 + assertEquals(NUM_VALS - (afterIdx + 1), hits.scoreDocs.length); + + // Verify that hits are actually "after" + int afterValue = ((Double) afterHit.fields[0]).intValue(); + for (ScoreDoc hit : hits.scoreDocs) { + int val = Integer.parseInt(reader.document(hit.doc).get("value")); + assertTrue(afterValue <= val); + assertFalse(hit.doc == afterHit.doc); + } + reader.close(); + dir.close(); + } +}