Fix setting of readerGen in BytesRefOrdValComparator on nested documents.
Sorting was broken on nested documents because the `missing(slot)` method didn't correctly set the segment ordinal (readerGen), causing term ordinals to be compared across segments. Close #5986
This commit is contained in:
parent
2076194d8f
commit
2eeaa56d95
|
@ -101,20 +101,14 @@ public final class BytesRefOrdValComparator extends NestedWrappableComparator<By
|
||||||
@Override
|
@Override
|
||||||
public int compare(int slot1, int slot2) {
|
public int compare(int slot1, int slot2) {
|
||||||
if (readerGen[slot1] == readerGen[slot2]) {
|
if (readerGen[slot1] == readerGen[slot2]) {
|
||||||
return Long.compare(ords[slot1], ords[slot2]);
|
final int res = Long.compare(ords[slot1], ords[slot2]);
|
||||||
|
assert Integer.signum(res) == Integer.signum(compareValues(values[slot1], values[slot2])) : values[slot1] + " " + values[slot2] + " " + ords[slot1] + " " + ords[slot2];
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
final BytesRef val1 = values[slot1];
|
final BytesRef val1 = values[slot1];
|
||||||
final BytesRef val2 = values[slot2];
|
final BytesRef val2 = values[slot2];
|
||||||
if (val1 == null) {
|
return compareValues(val1, val2);
|
||||||
if (val2 == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
} else if (val2 == null) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return val1.compareTo(val2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -257,6 +251,7 @@ public final class BytesRefOrdValComparator extends NestedWrappableComparator<By
|
||||||
public void missing(int slot) {
|
public void missing(int slot) {
|
||||||
ords[slot] = missingOrd;
|
ords[slot] = missingOrd;
|
||||||
values[slot] = missingValue;
|
values[slot] = missingValue;
|
||||||
|
readerGen[slot] = currentReaderGen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,7 +346,6 @@ public final class BytesRefOrdValComparator extends NestedWrappableComparator<By
|
||||||
}
|
}
|
||||||
assert consistentInsertedOrd(termsIndex, bottomOrd, bottomValue);
|
assert consistentInsertedOrd(termsIndex, bottomOrd, bottomValue);
|
||||||
}
|
}
|
||||||
readerGen[bottomSlot] = currentReaderGen;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -23,18 +23,15 @@ import org.apache.lucene.index.AtomicReaderContext;
|
||||||
import org.apache.lucene.index.IndexReader;
|
import org.apache.lucene.index.IndexReader;
|
||||||
import org.elasticsearch.index.Index;
|
import org.elasticsearch.index.Index;
|
||||||
import org.elasticsearch.index.fielddata.fieldcomparator.BytesRefFieldComparatorSource;
|
import org.elasticsearch.index.fielddata.fieldcomparator.BytesRefFieldComparatorSource;
|
||||||
import org.elasticsearch.search.MultiValueMode;
|
|
||||||
import org.elasticsearch.index.mapper.FieldMapper.Names;
|
import org.elasticsearch.index.mapper.FieldMapper.Names;
|
||||||
|
import org.elasticsearch.search.MultiValueMode;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
/** Returns an implementation based on paged bytes which doesn't implement WithOrdinals in order to visit different paths in the code,
|
/** Returns an implementation based on paged bytes which doesn't implement WithOrdinals in order to visit different paths in the code,
|
||||||
* eg. BytesRefFieldComparatorSource makes decisions based on whether the field data implements WithOrdinals. */
|
* eg. BytesRefFieldComparatorSource makes decisions based on whether the field data implements WithOrdinals. */
|
||||||
public class NoOrdinalsStringFieldDataTests extends PagedBytesStringFieldDataTests {
|
public class NoOrdinalsStringFieldDataTests extends PagedBytesStringFieldDataTests {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
public static IndexFieldData<AtomicFieldData<ScriptDocValues>> hideOrdinals(final IndexFieldData<?> in) {
|
||||||
@Override
|
|
||||||
public IndexFieldData<AtomicFieldData<ScriptDocValues>> getForField(String fieldName) {
|
|
||||||
final IndexFieldData<?> in = super.getForField(fieldName);
|
|
||||||
return new IndexFieldData<AtomicFieldData<ScriptDocValues>>() {
|
return new IndexFieldData<AtomicFieldData<ScriptDocValues>>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -85,6 +82,12 @@ public class NoOrdinalsStringFieldDataTests extends PagedBytesStringFieldDataTes
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public IndexFieldData<AtomicFieldData<ScriptDocValues>> getForField(String fieldName) {
|
||||||
|
return hideOrdinals(super.getForField(fieldName));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Override
|
@Override
|
||||||
public void testTermsEnum() throws Exception {
|
public void testTermsEnum() throws Exception {
|
||||||
|
|
|
@ -30,17 +30,19 @@ import org.apache.lucene.search.join.FixedBitSetCachingWrapperFilter;
|
||||||
import org.apache.lucene.search.join.ScoreMode;
|
import org.apache.lucene.search.join.ScoreMode;
|
||||||
import org.apache.lucene.search.join.ToParentBlockJoinQuery;
|
import org.apache.lucene.search.join.ToParentBlockJoinQuery;
|
||||||
import org.apache.lucene.util.BytesRef;
|
import org.apache.lucene.util.BytesRef;
|
||||||
|
import org.apache.lucene.util.TestUtil;
|
||||||
import org.elasticsearch.common.lucene.search.AndFilter;
|
import org.elasticsearch.common.lucene.search.AndFilter;
|
||||||
import org.elasticsearch.common.lucene.search.NotFilter;
|
import org.elasticsearch.common.lucene.search.NotFilter;
|
||||||
import org.elasticsearch.common.lucene.search.XFilteredQuery;
|
import org.elasticsearch.common.lucene.search.XFilteredQuery;
|
||||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||||
import org.elasticsearch.index.fielddata.AbstractFieldDataTests;
|
import org.elasticsearch.index.fielddata.*;
|
||||||
import org.elasticsearch.index.fielddata.FieldDataType;
|
import org.elasticsearch.index.fielddata.IndexFieldData.XFieldComparatorSource;
|
||||||
import org.elasticsearch.index.fielddata.fieldcomparator.BytesRefFieldComparatorSource;
|
import org.elasticsearch.index.fielddata.fieldcomparator.BytesRefFieldComparatorSource;
|
||||||
import org.elasticsearch.search.MultiValueMode;
|
|
||||||
import org.elasticsearch.index.fielddata.plain.PagedBytesIndexFieldData;
|
import org.elasticsearch.index.fielddata.plain.PagedBytesIndexFieldData;
|
||||||
|
import org.elasticsearch.search.MultiValueMode;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -56,6 +58,61 @@ public class NestedSortingTests extends AbstractFieldDataTests {
|
||||||
return new FieldDataType("string", ImmutableSettings.builder().put("format", "paged_bytes"));
|
return new FieldDataType("string", ImmutableSettings.builder().put("format", "paged_bytes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDuel() throws Exception {
|
||||||
|
final int numDocs = scaledRandomIntBetween(100, 1000);
|
||||||
|
for (int i = 0; i < numDocs; ++i) {
|
||||||
|
final int numChildren = randomInt(2);
|
||||||
|
List<Document> docs = new ArrayList<>(numChildren + 1);
|
||||||
|
for (int j = 0; j < numChildren; ++j) {
|
||||||
|
Document doc = new Document();
|
||||||
|
doc.add(new StringField("f", TestUtil.randomSimpleString(getRandom(), 2), Field.Store.NO));
|
||||||
|
doc.add(new StringField("__type", "child", Field.Store.NO));
|
||||||
|
docs.add(doc);
|
||||||
|
}
|
||||||
|
if (randomBoolean()) {
|
||||||
|
docs.add(new Document());
|
||||||
|
}
|
||||||
|
Document parent = new Document();
|
||||||
|
parent.add(new StringField("__type", "parent", Field.Store.NO));
|
||||||
|
docs.add(parent);
|
||||||
|
writer.addDocuments(docs);
|
||||||
|
if (rarely()) { // we need to have a bit more segments than what RandomIndexWriter would do by default
|
||||||
|
DirectoryReader.open(writer, false).close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writer.commit();
|
||||||
|
|
||||||
|
MultiValueMode sortMode = randomFrom(Arrays.asList(MultiValueMode.MIN, MultiValueMode.MAX));
|
||||||
|
IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(writer, false));
|
||||||
|
PagedBytesIndexFieldData indexFieldData1 = getForField("f");
|
||||||
|
IndexFieldData<?> indexFieldData2 = NoOrdinalsStringFieldDataTests.hideOrdinals(indexFieldData1);
|
||||||
|
final String missingValue = randomBoolean() ? null : TestUtil.randomSimpleString(getRandom(), 2);
|
||||||
|
final int n = randomIntBetween(1, numDocs + 2);
|
||||||
|
final boolean reverse = randomBoolean();
|
||||||
|
|
||||||
|
final TopDocs topDocs1 = getTopDocs(searcher, indexFieldData1, missingValue, sortMode, n, reverse);
|
||||||
|
final TopDocs topDocs2 = getTopDocs(searcher, indexFieldData2, missingValue, sortMode, n, reverse);
|
||||||
|
for (int i = 0; i < topDocs1.scoreDocs.length; ++i) {
|
||||||
|
final FieldDoc fieldDoc1 = (FieldDoc) topDocs1.scoreDocs[i];
|
||||||
|
final FieldDoc fieldDoc2 = (FieldDoc) topDocs2.scoreDocs[i];
|
||||||
|
assertEquals(fieldDoc1.doc, fieldDoc2.doc);
|
||||||
|
assertArrayEquals(fieldDoc1.fields, fieldDoc2.fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
searcher.getIndexReader().close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TopDocs getTopDocs(IndexSearcher searcher, IndexFieldData<?> indexFieldData, String missingValue, MultiValueMode sortMode, int n, boolean reverse) throws IOException {
|
||||||
|
Filter parentFilter = new TermFilter(new Term("__type", "parent"));
|
||||||
|
Filter childFilter = new TermFilter(new Term("__type", "child"));
|
||||||
|
XFieldComparatorSource innerSource = indexFieldData.comparatorSource(missingValue, sortMode);
|
||||||
|
NestedFieldComparatorSource nestedComparatorSource = new NestedFieldComparatorSource(sortMode, innerSource, parentFilter, childFilter);
|
||||||
|
Query query = new ConstantScoreQuery(parentFilter);
|
||||||
|
Sort sort = new Sort(new SortField("f", nestedComparatorSource, reverse));
|
||||||
|
return searcher.search(query, n, sort);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNestedSorting() throws Exception {
|
public void testNestedSorting() throws Exception {
|
||||||
List<Document> docs = new ArrayList<>();
|
List<Document> docs = new ArrayList<>();
|
||||||
|
|
Loading…
Reference in New Issue