Added sort_mode option that defines what value to pick in the case the sort field is multi-valued.

The `min` and `max` sort modes are supported for all field types. Either the lowest value or the highest value is picked. In addition to that number based fields also support `sum` and `avg` as sort mode. If `sum` sort mode is used then all the values for a field and belonging to a document are added together and the result of that is used as sort value. If the `avg` sort mode is used then the average of all values for the sort field belonging to that document is used as sort value.

Relates to #2634
This commit is contained in:
Martijn van Groningen 2013-02-12 20:34:26 +01:00
parent 7d13545e33
commit fc13499ff5
33 changed files with 465 additions and 220 deletions

View File

@ -27,6 +27,7 @@ import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexComponent; import org.elasticsearch.index.IndexComponent;
import org.elasticsearch.index.fielddata.fieldcomparator.SortMode;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.index.settings.IndexSettings;
@ -57,7 +58,7 @@ public interface IndexFieldData<FD extends AtomicFieldData> extends IndexCompone
/** /**
* Comparator used for sorting. * Comparator used for sorting.
*/ */
XFieldComparatorSource comparatorSource(@Nullable Object missingValue); XFieldComparatorSource comparatorSource(@Nullable Object missingValue, SortMode sortMode);
/** /**
* Clears any resources associated with this field data. * Clears any resources associated with this field data.

View File

@ -19,18 +19,18 @@
package org.elasticsearch.index.fielddata.fieldcomparator; package org.elasticsearch.index.fielddata.fieldcomparator;
import java.io.IOException;
import org.elasticsearch.index.fielddata.IndexNumericFieldData; import org.elasticsearch.index.fielddata.IndexNumericFieldData;
import java.io.IOException;
/** /**
*/ */
public final class ByteValuesComparator extends LongValuesComparatorBase<Byte> { public final class ByteValuesComparator extends LongValuesComparatorBase<Byte> {
private final byte[] values; private final byte[] values;
public ByteValuesComparator(IndexNumericFieldData<?> indexFieldData, byte missingValue, int numHits, boolean reversed) { public ByteValuesComparator(IndexNumericFieldData<?> indexFieldData, byte missingValue, int numHits, SortMode sortMode) {
super(indexFieldData, missingValue, reversed); super(indexFieldData, missingValue, sortMode);
this.values = new byte[numHits]; this.values = new byte[numHits];
assert indexFieldData.getNumericType().requiredBits() <= 8; assert indexFieldData.getNumericType().requiredBits() <= 8;
} }

View File

@ -33,10 +33,12 @@ public class ByteValuesComparatorSource extends IndexFieldData.XFieldComparatorS
private final IndexNumericFieldData<?> indexFieldData; private final IndexNumericFieldData<?> indexFieldData;
private final Object missingValue; private final Object missingValue;
private final SortMode sortMode;
public ByteValuesComparatorSource(IndexNumericFieldData<?> indexFieldData, @Nullable Object missingValue) { public ByteValuesComparatorSource(IndexNumericFieldData<?> indexFieldData, @Nullable Object missingValue, SortMode sortMode) {
this.indexFieldData = indexFieldData; this.indexFieldData = indexFieldData;
this.missingValue = missingValue; this.missingValue = missingValue;
this.sortMode = sortMode;
} }
@Override @Override
@ -57,6 +59,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, reversed); return new ByteValuesComparator(indexFieldData, dMissingValue, numHits, sortMode);
} }
} }

View File

@ -30,9 +30,11 @@ import java.io.IOException;
public class BytesRefFieldComparatorSource extends IndexFieldData.XFieldComparatorSource { public class BytesRefFieldComparatorSource extends IndexFieldData.XFieldComparatorSource {
private final IndexFieldData<?> indexFieldData; private final IndexFieldData<?> indexFieldData;
private final SortMode sortMode;
public BytesRefFieldComparatorSource(IndexFieldData<?> indexFieldData) { public BytesRefFieldComparatorSource(IndexFieldData<?> indexFieldData, SortMode sortMode) {
this.indexFieldData = indexFieldData; this.indexFieldData = indexFieldData;
this.sortMode = sortMode;
} }
@Override @Override
@ -44,8 +46,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, reversed); return new BytesRefOrdValComparator((IndexFieldData.WithOrdinals<?>) indexFieldData, numHits, sortMode);
} }
return new BytesRefValComparator(indexFieldData, numHits, reversed); return new BytesRefValComparator(indexFieldData, numHits, sortMode);
} }
} }

View File

@ -47,7 +47,7 @@ public final class BytesRefOrdValComparator extends FieldComparator<BytesRef> {
@lucene.internal */ @lucene.internal */
final int[] ords; final int[] ords;
final boolean reversed; final SortMode sortMode;
/* Values for each slot. /* Values for each slot.
@lucene.internal */ @lucene.internal */
@ -89,9 +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, boolean reversed) { public BytesRefOrdValComparator(IndexFieldData.WithOrdinals<?> indexFieldData, int numHits, SortMode sortMode) {
this.indexFieldData = indexFieldData; this.indexFieldData = indexFieldData;
this.reversed = reversed; this.sortMode = sortMode;
ords = new int[numHits]; ords = new int[numHits];
values = new BytesRef[numHits]; values = new BytesRef[numHits];
readerGen = new int[numHits]; readerGen = new int[numHits];
@ -487,7 +487,7 @@ public final class BytesRefOrdValComparator extends FieldComparator<BytesRef> {
@Override @Override
public int compareBottom(int doc) throws IOException { public int compareBottom(int doc) throws IOException {
final int docOrd = getRelevantOrd(readerOrds, doc, reversed); final int docOrd = getRelevantOrd(readerOrds, doc, sortMode);
if (bottomSameReader) { if (bottomSameReader) {
// ord is precisely comparable, even in the equal case // ord is precisely comparable, even in the equal case
return bottomOrd - docOrd; return bottomOrd - docOrd;
@ -503,7 +503,7 @@ public final class BytesRefOrdValComparator extends FieldComparator<BytesRef> {
@Override @Override
public void copy(int slot, int doc) throws IOException { public void copy(int slot, int doc) throws IOException {
final int ord = getRelevantOrd(readerOrds, doc, reversed); final int ord = getRelevantOrd(readerOrds, doc, sortMode);
ords[slot] = ord; ords[slot] = ord;
if (ord == 0) { if (ord == 0) {
values[slot] = null; values[slot] = null;
@ -519,7 +519,7 @@ public final class BytesRefOrdValComparator extends FieldComparator<BytesRef> {
@Override @Override
public int compareDocToValue(int doc, BytesRef value) { public int compareDocToValue(int doc, BytesRef value) {
BytesRef docValue = getRelevantValue(termsIndex, doc, reversed); BytesRef docValue = getRelevantValue(termsIndex, doc, sortMode);
if (docValue == null) { if (docValue == null) {
if (value == null) { if (value == null) {
return 0; return 0;
@ -533,7 +533,7 @@ public final class BytesRefOrdValComparator extends FieldComparator<BytesRef> {
} }
static BytesRef getRelevantValue(BytesValues.WithOrdinals readerValues, int docId, boolean reversed) { static BytesRef getRelevantValue(BytesValues.WithOrdinals readerValues, int docId, SortMode sortMode) {
BytesValues.Iter iter = readerValues.getIter(docId); BytesValues.Iter iter = readerValues.getIter(docId);
if (!iter.hasNext()) { if (!iter.hasNext()) {
return null; return null;
@ -543,7 +543,7 @@ public final class BytesRefOrdValComparator extends FieldComparator<BytesRef> {
BytesRef relevantVal = currentVal; BytesRef relevantVal = currentVal;
while (true) { while (true) {
int cmp = currentVal.compareTo(relevantVal); int cmp = currentVal.compareTo(relevantVal);
if (reversed) { if (sortMode == SortMode.MAX) {
if (cmp > 0) { if (cmp > 0) {
relevantVal = currentVal; relevantVal = currentVal;
} }
@ -560,7 +560,7 @@ public final class BytesRefOrdValComparator extends FieldComparator<BytesRef> {
return relevantVal; return relevantVal;
} }
static int getRelevantOrd(Ordinals.Docs readerOrds, int docId, boolean reversed) { static int getRelevantOrd(Ordinals.Docs readerOrds, int docId, SortMode sortMode) {
Ordinals.Docs.Iter iter = readerOrds.getIter(docId); Ordinals.Docs.Iter iter = readerOrds.getIter(docId);
int currentVal = iter.next(); int currentVal = iter.next();
if (currentVal == 0) { if (currentVal == 0) {
@ -569,7 +569,7 @@ public final class BytesRefOrdValComparator extends FieldComparator<BytesRef> {
int relevantVal = currentVal; int relevantVal = currentVal;
while (true) { while (true) {
if (reversed) { if (sortMode == SortMode.MAX) {
if (currentVal > relevantVal) { if (currentVal > relevantVal) {
relevantVal = currentVal; relevantVal = currentVal;
} }

View File

@ -37,14 +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 final boolean reversed; private final SortMode sortMode;
private final BytesRef[] values; private final BytesRef[] values;
private BytesRef bottom; private BytesRef bottom;
private BytesValues docTerms; private BytesValues docTerms;
BytesRefValComparator(IndexFieldData<?> indexFieldData, int numHits, boolean reversed) { BytesRefValComparator(IndexFieldData<?> indexFieldData, int numHits, SortMode sortMode) {
this.reversed = reversed; this.sortMode = sortMode;
values = new BytesRef[numHits]; values = new BytesRef[numHits];
this.indexFieldData = indexFieldData; this.indexFieldData = indexFieldData;
} }
@ -74,7 +74,7 @@ public final class BytesRefValComparator extends FieldComparator<BytesRef> {
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()) { if (docTerms.isMultiValued()) {
docTerms = new MultiValuedBytesWrapper(docTerms, reversed); docTerms = new MultiValuedBytesWrapper(docTerms, sortMode);
} }
return this; return this;
} }
@ -150,11 +150,11 @@ public final class BytesRefValComparator extends FieldComparator<BytesRef> {
private static final class MultiValuedBytesWrapper extends FilteredByteValues { private static final class MultiValuedBytesWrapper extends FilteredByteValues {
private final boolean reversed; private final SortMode sortMode;
public MultiValuedBytesWrapper(BytesValues delegate, boolean reversed) { public MultiValuedBytesWrapper(BytesValues delegate, SortMode sortMode) {
super(delegate); super(delegate);
this.reversed = reversed; this.sortMode = sortMode;
} }
@Override @Override
@ -168,7 +168,7 @@ public final class BytesRefValComparator extends FieldComparator<BytesRef> {
BytesRef relevantVal = currentVal; BytesRef relevantVal = currentVal;
while (true) { while (true) {
int cmp = currentVal.compareTo(relevantVal); int cmp = currentVal.compareTo(relevantVal);
if (reversed) { if (sortMode == SortMode.MAX) {
if (cmp > 0) { if (cmp > 0) {
relevantVal = currentVal; relevantVal = currentVal;
} }

View File

@ -19,18 +19,18 @@
package org.elasticsearch.index.fielddata.fieldcomparator; package org.elasticsearch.index.fielddata.fieldcomparator;
import java.io.IOException;
import org.elasticsearch.index.fielddata.IndexNumericFieldData; import org.elasticsearch.index.fielddata.IndexNumericFieldData;
import java.io.IOException;
/** /**
*/ */
public final class DoubleValuesComparator extends DoubleValuesComparatorBase<Double> { public final class DoubleValuesComparator extends DoubleValuesComparatorBase<Double> {
private final double[] values; private final double[] values;
public DoubleValuesComparator(IndexNumericFieldData<?> indexFieldData, double missingValue, int numHits, boolean reversed) { public DoubleValuesComparator(IndexNumericFieldData<?> indexFieldData, double missingValue, int numHits, SortMode sortMode) {
super(indexFieldData, missingValue, reversed); super(indexFieldData, missingValue, sortMode);
assert indexFieldData.getNumericType().requiredBits() <= 64; assert indexFieldData.getNumericType().requiredBits() <= 64;
this.values = new double[numHits]; this.values = new double[numHits];
} }

View File

@ -17,25 +17,26 @@ package org.elasticsearch.index.fielddata.fieldcomparator;
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import java.io.IOException;
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.elasticsearch.index.fielddata.DoubleValues; import org.elasticsearch.index.fielddata.DoubleValues;
import org.elasticsearch.index.fielddata.IndexNumericFieldData; import org.elasticsearch.index.fielddata.IndexNumericFieldData;
import java.io.IOException;
abstract class DoubleValuesComparatorBase<T extends Number> extends FieldComparator<T> { abstract class DoubleValuesComparatorBase<T extends Number> extends FieldComparator<T> {
protected final IndexNumericFieldData<?>indexFieldData; protected final IndexNumericFieldData<?> indexFieldData;
protected final double missingValue; protected final double missingValue;
protected double bottom; protected double bottom;
protected DoubleValues readerValues; protected DoubleValues readerValues;
private final boolean reversed; private final SortMode sortMode;
public DoubleValuesComparatorBase(IndexNumericFieldData<?> indexFieldData, double missingValue, boolean reversed) { public DoubleValuesComparatorBase(IndexNumericFieldData<?> indexFieldData, double missingValue, SortMode sortMode) {
this.indexFieldData = indexFieldData; this.indexFieldData = indexFieldData;
this.missingValue = missingValue; this.missingValue = missingValue;
this.reversed = reversed; this.sortMode = sortMode;
} }
@Override @Override
@ -43,23 +44,23 @@ abstract class DoubleValuesComparatorBase<T extends Number> extends FieldCompara
final double v2 = readerValues.getValueMissing(doc, missingValue); final double v2 = readerValues.getValueMissing(doc, missingValue);
return compare(bottom, v2); return compare(bottom, v2);
} }
@Override @Override
public final int compareDocToValue(int doc, T valueObj) throws IOException { public final int compareDocToValue(int doc, T valueObj) throws IOException {
final double value = valueObj.doubleValue(); final double value = valueObj.doubleValue();
final double docValue = readerValues.getValueMissing(doc, missingValue); final double docValue = readerValues.getValueMissing(doc, missingValue);
return compare(docValue, value); return compare(docValue, value);
} }
@Override @Override
public final FieldComparator<T> setNextReader(AtomicReaderContext context) throws IOException { public final FieldComparator<T> setNextReader(AtomicReaderContext context) throws IOException {
readerValues = indexFieldData.load(context).getDoubleValues(); readerValues = indexFieldData.load(context).getDoubleValues();
if (readerValues.isMultiValued()) { if (readerValues.isMultiValued()) {
readerValues = new MultiValuedBytesWrapper(readerValues, reversed); readerValues = new MultiValuedBytesWrapper(readerValues, sortMode);
} }
return this; return this;
} }
static final int compare(double left, double right) { static final int compare(double left, double right) {
if (left > right) { if (left > right) {
return 1; return 1;
@ -69,14 +70,14 @@ abstract class DoubleValuesComparatorBase<T extends Number> extends FieldCompara
return 0; return 0;
} }
} }
static final class MultiValuedBytesWrapper extends DoubleValues.FilteredDoubleValues { static final class MultiValuedBytesWrapper extends DoubleValues.FilteredDoubleValues {
private final boolean reversed; private final SortMode sortMode;
public MultiValuedBytesWrapper(DoubleValues delegate, boolean reversed) { public MultiValuedBytesWrapper(DoubleValues delegate, SortMode sortMode) {
super(delegate); super(delegate);
this.reversed = reversed; this.sortMode = sortMode;
} }
@Override @Override
@ -88,23 +89,35 @@ abstract class DoubleValuesComparatorBase<T extends Number> extends FieldCompara
double currentVal = iter.next(); double currentVal = iter.next();
double relevantVal = currentVal; double relevantVal = currentVal;
while (true) { int counter = 1;
int cmp = Double.compare(currentVal, relevantVal); while (iter.hasNext()) {
if (reversed) {
if (cmp > 0) {
relevantVal = currentVal;
}
} else {
if (cmp < 0) {
relevantVal = currentVal;
}
}
if (!iter.hasNext()) {
break;
}
currentVal = iter.next(); currentVal = iter.next();
int cmp = Double.compare(currentVal, relevantVal);
switch (sortMode) {
case SUM:
relevantVal += currentVal;
break;
case AVG:
relevantVal += currentVal;
counter++;
break;
case MIN:
if (cmp < 0) {
relevantVal = currentVal;
}
break;
case MAX:
if (cmp > 0) {
relevantVal = currentVal;
}
break;
}
}
if (sortMode == SortMode.AVG) {
return relevantVal / counter;
} else {
return relevantVal;
} }
return relevantVal;
} }
} }

View File

@ -33,10 +33,12 @@ public class DoubleValuesComparatorSource extends IndexFieldData.XFieldComparato
private final IndexNumericFieldData<?> indexFieldData; private final IndexNumericFieldData<?> indexFieldData;
private final Object missingValue; private final Object missingValue;
private final SortMode sortMode;
public DoubleValuesComparatorSource(IndexNumericFieldData<?> indexFieldData, @Nullable Object missingValue) { public DoubleValuesComparatorSource(IndexNumericFieldData<?> indexFieldData, @Nullable Object missingValue, SortMode sortMode) {
this.indexFieldData = indexFieldData; this.indexFieldData = indexFieldData;
this.missingValue = missingValue; this.missingValue = missingValue;
this.sortMode = sortMode;
} }
@Override @Override
@ -57,6 +59,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, reversed); return new DoubleValuesComparator(indexFieldData, dMissingValue, numHits, sortMode);
} }
} }

View File

@ -19,18 +19,18 @@
package org.elasticsearch.index.fielddata.fieldcomparator; package org.elasticsearch.index.fielddata.fieldcomparator;
import java.io.IOException;
import org.elasticsearch.index.fielddata.IndexNumericFieldData; import org.elasticsearch.index.fielddata.IndexNumericFieldData;
import java.io.IOException;
/** /**
*/ */
public final class FloatValuesComparator extends DoubleValuesComparatorBase<Float> { public final class FloatValuesComparator extends DoubleValuesComparatorBase<Float> {
private final float[] values; private final float[] values;
public FloatValuesComparator(IndexNumericFieldData<?> indexFieldData, float missingValue, int numHits, boolean reversed) { public FloatValuesComparator(IndexNumericFieldData<?> indexFieldData, float missingValue, int numHits, SortMode sortMode) {
super(indexFieldData, missingValue, reversed); super(indexFieldData, missingValue, sortMode);
assert indexFieldData.getNumericType().requiredBits() <= 32; assert indexFieldData.getNumericType().requiredBits() <= 32;
this.values = new float[numHits]; this.values = new float[numHits];
} }

View File

@ -33,10 +33,12 @@ public class FloatValuesComparatorSource extends IndexFieldData.XFieldComparator
private final IndexNumericFieldData<?> indexFieldData; private final IndexNumericFieldData<?> indexFieldData;
private final Object missingValue; private final Object missingValue;
private final SortMode sortMode;
public FloatValuesComparatorSource(IndexNumericFieldData<?> indexFieldData, @Nullable Object missingValue) { public FloatValuesComparatorSource(IndexNumericFieldData<?> indexFieldData, @Nullable Object missingValue, SortMode sortMode) {
this.indexFieldData = indexFieldData; this.indexFieldData = indexFieldData;
this.missingValue = missingValue; this.missingValue = missingValue;
this.sortMode = sortMode;
} }
@Override @Override
@ -57,6 +59,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, reversed); return new FloatValuesComparator(indexFieldData, dMissingValue, numHits, sortMode);
} }
} }

View File

@ -19,18 +19,18 @@
package org.elasticsearch.index.fielddata.fieldcomparator; package org.elasticsearch.index.fielddata.fieldcomparator;
import java.io.IOException;
import org.elasticsearch.index.fielddata.IndexNumericFieldData; import org.elasticsearch.index.fielddata.IndexNumericFieldData;
import java.io.IOException;
/** /**
*/ */
public final class IntValuesComparator extends LongValuesComparatorBase<Integer> { public final class IntValuesComparator extends LongValuesComparatorBase<Integer> {
private final int[] values; private final int[] values;
public IntValuesComparator(IndexNumericFieldData<?> indexFieldData, int missingValue, int numHits, boolean reversed) { public IntValuesComparator(IndexNumericFieldData<?> indexFieldData, int missingValue, int numHits, SortMode sortMode) {
super(indexFieldData, missingValue, reversed); super(indexFieldData, missingValue, sortMode);
assert indexFieldData.getNumericType().requiredBits() <= 32; assert indexFieldData.getNumericType().requiredBits() <= 32;
this.values = new int[numHits]; this.values = new int[numHits];
} }

View File

@ -33,10 +33,12 @@ public class IntValuesComparatorSource extends IndexFieldData.XFieldComparatorSo
private final IndexNumericFieldData<?> indexFieldData; private final IndexNumericFieldData<?> indexFieldData;
private final Object missingValue; private final Object missingValue;
private final SortMode sortMode;
public IntValuesComparatorSource(IndexNumericFieldData<?> indexFieldData, @Nullable Object missingValue) { public IntValuesComparatorSource(IndexNumericFieldData<?> indexFieldData, @Nullable Object missingValue, SortMode sortMode) {
this.indexFieldData = indexFieldData; this.indexFieldData = indexFieldData;
this.missingValue = missingValue; this.missingValue = missingValue;
this.sortMode = sortMode;
} }
@Override @Override
@ -57,6 +59,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, reversed); return new IntValuesComparator(indexFieldData, dMissingValue, numHits, sortMode);
} }
} }

View File

@ -19,18 +19,18 @@
package org.elasticsearch.index.fielddata.fieldcomparator; package org.elasticsearch.index.fielddata.fieldcomparator;
import java.io.IOException;
import org.elasticsearch.index.fielddata.IndexNumericFieldData; import org.elasticsearch.index.fielddata.IndexNumericFieldData;
import java.io.IOException;
/** /**
*/ */
public final class LongValuesComparator extends LongValuesComparatorBase<Long> { public final class LongValuesComparator extends LongValuesComparatorBase<Long> {
private final long[] values; private final long[] values;
public LongValuesComparator(IndexNumericFieldData<?> indexFieldData, long missingValue, int numHits, boolean reversed) { public LongValuesComparator(IndexNumericFieldData<?> indexFieldData, long missingValue, int numHits, SortMode sortMode) {
super(indexFieldData, missingValue, reversed); super(indexFieldData, missingValue, sortMode);
this.values = new long[numHits]; this.values = new long[numHits];
assert indexFieldData.getNumericType().requiredBits() <= 64; assert indexFieldData.getNumericType().requiredBits() <= 64;
} }
@ -50,7 +50,7 @@ public final class LongValuesComparator extends LongValuesComparatorBase<Long> {
public void copy(int slot, int doc) throws IOException { public void copy(int slot, int doc) throws IOException {
values[slot] = readerValues.getValueMissing(doc, missingValue); values[slot] = readerValues.getValueMissing(doc, missingValue);
} }
@Override @Override
public Long value(int slot) { public Long value(int slot) {
return Long.valueOf(values[slot]); return Long.valueOf(values[slot]);

View File

@ -17,26 +17,27 @@ package org.elasticsearch.index.fielddata.fieldcomparator;
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import java.io.IOException;
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.elasticsearch.index.fielddata.IndexNumericFieldData; import org.elasticsearch.index.fielddata.IndexNumericFieldData;
import org.elasticsearch.index.fielddata.LongValues; import org.elasticsearch.index.fielddata.LongValues;
import java.io.IOException;
abstract class LongValuesComparatorBase<T extends Number> extends FieldComparator<T> { abstract class LongValuesComparatorBase<T extends Number> extends FieldComparator<T> {
protected final IndexNumericFieldData<?> indexFieldData; protected final IndexNumericFieldData<?> indexFieldData;
private final boolean reversed;
protected final long missingValue; protected final long missingValue;
protected long bottom; protected long bottom;
protected LongValues readerValues; protected LongValues readerValues;
private final SortMode sortMode;
public LongValuesComparatorBase(IndexNumericFieldData<?> indexFieldData, long missingValue, boolean reversed) {
public LongValuesComparatorBase(IndexNumericFieldData<?> indexFieldData, long missingValue, SortMode sortMode) {
this.indexFieldData = indexFieldData; this.indexFieldData = indexFieldData;
this.missingValue = missingValue; this.missingValue = missingValue;
this.reversed = reversed; this.sortMode = sortMode;
} }
@Override @Override
@ -44,14 +45,14 @@ abstract class LongValuesComparatorBase<T extends Number> extends FieldComparato
long v2 = readerValues.getValueMissing(doc, missingValue); long v2 = readerValues.getValueMissing(doc, missingValue);
return compare(bottom, v2); return compare(bottom, v2);
} }
@Override @Override
public final int compareDocToValue(int doc, T valueObj) throws IOException { public final int compareDocToValue(int doc, T valueObj) throws IOException {
final long value = valueObj.longValue(); final long value = valueObj.longValue();
long docValue = readerValues.getValueMissing(doc, missingValue); long docValue = readerValues.getValueMissing(doc, missingValue);
return compare(docValue, value); return compare(docValue, value);
} }
static final int compare(long left, long right) { static final int compare(long left, long right) {
if (left > right) { if (left > right) {
return 1; return 1;
@ -61,23 +62,23 @@ abstract class LongValuesComparatorBase<T extends Number> extends FieldComparato
return 0; return 0;
} }
} }
@Override @Override
public final FieldComparator<T> setNextReader(AtomicReaderContext context) throws IOException { public final FieldComparator<T> setNextReader(AtomicReaderContext context) throws IOException {
readerValues = indexFieldData.load(context).getLongValues(); readerValues = indexFieldData.load(context).getLongValues();
if (readerValues.isMultiValued()) { if (readerValues.isMultiValued()) {
readerValues = new MultiValuedBytesWrapper(readerValues, reversed); readerValues = new MultiValuedBytesWrapper(readerValues, sortMode);
} }
return this; return this;
} }
private static final class MultiValuedBytesWrapper extends LongValues.FilteredLongValues { private static final class MultiValuedBytesWrapper extends LongValues.FilteredLongValues {
private final boolean reversed; private final SortMode sortMode;
public MultiValuedBytesWrapper(LongValues delegate, boolean reversed) { public MultiValuedBytesWrapper(LongValues delegate, SortMode sortMode) {
super(delegate); super(delegate);
this.reversed = reversed; this.sortMode = sortMode;
} }
@Override @Override
@ -89,22 +90,33 @@ abstract class LongValuesComparatorBase<T extends Number> extends FieldComparato
long currentVal = iter.next(); long currentVal = iter.next();
long relevantVal = currentVal; long relevantVal = currentVal;
while (true) { int counter = 1;
if (reversed) { while (iter.hasNext()) {
if (currentVal > relevantVal) {
relevantVal = currentVal;
}
} else {
if (currentVal < relevantVal) {
relevantVal = currentVal;
}
}
if (!iter.hasNext()) {
break;
}
currentVal = iter.next(); currentVal = iter.next();
switch (sortMode) {
case SUM:
relevantVal += currentVal;
break;
case AVG:
relevantVal += currentVal;
counter++;
break;
case MAX:
if (currentVal > relevantVal) {
relevantVal = currentVal;
}
break;
case MIN:
if (currentVal < relevantVal) {
relevantVal = currentVal;
}
}
}
if (sortMode == SortMode.AVG) {
return relevantVal / counter;
} else {
return relevantVal;
} }
return relevantVal;
// If we have a method on readerValues that tells if the values emitted by Iter or ArrayRef are sorted per // 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: // 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 // (This is already possible, if values are loaded from index, but we just need a method that tells us this

View File

@ -33,10 +33,12 @@ public class LongValuesComparatorSource extends IndexFieldData.XFieldComparatorS
private final IndexNumericFieldData<?> indexFieldData; private final IndexNumericFieldData<?> indexFieldData;
private final Object missingValue; private final Object missingValue;
private final SortMode sortMode;
public LongValuesComparatorSource(IndexNumericFieldData<?> indexFieldData, @Nullable Object missingValue) { public LongValuesComparatorSource(IndexNumericFieldData<?> indexFieldData, @Nullable Object missingValue, SortMode sortMode) {
this.indexFieldData = indexFieldData; this.indexFieldData = indexFieldData;
this.missingValue = missingValue; this.missingValue = missingValue;
this.sortMode = sortMode;
} }
@Override @Override
@ -57,6 +59,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, reversed); return new LongValuesComparator(indexFieldData, dMissingValue, numHits, sortMode);
} }
} }

View File

@ -19,27 +19,29 @@
package org.elasticsearch.index.fielddata.fieldcomparator; package org.elasticsearch.index.fielddata.fieldcomparator;
import java.io.IOException;
import org.elasticsearch.index.fielddata.IndexNumericFieldData; import org.elasticsearch.index.fielddata.IndexNumericFieldData;
import java.io.IOException;
/** /**
*/ */
public final class ShortValuesComparator extends LongValuesComparatorBase<Short> { public final class ShortValuesComparator extends LongValuesComparatorBase<Short> {
private final short[] values; private final short[] values;
private final SortMode sortMode;
public ShortValuesComparator(IndexNumericFieldData<?> indexFieldData, short missingValue, int numHits, boolean reversed) { public ShortValuesComparator(IndexNumericFieldData<?> indexFieldData, short missingValue, int numHits, SortMode sortMode) {
super(indexFieldData, missingValue, reversed); super(indexFieldData, missingValue, sortMode);
assert indexFieldData.getNumericType().requiredBits() <= 16; assert indexFieldData.getNumericType().requiredBits() <= 16;
this.values = new short[numHits]; this.values = new short[numHits];
this.sortMode = sortMode;
} }
@Override @Override
public int compare(int slot1, int slot2) { public int compare(int slot1, int slot2) {
final int v1 = values[slot1]; final int v1 = values[slot1];
final int v2 = values[slot2]; final int v2 = values[slot2];
return v1-v2; // we cast to int so it can't overflow return v1 - v2; // we cast to int so it can't overflow
} }
@Override @Override

View File

@ -33,10 +33,12 @@ public class ShortValuesComparatorSource extends IndexFieldData.XFieldComparator
private final IndexNumericFieldData<?> indexFieldData; private final IndexNumericFieldData<?> indexFieldData;
private final Object missingValue; private final Object missingValue;
private final SortMode sortMode;
public ShortValuesComparatorSource(IndexNumericFieldData<?> indexFieldData, @Nullable Object missingValue) { public ShortValuesComparatorSource(IndexNumericFieldData<?> indexFieldData, @Nullable Object missingValue, SortMode sortMode) {
this.indexFieldData = indexFieldData; this.indexFieldData = indexFieldData;
this.missingValue = missingValue; this.missingValue = missingValue;
this.sortMode = sortMode;
} }
@Override @Override
@ -57,6 +59,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, reversed); return new ShortValuesComparator(indexFieldData, dMissingValue, numHits, sortMode);
} }
} }

View File

@ -0,0 +1,64 @@
/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch licenses this
* file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.index.fielddata.fieldcomparator;
import org.elasticsearch.ElasticSearchIllegalArgumentException;
/**
* Defines what values to pick in the case a document contains multiple values for a particular field.
*/
public enum SortMode {
/**
* Sum of all the values.
*/
SUM,
/**
* Average of all the values.
*/
AVG,
/**
* Pick the lowest value.
*/
MIN,
/**
* Pick the highest value.
*/
MAX;
public static SortMode fromString(String sortMode) {
if ("min".equals(sortMode)) {
return MIN;
} else if ("max".equals(sortMode)) {
return MAX;
} else if ("sum".equals(sortMode)) {
return SUM;
} else if ("avg".equals(sortMode)) {
return AVG;
} else {
throw new ElasticSearchIllegalArgumentException("Illegal sort_mode " + sortMode);
}
}
}

View File

@ -20,9 +20,9 @@
package org.elasticsearch.index.fielddata.plain; package org.elasticsearch.index.fielddata.plain;
import gnu.trove.list.array.TByteArrayList; import gnu.trove.list.array.TByteArrayList;
import org.apache.lucene.index.*; import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.search.FieldCache; import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.search.FieldCache.StopFillCacheException; import org.apache.lucene.index.Terms;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefIterator; import org.apache.lucene.util.BytesRefIterator;
import org.apache.lucene.util.FixedBitSet; import org.apache.lucene.util.FixedBitSet;
@ -33,14 +33,13 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.fielddata.*; import org.elasticsearch.index.fielddata.*;
import org.elasticsearch.index.fielddata.fieldcomparator.ByteValuesComparatorSource; import org.elasticsearch.index.fielddata.fieldcomparator.ByteValuesComparatorSource;
import org.elasticsearch.index.fielddata.fieldcomparator.SortMode;
import org.elasticsearch.index.fielddata.ordinals.Ordinals; import org.elasticsearch.index.fielddata.ordinals.Ordinals;
import org.elasticsearch.index.fielddata.ordinals.Ordinals.Docs; import org.elasticsearch.index.fielddata.ordinals.Ordinals.Docs;
import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder; import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.index.settings.IndexSettings;
import java.util.ArrayList;
/** /**
*/ */
public class ByteArrayIndexFieldData extends AbstractIndexFieldData<ByteArrayAtomicFieldData> implements IndexNumericFieldData<ByteArrayAtomicFieldData> { public class ByteArrayIndexFieldData extends AbstractIndexFieldData<ByteArrayAtomicFieldData> implements IndexNumericFieldData<ByteArrayAtomicFieldData> {
@ -96,8 +95,8 @@ public class ByteArrayIndexFieldData extends AbstractIndexFieldData<ByteArrayAto
OrdinalsBuilder builder = new OrdinalsBuilder(terms, reader.maxDoc()); OrdinalsBuilder builder = new OrdinalsBuilder(terms, reader.maxDoc());
BytesRefIterator iter = builder.buildFromTerms(builder.wrapNumeric32Bit(terms.iterator(null)), reader.getLiveDocs()); BytesRefIterator iter = builder.buildFromTerms(builder.wrapNumeric32Bit(terms.iterator(null)), reader.getLiveDocs());
BytesRef term; BytesRef term;
while((term = iter.next()) != null) { while ((term = iter.next()) != null) {
values.add((byte) NumericUtils.prefixCodedToInt(term)); values.add((byte) NumericUtils.prefixCodedToInt(term));
} }
try { try {
Ordinals build = builder.build(fieldDataType.getSettings()); Ordinals build = builder.build(fieldDataType.getSettings());
@ -106,7 +105,7 @@ public class ByteArrayIndexFieldData extends AbstractIndexFieldData<ByteArrayAto
byte[] sValues = new byte[reader.maxDoc()]; byte[] sValues = new byte[reader.maxDoc()];
int maxDoc = reader.maxDoc(); int maxDoc = reader.maxDoc();
for (int i = 0; i < maxDoc; i++) { for (int i = 0; i < maxDoc; i++) {
sValues[i] = values.get(ordinals.getOrd(i)); sValues[i] = values.get(ordinals.getOrd(i));
} }
final FixedBitSet set = builder.buildDocsWithValuesSet(); final FixedBitSet set = builder.buildDocsWithValuesSet();
if (set == null) { if (set == null) {
@ -126,7 +125,7 @@ public class ByteArrayIndexFieldData extends AbstractIndexFieldData<ByteArrayAto
} }
@Override @Override
public XFieldComparatorSource comparatorSource(@Nullable Object missingValue) { public XFieldComparatorSource comparatorSource(@Nullable Object missingValue, SortMode sortMode) {
return new ByteValuesComparatorSource(this, missingValue); return new ByteValuesComparatorSource(this, missingValue, sortMode);
} }
} }

View File

@ -19,9 +19,9 @@
package org.elasticsearch.index.fielddata.plain; package org.elasticsearch.index.fielddata.plain;
import org.apache.lucene.index.*; import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.search.FieldCache; import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.search.FieldCache.StopFillCacheException; import org.apache.lucene.index.Terms;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefIterator; import org.apache.lucene.util.BytesRefIterator;
import org.elasticsearch.ElasticSearchException; import org.elasticsearch.ElasticSearchException;
@ -33,9 +33,8 @@ import org.elasticsearch.index.fielddata.FieldDataType;
import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.fielddata.IndexFieldDataCache; import org.elasticsearch.index.fielddata.IndexFieldDataCache;
import org.elasticsearch.index.fielddata.fieldcomparator.BytesRefFieldComparatorSource; import org.elasticsearch.index.fielddata.fieldcomparator.BytesRefFieldComparatorSource;
import org.elasticsearch.index.fielddata.ordinals.Ordinals; import org.elasticsearch.index.fielddata.fieldcomparator.SortMode;
import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder; import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder;
import org.elasticsearch.index.fielddata.ordinals.SingleArrayOrdinals;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.index.settings.IndexSettings;
@ -94,7 +93,7 @@ public class ConcreteBytesRefIndexFieldData extends AbstractIndexFieldData<Concr
try { try {
BytesRefIterator iter = builder.buildFromTerms(terms.iterator(null), reader.getLiveDocs()); BytesRefIterator iter = builder.buildFromTerms(terms.iterator(null), reader.getLiveDocs());
BytesRef term; BytesRef term;
while((term = iter.next()) != null) { while ((term = iter.next()) != null) {
values.add(BytesRef.deepCopyOf(term)); values.add(BytesRef.deepCopyOf(term));
} }
return new ConcreteBytesRefAtomicFieldData(values.toArray(new BytesRef[values.size()]), builder.build(fieldDataType.getSettings())); return new ConcreteBytesRefAtomicFieldData(values.toArray(new BytesRef[values.size()]), builder.build(fieldDataType.getSettings()));
@ -104,8 +103,8 @@ public class ConcreteBytesRefIndexFieldData extends AbstractIndexFieldData<Concr
} }
@Override @Override
public XFieldComparatorSource comparatorSource(@Nullable Object missingValue) { public XFieldComparatorSource comparatorSource(@Nullable Object missingValue, SortMode sortMode) {
// TODO support "missingValue" for sortMissingValue options here... // TODO support "missingValue" for sortMissingValue options here...
return new BytesRefFieldComparatorSource(this); return new BytesRefFieldComparatorSource(this, sortMode);
} }
} }

View File

@ -20,9 +20,9 @@
package org.elasticsearch.index.fielddata.plain; package org.elasticsearch.index.fielddata.plain;
import gnu.trove.list.array.TDoubleArrayList; import gnu.trove.list.array.TDoubleArrayList;
import org.apache.lucene.index.*; import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.search.FieldCache; import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.search.FieldCache.StopFillCacheException; import org.apache.lucene.index.Terms;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefIterator; import org.apache.lucene.util.BytesRefIterator;
import org.apache.lucene.util.FixedBitSet; import org.apache.lucene.util.FixedBitSet;
@ -33,9 +33,10 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.fielddata.*; import org.elasticsearch.index.fielddata.*;
import org.elasticsearch.index.fielddata.fieldcomparator.DoubleValuesComparatorSource; import org.elasticsearch.index.fielddata.fieldcomparator.DoubleValuesComparatorSource;
import org.elasticsearch.index.fielddata.fieldcomparator.SortMode;
import org.elasticsearch.index.fielddata.ordinals.Ordinals; import org.elasticsearch.index.fielddata.ordinals.Ordinals;
import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder;
import org.elasticsearch.index.fielddata.ordinals.Ordinals.Docs; import org.elasticsearch.index.fielddata.ordinals.Ordinals.Docs;
import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.index.settings.IndexSettings;
@ -82,7 +83,7 @@ public class DoubleArrayIndexFieldData extends AbstractIndexFieldData<DoubleArra
@Override @Override
public DoubleArrayAtomicFieldData loadDirect(AtomicReaderContext context) throws Exception { public DoubleArrayAtomicFieldData loadDirect(AtomicReaderContext context) throws Exception {
AtomicReader reader = context.reader(); AtomicReader reader = context.reader();
Terms terms = reader.terms(getFieldNames().indexName()); Terms terms = reader.terms(getFieldNames().indexName());
if (terms == null) { if (terms == null) {
@ -96,7 +97,7 @@ public class DoubleArrayIndexFieldData extends AbstractIndexFieldData<DoubleArra
try { try {
final BytesRefIterator iter = builder.buildFromTerms(builder.wrapNumeric64Bit(terms.iterator(null)), reader.getLiveDocs()); final BytesRefIterator iter = builder.buildFromTerms(builder.wrapNumeric64Bit(terms.iterator(null)), reader.getLiveDocs());
BytesRef term; BytesRef term;
while((term = iter.next()) != null) { while ((term = iter.next()) != null) {
values.add(NumericUtils.sortableLongToDouble(NumericUtils.prefixCodedToLong(term))); values.add(NumericUtils.sortableLongToDouble(NumericUtils.prefixCodedToLong(term)));
} }
Ordinals build = builder.build(fieldDataType.getSettings()); Ordinals build = builder.build(fieldDataType.getSettings());
@ -105,7 +106,7 @@ public class DoubleArrayIndexFieldData extends AbstractIndexFieldData<DoubleArra
double[] sValues = new double[reader.maxDoc()]; double[] sValues = new double[reader.maxDoc()];
int maxDoc = reader.maxDoc(); int maxDoc = reader.maxDoc();
for (int i = 0; i < maxDoc; i++) { for (int i = 0; i < maxDoc; i++) {
sValues[i] = values.get(ordinals.getOrd(i)); sValues[i] = values.get(ordinals.getOrd(i));
} }
final FixedBitSet set = builder.buildDocsWithValuesSet(); final FixedBitSet set = builder.buildDocsWithValuesSet();
if (set == null) { if (set == null) {
@ -122,11 +123,11 @@ public class DoubleArrayIndexFieldData extends AbstractIndexFieldData<DoubleArra
} finally { } finally {
builder.close(); builder.close();
} }
} }
@Override @Override
public XFieldComparatorSource comparatorSource(@Nullable Object missingValue) { public XFieldComparatorSource comparatorSource(@Nullable Object missingValue, SortMode sortMode) {
return new DoubleValuesComparatorSource(this, missingValue); return new DoubleValuesComparatorSource(this, missingValue, sortMode);
} }
} }

View File

@ -20,9 +20,9 @@
package org.elasticsearch.index.fielddata.plain; package org.elasticsearch.index.fielddata.plain;
import gnu.trove.list.array.TFloatArrayList; import gnu.trove.list.array.TFloatArrayList;
import org.apache.lucene.index.*; import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.search.FieldCache; import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.search.FieldCache.StopFillCacheException; import org.apache.lucene.index.Terms;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefIterator; import org.apache.lucene.util.BytesRefIterator;
import org.apache.lucene.util.FixedBitSet; import org.apache.lucene.util.FixedBitSet;
@ -33,9 +33,10 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.fielddata.*; import org.elasticsearch.index.fielddata.*;
import org.elasticsearch.index.fielddata.fieldcomparator.FloatValuesComparatorSource; import org.elasticsearch.index.fielddata.fieldcomparator.FloatValuesComparatorSource;
import org.elasticsearch.index.fielddata.fieldcomparator.SortMode;
import org.elasticsearch.index.fielddata.ordinals.Ordinals; import org.elasticsearch.index.fielddata.ordinals.Ordinals;
import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder;
import org.elasticsearch.index.fielddata.ordinals.Ordinals.Docs; import org.elasticsearch.index.fielddata.ordinals.Ordinals.Docs;
import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.index.settings.IndexSettings;
@ -91,12 +92,12 @@ public class FloatArrayIndexFieldData extends AbstractIndexFieldData<FloatArrayA
final TFloatArrayList values = new TFloatArrayList(); final TFloatArrayList values = new TFloatArrayList();
values.add(0); // first "t" indicates null value values.add(0); // first "t" indicates null value
OrdinalsBuilder builder = new OrdinalsBuilder(terms, reader.maxDoc()); OrdinalsBuilder builder = new OrdinalsBuilder(terms, reader.maxDoc());
try { try {
BytesRefIterator iter = builder.buildFromTerms(builder.wrapNumeric32Bit(terms.iterator(null)), reader.getLiveDocs()); BytesRefIterator iter = builder.buildFromTerms(builder.wrapNumeric32Bit(terms.iterator(null)), reader.getLiveDocs());
BytesRef term; BytesRef term;
while((term = iter.next()) != null) { while ((term = iter.next()) != null) {
values.add(NumericUtils.sortableIntToFloat(NumericUtils.prefixCodedToInt(term))); values.add(NumericUtils.sortableIntToFloat(NumericUtils.prefixCodedToInt(term)));
} }
Ordinals build = builder.build(fieldDataType.getSettings()); Ordinals build = builder.build(fieldDataType.getSettings());
@ -105,7 +106,7 @@ public class FloatArrayIndexFieldData extends AbstractIndexFieldData<FloatArrayA
float[] sValues = new float[reader.maxDoc()]; float[] sValues = new float[reader.maxDoc()];
int maxDoc = reader.maxDoc(); int maxDoc = reader.maxDoc();
for (int i = 0; i < maxDoc; i++) { for (int i = 0; i < maxDoc; i++) {
sValues[i] = values.get(ordinals.getOrd(i)); sValues[i] = values.get(ordinals.getOrd(i));
} }
final FixedBitSet set = builder.buildDocsWithValuesSet(); final FixedBitSet set = builder.buildDocsWithValuesSet();
if (set == null) { if (set == null) {
@ -126,7 +127,7 @@ public class FloatArrayIndexFieldData extends AbstractIndexFieldData<FloatArrayA
} }
@Override @Override
public XFieldComparatorSource comparatorSource(@Nullable Object missingValue) { public XFieldComparatorSource comparatorSource(@Nullable Object missingValue, SortMode sortMode) {
return new FloatValuesComparatorSource(this, missingValue); return new FloatValuesComparatorSource(this, missingValue, sortMode);
} }
} }

View File

@ -20,23 +20,20 @@
package org.elasticsearch.index.fielddata.plain; package org.elasticsearch.index.fielddata.plain;
import gnu.trove.list.array.TDoubleArrayList; import gnu.trove.list.array.TDoubleArrayList;
import org.apache.lucene.index.*; import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.search.FieldCache; import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.search.FieldCache.StopFillCacheException; import org.apache.lucene.index.Terms;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.*;
import org.apache.lucene.util.BytesRefIterator;
import org.apache.lucene.util.CharsRef;
import org.apache.lucene.util.FixedBitSet;
import org.apache.lucene.util.UnicodeUtil;
import org.elasticsearch.ElasticSearchException; import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.ElasticSearchIllegalArgumentException; import org.elasticsearch.ElasticSearchIllegalArgumentException;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.fielddata.*; import org.elasticsearch.index.fielddata.*;
import org.elasticsearch.index.fielddata.fieldcomparator.SortMode;
import org.elasticsearch.index.fielddata.ordinals.Ordinals; import org.elasticsearch.index.fielddata.ordinals.Ordinals;
import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder;
import org.elasticsearch.index.fielddata.ordinals.Ordinals.Docs; import org.elasticsearch.index.fielddata.ordinals.Ordinals.Docs;
import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.index.settings.IndexSettings;
@ -94,20 +91,20 @@ public class GeoPointDoubleArrayIndexFieldData extends AbstractIndexFieldData<Ge
try { try {
BytesRefIterator iter = builder.buildFromTerms(terms.iterator(null), reader.getLiveDocs()); BytesRefIterator iter = builder.buildFromTerms(terms.iterator(null), reader.getLiveDocs());
BytesRef term; BytesRef term;
while((term = iter.next()) != null) { while ((term = iter.next()) != null) {
UnicodeUtil.UTF8toUTF16(term, spare); UnicodeUtil.UTF8toUTF16(term, spare);
boolean parsed = false; boolean parsed = false;
for (int i = spare.offset; i < spare.length; i++) { for (int i = spare.offset; i < spare.length; i++) {
if (spare.chars[i] == ',') { // safes a string creation if (spare.chars[i] == ',') { // safes a string creation
lat.add(Double.parseDouble(new String(spare.chars, spare.offset, (i - spare.offset)))); lat.add(Double.parseDouble(new String(spare.chars, spare.offset, (i - spare.offset))));
lon.add(Double.parseDouble(new String(spare.chars, (spare.offset + (i+1)), spare.length - ((i + 1) - spare.offset)))); lon.add(Double.parseDouble(new String(spare.chars, (spare.offset + (i + 1)), spare.length - ((i + 1) - spare.offset))));
parsed = true; parsed = true;
break; break;
} }
} }
assert parsed; assert parsed;
} }
Ordinals build = builder.build(fieldDataType.getSettings()); Ordinals build = builder.build(fieldDataType.getSettings());
if (!build.isMultiValued()) { if (!build.isMultiValued()) {
Docs ordinals = build.ordinals(); Docs ordinals = build.ordinals();
@ -137,7 +134,7 @@ public class GeoPointDoubleArrayIndexFieldData extends AbstractIndexFieldData<Ge
} }
@Override @Override
public XFieldComparatorSource comparatorSource(@Nullable Object missingValue) { public XFieldComparatorSource comparatorSource(@Nullable Object missingValue, SortMode sortMode) {
throw new ElasticSearchIllegalArgumentException("can't sort on geo_point field without using specific sorting feature, like geo_distance"); throw new ElasticSearchIllegalArgumentException("can't sort on geo_point field without using specific sorting feature, like geo_distance");
} }
} }

View File

@ -20,9 +20,9 @@
package org.elasticsearch.index.fielddata.plain; package org.elasticsearch.index.fielddata.plain;
import gnu.trove.list.array.TIntArrayList; import gnu.trove.list.array.TIntArrayList;
import org.apache.lucene.index.*; import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.search.FieldCache; import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.search.FieldCache.StopFillCacheException; import org.apache.lucene.index.Terms;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefIterator; import org.apache.lucene.util.BytesRefIterator;
import org.apache.lucene.util.FixedBitSet; import org.apache.lucene.util.FixedBitSet;
@ -33,9 +33,10 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.fielddata.*; import org.elasticsearch.index.fielddata.*;
import org.elasticsearch.index.fielddata.fieldcomparator.IntValuesComparatorSource; import org.elasticsearch.index.fielddata.fieldcomparator.IntValuesComparatorSource;
import org.elasticsearch.index.fielddata.fieldcomparator.SortMode;
import org.elasticsearch.index.fielddata.ordinals.Ordinals; import org.elasticsearch.index.fielddata.ordinals.Ordinals;
import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder;
import org.elasticsearch.index.fielddata.ordinals.Ordinals.Docs; import org.elasticsearch.index.fielddata.ordinals.Ordinals.Docs;
import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.index.settings.IndexSettings;
@ -95,7 +96,7 @@ public class IntArrayIndexFieldData extends AbstractIndexFieldData<IntArrayAtomi
try { try {
BytesRefIterator iter = builder.buildFromTerms(builder.wrapNumeric32Bit(terms.iterator(null)), reader.getLiveDocs()); BytesRefIterator iter = builder.buildFromTerms(builder.wrapNumeric32Bit(terms.iterator(null)), reader.getLiveDocs());
BytesRef term; BytesRef term;
while((term = iter.next()) != null) { while ((term = iter.next()) != null) {
values.add(NumericUtils.prefixCodedToInt(term)); values.add(NumericUtils.prefixCodedToInt(term));
} }
Ordinals build = builder.build(fieldDataType.getSettings()); Ordinals build = builder.build(fieldDataType.getSettings());
@ -104,7 +105,7 @@ public class IntArrayIndexFieldData extends AbstractIndexFieldData<IntArrayAtomi
int[] sValues = new int[reader.maxDoc()]; int[] sValues = new int[reader.maxDoc()];
int maxDoc = reader.maxDoc(); int maxDoc = reader.maxDoc();
for (int i = 0; i < maxDoc; i++) { for (int i = 0; i < maxDoc; i++) {
sValues[i] = values.get(ordinals.getOrd(i)); sValues[i] = values.get(ordinals.getOrd(i));
} }
final FixedBitSet set = builder.buildDocsWithValuesSet(); final FixedBitSet set = builder.buildDocsWithValuesSet();
if (set == null) { if (set == null) {
@ -124,7 +125,7 @@ public class IntArrayIndexFieldData extends AbstractIndexFieldData<IntArrayAtomi
} }
@Override @Override
public XFieldComparatorSource comparatorSource(@Nullable Object missingValue) { public XFieldComparatorSource comparatorSource(@Nullable Object missingValue, SortMode sortMode) {
return new IntValuesComparatorSource(this, missingValue); return new IntValuesComparatorSource(this, missingValue, sortMode);
} }
} }

View File

@ -20,9 +20,9 @@
package org.elasticsearch.index.fielddata.plain; package org.elasticsearch.index.fielddata.plain;
import gnu.trove.list.array.TLongArrayList; import gnu.trove.list.array.TLongArrayList;
import org.apache.lucene.index.*; import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.search.FieldCache; import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.search.FieldCache.StopFillCacheException; import org.apache.lucene.index.Terms;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefIterator; import org.apache.lucene.util.BytesRefIterator;
import org.apache.lucene.util.FixedBitSet; import org.apache.lucene.util.FixedBitSet;
@ -33,9 +33,10 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.fielddata.*; import org.elasticsearch.index.fielddata.*;
import org.elasticsearch.index.fielddata.fieldcomparator.LongValuesComparatorSource; import org.elasticsearch.index.fielddata.fieldcomparator.LongValuesComparatorSource;
import org.elasticsearch.index.fielddata.fieldcomparator.SortMode;
import org.elasticsearch.index.fielddata.ordinals.Ordinals; import org.elasticsearch.index.fielddata.ordinals.Ordinals;
import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder;
import org.elasticsearch.index.fielddata.ordinals.Ordinals.Docs; import org.elasticsearch.index.fielddata.ordinals.Ordinals.Docs;
import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.index.settings.IndexSettings;
@ -93,8 +94,9 @@ public class LongArrayIndexFieldData extends AbstractIndexFieldData<LongArrayAto
values.add(0); // first "t" indicates null value values.add(0); // first "t" indicates null value
OrdinalsBuilder builder = new OrdinalsBuilder(terms, reader.maxDoc()); OrdinalsBuilder builder = new OrdinalsBuilder(terms, reader.maxDoc());
try { try {
BytesRefIterator iter = builder.buildFromTerms(builder.wrapNumeric64Bit(terms.iterator(null)), reader.getLiveDocs()); BytesRef term; BytesRefIterator iter = builder.buildFromTerms(builder.wrapNumeric64Bit(terms.iterator(null)), reader.getLiveDocs());
while((term = iter.next()) != null) { BytesRef term;
while ((term = iter.next()) != null) {
values.add(NumericUtils.prefixCodedToLong(term)); values.add(NumericUtils.prefixCodedToLong(term));
} }
Ordinals build = builder.build(fieldDataType.getSettings()); Ordinals build = builder.build(fieldDataType.getSettings());
@ -103,7 +105,7 @@ public class LongArrayIndexFieldData extends AbstractIndexFieldData<LongArrayAto
long[] sValues = new long[reader.maxDoc()]; long[] sValues = new long[reader.maxDoc()];
int maxDoc = reader.maxDoc(); int maxDoc = reader.maxDoc();
for (int i = 0; i < maxDoc; i++) { for (int i = 0; i < maxDoc; i++) {
sValues[i] = values.get(ordinals.getOrd(i)); sValues[i] = values.get(ordinals.getOrd(i));
} }
final FixedBitSet set = builder.buildDocsWithValuesSet(); final FixedBitSet set = builder.buildDocsWithValuesSet();
if (set == null) { if (set == null) {
@ -124,7 +126,7 @@ public class LongArrayIndexFieldData extends AbstractIndexFieldData<LongArrayAto
} }
@Override @Override
public XFieldComparatorSource comparatorSource(@Nullable Object missingValue) { public XFieldComparatorSource comparatorSource(@Nullable Object missingValue, SortMode sortMode) {
return new LongValuesComparatorSource(this, missingValue); return new LongValuesComparatorSource(this, missingValue, sortMode);
} }
} }

View File

@ -34,6 +34,7 @@ import org.elasticsearch.index.fielddata.FieldDataType;
import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.fielddata.IndexFieldDataCache; import org.elasticsearch.index.fielddata.IndexFieldDataCache;
import org.elasticsearch.index.fielddata.fieldcomparator.BytesRefFieldComparatorSource; import org.elasticsearch.index.fielddata.fieldcomparator.BytesRefFieldComparatorSource;
import org.elasticsearch.index.fielddata.fieldcomparator.SortMode;
import org.elasticsearch.index.fielddata.ordinals.Ordinals; import org.elasticsearch.index.fielddata.ordinals.Ordinals;
import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder; import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
@ -144,7 +145,7 @@ public class PagedBytesIndexFieldData extends AbstractIndexFieldData<PagedBytesA
PagedBytes.Reader bytesReader = bytes.freeze(true); PagedBytes.Reader bytesReader = bytes.freeze(true);
PackedInts.Reader termOrdToBytesOffsetReader = termOrdToBytesOffset.getMutable(); PackedInts.Reader termOrdToBytesOffsetReader = termOrdToBytesOffset.getMutable();
final Ordinals ordinals = builder.build(fieldDataType.getSettings()); final Ordinals ordinals = builder.build(fieldDataType.getSettings());
return new PagedBytesAtomicFieldData(bytesReader, termOrdToBytesOffsetReader, ordinals); return new PagedBytesAtomicFieldData(bytesReader, termOrdToBytesOffsetReader, ordinals);
} finally { } finally {
builder.close(); builder.close();
@ -152,8 +153,8 @@ public class PagedBytesIndexFieldData extends AbstractIndexFieldData<PagedBytesA
} }
@Override @Override
public XFieldComparatorSource comparatorSource(@Nullable Object missingValue) { public XFieldComparatorSource comparatorSource(@Nullable Object missingValue, SortMode sortMode) {
// TODO support "missingValue" for sortMissingValue options here... // TODO support "missingValue" for sortMissingValue options here...
return new BytesRefFieldComparatorSource(this); return new BytesRefFieldComparatorSource(this, sortMode);
} }
} }

View File

@ -20,9 +20,9 @@
package org.elasticsearch.index.fielddata.plain; package org.elasticsearch.index.fielddata.plain;
import gnu.trove.list.array.TShortArrayList; import gnu.trove.list.array.TShortArrayList;
import org.apache.lucene.index.*; import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.search.FieldCache; import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.search.FieldCache.StopFillCacheException; import org.apache.lucene.index.Terms;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefIterator; import org.apache.lucene.util.BytesRefIterator;
import org.apache.lucene.util.FixedBitSet; import org.apache.lucene.util.FixedBitSet;
@ -33,9 +33,10 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.fielddata.*; import org.elasticsearch.index.fielddata.*;
import org.elasticsearch.index.fielddata.fieldcomparator.ShortValuesComparatorSource; import org.elasticsearch.index.fielddata.fieldcomparator.ShortValuesComparatorSource;
import org.elasticsearch.index.fielddata.fieldcomparator.SortMode;
import org.elasticsearch.index.fielddata.ordinals.Ordinals; import org.elasticsearch.index.fielddata.ordinals.Ordinals;
import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder;
import org.elasticsearch.index.fielddata.ordinals.Ordinals.Docs; import org.elasticsearch.index.fielddata.ordinals.Ordinals.Docs;
import org.elasticsearch.index.fielddata.ordinals.OrdinalsBuilder;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.index.settings.IndexSettings;
@ -95,17 +96,17 @@ public class ShortArrayIndexFieldData extends AbstractIndexFieldData<ShortArrayA
try { try {
BytesRefIterator iter = builder.buildFromTerms(builder.wrapNumeric32Bit(terms.iterator(null)), reader.getLiveDocs()); BytesRefIterator iter = builder.buildFromTerms(builder.wrapNumeric32Bit(terms.iterator(null)), reader.getLiveDocs());
BytesRef term; BytesRef term;
while((term = iter.next()) != null) { while ((term = iter.next()) != null) {
values.add((short) NumericUtils.prefixCodedToInt(term)); values.add((short) NumericUtils.prefixCodedToInt(term));
} }
Ordinals build = builder.build(fieldDataType.getSettings()); Ordinals build = builder.build(fieldDataType.getSettings());
if (!build.isMultiValued()) { if (!build.isMultiValued()) {
Docs ordinals = build.ordinals(); Docs ordinals = build.ordinals();
short[] sValues = new short[reader.maxDoc()]; short[] sValues = new short[reader.maxDoc()];
int maxDoc = reader.maxDoc(); int maxDoc = reader.maxDoc();
for (int i = 0; i < maxDoc; i++) { for (int i = 0; i < maxDoc; i++) {
sValues[i] = values.get(ordinals.getOrd(i)); sValues[i] = values.get(ordinals.getOrd(i));
} }
final FixedBitSet set = builder.buildDocsWithValuesSet(); final FixedBitSet set = builder.buildDocsWithValuesSet();
if (set == null) { if (set == null) {
@ -125,7 +126,7 @@ public class ShortArrayIndexFieldData extends AbstractIndexFieldData<ShortArrayA
} }
@Override @Override
public XFieldComparatorSource comparatorSource(@Nullable Object missingValue) { public XFieldComparatorSource comparatorSource(@Nullable Object missingValue, SortMode sortMode) {
return new ShortValuesComparatorSource(this, missingValue); return new ShortValuesComparatorSource(this, missingValue, sortMode);
} }
} }

View File

@ -36,6 +36,8 @@ public class FieldSortBuilder extends SortBuilder {
private Boolean ignoreUnampped; private Boolean ignoreUnampped;
private String sortMode;
/** /**
* Constructs a new sort based on a document field. * Constructs a new sort based on a document field.
* *
@ -73,6 +75,17 @@ public class FieldSortBuilder extends SortBuilder {
return this; return this;
} }
/**
* Defines what values to pick in the case a document contains multiple values for the targeted sort field.
* Possible values: min, max, sum and avg
* <p/>
* The last two values are only applicable for number based fields.
*/
public FieldSortBuilder sortMode(String sortMode) {
this.sortMode = sortMode;
return this;
}
@Override @Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(fieldName); builder.startObject(fieldName);
@ -85,6 +98,9 @@ public class FieldSortBuilder extends SortBuilder {
if (ignoreUnampped != null) { if (ignoreUnampped != null) {
builder.field("ignore_unmapped", ignoreUnampped); builder.field("ignore_unmapped", ignoreUnampped);
} }
if (sortMode != null) {
builder.field("sort_mode", sortMode);
}
builder.endObject(); builder.endObject();
return builder; return builder;
} }

View File

@ -25,7 +25,9 @@ import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField; import org.apache.lucene.search.SortField;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.fielddata.fieldcomparator.SortMode;
import org.elasticsearch.index.mapper.FieldMapper; import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.core.NumberFieldMapper;
import org.elasticsearch.search.SearchParseElement; import org.elasticsearch.search.SearchParseElement;
import org.elasticsearch.search.SearchParseException; import org.elasticsearch.search.SearchParseException;
import org.elasticsearch.search.internal.SearchContext; import org.elasticsearch.search.internal.SearchContext;
@ -69,7 +71,7 @@ public class SortParseElement implements SearchParseElement {
if (token == XContentParser.Token.START_OBJECT) { if (token == XContentParser.Token.START_OBJECT) {
addCompoundSortField(parser, context, sortFields); addCompoundSortField(parser, context, sortFields);
} else if (token == XContentParser.Token.VALUE_STRING) { } else if (token == XContentParser.Token.VALUE_STRING) {
addSortField(context, sortFields, parser.text(), false, false, null); addSortField(context, sortFields, parser.text(), false, false, null, null);
} }
} }
} else { } else {
@ -103,6 +105,7 @@ public class SortParseElement implements SearchParseElement {
String missing = null; String missing = null;
String innerJsonName = null; String innerJsonName = null;
boolean ignoreUnmapped = false; boolean ignoreUnmapped = false;
SortMode sortMode = null;
token = parser.nextToken(); token = parser.nextToken();
if (token == XContentParser.Token.VALUE_STRING) { if (token == XContentParser.Token.VALUE_STRING) {
String direction = parser.text(); String direction = parser.text();
@ -111,7 +114,7 @@ public class SortParseElement implements SearchParseElement {
} else if (direction.equals("desc")) { } else if (direction.equals("desc")) {
reverse = !SCORE_FIELD_NAME.equals(fieldName); reverse = !SCORE_FIELD_NAME.equals(fieldName);
} }
addSortField(context, sortFields, fieldName, reverse, ignoreUnmapped, missing); addSortField(context, sortFields, fieldName, reverse, ignoreUnmapped, missing, sortMode);
} else { } else {
if (parsers.containsKey(fieldName)) { if (parsers.containsKey(fieldName)) {
sortFields.add(parsers.get(fieldName).parse(parser, context)); sortFields.add(parsers.get(fieldName).parse(parser, context));
@ -132,17 +135,19 @@ public class SortParseElement implements SearchParseElement {
missing = parser.textOrNull(); missing = parser.textOrNull();
} else if ("ignore_unmapped".equals(innerJsonName) || "ignoreUnmapped".equals(innerJsonName)) { } else if ("ignore_unmapped".equals(innerJsonName) || "ignoreUnmapped".equals(innerJsonName)) {
ignoreUnmapped = parser.booleanValue(); ignoreUnmapped = parser.booleanValue();
} else if ("sort_mode".equals(innerJsonName) || "sortMode".equals(innerJsonName)) {
sortMode = SortMode.fromString(parser.text());
} }
} }
} }
addSortField(context, sortFields, fieldName, reverse, ignoreUnmapped, missing); addSortField(context, sortFields, fieldName, reverse, ignoreUnmapped, missing, sortMode);
} }
} }
} }
} }
} }
private void addSortField(SearchContext context, List<SortField> sortFields, String fieldName, boolean reverse, boolean ignoreUnmapped, @Nullable final String missing) { private void addSortField(SearchContext context, List<SortField> sortFields, String fieldName, boolean reverse, boolean ignoreUnmapped, @Nullable final String missing, SortMode sortMode) {
if (SCORE_FIELD_NAME.equals(fieldName)) { if (SCORE_FIELD_NAME.equals(fieldName)) {
if (reverse) { if (reverse) {
sortFields.add(SORT_SCORE_REVERSE); sortFields.add(SORT_SCORE_REVERSE);
@ -173,7 +178,14 @@ public class SortParseElement implements SearchParseElement {
} }
}*/ }*/
sortFields.add(new SortField(fieldMapper.names().indexName(), context.fieldData().getForField(fieldMapper).comparatorSource(missing), reverse)); // We only support AVG and SUM on number based fields
if (!(fieldMapper instanceof NumberFieldMapper) && (sortMode == SortMode.SUM || sortMode == SortMode.AVG)) {
sortMode = null;
}
if (sortMode == null) {
sortMode = reverse ? SortMode.MAX : SortMode.MIN;
}
sortFields.add(new SortField(fieldMapper.names().indexName(), context.fieldData().getForField(fieldMapper).comparatorSource(missing, sortMode), reverse));
} }
} }
} }

View File

@ -693,6 +693,24 @@ public class SimpleSortTests extends AbstractNodesTests {
assertThat(searchResponse.hits().getAt(2).id(), equalTo(Integer.toString(3))); assertThat(searchResponse.hits().getAt(2).id(), equalTo(Integer.toString(3)));
assertThat(((Number) searchResponse.hits().getAt(2).sortValues()[0]).longValue(), equalTo(3l)); assertThat(((Number) searchResponse.hits().getAt(2).sortValues()[0]).longValue(), equalTo(3l));
searchResponse = client.prepareSearch()
.setQuery(matchAllQuery())
.setSize(10)
.addSort(SortBuilders.fieldSort("long_values").order(SortOrder.DESC).sortMode("sum"))
.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(53l));
assertThat(searchResponse.hits().getAt(1).id(), equalTo(Integer.toString(1)));
assertThat(((Number) searchResponse.hits().getAt(1).sortValues()[0]).longValue(), equalTo(24l));
assertThat(searchResponse.hits().getAt(2).id(), equalTo(Integer.toString(3)));
assertThat(((Number) searchResponse.hits().getAt(2).sortValues()[0]).longValue(), equalTo(2l));
searchResponse = client.prepareSearch() searchResponse = client.prepareSearch()
.setQuery(matchAllQuery()) .setQuery(matchAllQuery())
.setSize(10) .setSize(10)

View File

@ -25,7 +25,9 @@ import org.apache.lucene.document.StringField;
import org.apache.lucene.index.DirectoryReader; 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.fieldcomparator.SortMode;
import org.elasticsearch.index.fielddata.util.DoubleArrayRef;
import org.elasticsearch.index.fielddata.util.LongArrayRef;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
@ -144,14 +146,14 @@ public abstract class NumericFieldDataTests extends StringFieldDataTests {
TopFieldDocs topDocs; TopFieldDocs topDocs;
topDocs = searcher.search(new MatchAllDocsQuery(), 10, topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource(null)))); new Sort(new SortField("value", indexFieldData.comparatorSource(null, SortMode.MIN))));
assertThat(topDocs.totalHits, equalTo(3)); assertThat(topDocs.totalHits, equalTo(3));
assertThat(topDocs.scoreDocs[0].doc, equalTo(1)); assertThat(topDocs.scoreDocs[0].doc, equalTo(1));
assertThat(topDocs.scoreDocs[1].doc, equalTo(0)); assertThat(topDocs.scoreDocs[1].doc, equalTo(0));
assertThat(topDocs.scoreDocs[2].doc, equalTo(2)); assertThat(topDocs.scoreDocs[2].doc, equalTo(2));
topDocs = searcher.search(new MatchAllDocsQuery(), 10, topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource(null), true))); new Sort(new SortField("value", indexFieldData.comparatorSource(null, SortMode.MAX), true)));
assertThat(topDocs.totalHits, equalTo(3)); assertThat(topDocs.totalHits, equalTo(3));
assertThat(topDocs.scoreDocs[0].doc, equalTo(2)); assertThat(topDocs.scoreDocs[0].doc, equalTo(2));
assertThat(topDocs.scoreDocs[1].doc, equalTo(0)); assertThat(topDocs.scoreDocs[1].doc, equalTo(0));
@ -256,42 +258,42 @@ public abstract class NumericFieldDataTests extends StringFieldDataTests {
TopFieldDocs topDocs; TopFieldDocs topDocs;
topDocs = searcher.search(new MatchAllDocsQuery(), 10, topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource(null)))); // defaults to _last new Sort(new SortField("value", indexFieldData.comparatorSource(null, SortMode.MIN)))); // defaults to _last
assertThat(topDocs.totalHits, equalTo(3)); assertThat(topDocs.totalHits, equalTo(3));
assertThat(topDocs.scoreDocs[0].doc, equalTo(0)); assertThat(topDocs.scoreDocs[0].doc, equalTo(0));
assertThat(topDocs.scoreDocs[1].doc, equalTo(2)); assertThat(topDocs.scoreDocs[1].doc, equalTo(2));
assertThat(topDocs.scoreDocs[2].doc, equalTo(1)); assertThat(topDocs.scoreDocs[2].doc, equalTo(1));
topDocs = searcher.search(new MatchAllDocsQuery(), 10, topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource(null), true))); // defaults to _last new Sort(new SortField("value", indexFieldData.comparatorSource(null, SortMode.MAX), true))); // defaults to _last
assertThat(topDocs.totalHits, equalTo(3)); assertThat(topDocs.totalHits, equalTo(3));
assertThat(topDocs.scoreDocs[0].doc, equalTo(2)); assertThat(topDocs.scoreDocs[0].doc, equalTo(2));
assertThat(topDocs.scoreDocs[1].doc, equalTo(0)); assertThat(topDocs.scoreDocs[1].doc, equalTo(0));
assertThat(topDocs.scoreDocs[2].doc, equalTo(1)); assertThat(topDocs.scoreDocs[2].doc, equalTo(1));
topDocs = searcher.search(new MatchAllDocsQuery(), 10, topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource("_first")))); new Sort(new SortField("value", indexFieldData.comparatorSource("_first", SortMode.MIN))));
assertThat(topDocs.totalHits, equalTo(3)); assertThat(topDocs.totalHits, equalTo(3));
assertThat(topDocs.scoreDocs[0].doc, equalTo(1)); assertThat(topDocs.scoreDocs[0].doc, equalTo(1));
assertThat(topDocs.scoreDocs[1].doc, equalTo(0)); assertThat(topDocs.scoreDocs[1].doc, equalTo(0));
assertThat(topDocs.scoreDocs[2].doc, equalTo(2)); assertThat(topDocs.scoreDocs[2].doc, equalTo(2));
topDocs = searcher.search(new MatchAllDocsQuery(), 10, topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource("_first"), true))); new Sort(new SortField("value", indexFieldData.comparatorSource("_first", SortMode.MAX), true)));
assertThat(topDocs.totalHits, equalTo(3)); assertThat(topDocs.totalHits, equalTo(3));
assertThat(topDocs.scoreDocs[0].doc, equalTo(1)); assertThat(topDocs.scoreDocs[0].doc, equalTo(1));
assertThat(topDocs.scoreDocs[1].doc, equalTo(2)); assertThat(topDocs.scoreDocs[1].doc, equalTo(2));
assertThat(topDocs.scoreDocs[2].doc, equalTo(0)); assertThat(topDocs.scoreDocs[2].doc, equalTo(0));
topDocs = searcher.search(new MatchAllDocsQuery(), 10, topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource("1")))); new Sort(new SortField("value", indexFieldData.comparatorSource("1", SortMode.MIN))));
assertThat(topDocs.totalHits, equalTo(3)); assertThat(topDocs.totalHits, equalTo(3));
assertThat(topDocs.scoreDocs[0].doc, equalTo(1)); assertThat(topDocs.scoreDocs[0].doc, equalTo(1));
assertThat(topDocs.scoreDocs[1].doc, equalTo(0)); assertThat(topDocs.scoreDocs[1].doc, equalTo(0));
assertThat(topDocs.scoreDocs[2].doc, equalTo(2)); assertThat(topDocs.scoreDocs[2].doc, equalTo(2));
topDocs = searcher.search(new MatchAllDocsQuery(), 10, topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource("1"), true))); new Sort(new SortField("value", indexFieldData.comparatorSource("1", SortMode.MAX), true)));
assertThat(topDocs.totalHits, equalTo(3)); assertThat(topDocs.totalHits, equalTo(3));
assertThat(topDocs.scoreDocs[0].doc, equalTo(2)); assertThat(topDocs.scoreDocs[0].doc, equalTo(2));
assertThat(topDocs.scoreDocs[1].doc, equalTo(0)); assertThat(topDocs.scoreDocs[1].doc, equalTo(0));
@ -610,7 +612,7 @@ public abstract class NumericFieldDataTests extends StringFieldDataTests {
IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(writer, true)); IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(writer, true));
TopFieldDocs topDocs = searcher.search(new MatchAllDocsQuery(), 10, TopFieldDocs topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource(null)))); // defaults to _last new Sort(new SortField("value", indexFieldData.comparatorSource(null, SortMode.MIN)))); // defaults to _last
assertThat(topDocs.totalHits, equalTo(8)); assertThat(topDocs.totalHits, equalTo(8));
assertThat(topDocs.scoreDocs.length, equalTo(8)); assertThat(topDocs.scoreDocs.length, equalTo(8));
assertThat(topDocs.scoreDocs[0].doc, equalTo(7)); assertThat(topDocs.scoreDocs[0].doc, equalTo(7));
@ -631,7 +633,7 @@ public abstract class NumericFieldDataTests extends StringFieldDataTests {
// assertThat(((FieldDoc) topDocs.scoreDocs[7]).fields[0], equalTo(null)); // assertThat(((FieldDoc) topDocs.scoreDocs[7]).fields[0], equalTo(null));
topDocs = searcher.search(new MatchAllDocsQuery(), 10, topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource(null), true))); // defaults to _last new Sort(new SortField("value", indexFieldData.comparatorSource(null, SortMode.MAX), true))); // defaults to _last
assertThat(topDocs.totalHits, equalTo(8)); assertThat(topDocs.totalHits, equalTo(8));
assertThat(topDocs.scoreDocs.length, equalTo(8)); assertThat(topDocs.scoreDocs.length, equalTo(8));
assertThat(topDocs.scoreDocs[0].doc, equalTo(6)); assertThat(topDocs.scoreDocs[0].doc, equalTo(6));
@ -651,8 +653,96 @@ public abstract class NumericFieldDataTests extends StringFieldDataTests {
assertThat(topDocs.scoreDocs[7].doc, equalTo(5)); assertThat(topDocs.scoreDocs[7].doc, equalTo(5));
// assertThat(((FieldDoc) topDocs.scoreDocs[7]).fields[0], equalTo(null)); // assertThat(((FieldDoc) topDocs.scoreDocs[7]).fields[0], equalTo(null));
searcher = new IndexSearcher(DirectoryReader.open(writer, true));
topDocs = searcher.search(new MatchAllDocsQuery(), 10, topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource("_first")))); new Sort(new SortField("value", indexFieldData.comparatorSource(null, SortMode.SUM)))); // 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(-27));
assertThat(topDocs.scoreDocs[1].doc, equalTo(2));
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[1]).fields[0]).intValue(), equalTo(3));
assertThat(topDocs.scoreDocs[2].doc, equalTo(0));
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[2]).fields[0]).intValue(), equalTo(6));
assertThat(topDocs.scoreDocs[3].doc, equalTo(3));
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[3]).fields[0]).intValue(), equalTo(15));
assertThat(topDocs.scoreDocs[4].doc, equalTo(4));
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[4]).fields[0]).intValue(), equalTo(21));
assertThat(topDocs.scoreDocs[5].doc, equalTo(6));
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[5]).fields[0]).intValue(), equalTo(27));
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));
searcher = new IndexSearcher(DirectoryReader.open(writer, true));
topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource(null, SortMode.SUM), 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(27));
assertThat(topDocs.scoreDocs[1].doc, equalTo(4));
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[1]).fields[0]).intValue(), equalTo(21));
assertThat(topDocs.scoreDocs[2].doc, equalTo(3));
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[2]).fields[0]).intValue(), equalTo(15));
assertThat(topDocs.scoreDocs[3].doc, equalTo(0));
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[3]).fields[0]).intValue(), equalTo(6));
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(-27));
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));
searcher = new IndexSearcher(DirectoryReader.open(writer, true));
topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource(null, SortMode.AVG)))); // 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(-9));
assertThat(topDocs.scoreDocs[1].doc, equalTo(0));
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[1]).fields[0]).intValue(), equalTo(3));
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(5));
assertThat(topDocs.scoreDocs[4].doc, equalTo(4));
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[4]).fields[0]).intValue(), equalTo(7));
assertThat(topDocs.scoreDocs[5].doc, equalTo(6));
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[5]).fields[0]).intValue(), equalTo(9));
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));
searcher = new IndexSearcher(DirectoryReader.open(writer, true));
topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource(null, SortMode.AVG), 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(9));
assertThat(topDocs.scoreDocs[1].doc, equalTo(4));
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[1]).fields[0]).intValue(), equalTo(7));
assertThat(topDocs.scoreDocs[2].doc, equalTo(3));
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[2]).fields[0]).intValue(), equalTo(5));
assertThat(topDocs.scoreDocs[3].doc, equalTo(0));
assertThat(((Number) ((FieldDoc) topDocs.scoreDocs[3]).fields[0]).intValue(), equalTo(3));
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(-9));
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", SortMode.MIN))));
assertThat(topDocs.totalHits, equalTo(8)); assertThat(topDocs.totalHits, equalTo(8));
assertThat(topDocs.scoreDocs.length, equalTo(8)); assertThat(topDocs.scoreDocs.length, equalTo(8));
assertThat(topDocs.scoreDocs[0].doc, equalTo(1)); assertThat(topDocs.scoreDocs[0].doc, equalTo(1));
@ -665,7 +755,7 @@ public abstract class NumericFieldDataTests extends StringFieldDataTests {
assertThat(topDocs.scoreDocs[7].doc, equalTo(6)); assertThat(topDocs.scoreDocs[7].doc, equalTo(6));
topDocs = searcher.search(new MatchAllDocsQuery(), 10, topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource("_first"), true))); new Sort(new SortField("value", indexFieldData.comparatorSource("_first", SortMode.MAX), true)));
assertThat(topDocs.totalHits, equalTo(8)); assertThat(topDocs.totalHits, equalTo(8));
assertThat(topDocs.scoreDocs.length, equalTo(8)); assertThat(topDocs.scoreDocs.length, equalTo(8));
assertThat(topDocs.scoreDocs[0].doc, equalTo(1)); assertThat(topDocs.scoreDocs[0].doc, equalTo(1));
@ -678,7 +768,7 @@ public abstract class NumericFieldDataTests extends StringFieldDataTests {
assertThat(topDocs.scoreDocs[7].doc, equalTo(7)); assertThat(topDocs.scoreDocs[7].doc, equalTo(7));
topDocs = searcher.search(new MatchAllDocsQuery(), 10, topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource("-9")))); new Sort(new SortField("value", indexFieldData.comparatorSource("-9", SortMode.MIN))));
assertThat(topDocs.totalHits, equalTo(8)); assertThat(topDocs.totalHits, equalTo(8));
assertThat(topDocs.scoreDocs.length, equalTo(8)); assertThat(topDocs.scoreDocs.length, equalTo(8));
assertThat(topDocs.scoreDocs[0].doc, equalTo(7)); assertThat(topDocs.scoreDocs[0].doc, equalTo(7));
@ -691,7 +781,7 @@ public abstract class NumericFieldDataTests extends StringFieldDataTests {
assertThat(topDocs.scoreDocs[7].doc, equalTo(6)); assertThat(topDocs.scoreDocs[7].doc, equalTo(6));
topDocs = searcher.search(new MatchAllDocsQuery(), 10, topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource("9"), true))); new Sort(new SortField("value", indexFieldData.comparatorSource("9", SortMode.MAX), true)));
assertThat(topDocs.totalHits, equalTo(8)); assertThat(topDocs.totalHits, equalTo(8));
assertThat(topDocs.scoreDocs.length, equalTo(8)); assertThat(topDocs.scoreDocs.length, equalTo(8));
assertThat(topDocs.scoreDocs[0].doc, equalTo(6)); assertThat(topDocs.scoreDocs[0].doc, equalTo(6));

View File

@ -28,6 +28,7 @@ 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;
import org.elasticsearch.index.fielddata.*; import org.elasticsearch.index.fielddata.*;
import org.elasticsearch.index.fielddata.fieldcomparator.SortMode;
import org.elasticsearch.index.fielddata.util.BytesRefArrayRef; import org.elasticsearch.index.fielddata.util.BytesRefArrayRef;
import org.elasticsearch.index.fielddata.util.StringArrayRef; import org.elasticsearch.index.fielddata.util.StringArrayRef;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -197,7 +198,7 @@ public abstract class StringFieldDataTests extends AbstractFieldDataTests {
TopFieldDocs topDocs; TopFieldDocs topDocs;
topDocs = searcher.search(new MatchAllDocsQuery(), 10, topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource(null)))); new Sort(new SortField("value", indexFieldData.comparatorSource(null, SortMode.MIN))));
assertThat(topDocs.totalHits, equalTo(3)); assertThat(topDocs.totalHits, equalTo(3));
assertThat(topDocs.scoreDocs[0].doc, equalTo(1)); assertThat(topDocs.scoreDocs[0].doc, equalTo(1));
assertThat(toString(((FieldDoc) topDocs.scoreDocs[0]).fields[0]), equalTo(one())); assertThat(toString(((FieldDoc) topDocs.scoreDocs[0]).fields[0]), equalTo(one()));
@ -207,7 +208,7 @@ public abstract class StringFieldDataTests extends AbstractFieldDataTests {
assertThat(toString(((FieldDoc) topDocs.scoreDocs[2]).fields[0]), equalTo(three())); assertThat(toString(((FieldDoc) topDocs.scoreDocs[2]).fields[0]), equalTo(three()));
topDocs = searcher.search(new MatchAllDocsQuery(), 10, topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource(null), true))); new Sort(new SortField("value", indexFieldData.comparatorSource(null, SortMode.MAX), true)));
assertThat(topDocs.totalHits, equalTo(3)); assertThat(topDocs.totalHits, equalTo(3));
assertThat(topDocs.scoreDocs[0].doc, equalTo(2)); assertThat(topDocs.scoreDocs[0].doc, equalTo(2));
assertThat(topDocs.scoreDocs[1].doc, equalTo(0)); assertThat(topDocs.scoreDocs[1].doc, equalTo(0));
@ -487,14 +488,14 @@ public abstract class StringFieldDataTests extends AbstractFieldDataTests {
stringValues.forEachValueInDoc(2, new StringValuesVerifierProc(2).addExpected(three())); stringValues.forEachValueInDoc(2, new StringValuesVerifierProc(2).addExpected(three()));
IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(writer, true)); IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(writer, true));
TopFieldDocs topDocs = searcher.search(new MatchAllDocsQuery(), 10, new Sort(new SortField("value", indexFieldData.comparatorSource(null)))); TopFieldDocs topDocs = searcher.search(new MatchAllDocsQuery(), 10, new Sort(new SortField("value", indexFieldData.comparatorSource(null, SortMode.MIN))));
assertThat(topDocs.totalHits, equalTo(3)); assertThat(topDocs.totalHits, equalTo(3));
assertThat(topDocs.scoreDocs.length, equalTo(3)); assertThat(topDocs.scoreDocs.length, equalTo(3));
assertThat(topDocs.scoreDocs[0].doc, equalTo(1)); assertThat(topDocs.scoreDocs[0].doc, equalTo(1));
assertThat(topDocs.scoreDocs[1].doc, equalTo(0)); assertThat(topDocs.scoreDocs[1].doc, equalTo(0));
assertThat(topDocs.scoreDocs[2].doc, equalTo(2)); assertThat(topDocs.scoreDocs[2].doc, equalTo(2));
topDocs = searcher.search(new MatchAllDocsQuery(), 10, new Sort(new SortField("value", indexFieldData.comparatorSource(null), true))); topDocs = searcher.search(new MatchAllDocsQuery(), 10, new Sort(new SortField("value", indexFieldData.comparatorSource(null, SortMode.MAX), true)));
assertThat(topDocs.totalHits, equalTo(3)); assertThat(topDocs.totalHits, equalTo(3));
assertThat(topDocs.scoreDocs.length, equalTo(3)); assertThat(topDocs.scoreDocs.length, equalTo(3));
assertThat(topDocs.scoreDocs[0].doc, equalTo(0)); assertThat(topDocs.scoreDocs[0].doc, equalTo(0));
@ -770,7 +771,7 @@ public abstract class StringFieldDataTests extends AbstractFieldDataTests {
IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(writer, true)); IndexSearcher searcher = new IndexSearcher(DirectoryReader.open(writer, true));
TopFieldDocs topDocs = searcher.search(new MatchAllDocsQuery(), 10, TopFieldDocs topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource(null)))); new Sort(new SortField("value", indexFieldData.comparatorSource(null, SortMode.MIN))));
assertThat(topDocs.totalHits, equalTo(8)); assertThat(topDocs.totalHits, equalTo(8));
assertThat(topDocs.scoreDocs.length, equalTo(8)); assertThat(topDocs.scoreDocs.length, equalTo(8));
assertThat(topDocs.scoreDocs[0].doc, equalTo(1)); assertThat(topDocs.scoreDocs[0].doc, equalTo(1));
@ -791,7 +792,7 @@ public abstract class StringFieldDataTests extends AbstractFieldDataTests {
assertThat(((BytesRef) ((FieldDoc) topDocs.scoreDocs[7]).fields[0]).utf8ToString(), equalTo("08")); assertThat(((BytesRef) ((FieldDoc) topDocs.scoreDocs[7]).fields[0]).utf8ToString(), equalTo("08"));
topDocs = searcher.search(new MatchAllDocsQuery(), 10, topDocs = searcher.search(new MatchAllDocsQuery(), 10,
new Sort(new SortField("value", indexFieldData.comparatorSource(null), true))); new Sort(new SortField("value", indexFieldData.comparatorSource(null, SortMode.MAX), true)));
assertThat(topDocs.totalHits, equalTo(8)); assertThat(topDocs.totalHits, equalTo(8));
assertThat(topDocs.scoreDocs.length, equalTo(8)); assertThat(topDocs.scoreDocs.length, equalTo(8));
assertThat(topDocs.scoreDocs[0].doc, equalTo(6)); assertThat(topDocs.scoreDocs[0].doc, equalTo(6));