Sort: Support "missing" specific handling, include _last, _first, and custom value (for numeric values), closes #772.
This commit is contained in:
parent
7c04ef6cbc
commit
700a2a9577
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue