Added sort by field that have multiple values per document.
Closes #2634
This commit is contained in:
parent
033d6e4306
commit
8c7779057c
|
@ -23,6 +23,7 @@ import org.apache.lucene.index.AtomicReaderContext;
|
||||||
import org.apache.lucene.search.FieldComparator;
|
import org.apache.lucene.search.FieldComparator;
|
||||||
import org.elasticsearch.index.fielddata.ByteValues;
|
import org.elasticsearch.index.fielddata.ByteValues;
|
||||||
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
|
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
|
||||||
|
import org.elasticsearch.index.fielddata.util.ByteArrayRef;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -32,14 +33,16 @@ public class ByteValuesComparator extends FieldComparator<Byte> {
|
||||||
|
|
||||||
private final IndexNumericFieldData indexFieldData;
|
private final IndexNumericFieldData indexFieldData;
|
||||||
private final byte missingValue;
|
private final byte missingValue;
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
protected final byte[] values;
|
private final byte[] values;
|
||||||
private byte bottom;
|
private byte bottom;
|
||||||
private ByteValues readerValues;
|
private ByteValues readerValues;
|
||||||
|
|
||||||
public ByteValuesComparator(IndexNumericFieldData indexFieldData, byte missingValue, int numHits) {
|
public ByteValuesComparator(IndexNumericFieldData indexFieldData, byte missingValue, int numHits, boolean reversed) {
|
||||||
this.indexFieldData = indexFieldData;
|
this.indexFieldData = indexFieldData;
|
||||||
this.missingValue = missingValue;
|
this.missingValue = missingValue;
|
||||||
|
this.reversed = reversed;
|
||||||
this.values = new byte[numHits];
|
this.values = new byte[numHits];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +84,10 @@ public class ByteValuesComparator extends FieldComparator<Byte> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FieldComparator<Byte> setNextReader(AtomicReaderContext context) throws IOException {
|
public FieldComparator<Byte> setNextReader(AtomicReaderContext context) throws IOException {
|
||||||
this.readerValues = indexFieldData.load(context).getByteValues();
|
readerValues = indexFieldData.load(context).getByteValues();
|
||||||
|
if (readerValues.isMultiValued()) {
|
||||||
|
readerValues = new MultiValuedBytesWrapper(readerValues, reversed);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,4 +108,80 @@ public class ByteValuesComparator extends FieldComparator<Byte> {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class FilteredByteValues implements ByteValues {
|
||||||
|
|
||||||
|
protected final ByteValues delegate;
|
||||||
|
|
||||||
|
public FilteredByteValues(ByteValues delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMultiValued() {
|
||||||
|
return delegate.isMultiValued();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasValue(int docId) {
|
||||||
|
return delegate.hasValue(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getValue(int docId) {
|
||||||
|
return delegate.getValue(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getValueMissing(int docId, byte missingValue) {
|
||||||
|
return delegate.getValueMissing(docId, missingValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteArrayRef getValues(int docId) {
|
||||||
|
return delegate.getValues(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iter getIter(int docId) {
|
||||||
|
return delegate.getIter(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forEachValueInDoc(int docId, ValueInDocProc proc) {
|
||||||
|
delegate.forEachValueInDoc(docId, proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class MultiValuedBytesWrapper extends FilteredByteValues {
|
||||||
|
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
|
public MultiValuedBytesWrapper(ByteValues delegate, boolean reversed) {
|
||||||
|
super(delegate);
|
||||||
|
this.reversed = reversed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte getValueMissing(int docId, byte missing) {
|
||||||
|
ByteValues.Iter iter = delegate.getIter(docId);
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
return missing;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte currentVal = iter.next();
|
||||||
|
byte relevantVal = currentVal;
|
||||||
|
while (true) {
|
||||||
|
if (reversed) {
|
||||||
|
if (currentVal > relevantVal) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentVal < relevantVal) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentVal = iter.next();
|
||||||
|
}
|
||||||
|
return relevantVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,6 @@ public class ByteValuesComparatorSource extends IndexFieldData.XFieldComparatorS
|
||||||
dMissingValue = missingValue instanceof Number ? ((Number) missingValue).byteValue() : Byte.parseByte(missingValue.toString());
|
dMissingValue = missingValue instanceof Number ? ((Number) missingValue).byteValue() : Byte.parseByte(missingValue.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ByteValuesComparator(indexFieldData, dMissingValue, numHits);
|
return new ByteValuesComparator(indexFieldData, dMissingValue, numHits, reversed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,8 +44,8 @@ public class BytesRefFieldComparatorSource extends IndexFieldData.XFieldComparat
|
||||||
public FieldComparator<?> newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
|
public FieldComparator<?> newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
|
||||||
assert fieldname.equals(indexFieldData.getFieldNames().indexName());
|
assert fieldname.equals(indexFieldData.getFieldNames().indexName());
|
||||||
if (indexFieldData.valuesOrdered() && indexFieldData instanceof IndexFieldData.WithOrdinals) {
|
if (indexFieldData.valuesOrdered() && indexFieldData instanceof IndexFieldData.WithOrdinals) {
|
||||||
return new BytesRefOrdValComparator((IndexFieldData.WithOrdinals) indexFieldData, numHits);
|
return new BytesRefOrdValComparator((IndexFieldData.WithOrdinals) indexFieldData, numHits, reversed);
|
||||||
}
|
}
|
||||||
return new BytesRefValComparator(indexFieldData, numHits);
|
return new BytesRefValComparator(indexFieldData, numHits, reversed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ package org.elasticsearch.index.fielddata.fieldcomparator;
|
||||||
import org.apache.lucene.index.AtomicReaderContext;
|
import org.apache.lucene.index.AtomicReaderContext;
|
||||||
import org.apache.lucene.search.FieldComparator;
|
import org.apache.lucene.search.FieldComparator;
|
||||||
import org.apache.lucene.util.BytesRef;
|
import org.apache.lucene.util.BytesRef;
|
||||||
import org.elasticsearch.ElasticSearchIllegalArgumentException;
|
|
||||||
import org.elasticsearch.index.fielddata.BytesValues;
|
import org.elasticsearch.index.fielddata.BytesValues;
|
||||||
import org.elasticsearch.index.fielddata.IndexFieldData;
|
import org.elasticsearch.index.fielddata.IndexFieldData;
|
||||||
import org.elasticsearch.index.fielddata.ordinals.Ordinals;
|
import org.elasticsearch.index.fielddata.ordinals.Ordinals;
|
||||||
|
@ -48,6 +47,8 @@ public final class BytesRefOrdValComparator extends FieldComparator<BytesRef> {
|
||||||
@lucene.internal */
|
@lucene.internal */
|
||||||
final int[] ords;
|
final int[] ords;
|
||||||
|
|
||||||
|
final boolean reversed;
|
||||||
|
|
||||||
/* Values for each slot.
|
/* Values for each slot.
|
||||||
@lucene.internal */
|
@lucene.internal */
|
||||||
final BytesRef[] values;
|
final BytesRef[] values;
|
||||||
|
@ -88,8 +89,9 @@ public final class BytesRefOrdValComparator extends FieldComparator<BytesRef> {
|
||||||
|
|
||||||
final BytesRef tempBR = new BytesRef();
|
final BytesRef tempBR = new BytesRef();
|
||||||
|
|
||||||
public BytesRefOrdValComparator(IndexFieldData.WithOrdinals indexFieldData, int numHits) {
|
public BytesRefOrdValComparator(IndexFieldData.WithOrdinals indexFieldData, int numHits, boolean reversed) {
|
||||||
this.indexFieldData = indexFieldData;
|
this.indexFieldData = indexFieldData;
|
||||||
|
this.reversed = reversed;
|
||||||
ords = new int[numHits];
|
ords = new int[numHits];
|
||||||
values = new BytesRef[numHits];
|
values = new BytesRef[numHits];
|
||||||
readerGen = new int[numHits];
|
readerGen = new int[numHits];
|
||||||
|
@ -378,37 +380,35 @@ public final class BytesRefOrdValComparator extends FieldComparator<BytesRef> {
|
||||||
public FieldComparator<BytesRef> setNextReader(AtomicReaderContext context) throws IOException {
|
public FieldComparator<BytesRef> setNextReader(AtomicReaderContext context) throws IOException {
|
||||||
final int docBase = context.docBase;
|
final int docBase = context.docBase;
|
||||||
termsIndex = indexFieldData.load(context).getBytesValues();
|
termsIndex = indexFieldData.load(context).getBytesValues();
|
||||||
// TODO, we should support sorting on multi valued field, take the best ascending value out of all the values
|
|
||||||
if (termsIndex.isMultiValued()) {
|
|
||||||
throw new ElasticSearchIllegalArgumentException("can't sort on a multi valued field");
|
|
||||||
}
|
|
||||||
final Ordinals.Docs docToOrd = termsIndex.ordinals();
|
|
||||||
Object ordsStorage = docToOrd.ordinals().getBackingStorage();
|
|
||||||
FieldComparator<BytesRef> perSegComp = null;
|
FieldComparator<BytesRef> perSegComp = null;
|
||||||
|
if (termsIndex.isMultiValued()) {
|
||||||
|
perSegComp = new MultiAnyOrdComparator(termsIndex);
|
||||||
|
} else {
|
||||||
|
final Ordinals.Docs docToOrd = termsIndex.ordinals();
|
||||||
|
Object ordsStorage = docToOrd.ordinals().getBackingStorage();
|
||||||
|
|
||||||
if (docToOrd.ordinals().hasSingleArrayBackingStorage()) {
|
if (docToOrd.ordinals().hasSingleArrayBackingStorage()) {
|
||||||
if (ordsStorage instanceof byte[]) {
|
if (ordsStorage instanceof byte[]) {
|
||||||
perSegComp = new ByteOrdComparator((byte[]) ordsStorage, termsIndex, docBase);
|
perSegComp = new ByteOrdComparator((byte[]) ordsStorage, termsIndex, docBase);
|
||||||
} else if (ordsStorage instanceof short[]) {
|
} else if (ordsStorage instanceof short[]) {
|
||||||
perSegComp = new ShortOrdComparator((short[]) ordsStorage, termsIndex, docBase);
|
perSegComp = new ShortOrdComparator((short[]) ordsStorage, termsIndex, docBase);
|
||||||
} else if (ordsStorage instanceof int[]) {
|
} else if (ordsStorage instanceof int[]) {
|
||||||
perSegComp = new IntOrdComparator((int[]) ordsStorage, termsIndex, docBase);
|
perSegComp = new IntOrdComparator((int[]) ordsStorage, termsIndex, docBase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Don't specialize the long[] case since it's not
|
||||||
|
// possible, ie, worse case is MAX_INT-1 docs with
|
||||||
|
// every one having a unique value.
|
||||||
|
|
||||||
|
// TODO: ES - should we optimize for the PackedInts.Reader case as well?
|
||||||
|
if (perSegComp == null) {
|
||||||
|
perSegComp = new AnyOrdComparator(indexFieldData, termsIndex, docBase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Don't specialize the long[] case since it's not
|
|
||||||
// possible, ie, worse case is MAX_INT-1 docs with
|
|
||||||
// every one having a unique value.
|
|
||||||
|
|
||||||
// TODO: ES - should we optimize for the PackedInts.Reader case as well?
|
|
||||||
if (perSegComp == null) {
|
|
||||||
perSegComp = new AnyOrdComparator(indexFieldData, termsIndex, docBase);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentReaderGen++;
|
currentReaderGen++;
|
||||||
if (bottomSlot != -1) {
|
if (bottomSlot != -1) {
|
||||||
perSegComp.setBottom(bottomSlot);
|
perSegComp.setBottom(bottomSlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
return perSegComp;
|
return perSegComp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,4 +473,128 @@ public final class BytesRefOrdValComparator extends FieldComparator<BytesRef> {
|
||||||
}
|
}
|
||||||
return -(low + 1);
|
return -(low + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MultiAnyOrdComparator extends PerSegmentComparator {
|
||||||
|
|
||||||
|
private final BytesValues.WithOrdinals termsIndex;
|
||||||
|
private final Ordinals.Docs readerOrds;
|
||||||
|
|
||||||
|
private MultiAnyOrdComparator(BytesValues.WithOrdinals termsIndex) {
|
||||||
|
this.termsIndex = termsIndex;
|
||||||
|
this.readerOrds = termsIndex.ordinals();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareBottom(int doc) throws IOException {
|
||||||
|
final int docOrd = getRelevantOrd(readerOrds, doc, reversed);
|
||||||
|
if (bottomSameReader) {
|
||||||
|
// ord is precisely comparable, even in the equal case
|
||||||
|
return bottomOrd - docOrd;
|
||||||
|
} else if (bottomOrd >= docOrd) {
|
||||||
|
// the equals case always means bottom is > doc
|
||||||
|
// (because we set bottomOrd to the lower bound in
|
||||||
|
// setBottom):
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void copy(int slot, int doc) throws IOException {
|
||||||
|
final int ord = getRelevantOrd(readerOrds, doc, reversed);
|
||||||
|
ords[slot] = ord;
|
||||||
|
if (ord == 0) {
|
||||||
|
values[slot] = null;
|
||||||
|
} else {
|
||||||
|
assert ord > 0;
|
||||||
|
if (values[slot] == null) {
|
||||||
|
values[slot] = new BytesRef();
|
||||||
|
}
|
||||||
|
termsIndex.getValueScratchByOrd(ord, values[slot]);
|
||||||
|
}
|
||||||
|
readerGen[slot] = currentReaderGen;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareDocToValue(int doc, BytesRef value) {
|
||||||
|
BytesRef docValue = getRelevantValue(termsIndex, doc, reversed);
|
||||||
|
if (docValue == null) {
|
||||||
|
if (value == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
} else if (value == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return docValue.compareTo(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static BytesRef getRelevantValue(BytesValues.WithOrdinals readerValues, int docId, boolean reversed) {
|
||||||
|
BytesValues.Iter iter = readerValues.getIter(docId);
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
BytesRef currentVal = iter.next();
|
||||||
|
BytesRef relevantVal = currentVal;
|
||||||
|
while (true) {
|
||||||
|
int cmp = currentVal.compareTo(relevantVal);
|
||||||
|
if (reversed) {
|
||||||
|
if (cmp > 0) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (cmp < 0) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentVal = iter.next();
|
||||||
|
}
|
||||||
|
return relevantVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getRelevantOrd(Ordinals.Docs readerOrds, int docId, boolean reversed) {
|
||||||
|
Ordinals.Docs.Iter iter = readerOrds.getIter(docId);
|
||||||
|
int currentVal = iter.next();
|
||||||
|
if (currentVal == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int relevantVal = currentVal;
|
||||||
|
while (true) {
|
||||||
|
if (reversed) {
|
||||||
|
if (currentVal > relevantVal) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentVal < relevantVal) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentVal = iter.next();
|
||||||
|
if (currentVal == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return relevantVal;
|
||||||
|
// Enable this when the api can tell us that the ords per doc are ordered
|
||||||
|
/*if (reversed) {
|
||||||
|
IntArrayRef ref = readerOrds.getOrds(docId);
|
||||||
|
if (ref.isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return ref.values[ref.end - 1]; // last element is the highest value.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return readerOrds.getOrd(docId); // returns the lowest value
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.apache.lucene.search.FieldComparator;
|
||||||
import org.apache.lucene.util.BytesRef;
|
import org.apache.lucene.util.BytesRef;
|
||||||
import org.elasticsearch.index.fielddata.BytesValues;
|
import org.elasticsearch.index.fielddata.BytesValues;
|
||||||
import org.elasticsearch.index.fielddata.IndexFieldData;
|
import org.elasticsearch.index.fielddata.IndexFieldData;
|
||||||
|
import org.elasticsearch.index.fielddata.util.BytesRefArrayRef;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -36,11 +37,14 @@ import java.io.IOException;
|
||||||
public final class BytesRefValComparator extends FieldComparator<BytesRef> {
|
public final class BytesRefValComparator extends FieldComparator<BytesRef> {
|
||||||
|
|
||||||
private final IndexFieldData indexFieldData;
|
private final IndexFieldData indexFieldData;
|
||||||
private BytesRef[] values;
|
private final boolean reversed;
|
||||||
private BytesValues docTerms;
|
|
||||||
private BytesRef bottom;
|
|
||||||
|
|
||||||
BytesRefValComparator(IndexFieldData indexFieldData, int numHits) {
|
private final BytesRef[] values;
|
||||||
|
private BytesRef bottom;
|
||||||
|
private BytesValues docTerms;
|
||||||
|
|
||||||
|
BytesRefValComparator(IndexFieldData indexFieldData, int numHits, boolean reversed) {
|
||||||
|
this.reversed = reversed;
|
||||||
values = new BytesRef[numHits];
|
values = new BytesRef[numHits];
|
||||||
this.indexFieldData = indexFieldData;
|
this.indexFieldData = indexFieldData;
|
||||||
}
|
}
|
||||||
|
@ -62,7 +66,7 @@ public final class BytesRefValComparator extends FieldComparator<BytesRef> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareBottom(int doc) {
|
public int compareBottom(int doc) throws IOException {
|
||||||
BytesRef val2 = docTerms.getValue(doc);
|
BytesRef val2 = docTerms.getValue(doc);
|
||||||
if (bottom == null) {
|
if (bottom == null) {
|
||||||
if (val2 == null) {
|
if (val2 == null) {
|
||||||
|
@ -76,7 +80,7 @@ public final class BytesRefValComparator extends FieldComparator<BytesRef> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void copy(int slot, int doc) {
|
public void copy(int slot, int doc) throws IOException {
|
||||||
if (values[slot] == null) {
|
if (values[slot] == null) {
|
||||||
values[slot] = new BytesRef();
|
values[slot] = new BytesRef();
|
||||||
}
|
}
|
||||||
|
@ -86,6 +90,9 @@ public final class BytesRefValComparator extends FieldComparator<BytesRef> {
|
||||||
@Override
|
@Override
|
||||||
public FieldComparator<BytesRef> setNextReader(AtomicReaderContext context) throws IOException {
|
public FieldComparator<BytesRef> setNextReader(AtomicReaderContext context) throws IOException {
|
||||||
docTerms = indexFieldData.load(context).getBytesValues();
|
docTerms = indexFieldData.load(context).getBytesValues();
|
||||||
|
if (docTerms.isMultiValued()) {
|
||||||
|
docTerms = new MultiValuedBytesWrapper(docTerms, reversed);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,4 +123,95 @@ public final class BytesRefValComparator extends FieldComparator<BytesRef> {
|
||||||
public int compareDocToValue(int doc, BytesRef value) {
|
public int compareDocToValue(int doc, BytesRef value) {
|
||||||
return docTerms.getValue(doc).compareTo(value);
|
return docTerms.getValue(doc).compareTo(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class FilteredByteValues implements BytesValues {
|
||||||
|
|
||||||
|
protected final BytesValues delegate;
|
||||||
|
|
||||||
|
public FilteredByteValues(BytesValues delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMultiValued() {
|
||||||
|
return delegate.isMultiValued();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasValue(int docId) {
|
||||||
|
return delegate.hasValue(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BytesRef getValue(int docId) {
|
||||||
|
return delegate.getValue(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BytesRef makeSafe(BytesRef bytes) {
|
||||||
|
return delegate.makeSafe(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BytesRef getValueScratch(int docId, BytesRef ret) {
|
||||||
|
return delegate.getValueScratch(docId, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BytesRefArrayRef getValues(int docId) {
|
||||||
|
return delegate.getValues(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iter getIter(int docId) {
|
||||||
|
return delegate.getIter(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forEachValueInDoc(int docId, ValueInDocProc proc) {
|
||||||
|
delegate.forEachValueInDoc(docId, proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class MultiValuedBytesWrapper extends FilteredByteValues {
|
||||||
|
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
|
public MultiValuedBytesWrapper(BytesValues delegate, boolean reversed) {
|
||||||
|
super(delegate);
|
||||||
|
this.reversed = reversed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BytesRef getValueScratch(int docId, BytesRef scratch) {
|
||||||
|
BytesValues.Iter iter = delegate.getIter(docId);
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
BytesRef currentVal = iter.next();
|
||||||
|
BytesRef relevantVal = currentVal;
|
||||||
|
while (true) {
|
||||||
|
int cmp = currentVal.compareTo(relevantVal);
|
||||||
|
if (reversed) {
|
||||||
|
if (cmp > 0) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (cmp < 0) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentVal = iter.next();
|
||||||
|
}
|
||||||
|
return relevantVal;
|
||||||
|
/*if (reversed) {
|
||||||
|
BytesRefArrayRef ref = readerValues.getValues(docId);
|
||||||
|
if (ref.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return ref.values[ref.end - 1]; // last element is the highest value.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return readerValues.getValue(docId); // returns the lowest value
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.apache.lucene.index.AtomicReaderContext;
|
||||||
import org.apache.lucene.search.FieldComparator;
|
import org.apache.lucene.search.FieldComparator;
|
||||||
import org.elasticsearch.index.fielddata.DoubleValues;
|
import org.elasticsearch.index.fielddata.DoubleValues;
|
||||||
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
|
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
|
||||||
|
import org.elasticsearch.index.fielddata.util.DoubleArrayRef;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -32,14 +33,16 @@ public class DoubleValuesComparator extends FieldComparator<Double> {
|
||||||
|
|
||||||
private final IndexNumericFieldData indexFieldData;
|
private final IndexNumericFieldData indexFieldData;
|
||||||
private final double missingValue;
|
private final double missingValue;
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
protected final double[] values;
|
private final double[] values;
|
||||||
private double bottom;
|
private double bottom;
|
||||||
private DoubleValues readerValues;
|
private DoubleValues readerValues;
|
||||||
|
|
||||||
public DoubleValuesComparator(IndexNumericFieldData indexFieldData, double missingValue, int numHits) {
|
public DoubleValuesComparator(IndexNumericFieldData indexFieldData, double missingValue, int numHits, boolean reversed) {
|
||||||
this.indexFieldData = indexFieldData;
|
this.indexFieldData = indexFieldData;
|
||||||
this.missingValue = missingValue;
|
this.missingValue = missingValue;
|
||||||
|
this.reversed = reversed;
|
||||||
this.values = new double[numHits];
|
this.values = new double[numHits];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +84,10 @@ public class DoubleValuesComparator extends FieldComparator<Double> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FieldComparator<Double> setNextReader(AtomicReaderContext context) throws IOException {
|
public FieldComparator<Double> setNextReader(AtomicReaderContext context) throws IOException {
|
||||||
this.readerValues = indexFieldData.load(context).getDoubleValues();
|
readerValues = indexFieldData.load(context).getDoubleValues();
|
||||||
|
if (readerValues.isMultiValued()) {
|
||||||
|
readerValues = new MultiValuedBytesWrapper(readerValues, reversed);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,4 +108,81 @@ public class DoubleValuesComparator extends FieldComparator<Double> {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class FilteredByteValues implements DoubleValues {
|
||||||
|
|
||||||
|
protected final DoubleValues delegate;
|
||||||
|
|
||||||
|
public FilteredByteValues(DoubleValues delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMultiValued() {
|
||||||
|
return delegate.isMultiValued();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasValue(int docId) {
|
||||||
|
return delegate.hasValue(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValue(int docId) {
|
||||||
|
return delegate.getValue(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getValueMissing(int docId, double missingValue) {
|
||||||
|
return delegate.getValueMissing(docId, missingValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DoubleArrayRef getValues(int docId) {
|
||||||
|
return delegate.getValues(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iter getIter(int docId) {
|
||||||
|
return delegate.getIter(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forEachValueInDoc(int docId, ValueInDocProc proc) {
|
||||||
|
delegate.forEachValueInDoc(docId, proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class MultiValuedBytesWrapper extends FilteredByteValues {
|
||||||
|
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
|
public MultiValuedBytesWrapper(DoubleValues delegate, boolean reversed) {
|
||||||
|
super(delegate);
|
||||||
|
this.reversed = reversed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getValueMissing(int docId, double missing) {
|
||||||
|
DoubleValues.Iter iter = delegate.getIter(docId);
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
return missing;
|
||||||
|
}
|
||||||
|
|
||||||
|
double currentVal = iter.next();
|
||||||
|
double relevantVal = currentVal;
|
||||||
|
while (true) {
|
||||||
|
int cmp = Double.compare(currentVal, relevantVal);
|
||||||
|
if (reversed) {
|
||||||
|
if (cmp > 0) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (cmp < 0) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentVal = iter.next();
|
||||||
|
}
|
||||||
|
return relevantVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,6 @@ public class DoubleValuesComparatorSource extends IndexFieldData.XFieldComparato
|
||||||
dMissingValue = missingValue instanceof Number ? ((Number) missingValue).doubleValue() : Double.parseDouble(missingValue.toString());
|
dMissingValue = missingValue instanceof Number ? ((Number) missingValue).doubleValue() : Double.parseDouble(missingValue.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DoubleValuesComparator(indexFieldData, dMissingValue, numHits);
|
return new DoubleValuesComparator(indexFieldData, dMissingValue, numHits, reversed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.apache.lucene.index.AtomicReaderContext;
|
||||||
import org.apache.lucene.search.FieldComparator;
|
import org.apache.lucene.search.FieldComparator;
|
||||||
import org.elasticsearch.index.fielddata.FloatValues;
|
import org.elasticsearch.index.fielddata.FloatValues;
|
||||||
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
|
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
|
||||||
|
import org.elasticsearch.index.fielddata.util.FloatArrayRef;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -32,14 +33,16 @@ public class FloatValuesComparator extends FieldComparator<Float> {
|
||||||
|
|
||||||
private final IndexNumericFieldData indexFieldData;
|
private final IndexNumericFieldData indexFieldData;
|
||||||
private final float missingValue;
|
private final float missingValue;
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
protected final float[] values;
|
private final float[] values;
|
||||||
private float bottom;
|
private float bottom;
|
||||||
private FloatValues readerValues;
|
private FloatValues readerValues;
|
||||||
|
|
||||||
public FloatValuesComparator(IndexNumericFieldData indexFieldData, float missingValue, int numHits) {
|
public FloatValuesComparator(IndexNumericFieldData indexFieldData, float missingValue, int numHits, boolean reversed) {
|
||||||
this.indexFieldData = indexFieldData;
|
this.indexFieldData = indexFieldData;
|
||||||
this.missingValue = missingValue;
|
this.missingValue = missingValue;
|
||||||
|
this.reversed = reversed;
|
||||||
this.values = new float[numHits];
|
this.values = new float[numHits];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +84,10 @@ public class FloatValuesComparator extends FieldComparator<Float> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FieldComparator<Float> setNextReader(AtomicReaderContext context) throws IOException {
|
public FieldComparator<Float> setNextReader(AtomicReaderContext context) throws IOException {
|
||||||
this.readerValues = indexFieldData.load(context).getFloatValues();
|
readerValues = indexFieldData.load(context).getFloatValues();
|
||||||
|
if (readerValues.isMultiValued()) {
|
||||||
|
readerValues = new MultiValuedBytesWrapper(readerValues, reversed);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,4 +108,81 @@ public class FloatValuesComparator extends FieldComparator<Float> {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class FilteredByteValues implements FloatValues {
|
||||||
|
|
||||||
|
protected final FloatValues delegate;
|
||||||
|
|
||||||
|
public FilteredByteValues(FloatValues delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMultiValued() {
|
||||||
|
return delegate.isMultiValued();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasValue(int docId) {
|
||||||
|
return delegate.hasValue(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getValue(int docId) {
|
||||||
|
return delegate.getValue(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getValueMissing(int docId, float missingValue) {
|
||||||
|
return delegate.getValueMissing(docId, missingValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FloatArrayRef getValues(int docId) {
|
||||||
|
return delegate.getValues(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iter getIter(int docId) {
|
||||||
|
return delegate.getIter(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forEachValueInDoc(int docId, ValueInDocProc proc) {
|
||||||
|
delegate.forEachValueInDoc(docId, proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class MultiValuedBytesWrapper extends FilteredByteValues {
|
||||||
|
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
|
public MultiValuedBytesWrapper(FloatValues delegate, boolean reversed) {
|
||||||
|
super(delegate);
|
||||||
|
this.reversed = reversed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getValueMissing(int docId, float missing) {
|
||||||
|
FloatValues.Iter iter = delegate.getIter(docId);
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
return missing;
|
||||||
|
}
|
||||||
|
|
||||||
|
float currentVal = iter.next();
|
||||||
|
float relevantVal = currentVal;
|
||||||
|
while (true) {
|
||||||
|
int cmp = Float.compare(currentVal, relevantVal);
|
||||||
|
if (reversed) {
|
||||||
|
if (cmp > 0) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (cmp < 0) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentVal = iter.next();
|
||||||
|
}
|
||||||
|
return relevantVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,6 @@ public class FloatValuesComparatorSource extends IndexFieldData.XFieldComparator
|
||||||
dMissingValue = missingValue instanceof Number ? ((Number) missingValue).floatValue() : Float.parseFloat(missingValue.toString());
|
dMissingValue = missingValue instanceof Number ? ((Number) missingValue).floatValue() : Float.parseFloat(missingValue.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FloatValuesComparator(indexFieldData, dMissingValue, numHits);
|
return new FloatValuesComparator(indexFieldData, dMissingValue, numHits, reversed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.apache.lucene.index.AtomicReaderContext;
|
||||||
import org.apache.lucene.search.FieldComparator;
|
import org.apache.lucene.search.FieldComparator;
|
||||||
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
|
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
|
||||||
import org.elasticsearch.index.fielddata.IntValues;
|
import org.elasticsearch.index.fielddata.IntValues;
|
||||||
|
import org.elasticsearch.index.fielddata.util.IntArrayRef;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -32,14 +33,16 @@ public class IntValuesComparator extends FieldComparator<Integer> {
|
||||||
|
|
||||||
private final IndexNumericFieldData indexFieldData;
|
private final IndexNumericFieldData indexFieldData;
|
||||||
private final int missingValue;
|
private final int missingValue;
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
protected final int[] values;
|
private final int[] values;
|
||||||
private int bottom;
|
private int bottom;
|
||||||
private IntValues readerValues;
|
private IntValues readerValues;
|
||||||
|
|
||||||
public IntValuesComparator(IndexNumericFieldData indexFieldData, int missingValue, int numHits) {
|
public IntValuesComparator(IndexNumericFieldData indexFieldData, int missingValue, int numHits, boolean reversed) {
|
||||||
this.indexFieldData = indexFieldData;
|
this.indexFieldData = indexFieldData;
|
||||||
this.missingValue = missingValue;
|
this.missingValue = missingValue;
|
||||||
|
this.reversed = reversed;
|
||||||
this.values = new int[numHits];
|
this.values = new int[numHits];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +84,10 @@ public class IntValuesComparator extends FieldComparator<Integer> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FieldComparator<Integer> setNextReader(AtomicReaderContext context) throws IOException {
|
public FieldComparator<Integer> setNextReader(AtomicReaderContext context) throws IOException {
|
||||||
this.readerValues = indexFieldData.load(context).getIntValues();
|
readerValues = indexFieldData.load(context).getIntValues();
|
||||||
|
if (readerValues.isMultiValued()) {
|
||||||
|
readerValues = new MultiValuedBytesWrapper(readerValues, reversed);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,4 +108,80 @@ public class IntValuesComparator extends FieldComparator<Integer> {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class FilteredByteValues implements IntValues {
|
||||||
|
|
||||||
|
protected final IntValues delegate;
|
||||||
|
|
||||||
|
public FilteredByteValues(IntValues delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMultiValued() {
|
||||||
|
return delegate.isMultiValued();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasValue(int docId) {
|
||||||
|
return delegate.hasValue(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue(int docId) {
|
||||||
|
return delegate.getValue(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValueMissing(int docId, int missingValue) {
|
||||||
|
return delegate.getValueMissing(docId, missingValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IntArrayRef getValues(int docId) {
|
||||||
|
return delegate.getValues(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iter getIter(int docId) {
|
||||||
|
return delegate.getIter(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forEachValueInDoc(int docId, ValueInDocProc proc) {
|
||||||
|
delegate.forEachValueInDoc(docId, proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class MultiValuedBytesWrapper extends FilteredByteValues {
|
||||||
|
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
|
public MultiValuedBytesWrapper(IntValues delegate, boolean reversed) {
|
||||||
|
super(delegate);
|
||||||
|
this.reversed = reversed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getValueMissing(int docId, int missing) {
|
||||||
|
IntValues.Iter iter = delegate.getIter(docId);
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
return missing;
|
||||||
|
}
|
||||||
|
|
||||||
|
int currentVal = iter.next();
|
||||||
|
int relevantVal = currentVal;
|
||||||
|
while (true) {
|
||||||
|
if (reversed) {
|
||||||
|
if (currentVal > relevantVal) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentVal < relevantVal) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentVal = iter.next();
|
||||||
|
}
|
||||||
|
return relevantVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,6 @@ public class IntValuesComparatorSource extends IndexFieldData.XFieldComparatorSo
|
||||||
dMissingValue = missingValue instanceof Number ? ((Number) missingValue).intValue() : Integer.parseInt(missingValue.toString());
|
dMissingValue = missingValue instanceof Number ? ((Number) missingValue).intValue() : Integer.parseInt(missingValue.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new IntValuesComparator(indexFieldData, dMissingValue, numHits);
|
return new IntValuesComparator(indexFieldData, dMissingValue, numHits, reversed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.apache.lucene.index.AtomicReaderContext;
|
||||||
import org.apache.lucene.search.FieldComparator;
|
import org.apache.lucene.search.FieldComparator;
|
||||||
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
|
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
|
||||||
import org.elasticsearch.index.fielddata.LongValues;
|
import org.elasticsearch.index.fielddata.LongValues;
|
||||||
|
import org.elasticsearch.index.fielddata.util.LongArrayRef;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -32,15 +33,17 @@ public class LongValuesComparator extends FieldComparator<Long> {
|
||||||
|
|
||||||
private final IndexNumericFieldData indexFieldData;
|
private final IndexNumericFieldData indexFieldData;
|
||||||
private final long missingValue;
|
private final long missingValue;
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
protected final long[] values;
|
private final long[] values;
|
||||||
private long bottom;
|
|
||||||
private LongValues readerValues;
|
private LongValues readerValues;
|
||||||
|
private long bottom;
|
||||||
|
|
||||||
public LongValuesComparator(IndexNumericFieldData indexFieldData, long missingValue, int numHits) {
|
public LongValuesComparator(IndexNumericFieldData indexFieldData, long missingValue, int numHits, boolean reversed) {
|
||||||
this.indexFieldData = indexFieldData;
|
this.indexFieldData = indexFieldData;
|
||||||
this.missingValue = missingValue;
|
this.missingValue = missingValue;
|
||||||
this.values = new long[numHits];
|
this.values = new long[numHits];
|
||||||
|
this.reversed = reversed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -64,7 +67,6 @@ public class LongValuesComparator extends FieldComparator<Long> {
|
||||||
@Override
|
@Override
|
||||||
public int compareBottom(int doc) throws IOException {
|
public int compareBottom(int doc) throws IOException {
|
||||||
long v2 = readerValues.getValueMissing(doc, missingValue);
|
long v2 = readerValues.getValueMissing(doc, missingValue);
|
||||||
|
|
||||||
if (bottom > v2) {
|
if (bottom > v2) {
|
||||||
return 1;
|
return 1;
|
||||||
} else if (bottom < v2) {
|
} else if (bottom < v2) {
|
||||||
|
@ -81,7 +83,10 @@ public class LongValuesComparator extends FieldComparator<Long> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FieldComparator<Long> setNextReader(AtomicReaderContext context) throws IOException {
|
public FieldComparator<Long> setNextReader(AtomicReaderContext context) throws IOException {
|
||||||
this.readerValues = indexFieldData.load(context).getLongValues();
|
readerValues = indexFieldData.load(context).getLongValues();
|
||||||
|
if (readerValues.isMultiValued()) {
|
||||||
|
readerValues = new MultiValuedBytesWrapper(readerValues, reversed);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,4 +107,96 @@ public class LongValuesComparator extends FieldComparator<Long> {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// THIS SHOULD GO INTO the fielddata package
|
||||||
|
public static class FilteredByteValues implements LongValues {
|
||||||
|
|
||||||
|
protected final LongValues delegate;
|
||||||
|
|
||||||
|
public FilteredByteValues(LongValues delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMultiValued() {
|
||||||
|
return delegate.isMultiValued();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasValue(int docId) {
|
||||||
|
return delegate.hasValue(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getValue(int docId) {
|
||||||
|
return delegate.getValue(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getValueMissing(int docId, long missingValue) {
|
||||||
|
return delegate.getValueMissing(docId, missingValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LongArrayRef getValues(int docId) {
|
||||||
|
return delegate.getValues(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iter getIter(int docId) {
|
||||||
|
return delegate.getIter(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forEachValueInDoc(int docId, ValueInDocProc proc) {
|
||||||
|
delegate.forEachValueInDoc(docId, proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class MultiValuedBytesWrapper extends FilteredByteValues {
|
||||||
|
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
|
public MultiValuedBytesWrapper(LongValues delegate, boolean reversed) {
|
||||||
|
super(delegate);
|
||||||
|
this.reversed = reversed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getValueMissing(int docId, long missing) {
|
||||||
|
LongValues.Iter iter = delegate.getIter(docId);
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
return missing;
|
||||||
|
}
|
||||||
|
|
||||||
|
long currentVal = iter.next();
|
||||||
|
long relevantVal = currentVal;
|
||||||
|
while (true) {
|
||||||
|
if (reversed) {
|
||||||
|
if (currentVal > relevantVal) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentVal < relevantVal) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentVal = iter.next();
|
||||||
|
}
|
||||||
|
return relevantVal;
|
||||||
|
// If we have a method on readerValues that tells if the values emitted by Iter or ArrayRef are sorted per
|
||||||
|
// document that we can do this or something similar:
|
||||||
|
// (This is already possible, if values are loaded from index, but we just need a method that tells us this
|
||||||
|
// For example a impl that read values from the _source field might not read values in order)
|
||||||
|
/*if (reversed) {
|
||||||
|
// Would be nice if there is a way to get highest value from LongValues. The values are sorted anyway.
|
||||||
|
LongArrayRef ref = readerValues.getValues(doc);
|
||||||
|
if (ref.isEmpty()) {
|
||||||
|
return missing;
|
||||||
|
} else {
|
||||||
|
return ref.values[ref.end - 1]; // last element is the highest value.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return readerValues.getValueMissing(doc, missing); // returns lowest
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,6 @@ public class LongValuesComparatorSource extends IndexFieldData.XFieldComparatorS
|
||||||
dMissingValue = missingValue instanceof Number ? ((Number) missingValue).longValue() : Long.parseLong(missingValue.toString());
|
dMissingValue = missingValue instanceof Number ? ((Number) missingValue).longValue() : Long.parseLong(missingValue.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LongValuesComparator(indexFieldData, dMissingValue, numHits);
|
return new LongValuesComparator(indexFieldData, dMissingValue, numHits, reversed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.apache.lucene.index.AtomicReaderContext;
|
||||||
import org.apache.lucene.search.FieldComparator;
|
import org.apache.lucene.search.FieldComparator;
|
||||||
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
|
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
|
||||||
import org.elasticsearch.index.fielddata.ShortValues;
|
import org.elasticsearch.index.fielddata.ShortValues;
|
||||||
|
import org.elasticsearch.index.fielddata.util.ShortArrayRef;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -32,14 +33,16 @@ public class ShortValuesComparator extends FieldComparator<Short> {
|
||||||
|
|
||||||
private final IndexNumericFieldData indexFieldData;
|
private final IndexNumericFieldData indexFieldData;
|
||||||
private final short missingValue;
|
private final short missingValue;
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
protected final short[] values;
|
private final short[] values;
|
||||||
private short bottom;
|
private short bottom;
|
||||||
private ShortValues readerValues;
|
private ShortValues readerValues;
|
||||||
|
|
||||||
public ShortValuesComparator(IndexNumericFieldData indexFieldData, short missingValue, int numHits) {
|
public ShortValuesComparator(IndexNumericFieldData indexFieldData, short missingValue, int numHits, boolean reversed) {
|
||||||
this.indexFieldData = indexFieldData;
|
this.indexFieldData = indexFieldData;
|
||||||
this.missingValue = missingValue;
|
this.missingValue = missingValue;
|
||||||
|
this.reversed = reversed;
|
||||||
this.values = new short[numHits];
|
this.values = new short[numHits];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +84,10 @@ public class ShortValuesComparator extends FieldComparator<Short> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FieldComparator<Short> setNextReader(AtomicReaderContext context) throws IOException {
|
public FieldComparator<Short> setNextReader(AtomicReaderContext context) throws IOException {
|
||||||
this.readerValues = indexFieldData.load(context).getShortValues();
|
readerValues = indexFieldData.load(context).getShortValues();
|
||||||
|
if (readerValues.isMultiValued()) {
|
||||||
|
readerValues = new MultiValuedBytesWrapper(readerValues, reversed);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,4 +108,80 @@ public class ShortValuesComparator extends FieldComparator<Short> {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class FilteredByteValues implements ShortValues {
|
||||||
|
|
||||||
|
protected final ShortValues delegate;
|
||||||
|
|
||||||
|
public FilteredByteValues(ShortValues delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMultiValued() {
|
||||||
|
return delegate.isMultiValued();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasValue(int docId) {
|
||||||
|
return delegate.hasValue(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getValue(int docId) {
|
||||||
|
return delegate.getValue(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public short getValueMissing(int docId, short missingValue) {
|
||||||
|
return delegate.getValueMissing(docId, missingValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShortArrayRef getValues(int docId) {
|
||||||
|
return delegate.getValues(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iter getIter(int docId) {
|
||||||
|
return delegate.getIter(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void forEachValueInDoc(int docId, ValueInDocProc proc) {
|
||||||
|
delegate.forEachValueInDoc(docId, proc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class MultiValuedBytesWrapper extends FilteredByteValues {
|
||||||
|
|
||||||
|
private final boolean reversed;
|
||||||
|
|
||||||
|
public MultiValuedBytesWrapper(ShortValues delegate, boolean reversed) {
|
||||||
|
super(delegate);
|
||||||
|
this.reversed = reversed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public short getValueMissing(int docId, short missing) {
|
||||||
|
ShortValues.Iter iter = delegate.getIter(docId);
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
return missing;
|
||||||
|
}
|
||||||
|
|
||||||
|
short currentVal = iter.next();
|
||||||
|
short relevantVal = currentVal;
|
||||||
|
while (true) {
|
||||||
|
if (reversed) {
|
||||||
|
if (currentVal > relevantVal) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentVal < relevantVal) {
|
||||||
|
relevantVal = currentVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!iter.hasNext()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentVal = iter.next();
|
||||||
|
}
|
||||||
|
return relevantVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,6 @@ public class ShortValuesComparatorSource extends IndexFieldData.XFieldComparator
|
||||||
dMissingValue = missingValue instanceof Number ? ((Number) missingValue).shortValue() : Short.parseShort(missingValue.toString());
|
dMissingValue = missingValue instanceof Number ? ((Number) missingValue).shortValue() : Short.parseShort(missingValue.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ShortValuesComparator(indexFieldData, dMissingValue, numHits);
|
return new ShortValuesComparator(indexFieldData, dMissingValue, numHits, reversed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class ByteArrayRef extends AbstractList<Byte> implements RandomAccess {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return size() != 0;
|
return size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -53,6 +53,10 @@ public class BytesRefArrayRef {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
return end - start;
|
return end - start;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class DoubleArrayRef extends AbstractList<Double> implements RandomAccess
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return size() != 0;
|
return size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class FloatArrayRef extends AbstractList<Float> implements RandomAccess {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return size() != 0;
|
return size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -70,7 +70,7 @@ public class IntArrayRef extends AbstractList<Integer> implements RandomAccess {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return size() != 0;
|
return size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class LongArrayRef extends AbstractList<Long> implements RandomAccess {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return size() != 0;
|
return size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class ShortArrayRef extends AbstractList<Short> implements RandomAccess {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return size() != 0;
|
return size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -163,6 +163,16 @@ public class SortParseElement implements SearchParseElement {
|
||||||
}
|
}
|
||||||
throw new SearchParseException(context, "No mapping found for [" + fieldName + "] in order to sort on");
|
throw new SearchParseException(context, "No mapping found for [" + fieldName + "] in order to sort on");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable when we also know how to detect fields that do tokenize, but only emit one token
|
||||||
|
/*if (fieldMapper instanceof StringFieldMapper) {
|
||||||
|
StringFieldMapper stringFieldMapper = (StringFieldMapper) fieldMapper;
|
||||||
|
if (stringFieldMapper.fieldType().tokenized()) {
|
||||||
|
// Fail early
|
||||||
|
throw new SearchParseException(context, "Can't sort on tokenized string field[" + fieldName + "]");
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
sortFields.add(new SortField(fieldMapper.names().indexName(), context.fieldData().getForField(fieldMapper).comparatorSource(missing), reverse));
|
sortFields.add(new SortField(fieldMapper.names().indexName(), context.fieldData().getForField(fieldMapper).comparatorSource(missing), reverse));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -604,4 +604,69 @@ public class SimpleSortTests extends AbstractNodesTests {
|
||||||
|
|
||||||
assertThat(searchResponse.failedShards(), equalTo(0));
|
assertThat(searchResponse.failedShards(), equalTo(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSortLongMVField() throws Exception {
|
||||||
|
try {
|
||||||
|
client.admin().indices().prepareDelete("test").execute().actionGet();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
client.admin().indices().prepareCreate("test")
|
||||||
|
.setSettings(ImmutableSettings.settingsBuilder().put("index.number_of_shards", 1).put("index.number_of_replicas", 0))
|
||||||
|
.addMapping("type1", XContentFactory.jsonBuilder().startObject().startObject("type1").startObject("properties")
|
||||||
|
.startObject("long_values").field("type", "long").endObject()
|
||||||
|
.endObject().endObject().endObject())
|
||||||
|
.execute().actionGet();
|
||||||
|
client.admin().cluster().prepareHealth().setWaitForGreenStatus().execute().actionGet();
|
||||||
|
|
||||||
|
client.prepareIndex("test", "type1", Integer.toString(1)).setSource(jsonBuilder().startObject()
|
||||||
|
.array("long_values", 1l, 5l, 10l, 8l)
|
||||||
|
.endObject()).execute().actionGet();
|
||||||
|
client.prepareIndex("test", "type1", Integer.toString(2)).setSource(jsonBuilder().startObject()
|
||||||
|
.array("long_values", 11l, 15l, 20l, 7l)
|
||||||
|
.endObject()).execute().actionGet();
|
||||||
|
client.prepareIndex("test", "type1", Integer.toString(3)).setSource(jsonBuilder().startObject()
|
||||||
|
.array("long_values", 2l, 1l, 3l, -4l)
|
||||||
|
.endObject()).execute().actionGet();
|
||||||
|
|
||||||
|
client.admin().indices().prepareRefresh().execute().actionGet();
|
||||||
|
|
||||||
|
SearchResponse searchResponse = client.prepareSearch()
|
||||||
|
.setQuery(matchAllQuery())
|
||||||
|
.setSize(10)
|
||||||
|
.addSort("long_values", SortOrder.ASC)
|
||||||
|
.execute().actionGet();
|
||||||
|
|
||||||
|
assertThat(searchResponse.hits().getTotalHits(), equalTo(3l));
|
||||||
|
assertThat(searchResponse.hits().hits().length, equalTo(3));
|
||||||
|
|
||||||
|
assertThat(searchResponse.hits().getAt(0).id(), equalTo(Integer.toString(3)));
|
||||||
|
assertThat(((Number) searchResponse.hits().getAt(0).sortValues()[0]).longValue(), equalTo(-4l));
|
||||||
|
|
||||||
|
assertThat(searchResponse.hits().getAt(1).id(), equalTo(Integer.toString(1)));
|
||||||
|
assertThat(((Number) searchResponse.hits().getAt(1).sortValues()[0]).longValue(), equalTo(1l));
|
||||||
|
|
||||||
|
assertThat(searchResponse.hits().getAt(2).id(), equalTo(Integer.toString(2)));
|
||||||
|
assertThat(((Number) searchResponse.hits().getAt(2).sortValues()[0]).longValue(), equalTo(7l));
|
||||||
|
|
||||||
|
searchResponse = client.prepareSearch()
|
||||||
|
.setQuery(matchAllQuery())
|
||||||
|
.setSize(10)
|
||||||
|
.addSort("long_values", SortOrder.DESC)
|
||||||
|
.execute().actionGet();
|
||||||
|
|
||||||
|
assertThat(searchResponse.hits().getTotalHits(), equalTo(3l));
|
||||||
|
assertThat(searchResponse.hits().hits().length, equalTo(3));
|
||||||
|
|
||||||
|
assertThat(searchResponse.hits().getAt(0).id(), equalTo(Integer.toString(2)));
|
||||||
|
assertThat(((Number) searchResponse.hits().getAt(0).sortValues()[0]).longValue(), equalTo(20l));
|
||||||
|
|
||||||
|
assertThat(searchResponse.hits().getAt(1).id(), equalTo(Integer.toString(1)));
|
||||||
|
assertThat(((Number) searchResponse.hits().getAt(1).sortValues()[0]).longValue(), equalTo(10l));
|
||||||
|
|
||||||
|
assertThat(searchResponse.hits().getAt(2).id(), equalTo(Integer.toString(3)));
|
||||||
|
assertThat(((Number) searchResponse.hits().getAt(2).sortValues()[0]).longValue(), equalTo(3l));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,4 +30,5 @@ public class ByteFieldDataTests extends IntFieldDataTests {
|
||||||
return new FieldDataType("byte");
|
return new FieldDataType("byte");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,4 +124,56 @@ public class DoubleFieldDataTests extends NumericFieldDataTests {
|
||||||
d.add(new DoubleField("value", 3.0f, Field.Store.NO));
|
d.add(new DoubleField("value", 3.0f, Field.Store.NO));
|
||||||
writer.addDocument(d);
|
writer.addDocument(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void fillExtendedMvSet() throws Exception {
|
||||||
|
Document d = new Document();
|
||||||
|
d.add(new StringField("_id", "1", Field.Store.NO));
|
||||||
|
d.add(new DoubleField("value", 2, Field.Store.NO));
|
||||||
|
d.add(new DoubleField("value", 4, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "2", Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "3", Field.Store.NO));
|
||||||
|
d.add(new DoubleField("value", 3, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
writer.commit();
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "4", Field.Store.NO));
|
||||||
|
d.add(new DoubleField("value", 4, Field.Store.NO));
|
||||||
|
d.add(new DoubleField("value", 5, Field.Store.NO));
|
||||||
|
d.add(new DoubleField("value", 6, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "5", Field.Store.NO));
|
||||||
|
d.add(new DoubleField("value", 6, Field.Store.NO));
|
||||||
|
d.add(new DoubleField("value", 7, Field.Store.NO));
|
||||||
|
d.add(new DoubleField("value", 8, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "6", Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "7", Field.Store.NO));
|
||||||
|
d.add(new DoubleField("value", 8, Field.Store.NO));
|
||||||
|
d.add(new DoubleField("value", 9, Field.Store.NO));
|
||||||
|
d.add(new DoubleField("value", 10, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
writer.commit();
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "8", Field.Store.NO));
|
||||||
|
d.add(new DoubleField("value", -8, Field.Store.NO));
|
||||||
|
d.add(new DoubleField("value", -9, Field.Store.NO));
|
||||||
|
d.add(new DoubleField("value", -10, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,4 +124,56 @@ public class FloatFieldDataTests extends NumericFieldDataTests {
|
||||||
d.add(new FloatField("value", 3.0f, Field.Store.NO));
|
d.add(new FloatField("value", 3.0f, Field.Store.NO));
|
||||||
writer.addDocument(d);
|
writer.addDocument(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void fillExtendedMvSet() throws Exception {
|
||||||
|
Document d = new Document();
|
||||||
|
d.add(new StringField("_id", "1", Field.Store.NO));
|
||||||
|
d.add(new FloatField("value", 2, Field.Store.NO));
|
||||||
|
d.add(new FloatField("value", 4, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "2", Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "3", Field.Store.NO));
|
||||||
|
d.add(new FloatField("value", 3, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
writer.commit();
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "4", Field.Store.NO));
|
||||||
|
d.add(new FloatField("value", 4, Field.Store.NO));
|
||||||
|
d.add(new FloatField("value", 5, Field.Store.NO));
|
||||||
|
d.add(new FloatField("value", 6, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "5", Field.Store.NO));
|
||||||
|
d.add(new FloatField("value", 6, Field.Store.NO));
|
||||||
|
d.add(new FloatField("value", 7, Field.Store.NO));
|
||||||
|
d.add(new FloatField("value", 8, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "6", Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "7", Field.Store.NO));
|
||||||
|
d.add(new FloatField("value", 8, Field.Store.NO));
|
||||||
|
d.add(new FloatField("value", 9, Field.Store.NO));
|
||||||
|
d.add(new FloatField("value", 10, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
writer.commit();
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "8", Field.Store.NO));
|
||||||
|
d.add(new FloatField("value", -8, Field.Store.NO));
|
||||||
|
d.add(new FloatField("value", -9, Field.Store.NO));
|
||||||
|
d.add(new FloatField("value", -10, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,10 @@
|
||||||
|
|
||||||
package org.elasticsearch.test.unit.index.fielddata;
|
package org.elasticsearch.test.unit.index.fielddata;
|
||||||
|
|
||||||
import org.apache.lucene.document.*;
|
import org.apache.lucene.document.Document;
|
||||||
|
import org.apache.lucene.document.Field;
|
||||||
|
import org.apache.lucene.document.IntField;
|
||||||
|
import org.apache.lucene.document.StringField;
|
||||||
import org.elasticsearch.index.fielddata.FieldDataType;
|
import org.elasticsearch.index.fielddata.FieldDataType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,4 +107,56 @@ public class IntFieldDataTests extends NumericFieldDataTests {
|
||||||
d.add(new IntField("value", 3, Field.Store.NO));
|
d.add(new IntField("value", 3, Field.Store.NO));
|
||||||
writer.addDocument(d);
|
writer.addDocument(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void fillExtendedMvSet() throws Exception {
|
||||||
|
Document d = new Document();
|
||||||
|
d.add(new StringField("_id", "1", Field.Store.NO));
|
||||||
|
d.add(new IntField("value", 2, Field.Store.NO));
|
||||||
|
d.add(new IntField("value", 4, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "2", Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "3", Field.Store.NO));
|
||||||
|
d.add(new IntField("value", 3, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
writer.commit();
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "4", Field.Store.NO));
|
||||||
|
d.add(new IntField("value", 4, Field.Store.NO));
|
||||||
|
d.add(new IntField("value", 5, Field.Store.NO));
|
||||||
|
d.add(new IntField("value", 6, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "5", Field.Store.NO));
|
||||||
|
d.add(new IntField("value", 6, Field.Store.NO));
|
||||||
|
d.add(new IntField("value", 7, Field.Store.NO));
|
||||||
|
d.add(new IntField("value", 8, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "6", Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "7", Field.Store.NO));
|
||||||
|
d.add(new IntField("value", 8, Field.Store.NO));
|
||||||
|
d.add(new IntField("value", 9, Field.Store.NO));
|
||||||
|
d.add(new IntField("value", 10, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
writer.commit();
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "8", Field.Store.NO));
|
||||||
|
d.add(new IntField("value", -8, Field.Store.NO));
|
||||||
|
d.add(new IntField("value", -9, Field.Store.NO));
|
||||||
|
d.add(new IntField("value", -10, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,4 +107,56 @@ public class LongFieldDataTests extends NumericFieldDataTests {
|
||||||
d.add(new LongField("value", 3, Field.Store.NO));
|
d.add(new LongField("value", 3, Field.Store.NO));
|
||||||
writer.addDocument(d);
|
writer.addDocument(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void fillExtendedMvSet() throws Exception {
|
||||||
|
Document d = new Document();
|
||||||
|
d.add(new StringField("_id", "1", Field.Store.NO));
|
||||||
|
d.add(new LongField("value", 2, Field.Store.NO));
|
||||||
|
d.add(new LongField("value", 4, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "2", Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "3", Field.Store.NO));
|
||||||
|
d.add(new LongField("value", 3, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
writer.commit();
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "4", Field.Store.NO));
|
||||||
|
d.add(new LongField("value", 4, Field.Store.NO));
|
||||||
|
d.add(new LongField("value", 5, Field.Store.NO));
|
||||||
|
d.add(new LongField("value", 6, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "5", Field.Store.NO));
|
||||||
|
d.add(new LongField("value", 6, Field.Store.NO));
|
||||||
|
d.add(new LongField("value", 7, Field.Store.NO));
|
||||||
|
d.add(new LongField("value", 8, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "6", Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "7", Field.Store.NO));
|
||||||
|
d.add(new LongField("value", 8, Field.Store.NO));
|
||||||
|
d.add(new LongField("value", 9, Field.Store.NO));
|
||||||
|
d.add(new LongField("value", 10, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
writer.commit();
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "8", Field.Store.NO));
|
||||||
|
d.add(new LongField("value", -8, Field.Store.NO));
|
||||||
|
d.add(new LongField("value", -9, Field.Store.NO));
|
||||||
|
d.add(new LongField("value", -10, Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ package org.elasticsearch.test.unit.index.fielddata;
|
||||||
import org.apache.lucene.document.Document;
|
import org.apache.lucene.document.Document;
|
||||||
import org.apache.lucene.document.Field;
|
import org.apache.lucene.document.Field;
|
||||||
import org.apache.lucene.document.StringField;
|
import org.apache.lucene.document.StringField;
|
||||||
|
import org.apache.lucene.index.DirectoryReader;
|
||||||
import org.apache.lucene.search.*;
|
import org.apache.lucene.search.*;
|
||||||
import org.elasticsearch.index.fielddata.*;
|
import org.elasticsearch.index.fielddata.*;
|
||||||
import org.elasticsearch.index.fielddata.util.*;
|
import org.elasticsearch.index.fielddata.util.*;
|
||||||
|
@ -1489,4 +1490,106 @@ public abstract class NumericFieldDataTests extends StringFieldDataTests {
|
||||||
d.add(new StringField("_id", "3", Field.Store.NO));
|
d.add(new StringField("_id", "3", Field.Store.NO));
|
||||||
writer.addDocument(d);
|
writer.addDocument(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSortMultiValuesFields() throws Exception {
|
||||||
|
fillExtendedMvSet();
|
||||||
|
IndexFieldData indexFieldData = getForField("value");
|
||||||
|
|
||||||
|
IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(writer, true));
|
||||||
|
TopFieldDocs topDocs = searcher.search(new MatchAllDocsQuery(), 10,
|
||||||
|
new Sort(new SortField("value", indexFieldData.comparatorSource(null)))); // defaults to _last
|
||||||
|
assertThat(topDocs.totalHits, equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs.length, equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs[0].doc, equalTo(7));
|
||||||
|
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[0]).fields[0]).intValue(), equalTo(-10));
|
||||||
|
assertThat(topDocs.scoreDocs[1].doc, equalTo(0));
|
||||||
|
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[1]).fields[0]).intValue(), equalTo(2));
|
||||||
|
assertThat(topDocs.scoreDocs[2].doc, equalTo(2));
|
||||||
|
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[2]).fields[0]).intValue(), equalTo(3));
|
||||||
|
assertThat(topDocs.scoreDocs[3].doc, equalTo(3));
|
||||||
|
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[3]).fields[0]).intValue(), equalTo(4));
|
||||||
|
assertThat(topDocs.scoreDocs[4].doc, equalTo(4));
|
||||||
|
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[4]).fields[0]).intValue(), equalTo(6));
|
||||||
|
assertThat(topDocs.scoreDocs[5].doc, equalTo(6));
|
||||||
|
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[5]).fields[0]).intValue(), equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs[6].doc, equalTo(1));
|
||||||
|
// assertThat(((FieldDoc) topDocs.scoreDocs[6]).fields[0], equalTo(null));
|
||||||
|
assertThat(topDocs.scoreDocs[7].doc, equalTo(5));
|
||||||
|
// assertThat(((FieldDoc) topDocs.scoreDocs[7]).fields[0], equalTo(null));
|
||||||
|
|
||||||
|
topDocs = searcher.search(new MatchAllDocsQuery(), 10,
|
||||||
|
new Sort(new SortField("value", indexFieldData.comparatorSource(null), true))); // defaults to _last
|
||||||
|
assertThat(topDocs.totalHits, equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs.length, equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs[0].doc, equalTo(6));
|
||||||
|
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[0]).fields[0]).intValue(), equalTo(10));
|
||||||
|
assertThat(topDocs.scoreDocs[1].doc, equalTo(4));
|
||||||
|
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[1]).fields[0]).intValue(), equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs[2].doc, equalTo(3));
|
||||||
|
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[2]).fields[0]).intValue(), equalTo(6));
|
||||||
|
assertThat(topDocs.scoreDocs[3].doc, equalTo(0));
|
||||||
|
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[3]).fields[0]).intValue(), equalTo(4));
|
||||||
|
assertThat(topDocs.scoreDocs[4].doc, equalTo(2));
|
||||||
|
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[4]).fields[0]).intValue(), equalTo(3));
|
||||||
|
assertThat(topDocs.scoreDocs[5].doc, equalTo(7));
|
||||||
|
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[5]).fields[0]).intValue(), equalTo(-8));
|
||||||
|
assertThat(topDocs.scoreDocs[6].doc, equalTo(1));
|
||||||
|
// assertThat(((FieldDoc) topDocs.scoreDocs[6]).fields[0], equalTo(null));
|
||||||
|
assertThat(topDocs.scoreDocs[7].doc, equalTo(5));
|
||||||
|
// assertThat(((FieldDoc) topDocs.scoreDocs[7]).fields[0], equalTo(null));
|
||||||
|
|
||||||
|
topDocs = searcher.search(new MatchAllDocsQuery(), 10,
|
||||||
|
new Sort(new SortField("value", indexFieldData.comparatorSource("_first"))));
|
||||||
|
assertThat(topDocs.totalHits, equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs.length, equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs[0].doc, equalTo(1));
|
||||||
|
assertThat(topDocs.scoreDocs[1].doc, equalTo(5));
|
||||||
|
assertThat(topDocs.scoreDocs[2].doc, equalTo(7));
|
||||||
|
assertThat(topDocs.scoreDocs[3].doc, equalTo(0));
|
||||||
|
assertThat(topDocs.scoreDocs[4].doc, equalTo(2));
|
||||||
|
assertThat(topDocs.scoreDocs[5].doc, equalTo(3));
|
||||||
|
assertThat(topDocs.scoreDocs[6].doc, equalTo(4));
|
||||||
|
assertThat(topDocs.scoreDocs[7].doc, equalTo(6));
|
||||||
|
|
||||||
|
topDocs = searcher.search(new MatchAllDocsQuery(), 10,
|
||||||
|
new Sort(new SortField("value", indexFieldData.comparatorSource("_first"), true)));
|
||||||
|
assertThat(topDocs.totalHits, equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs.length, equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs[0].doc, equalTo(1));
|
||||||
|
assertThat(topDocs.scoreDocs[1].doc, equalTo(5));
|
||||||
|
assertThat(topDocs.scoreDocs[2].doc, equalTo(6));
|
||||||
|
assertThat(topDocs.scoreDocs[3].doc, equalTo(4));
|
||||||
|
assertThat(topDocs.scoreDocs[4].doc, equalTo(3));
|
||||||
|
assertThat(topDocs.scoreDocs[5].doc, equalTo(0));
|
||||||
|
assertThat(topDocs.scoreDocs[6].doc, equalTo(2));
|
||||||
|
assertThat(topDocs.scoreDocs[7].doc, equalTo(7));
|
||||||
|
|
||||||
|
topDocs = searcher.search(new MatchAllDocsQuery(), 10,
|
||||||
|
new Sort(new SortField("value", indexFieldData.comparatorSource("-9"))));
|
||||||
|
assertThat(topDocs.totalHits, equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs.length, equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs[0].doc, equalTo(7));
|
||||||
|
assertThat(topDocs.scoreDocs[1].doc, equalTo(1));
|
||||||
|
assertThat(topDocs.scoreDocs[2].doc, equalTo(5));
|
||||||
|
assertThat(topDocs.scoreDocs[3].doc, equalTo(0));
|
||||||
|
assertThat(topDocs.scoreDocs[4].doc, equalTo(2));
|
||||||
|
assertThat(topDocs.scoreDocs[5].doc, equalTo(3));
|
||||||
|
assertThat(topDocs.scoreDocs[6].doc, equalTo(4));
|
||||||
|
assertThat(topDocs.scoreDocs[7].doc, equalTo(6));
|
||||||
|
|
||||||
|
topDocs = searcher.search(new MatchAllDocsQuery(), 10,
|
||||||
|
new Sort(new SortField("value", indexFieldData.comparatorSource("9"), true)));
|
||||||
|
assertThat(topDocs.totalHits, equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs.length, equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs[0].doc, equalTo(6));
|
||||||
|
assertThat(topDocs.scoreDocs[1].doc, equalTo(1));
|
||||||
|
assertThat(topDocs.scoreDocs[2].doc, equalTo(5));
|
||||||
|
assertThat(topDocs.scoreDocs[3].doc, equalTo(4));
|
||||||
|
assertThat(topDocs.scoreDocs[4].doc, equalTo(3));
|
||||||
|
assertThat(topDocs.scoreDocs[5].doc, equalTo(0));
|
||||||
|
assertThat(topDocs.scoreDocs[6].doc, equalTo(2));
|
||||||
|
assertThat(topDocs.scoreDocs[7].doc, equalTo(7));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.apache.lucene.document.Document;
|
||||||
import org.apache.lucene.document.Field;
|
import org.apache.lucene.document.Field;
|
||||||
import org.apache.lucene.document.StringField;
|
import org.apache.lucene.document.StringField;
|
||||||
import org.apache.lucene.index.AtomicReaderContext;
|
import org.apache.lucene.index.AtomicReaderContext;
|
||||||
|
import org.apache.lucene.index.DirectoryReader;
|
||||||
import org.apache.lucene.search.*;
|
import org.apache.lucene.search.*;
|
||||||
import org.apache.lucene.util.BytesRef;
|
import org.apache.lucene.util.BytesRef;
|
||||||
import org.elasticsearch.common.lucene.HashedBytesRef;
|
import org.elasticsearch.common.lucene.HashedBytesRef;
|
||||||
|
@ -358,6 +359,7 @@ public abstract class StringFieldDataTests extends AbstractFieldDataTests {
|
||||||
d.add(new StringField("_id", "2", Field.Store.NO));
|
d.add(new StringField("_id", "2", Field.Store.NO));
|
||||||
d.add(new StringField("value", "1", Field.Store.NO));
|
d.add(new StringField("value", "1", Field.Store.NO));
|
||||||
writer.addDocument(d);
|
writer.addDocument(d);
|
||||||
|
writer.commit(); // TODO: Have tests with more docs for sorting
|
||||||
|
|
||||||
d = new Document();
|
d = new Document();
|
||||||
d.add(new StringField("_id", "3", Field.Store.NO));
|
d.add(new StringField("_id", "3", Field.Store.NO));
|
||||||
|
@ -483,6 +485,21 @@ public abstract class StringFieldDataTests extends AbstractFieldDataTests {
|
||||||
stringValues.forEachValueInDoc(0, new StringValuesVerifierProc(0).addExpected(two()).addExpected(four()));
|
stringValues.forEachValueInDoc(0, new StringValuesVerifierProc(0).addExpected(two()).addExpected(four()));
|
||||||
stringValues.forEachValueInDoc(1, new StringValuesVerifierProc(1).addExpected(one()));
|
stringValues.forEachValueInDoc(1, new StringValuesVerifierProc(1).addExpected(one()));
|
||||||
stringValues.forEachValueInDoc(2, new StringValuesVerifierProc(2).addExpected(three()));
|
stringValues.forEachValueInDoc(2, new StringValuesVerifierProc(2).addExpected(three()));
|
||||||
|
|
||||||
|
IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(writer, true));
|
||||||
|
TopFieldDocs topDocs = searcher.search(new MatchAllDocsQuery(), 10, new Sort(new SortField("value", indexFieldData.comparatorSource(null))));
|
||||||
|
assertThat(topDocs.totalHits, equalTo(3));
|
||||||
|
assertThat(topDocs.scoreDocs.length, equalTo(3));
|
||||||
|
assertThat(topDocs.scoreDocs[0].doc, equalTo(1));
|
||||||
|
assertThat(topDocs.scoreDocs[1].doc, equalTo(0));
|
||||||
|
assertThat(topDocs.scoreDocs[2].doc, equalTo(2));
|
||||||
|
|
||||||
|
topDocs = searcher.search(new MatchAllDocsQuery(), 10, new Sort(new SortField("value", indexFieldData.comparatorSource(null), true)));
|
||||||
|
assertThat(topDocs.totalHits, equalTo(3));
|
||||||
|
assertThat(topDocs.scoreDocs.length, equalTo(3));
|
||||||
|
assertThat(topDocs.scoreDocs[0].doc, equalTo(0));
|
||||||
|
assertThat(topDocs.scoreDocs[1].doc, equalTo(2));
|
||||||
|
assertThat(topDocs.scoreDocs[2].doc, equalTo(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void fillMultiValueWithMissing() throws Exception {
|
protected void fillMultiValueWithMissing() throws Exception {
|
||||||
|
@ -745,4 +762,105 @@ public abstract class StringFieldDataTests extends AbstractFieldDataTests {
|
||||||
d.add(new StringField("_id", "3", Field.Store.NO));
|
d.add(new StringField("_id", "3", Field.Store.NO));
|
||||||
writer.addDocument(d);
|
writer.addDocument(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSortMultiValuesFields() throws Exception {
|
||||||
|
fillExtendedMvSet();
|
||||||
|
IndexFieldData indexFieldData = getForField("value");
|
||||||
|
|
||||||
|
IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(writer, true));
|
||||||
|
TopFieldDocs topDocs = searcher.search(new MatchAllDocsQuery(), 10,
|
||||||
|
new Sort(new SortField("value", indexFieldData.comparatorSource(null))));
|
||||||
|
assertThat(topDocs.totalHits, equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs.length, equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs[0].doc, equalTo(1));
|
||||||
|
assertThat(((FieldDoc) topDocs.scoreDocs[0]).fields[0], equalTo(null));
|
||||||
|
assertThat(topDocs.scoreDocs[1].doc, equalTo(5));
|
||||||
|
assertThat(((FieldDoc) topDocs.scoreDocs[1]).fields[0], equalTo(null));
|
||||||
|
assertThat(topDocs.scoreDocs[2].doc, equalTo(7));
|
||||||
|
assertThat(((BytesRef) ((FieldDoc) topDocs.scoreDocs[2]).fields[0]).utf8ToString(), equalTo("!08"));
|
||||||
|
assertThat(topDocs.scoreDocs[3].doc, equalTo(0));
|
||||||
|
assertThat(((BytesRef) ((FieldDoc) topDocs.scoreDocs[3]).fields[0]).utf8ToString(), equalTo("02"));
|
||||||
|
assertThat(topDocs.scoreDocs[4].doc, equalTo(2));
|
||||||
|
assertThat(((BytesRef) ((FieldDoc) topDocs.scoreDocs[4]).fields[0]).utf8ToString(), equalTo("03"));
|
||||||
|
assertThat(topDocs.scoreDocs[5].doc, equalTo(3));
|
||||||
|
assertThat(((BytesRef) ((FieldDoc) topDocs.scoreDocs[5]).fields[0]).utf8ToString(), equalTo("04"));
|
||||||
|
assertThat(topDocs.scoreDocs[6].doc, equalTo(4));
|
||||||
|
assertThat(((BytesRef) ((FieldDoc) topDocs.scoreDocs[6]).fields[0]).utf8ToString(), equalTo("06"));
|
||||||
|
assertThat(topDocs.scoreDocs[7].doc, equalTo(6));
|
||||||
|
assertThat(((BytesRef) ((FieldDoc) topDocs.scoreDocs[7]).fields[0]).utf8ToString(), equalTo("08"));
|
||||||
|
|
||||||
|
topDocs = searcher.search(new MatchAllDocsQuery(), 10,
|
||||||
|
new Sort(new SortField("value", indexFieldData.comparatorSource(null), true)));
|
||||||
|
assertThat(topDocs.totalHits, equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs.length, equalTo(8));
|
||||||
|
assertThat(topDocs.scoreDocs[0].doc, equalTo(6));
|
||||||
|
assertThat(((BytesRef) ((FieldDoc) topDocs.scoreDocs[0]).fields[0]).utf8ToString(), equalTo("10"));
|
||||||
|
assertThat(topDocs.scoreDocs[1].doc, equalTo(4));
|
||||||
|
assertThat(((BytesRef) ((FieldDoc) topDocs.scoreDocs[1]).fields[0]).utf8ToString(), equalTo("08"));
|
||||||
|
assertThat(topDocs.scoreDocs[2].doc, equalTo(3));
|
||||||
|
assertThat(((BytesRef) ((FieldDoc) topDocs.scoreDocs[2]).fields[0]).utf8ToString(), equalTo("06"));
|
||||||
|
assertThat(topDocs.scoreDocs[3].doc, equalTo(0));
|
||||||
|
assertThat(((BytesRef) ((FieldDoc) topDocs.scoreDocs[3]).fields[0]).utf8ToString(), equalTo("04"));
|
||||||
|
assertThat(topDocs.scoreDocs[4].doc, equalTo(2));
|
||||||
|
assertThat(((BytesRef) ((FieldDoc) topDocs.scoreDocs[4]).fields[0]).utf8ToString(), equalTo("03"));
|
||||||
|
assertThat(topDocs.scoreDocs[5].doc, equalTo(7));
|
||||||
|
assertThat(((BytesRef) ((FieldDoc) topDocs.scoreDocs[5]).fields[0]).utf8ToString(), equalTo("!10"));
|
||||||
|
assertThat(topDocs.scoreDocs[6].doc, equalTo(1));
|
||||||
|
assertThat(((FieldDoc) topDocs.scoreDocs[6]).fields[0], equalTo(null));
|
||||||
|
assertThat(topDocs.scoreDocs[7].doc, equalTo(5));
|
||||||
|
assertThat(((FieldDoc) topDocs.scoreDocs[7]).fields[0], equalTo(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fillExtendedMvSet() throws Exception {
|
||||||
|
Document d = new Document();
|
||||||
|
d.add(new StringField("_id", "1", Field.Store.NO));
|
||||||
|
d.add(new StringField("value", "02", Field.Store.NO));
|
||||||
|
d.add(new StringField("value", "04", Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "2", Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "3", Field.Store.NO));
|
||||||
|
d.add(new StringField("value", "03", Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
writer.commit();
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "4", Field.Store.NO));
|
||||||
|
d.add(new StringField("value", "04", Field.Store.NO));
|
||||||
|
d.add(new StringField("value", "05", Field.Store.NO));
|
||||||
|
d.add(new StringField("value", "06", Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "5", Field.Store.NO));
|
||||||
|
d.add(new StringField("value", "06", Field.Store.NO));
|
||||||
|
d.add(new StringField("value", "07", Field.Store.NO));
|
||||||
|
d.add(new StringField("value", "08", Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "6", Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "7", Field.Store.NO));
|
||||||
|
d.add(new StringField("value", "08", Field.Store.NO));
|
||||||
|
d.add(new StringField("value", "09", Field.Store.NO));
|
||||||
|
d.add(new StringField("value", "10", Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
writer.commit();
|
||||||
|
|
||||||
|
d = new Document();
|
||||||
|
d.add(new StringField("_id", "8", Field.Store.NO));
|
||||||
|
d.add(new StringField("value", "!08", Field.Store.NO));
|
||||||
|
d.add(new StringField("value", "!09", Field.Store.NO));
|
||||||
|
d.add(new StringField("value", "!10", Field.Store.NO));
|
||||||
|
writer.addDocument(d);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue