Sort: Support "missing" specific handling, include _last, _first, and custom value (for numeric values), closes #772.

This commit is contained in:
kimchy 2011-03-13 02:29:05 +02:00
parent 7c04ef6cbc
commit 700a2a9577
22 changed files with 775 additions and 20 deletions

View File

@ -21,6 +21,7 @@ package org.elasticsearch.index.field.data;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.FieldComparatorSource;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.index.cache.field.data.FieldDataCache;
import org.elasticsearch.index.field.data.bytes.ByteFieldDataType;
import org.elasticsearch.index.field.data.doubles.DoubleFieldDataType;
@ -47,7 +48,7 @@ public interface FieldDataType<T extends FieldData> {
public static final DoubleFieldDataType DOUBLE = new DoubleFieldDataType();
}
FieldComparatorSource newFieldComparatorSource(FieldDataCache cache);
FieldComparatorSource newFieldComparatorSource(FieldDataCache cache, @Nullable String missing);
T load(IndexReader reader, String fieldName) throws IOException;
}

View File

@ -0,0 +1,73 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.field.data.bytes;
import org.elasticsearch.index.cache.field.data.FieldDataCache;
import org.elasticsearch.index.field.data.FieldDataType;
import org.elasticsearch.index.field.data.support.NumericFieldDataComparator;
/**
* @author kimchy (shay.banon)
*/
// LUCENE MONITOR: Monitor against FieldComparator.Short
public class ByteFieldDataMissingComparator extends NumericFieldDataComparator {
private final byte[] values;
private short bottom;
private final byte missingValue;
public ByteFieldDataMissingComparator(int numHits, String fieldName, FieldDataCache fieldDataCache, byte missingValue) {
super(fieldName, fieldDataCache);
values = new byte[numHits];
this.missingValue = missingValue;
}
@Override public FieldDataType fieldDataType() {
return FieldDataType.DefaultTypes.BYTE;
}
@Override public int compare(int slot1, int slot2) {
return values[slot1] - values[slot2];
}
@Override public int compareBottom(int doc) {
byte value = missingValue;
if (currentFieldData.hasValue(doc)) {
value = currentFieldData.byteValue(doc);
}
return bottom - value;
}
@Override public void copy(int slot, int doc) {
byte value = missingValue;
if (currentFieldData.hasValue(doc)) {
value = currentFieldData.byteValue(doc);
}
values[slot] = value;
}
@Override public void setBottom(final int bottom) {
this.bottom = values[bottom];
}
@Override public Comparable value(int slot) {
return Byte.valueOf(values[slot]);
}
}

View File

@ -32,10 +32,31 @@ import java.io.IOException;
*/
public class ByteFieldDataType implements FieldDataType<ByteFieldData> {
@Override public FieldComparatorSource newFieldComparatorSource(final FieldDataCache cache) {
@Override public FieldComparatorSource newFieldComparatorSource(final FieldDataCache cache, final String missing) {
if (missing == null) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new ByteFieldDataComparator(numHits, fieldname, cache);
}
};
}
if (missing.equals("_last")) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new ByteFieldDataMissingComparator(numHits, fieldname, cache, reversed ? Byte.MIN_VALUE : Byte.MAX_VALUE);
}
};
}
if (missing.equals("_first")) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new ByteFieldDataMissingComparator(numHits, fieldname, cache, reversed ? Byte.MAX_VALUE : Byte.MIN_VALUE);
}
};
}
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new ByteFieldDataComparator(numHits, fieldname, cache);
return new ByteFieldDataMissingComparator(numHits, fieldname, cache, Byte.parseByte(missing));
}
};
}

View File

@ -0,0 +1,87 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.field.data.doubles;
import org.elasticsearch.index.cache.field.data.FieldDataCache;
import org.elasticsearch.index.field.data.FieldDataType;
import org.elasticsearch.index.field.data.support.NumericFieldDataComparator;
/**
* @author kimchy (shay.banon)
*/
// LUCENE MONITOR: Monitor against FieldComparator.Double
public class DoubleFieldDataMissingComparator extends NumericFieldDataComparator {
private final double[] values;
private double bottom;
private final double missingValue;
public DoubleFieldDataMissingComparator(int numHits, String fieldName, FieldDataCache fieldDataCache, double missingValue) {
super(fieldName, fieldDataCache);
values = new double[numHits];
this.missingValue = missingValue;
}
@Override public FieldDataType fieldDataType() {
return FieldDataType.DefaultTypes.DOUBLE;
}
@Override public int compare(int slot1, int slot2) {
final double v1 = values[slot1];
final double v2 = values[slot2];
if (v1 > v2) {
return 1;
} else if (v1 < v2) {
return -1;
} else {
return 0;
}
}
@Override public int compareBottom(int doc) {
double v2 = missingValue;
if (currentFieldData.hasValue(doc)) {
v2 = currentFieldData.doubleValue(doc);
}
if (bottom > v2) {
return 1;
} else if (bottom < v2) {
return -1;
} else {
return 0;
}
}
@Override public void copy(int slot, int doc) {
double value = missingValue;
if (currentFieldData.hasValue(doc)) {
value = currentFieldData.doubleValue(doc);
}
values[slot] = value;
}
@Override public void setBottom(final int bottom) {
this.bottom = values[bottom];
}
@Override public Comparable value(int slot) {
return Double.valueOf(values[slot]);
}
}

View File

@ -32,10 +32,31 @@ import java.io.IOException;
*/
public class DoubleFieldDataType implements FieldDataType<DoubleFieldData> {
@Override public FieldComparatorSource newFieldComparatorSource(final FieldDataCache cache) {
@Override public FieldComparatorSource newFieldComparatorSource(final FieldDataCache cache, final String missing) {
if (missing == null) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new DoubleFieldDataComparator(numHits, fieldname, cache);
}
};
}
if (missing.equals("_last")) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new DoubleFieldDataMissingComparator(numHits, fieldname, cache, reversed ? Double.MIN_VALUE : Double.MAX_VALUE);
}
};
}
if (missing.equals("_first")) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new DoubleFieldDataMissingComparator(numHits, fieldname, cache, reversed ? Double.MAX_VALUE : Double.MIN_VALUE);
}
};
}
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new DoubleFieldDataComparator(numHits, fieldname, cache);
return new DoubleFieldDataMissingComparator(numHits, fieldname, cache, Double.parseDouble(missing));
}
};
}

View File

@ -0,0 +1,92 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.field.data.floats;
import org.elasticsearch.index.cache.field.data.FieldDataCache;
import org.elasticsearch.index.field.data.FieldDataType;
import org.elasticsearch.index.field.data.support.NumericFieldDataComparator;
/**
* @author kimchy (shay.banon)
*/
// LUCENE MONITOR - Monitor against FieldComparator.Float
public class FloatFieldDataMissingComparator extends NumericFieldDataComparator {
private final float[] values;
private float bottom;
private final float missingValue;
public FloatFieldDataMissingComparator(int numHits, String fieldName, FieldDataCache fieldDataCache, float missingValue) {
super(fieldName, fieldDataCache);
values = new float[numHits];
this.missingValue = missingValue;
}
@Override public FieldDataType fieldDataType() {
return FieldDataType.DefaultTypes.FLOAT;
}
@Override public int compare(int slot1, int slot2) {
// TODO: are there sneaky non-branch ways to compute
// sign of float?
final float v1 = values[slot1];
final float v2 = values[slot2];
if (v1 > v2) {
return 1;
} else if (v1 < v2) {
return -1;
} else {
return 0;
}
}
@Override public int compareBottom(int doc) {
// TODO: are there sneaky non-branch ways to compute
// sign of float?
float v2 = missingValue;
if (currentFieldData.hasValue(doc)) {
v2 = currentFieldData.floatValue(doc);
}
if (bottom > v2) {
return 1;
} else if (bottom < v2) {
return -1;
} else {
return 0;
}
}
@Override
public void copy(int slot, int doc) {
float value = missingValue;
if (currentFieldData.hasValue(doc)) {
value = currentFieldData.floatValue(doc);
}
values[slot] = value;
}
@Override public void setBottom(final int bottom) {
this.bottom = values[bottom];
}
@Override public Comparable value(int slot) {
return Float.valueOf(values[slot]);
}
}

View File

@ -32,10 +32,31 @@ import java.io.IOException;
*/
public class FloatFieldDataType implements FieldDataType<FloatFieldData> {
@Override public FieldComparatorSource newFieldComparatorSource(final FieldDataCache cache) {
@Override public FieldComparatorSource newFieldComparatorSource(final FieldDataCache cache, final String missing) {
if (missing == null) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new FloatFieldDataComparator(numHits, fieldname, cache);
}
};
}
if (missing.equals("_last")) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new FloatFieldDataMissingComparator(numHits, fieldname, cache, reversed ? Float.MIN_VALUE : Float.MAX_VALUE);
}
};
}
if (missing.equals("_first")) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new FloatFieldDataMissingComparator(numHits, fieldname, cache, reversed ? Float.MAX_VALUE : Float.MIN_VALUE);
}
};
}
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new FloatFieldDataComparator(numHits, fieldname, cache);
return new FloatFieldDataMissingComparator(numHits, fieldname, cache, Float.parseFloat(missing));
}
};
}

View File

@ -0,0 +1,97 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.field.data.ints;
import org.elasticsearch.index.cache.field.data.FieldDataCache;
import org.elasticsearch.index.field.data.FieldDataType;
import org.elasticsearch.index.field.data.support.NumericFieldDataComparator;
/**
* @author kimchy (shay.banon)
*/
// LUCENE MONITOR - Monitor against FieldComparator.Int
public class IntFieldDataMissingComparator extends NumericFieldDataComparator {
private final int[] values;
private int bottom; // Value of bottom of queue
private final int missingValue;
public IntFieldDataMissingComparator(int numHits, String fieldName, FieldDataCache fieldDataCache, int missingValue) {
super(fieldName, fieldDataCache);
values = new int[numHits];
this.missingValue = missingValue;
}
@Override public FieldDataType fieldDataType() {
return FieldDataType.DefaultTypes.INT;
}
@Override public int compare(int slot1, int slot2) {
// TODO: there are sneaky non-branch ways to compute
// -1/+1/0 sign
// Cannot return values[slot1] - values[slot2] because that
// may overflow
final int v1 = values[slot1];
final int v2 = values[slot2];
if (v1 > v2) {
return 1;
} else if (v1 < v2) {
return -1;
} else {
return 0;
}
}
@Override public int compareBottom(int doc) {
// TODO: there are sneaky non-branch ways to compute
// -1/+1/0 sign
// Cannot return bottom - values[slot2] because that
// may overflow
// final int v2 = currentReaderValues[doc];
int v2 = missingValue;
if (currentFieldData.hasValue(doc)) {
v2 = currentFieldData.intValue(doc);
}
if (bottom > v2) {
return 1;
} else if (bottom < v2) {
return -1;
} else {
return 0;
}
}
@Override public void copy(int slot, int doc) {
int value = missingValue;
if (currentFieldData.hasValue(doc)) {
value = currentFieldData.intValue(doc);
}
values[slot] = value;
}
@Override public void setBottom(final int bottom) {
this.bottom = values[bottom];
}
@Override public Comparable value(int slot) {
return Integer.valueOf(values[slot]);
}
}

View File

@ -32,10 +32,31 @@ import java.io.IOException;
*/
public class IntFieldDataType implements FieldDataType<IntFieldData> {
@Override public FieldComparatorSource newFieldComparatorSource(final FieldDataCache cache) {
@Override public FieldComparatorSource newFieldComparatorSource(final FieldDataCache cache, final String missing) {
if (missing == null) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new IntFieldDataComparator(numHits, fieldname, cache);
}
};
}
if (missing.equals("_last")) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new IntFieldDataMissingComparator(numHits, fieldname, cache, reversed ? Integer.MIN_VALUE : Integer.MAX_VALUE);
}
};
}
if (missing.equals("_first")) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new IntFieldDataMissingComparator(numHits, fieldname, cache, reversed ? Integer.MAX_VALUE : Integer.MIN_VALUE);
}
};
}
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new IntFieldDataComparator(numHits, fieldname, cache);
return new IntFieldDataMissingComparator(numHits, fieldname, cache, Integer.parseInt(missing));
}
};
}

View File

@ -0,0 +1,95 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.field.data.longs;
import org.elasticsearch.index.cache.field.data.FieldDataCache;
import org.elasticsearch.index.field.data.FieldDataType;
import org.elasticsearch.index.field.data.support.NumericFieldDataComparator;
/**
* @author kimchy (shay.banon)
*/
// LUCENE MONITOR - Monitor against FieldComparator.Long
public class LongFieldDataMissingComparator extends NumericFieldDataComparator {
private final long[] values;
private long bottom;
private final long missingValue;
public LongFieldDataMissingComparator(int numHits, String fieldName, FieldDataCache fieldDataCache, long missingValue) {
super(fieldName, fieldDataCache);
values = new long[numHits];
this.missingValue = missingValue;
}
@Override public FieldDataType fieldDataType() {
return FieldDataType.DefaultTypes.LONG;
}
@Override public int compare(int slot1, int slot2) {
// TODO: there are sneaky non-branch ways to compute
// -1/+1/0 sign
final long v1 = values[slot1];
final long v2 = values[slot2];
if (v1 > v2) {
return 1;
} else if (v1 < v2) {
return -1;
} else {
return 0;
}
}
@Override
public int compareBottom(int doc) {
// TODO: there are sneaky non-branch ways to compute
// -1/+1/0 sign
// final long v2 = currentReaderValues[doc];
long v2 = missingValue;
if (currentFieldData.hasValue(doc)) {
v2 = currentFieldData.longValue(doc);
}
if (bottom > v2) {
return 1;
} else if (bottom < v2) {
return -1;
} else {
return 0;
}
}
@Override
public void copy(int slot, int doc) {
long value = missingValue;
if (currentFieldData.hasValue(doc)) {
value = currentFieldData.longValue(doc);
}
values[slot] = value;
}
@Override public void setBottom(final int bottom) {
this.bottom = values[bottom];
}
@Override public Comparable value(int slot) {
return Long.valueOf(values[slot]);
}
}

View File

@ -32,10 +32,31 @@ import java.io.IOException;
*/
public class LongFieldDataType implements FieldDataType<LongFieldData> {
@Override public FieldComparatorSource newFieldComparatorSource(final FieldDataCache cache) {
@Override public FieldComparatorSource newFieldComparatorSource(final FieldDataCache cache, final String missing) {
if (missing == null) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new LongFieldDataComparator(numHits, fieldname, cache);
}
};
}
if (missing.equals("_last")) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new LongFieldDataMissingComparator(numHits, fieldname, cache, reversed ? Long.MIN_VALUE : Long.MAX_VALUE);
}
};
}
if (missing.equals("_first")) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new LongFieldDataMissingComparator(numHits, fieldname, cache, reversed ? Long.MAX_VALUE : Long.MIN_VALUE);
}
};
}
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new LongFieldDataComparator(numHits, fieldname, cache);
return new LongFieldDataMissingComparator(numHits, fieldname, cache, Long.parseLong(missing));
}
};
}

View File

@ -0,0 +1,73 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.field.data.shorts;
import org.elasticsearch.index.cache.field.data.FieldDataCache;
import org.elasticsearch.index.field.data.FieldDataType;
import org.elasticsearch.index.field.data.support.NumericFieldDataComparator;
/**
* @author kimchy (shay.banon)
*/
// LUCENE MONITOR: Monitor against FieldComparator.Short
public class ShortFieldDataMissingComparator extends NumericFieldDataComparator {
private final short[] values;
private short bottom;
private final short missingValue;
public ShortFieldDataMissingComparator(int numHits, String fieldName, FieldDataCache fieldDataCache, short missingValue) {
super(fieldName, fieldDataCache);
values = new short[numHits];
this.missingValue = missingValue;
}
@Override public FieldDataType fieldDataType() {
return FieldDataType.DefaultTypes.SHORT;
}
@Override public int compare(int slot1, int slot2) {
return values[slot1] - values[slot2];
}
@Override public int compareBottom(int doc) {
short value = missingValue;
if (currentFieldData.hasValue(doc)) {
value = currentFieldData.shortValue(doc);
}
return bottom - value;
}
@Override public void copy(int slot, int doc) {
short value = missingValue;
if (currentFieldData.hasValue(doc)) {
value = currentFieldData.shortValue(doc);
}
values[slot] = value;
}
@Override public void setBottom(final int bottom) {
this.bottom = values[bottom];
}
@Override public Comparable value(int slot) {
return Short.valueOf(values[slot]);
}
}

View File

@ -32,10 +32,31 @@ import java.io.IOException;
*/
public class ShortFieldDataType implements FieldDataType<ShortFieldData> {
@Override public FieldComparatorSource newFieldComparatorSource(final FieldDataCache cache) {
@Override public FieldComparatorSource newFieldComparatorSource(final FieldDataCache cache, final String missing) {
if (missing == null) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new ShortFieldDataComparator(numHits, fieldname, cache);
}
};
}
if (missing.equals("_last")) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new ShortFieldDataMissingComparator(numHits, fieldname, cache, reversed ? Short.MIN_VALUE : Short.MAX_VALUE);
}
};
}
if (missing.equals("_first")) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new ShortFieldDataMissingComparator(numHits, fieldname, cache, reversed ? Short.MAX_VALUE : Short.MIN_VALUE);
}
};
}
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new ShortFieldDataComparator(numHits, fieldname, cache);
return new ShortFieldDataMissingComparator(numHits, fieldname, cache, Short.parseShort(missing));
}
};
}

View File

@ -22,6 +22,7 @@ package org.elasticsearch.index.field.data.strings;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.FieldComparator;
import org.apache.lucene.search.FieldComparatorSource;
import org.elasticsearch.ElasticSearchIllegalArgumentException;
import org.elasticsearch.index.cache.field.data.FieldDataCache;
import org.elasticsearch.index.field.data.FieldDataType;
@ -32,7 +33,10 @@ import java.io.IOException;
*/
public class StringFieldDataType implements FieldDataType<StringFieldData> {
@Override public FieldComparatorSource newFieldComparatorSource(final FieldDataCache cache) {
@Override public FieldComparatorSource newFieldComparatorSource(final FieldDataCache cache, final String missing) {
if (missing != null) {
throw new ElasticSearchIllegalArgumentException("Sorting on string type field does not support missing parameter");
}
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new StringOrdValFieldDataComparator(numHits, fieldname, sortPos, reversed, cache);

View File

@ -35,7 +35,7 @@ public class GeoPointFieldDataType implements FieldDataType<GeoPointFieldData> {
public static final GeoPointFieldDataType TYPE = new GeoPointFieldDataType();
@Override public FieldComparatorSource newFieldComparatorSource(final FieldDataCache cache) {
@Override public FieldComparatorSource newFieldComparatorSource(final FieldDataCache cache, final String missing) {
return new FieldComparatorSource() {
@Override public FieldComparator newComparator(String fieldname, int numHits, int sortPos, boolean reversed) throws IOException {
return new StringOrdValFieldDataComparator(numHits, fieldname, sortPos, reversed, cache);

View File

@ -34,6 +34,8 @@ public class FieldSortBuilder extends SortBuilder {
private SortOrder order;
private Object missing;
/**
* Constructs a new sort based on a document field.
*
@ -51,11 +53,23 @@ public class FieldSortBuilder extends SortBuilder {
return this;
}
/**
* Sets the value when a field is missing in a doc. Can also be set to <tt>_last</tt> or
* <tt>_first</tt> to sort missing last or first respectively.
*/
public FieldSortBuilder missing(Object missing) {
this.missing = missing;
return this;
}
@Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(fieldName);
if (order == SortOrder.DESC) {
builder.field("reverse", true);
}
if (missing != null) {
builder.field("missing", missing);
}
builder.endObject();
return builder;
}

View File

@ -95,6 +95,13 @@ public class GeoDistanceSortBuilder extends SortBuilder {
return this;
}
/**
* Not relevant.
*/
@Override public SortBuilder missing(Object missing) {
return this;
}
@Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject("_geo_distance");

View File

@ -40,6 +40,10 @@ public class ScoreSortBuilder extends SortBuilder {
return this;
}
@Override public SortBuilder missing(Object missing) {
return this;
}
@Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject("_score");
if (order == SortOrder.ASC) {

View File

@ -75,6 +75,13 @@ public class ScriptSortBuilder extends SortBuilder {
return this;
}
/**
* Not really relevant.
*/
@Override public SortBuilder missing(Object missing) {
return this;
}
/**
* The language of the script.
*/

View File

@ -26,5 +26,14 @@ import org.elasticsearch.common.xcontent.ToXContent;
*/
public abstract class SortBuilder implements ToXContent {
/**
* The order of sorting. Defaults to {@link SortOrder#ASC}.
*/
public abstract SortBuilder order(SortOrder order);
/**
* Sets the value when a field is missing in a doc. Can also be set to <tt>_last</tt> or
* <tt>_first</tt> to sort missing last or first respectively.
*/
public abstract SortBuilder missing(Object missing);
}

View File

@ -21,6 +21,7 @@ package org.elasticsearch.search.sort;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.collect.Lists;
import org.elasticsearch.common.xcontent.XContentParser;
@ -67,7 +68,7 @@ public class SortParseElement implements SearchParseElement {
if (token == XContentParser.Token.START_OBJECT) {
addCompoundSortField(parser, context, sortFields);
} else if (token == XContentParser.Token.VALUE_STRING) {
addSortField(context, sortFields, parser.text(), false);
addSortField(context, sortFields, parser.text(), false, null);
}
}
} else {
@ -84,6 +85,7 @@ public class SortParseElement implements SearchParseElement {
if (token == XContentParser.Token.FIELD_NAME) {
String fieldName = parser.currentName();
boolean reverse = false;
String missing = null;
String innerJsonName = null;
token = parser.nextToken();
if (token == XContentParser.Token.VALUE_STRING) {
@ -93,7 +95,7 @@ public class SortParseElement implements SearchParseElement {
} else if (direction.equals("desc")) {
reverse = !SCORE_FIELD_NAME.equals(fieldName);
}
addSortField(context, sortFields, fieldName, reverse);
addSortField(context, sortFields, fieldName, reverse, missing);
} else {
if (parsers.containsKey(fieldName)) {
sortFields.add(parsers.get(fieldName).parse(parser, context));
@ -110,17 +112,19 @@ public class SortParseElement implements SearchParseElement {
} else if ("desc".equals(parser.text())) {
reverse = !SCORE_FIELD_NAME.equals(fieldName);
}
} else if ("missing".equals(innerJsonName)) {
missing = parser.textOrNull();
}
}
}
addSortField(context, sortFields, fieldName, reverse);
addSortField(context, sortFields, fieldName, reverse, missing);
}
}
}
}
}
private void addSortField(SearchContext context, List<SortField> sortFields, String fieldName, boolean reverse) {
private void addSortField(SearchContext context, List<SortField> sortFields, String fieldName, boolean reverse, @Nullable final String missing) {
if (SCORE_FIELD_NAME.equals(fieldName)) {
if (reverse) {
sortFields.add(SORT_SCORE_REVERSE);
@ -138,7 +142,7 @@ public class SortParseElement implements SearchParseElement {
if (fieldMapper == null) {
throw new SearchParseException(context, "No mapping found for [" + fieldName + "]");
}
sortFields.add(new SortField(fieldName, fieldMapper.fieldDataType().newFieldComparatorSource(context.fieldDataCache()), reverse));
sortFields.add(new SortField(fieldName, fieldMapper.fieldDataType().newFieldComparatorSource(context.fieldDataCache(), missing), reverse));
}
}
}

View File

@ -26,6 +26,7 @@ import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.test.integration.AbstractNodesTests;
import org.testng.annotations.AfterClass;
@ -438,4 +439,65 @@ public class SimpleSortTests extends AbstractNodesTests {
assertThat(searchResponse.hits().getTotalHits(), equalTo(1l));
assertThat((String) searchResponse.hits().getAt(0).field("id").value(), equalTo("2"));
}
@Test public void testSortMissing() throws Exception {
try {
client.admin().indices().prepareDelete("test").execute().actionGet();
} catch (Exception e) {
// ignore
}
client.admin().indices().prepareCreate("test").execute().actionGet();
client.admin().cluster().prepareHealth().setWaitForGreenStatus().execute().actionGet();
client.prepareIndex("test", "type1", "1").setSource(jsonBuilder().startObject()
.field("id", "1")
.field("i_value", -1)
.field("d_value", -1.1)
.endObject()).execute().actionGet();
client.prepareIndex("test", "type1", "2").setSource(jsonBuilder().startObject()
.field("id", "2")
.endObject()).execute().actionGet();
client.prepareIndex("test", "type1", "3").setSource(jsonBuilder().startObject()
.field("id", "1")
.field("i_value", 2)
.field("d_value", 2.2)
.endObject()).execute().actionGet();
client.admin().indices().prepareFlush().setRefresh(true).execute().actionGet();
logger.info("--> sort with no missing");
SearchResponse searchResponse = client.prepareSearch()
.setQuery(matchAllQuery())
.addSort(SortBuilders.fieldSort("i_value").order(SortOrder.ASC))
.execute().actionGet();
assertThat(searchResponse.hits().getTotalHits(), equalTo(3l));
assertThat(searchResponse.hits().getAt(0).id(), equalTo("1"));
assertThat(searchResponse.hits().getAt(1).id(), equalTo("2"));
assertThat(searchResponse.hits().getAt(2).id(), equalTo("3"));
logger.info("--> sort with missing _last");
searchResponse = client.prepareSearch()
.setQuery(matchAllQuery())
.addSort(SortBuilders.fieldSort("i_value").order(SortOrder.ASC).missing("_last"))
.execute().actionGet();
assertThat(searchResponse.hits().getTotalHits(), equalTo(3l));
assertThat(searchResponse.hits().getAt(0).id(), equalTo("1"));
assertThat(searchResponse.hits().getAt(1).id(), equalTo("3"));
assertThat(searchResponse.hits().getAt(2).id(), equalTo("2"));
logger.info("--> sort with missing _first");
searchResponse = client.prepareSearch()
.setQuery(matchAllQuery())
.addSort(SortBuilders.fieldSort("i_value").order(SortOrder.ASC).missing("_first"))
.execute().actionGet();
assertThat(searchResponse.hits().getTotalHits(), equalTo(3l));
assertThat(searchResponse.hits().getAt(0).id(), equalTo("2"));
assertThat(searchResponse.hits().getAt(1).id(), equalTo("1"));
assertThat(searchResponse.hits().getAt(2).id(), equalTo("3"));
}
}