Skip to content

Add the ability to inverse a Sort #14775

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ public FeatureSortField(String field, String featureName) {
this.featureName = Objects.requireNonNull(featureName);
}

@Override
public SortField inverseSort() {
throw new UnsupportedOperationException("Inverse sort not supported on FeatureSortField");
}

@Override
public FieldComparator<?> getComparator(int numHits, Pruning pruning) {
return new FeatureComparator(numHits, getField(), featureName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ final class LatLonPointSortField extends SortField {
setMissingValue(Double.POSITIVE_INFINITY);
}

@Override
public SortField inverseSort() {
throw new UnsupportedOperationException("Inverse sort not supported on LatLonPointSortField");
}

@Override
public FieldComparator<?> getComparator(int numHits, Pruning pruning) {
return new LatLonPointDistanceComparator(getField(), latitude, longitude, numHits);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ final class XYPointSortField extends SortField {
setMissingValue(Double.POSITIVE_INFINITY);
}

@Override
public SortField inverseSort() {
throw new UnsupportedOperationException("Inverse sort not supported on XYPointSortField");
}

@Override
public FieldComparator<?> getComparator(int numHits, Pruning pruning) {
return new XYPointDistanceComparator(getField(), x, y, numHits);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,15 @@ private static class DoubleValuesSortField extends SortField {
this.producer = producer;
}

@Override
public SortField inverseSort() {
DoubleValuesSortField inverse = new DoubleValuesSortField(producer, !reverse);
if (missingValue != null) {
inverse.setMissingValue(missingValue);
}
return inverse;
}

@Override
public void setMissingValue(Object missingValue) {
if (missingValue instanceof Number) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,15 @@ public LongValuesSortField(LongValuesSource producer, boolean reverse) {
this.producer = producer;
}

@Override
public SortField inverseSort() {
LongValuesSortField inverse = new LongValuesSortField(producer, !reverse);
if (missingValue != null) {
inverse.setMissingValue(missingValue);
}
return inverse;
}

@Override
public void setMissingValue(Object missingValue) {
if (missingValue instanceof Number) {
Expand Down
13 changes: 13 additions & 0 deletions lucene/core/src/java/org/apache/lucene/search/Sort.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,19 @@ public SortField[] getSort() {
return fields;
}

/**
* Creates a new Sort that represents the inverse ordering of this Sort
*
* @return this Sort in inverse order
*/
public Sort inverse() {
SortField[] reversedFields = new SortField[fields.length];
for (int i = 0; i < fields.length; i++) {
reversedFields[i] = fields[i].inverseSort();
}
return new Sort(reversedFields);
}

/**
* Rewrites the SortFields in this Sort, returning a new Sort if any of the fields changes during
* their rewriting.
Expand Down
19 changes: 19 additions & 0 deletions lucene/core/src/java/org/apache/lucene/search/SortField.java
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,25 @@ public SortField(String field, FieldComparatorSource comparator, boolean reverse
this.comparatorSource = comparator;
}

/**
* Duplicates this SortField, but with the inverse ordering. So reverse if this sort is
* non-reverse, and non-reverse if this sort is reverse.
*
* @return An identical sort, but with reverse flipped.
*/
public SortField inverseSort() {
SortField inverse;
if (type == Type.CUSTOM) {
inverse = new SortField(field, comparatorSource, !reverse);
} else {
inverse = new SortField(field, type, !reverse);
}
if (missingValue != null) {
inverse.setMissingValue(missingValue);
}
return inverse;
}

// Sets field & type, and ensures field is not NULL unless
// type is SCORE or DOC
private void initFieldType(String field, Type type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,16 @@ public SortedNumericSortField(
this.type = type;
}

@Override
public SortField inverseSort() {
SortedNumericSortField inverse =
new SortedNumericSortField(getField(), type, !reverse, selector);
if (missingValue != null) {
inverse.setMissingValue(missingValue);
}
return inverse;
}

/** A SortFieldProvider for this sort field */
public static final class Provider extends SortFieldProvider {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ public SortedSetSortField(String field, boolean reverse, SortedSetSelector.Type
this.selector = selector;
}

@Override
public SortField inverseSort() {
SortedSetSortField inverse = new SortedSetSortField(getField(), !reverse, selector);
if (missingValue != null) {
inverse.setMissingValue(missingValue);
}
return inverse;
}

/** A SortFieldProvider for this sort */
public static final class Provider extends SortFieldProvider {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,21 @@ public void testSortMissingExplicit() throws Exception {
FieldDoc first = (FieldDoc) results.scoreDocs[0];
assertEquals(LEAST_DOUBLE_VALUE, first.fields[0]);

results = searcher.search(new MatchAllDocsQuery(), 1, new Sort(oneFieldSort).inverse());
first = (FieldDoc) results.scoreDocs[0];
assertEquals(Double.MIN_VALUE, first.fields[0]);

// sort increasing, missing last
oneFieldSort = onefield.getSortField(false);
oneFieldSort.setMissingValue(Double.MAX_VALUE);

results = searcher.search(new MatchAllDocsQuery(), 1, new Sort(oneFieldSort));
first = (FieldDoc) results.scoreDocs[0];
assertEquals(LEAST_DOUBLE_VALUE, first.fields[0]);

results = searcher.search(new MatchAllDocsQuery(), 1, new Sort(oneFieldSort).inverse());
first = (FieldDoc) results.scoreDocs[0];
assertEquals(Double.MAX_VALUE, first.fields[0]);
}

public void testSimpleFieldEquivalences() throws Exception {
Expand Down Expand Up @@ -226,6 +234,12 @@ void checkSorts(Query query, Sort sort) throws Exception {

CheckHits.checkEqual(query, expected.scoreDocs, actual.scoreDocs);

TopDocs actualInverse =
searcher.search(query, size, mutatedSort.inverse(), random().nextBoolean());
TopDocs expectedInverse = searcher.search(query, size, sort.inverse(), random().nextBoolean());

CheckHits.checkEqual(query, expectedInverse.scoreDocs, actualInverse.scoreDocs);

if (size < actual.totalHits.value()) {
expected = searcher.searchAfter(expected.scoreDocs[size - 1], query, size, sort);
actual = searcher.searchAfter(actual.scoreDocs[size - 1], query, size, mutatedSort);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,21 @@ public void testSortMissingExplicit() throws Exception {
FieldDoc first = (FieldDoc) results.scoreDocs[0];
assertEquals(LEAST_LONG_VALUE, first.fields[0]);

results = searcher.search(new MatchAllDocsQuery(), 1, new Sort(oneFieldSort).inverse());
first = (FieldDoc) results.scoreDocs[0];
assertEquals(Long.MIN_VALUE, first.fields[0]);

// sort increasing, missing last
oneFieldSort = onefield.getSortField(false);
oneFieldSort.setMissingValue(Long.MAX_VALUE);

results = searcher.search(new MatchAllDocsQuery(), 1, new Sort(oneFieldSort));
first = (FieldDoc) results.scoreDocs[0];
assertEquals(LEAST_LONG_VALUE, first.fields[0]);

results = searcher.search(new MatchAllDocsQuery(), 1, new Sort(oneFieldSort).inverse());
first = (FieldDoc) results.scoreDocs[0];
assertEquals(Long.MAX_VALUE, first.fields[0]);
}

public void testSimpleFieldEquivalences() throws Exception {
Expand Down Expand Up @@ -192,6 +200,12 @@ void checkSorts(Query query, Sort sort) throws Exception {

CheckHits.checkEqual(query, expected.scoreDocs, actual.scoreDocs);

TopDocs actualInverse =
searcher.search(query, size, mutatedSort.inverse(), random().nextBoolean());
TopDocs expectedInverse = searcher.search(query, size, sort.inverse(), random().nextBoolean());

CheckHits.checkEqual(query, expectedInverse.scoreDocs, actualInverse.scoreDocs);

if (size < actual.totalHits.value()) {
expected = searcher.searchAfter(expected.scoreDocs[size - 1], query, size, sort);
actual = searcher.searchAfter(actual.scoreDocs[size - 1], query, size, mutatedSort);
Expand Down
60 changes: 60 additions & 0 deletions lucene/core/src/test/org/apache/lucene/search/TestSort.java
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ public void testStringVal() throws IOException {
assertEquals("bar", searcher.storedFields().document(td.scoreDocs[0].doc).get("value"));
assertEquals("foo", searcher.storedFields().document(td.scoreDocs[1].doc).get("value"));

td = searcher.search(new MatchAllDocsQuery(), 10, sort.inverse());
assertEquals(2, td.totalHits.value());
// inverse of normal order is reverse
assertEquals("foo", searcher.storedFields().document(td.scoreDocs[0].doc).get("value"));
assertEquals("bar", searcher.storedFields().document(td.scoreDocs[1].doc).get("value"));

ir.close();
dir.close();
}
Expand Down Expand Up @@ -198,6 +204,12 @@ public void testStringValReverse() throws IOException {
assertEquals("foo", searcher.storedFields().document(td.scoreDocs[0].doc).get("value"));
assertEquals("bar", searcher.storedFields().document(td.scoreDocs[1].doc).get("value"));

td = searcher.search(new MatchAllDocsQuery(), 10, sort.inverse());
assertEquals(2, td.totalHits.value());
// inverse of reverse order is normal ordering
assertEquals("bar", searcher.storedFields().document(td.scoreDocs[0].doc).get("value"));
assertEquals("foo", searcher.storedFields().document(td.scoreDocs[1].doc).get("value"));

ir.close();
dir.close();
}
Expand Down Expand Up @@ -231,6 +243,13 @@ public void testInt() throws IOException {
assertEquals("4", searcher.storedFields().document(td.scoreDocs[1].doc).get("value"));
assertEquals("300000", searcher.storedFields().document(td.scoreDocs[2].doc).get("value"));

td = searcher.search(new MatchAllDocsQuery(), 10, sort.inverse());
assertEquals(3, td.totalHits.value());
// inverse of normal numeric order is reverse
assertEquals("300000", searcher.storedFields().document(td.scoreDocs[0].doc).get("value"));
assertEquals("4", searcher.storedFields().document(td.scoreDocs[1].doc).get("value"));
assertEquals("-1", searcher.storedFields().document(td.scoreDocs[2].doc).get("value"));

ir.close();
dir.close();
}
Expand Down Expand Up @@ -264,6 +283,13 @@ public void testIntReverse() throws IOException {
assertEquals("4", searcher.storedFields().document(td.scoreDocs[1].doc).get("value"));
assertEquals("-1", searcher.storedFields().document(td.scoreDocs[2].doc).get("value"));

td = searcher.search(new MatchAllDocsQuery(), 10, sort.inverse());
assertEquals(3, td.totalHits.value());
// inverse of reverse is normal numeric order
assertEquals("-1", searcher.storedFields().document(td.scoreDocs[0].doc).get("value"));
assertEquals("4", searcher.storedFields().document(td.scoreDocs[1].doc).get("value"));
assertEquals("300000", searcher.storedFields().document(td.scoreDocs[2].doc).get("value"));

ir.close();
dir.close();
}
Expand Down Expand Up @@ -295,6 +321,13 @@ public void testIntMissing() throws IOException {
assertNull(searcher.storedFields().document(td.scoreDocs[1].doc).get("value"));
assertEquals("4", searcher.storedFields().document(td.scoreDocs[2].doc).get("value"));

td = searcher.search(new MatchAllDocsQuery(), 10, sort.inverse());
assertEquals(3, td.totalHits.value());
// make sure the missing value is kept during the inverse generation
assertEquals("4", searcher.storedFields().document(td.scoreDocs[0].doc).get("value"));
assertNull(searcher.storedFields().document(td.scoreDocs[1].doc).get("value"));
assertEquals("-1", searcher.storedFields().document(td.scoreDocs[2].doc).get("value"));

ir.close();
dir.close();
}
Expand Down Expand Up @@ -330,6 +363,13 @@ public void testIntMissingLast() throws IOException {
assertEquals("4", searcher.storedFields().document(td.scoreDocs[1].doc).get("value"));
assertNull(searcher.storedFields().document(td.scoreDocs[2].doc).get("value"));

td = searcher.search(new MatchAllDocsQuery(), 10, sort.inverse());
assertEquals(3, td.totalHits.value());
// make sure the missing last is reversed during the inverse generation
assertNull(searcher.storedFields().document(td.scoreDocs[0].doc).get("value"));
assertEquals("4", searcher.storedFields().document(td.scoreDocs[1].doc).get("value"));
assertEquals("-1", searcher.storedFields().document(td.scoreDocs[2].doc).get("value"));

ir.close();
dir.close();
}
Expand Down Expand Up @@ -844,6 +884,26 @@ public void testMultiSort() throws IOException {
assertEquals("bar", searcher.storedFields().document(td.scoreDocs[0].doc).get("value1"));
assertEquals("0", searcher.storedFields().document(td.scoreDocs[0].doc).get("value2"));

// Inverse the sort and make sure that each sortField is inversed
td = searcher.search(new MatchAllDocsQuery(), 10, sort.inverse());
assertEquals(4, td.totalHits.value());
// 'foo' comes before 'bar'
assertEquals("foo", searcher.storedFields().document(td.scoreDocs[0].doc).get("value1"));
assertEquals("foo", searcher.storedFields().document(td.scoreDocs[1].doc).get("value1"));
assertEquals("bar", searcher.storedFields().document(td.scoreDocs[2].doc).get("value1"));
assertEquals("bar", searcher.storedFields().document(td.scoreDocs[3].doc).get("value1"));
// 1 comes before 0
assertEquals("1", searcher.storedFields().document(td.scoreDocs[0].doc).get("value2"));
assertEquals("0", searcher.storedFields().document(td.scoreDocs[1].doc).get("value2"));
assertEquals("1", searcher.storedFields().document(td.scoreDocs[2].doc).get("value2"));
assertEquals("0", searcher.storedFields().document(td.scoreDocs[3].doc).get("value2"));

// Now with overflow
td = searcher.search(new MatchAllDocsQuery(), 1, sort.inverse());
assertEquals(4, td.totalHits.value());
assertEquals("foo", searcher.storedFields().document(td.scoreDocs[0].doc).get("value1"));
assertEquals("1", searcher.storedFields().document(td.scoreDocs[0].doc).get("value2"));

ir.close();
dir.close();
}
Expand Down
Loading
Loading