/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.solr.schema;
import org.apache.lucene.document.NumericDocValuesField;
import org.apache.lucene.document.SortedNumericDocValuesField;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.NumericUtils;
import org.apache.solr.common.SolrException;
import org.apache.solr.search.FunctionRangeQuery;
import org.apache.solr.search.QParser;
import org.apache.solr.search.function.ValueSourceRangeFilter;
import org.apache.solr.util.DateMathParser;
public abstract class NumericFieldType extends PrimitiveFieldType {
protected NumberType type;
/**
* @return the type of this field
*/
@Override
public NumberType getNumberType() {
return type;
}
private static long FLOAT_NEGATIVE_INFINITY_BITS = (long)Float.floatToIntBits(Float.NEGATIVE_INFINITY);
private static long DOUBLE_NEGATIVE_INFINITY_BITS = Double.doubleToLongBits(Double.NEGATIVE_INFINITY);
private static long FLOAT_POSITIVE_INFINITY_BITS = (long)Float.floatToIntBits(Float.POSITIVE_INFINITY);
private static long DOUBLE_POSITIVE_INFINITY_BITS = Double.doubleToLongBits(Double.POSITIVE_INFINITY);
private static long FLOAT_MINUS_ZERO_BITS = (long)Float.floatToIntBits(-0f);
private static long DOUBLE_MINUS_ZERO_BITS = Double.doubleToLongBits(-0d);
private static long FLOAT_ZERO_BITS = (long)Float.floatToIntBits(0f);
private static long DOUBLE_ZERO_BITS = Double.doubleToLongBits(0d);
protected Query getDocValuesRangeQuery(QParser parser, SchemaField field, String min, String max,
boolean minInclusive, boolean maxInclusive) {
assert field.hasDocValues() && (field.getType().isPointField() || !field.multiValued());
switch (getNumberType()) {
case INTEGER:
return numericDocValuesRangeQuery(field.getName(),
min == null ? null : (long) Integer.parseInt(min),
max == null ? null : (long) Integer.parseInt(max),
minInclusive, maxInclusive, field.multiValued());
case FLOAT:
if (field.multiValued()) {
return getRangeQueryForMultiValuedFloatDocValues(field, min, max, minInclusive, maxInclusive);
} else {
return getRangeQueryForFloatDoubleDocValues(field, min, max, minInclusive, maxInclusive);
}
case LONG:
return numericDocValuesRangeQuery(field.getName(),
min == null ? null : Long.parseLong(min),
max == null ? null : Long.parseLong(max),
minInclusive, maxInclusive, field.multiValued());
case DOUBLE:
if (field.multiValued()) {
return getRangeQueryForMultiValuedDoubleDocValues(field, min, max, minInclusive, maxInclusive);
} else {
return getRangeQueryForFloatDoubleDocValues(field, min, max, minInclusive, maxInclusive);
}
case DATE:
return numericDocValuesRangeQuery(field.getName(),
min == null ? null : DateMathParser.parseMath(null, min).getTime(),
max == null ? null : DateMathParser.parseMath(null, max).getTime(),
minInclusive, maxInclusive, field.multiValued());
default:
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for numeric field");
}
}
protected Query getRangeQueryForFloatDoubleDocValues(SchemaField sf, String min, String max, boolean minInclusive, boolean maxInclusive) {
Query query;
String fieldName = sf.getName();
Number minVal = min == null ? null : getNumberType() == NumberType.FLOAT ? Float.parseFloat(min): Double.parseDouble(min);
Number maxVal = max == null ? null : getNumberType() == NumberType.FLOAT ? Float.parseFloat(max): Double.parseDouble(max);
Long minBits =
min == null ? null : getNumberType() == NumberType.FLOAT ? (long) Float.floatToIntBits(minVal.floatValue()): Double.doubleToLongBits(minVal.doubleValue());
Long maxBits =
max == null ? null : getNumberType() == NumberType.FLOAT ? (long) Float.floatToIntBits(maxVal.floatValue()): Double.doubleToLongBits(maxVal.doubleValue());
long negativeInfinityBits = getNumberType() == NumberType.FLOAT ? FLOAT_NEGATIVE_INFINITY_BITS : DOUBLE_NEGATIVE_INFINITY_BITS;
long positiveInfinityBits = getNumberType() == NumberType.FLOAT ? FLOAT_POSITIVE_INFINITY_BITS : DOUBLE_POSITIVE_INFINITY_BITS;
long minusZeroBits = getNumberType() == NumberType.FLOAT ? FLOAT_MINUS_ZERO_BITS : DOUBLE_MINUS_ZERO_BITS;
long zeroBits = getNumberType() == NumberType.FLOAT ? FLOAT_ZERO_BITS : DOUBLE_ZERO_BITS;
// If min is negative (or -0d) and max is positive (or +0d), then issue a FunctionRangeQuery
if ((minVal == null || minVal.doubleValue() < 0d || minBits == minusZeroBits) &&
(maxVal == null || (maxVal.doubleValue() > 0d || maxBits == zeroBits))) {
ValueSource vs = getValueSource(sf, null);
query = new FunctionRangeQuery(new ValueSourceRangeFilter(vs, min, max, minInclusive, maxInclusive));
} else { // If both max and min are negative (or -0d), then issue range query with max and min reversed
if ((minVal == null || minVal.doubleValue() < 0d || minBits == minusZeroBits) &&
(maxVal != null && (maxVal.doubleValue() < 0d || maxBits == minusZeroBits))) {
query = numericDocValuesRangeQuery
(fieldName, maxBits, (min == null ? Long.valueOf(negativeInfinityBits) : minBits), maxInclusive, minInclusive, false);
} else { // If both max and min are positive, then issue range query
query = numericDocValuesRangeQuery
(fieldName, minBits, (max == null ? Long.valueOf(positiveInfinityBits) : maxBits), minInclusive, maxInclusive, false);
}
}
return query;
}
protected Query getRangeQueryForMultiValuedDoubleDocValues(SchemaField sf, String min, String max, boolean minInclusive, boolean maxInclusive) {
Long minBits = min == null ? NumericUtils.doubleToSortableLong(Double.NEGATIVE_INFINITY): NumericUtils.doubleToSortableLong(Double.parseDouble(min));
Long maxBits = max == null ? NumericUtils.doubleToSortableLong(Double.POSITIVE_INFINITY): NumericUtils.doubleToSortableLong(Double.parseDouble(max));
return numericDocValuesRangeQuery(sf.getName(), minBits, maxBits, minInclusive, maxInclusive, true);
}
protected Query getRangeQueryForMultiValuedFloatDocValues(SchemaField sf, String min, String max, boolean minInclusive, boolean maxInclusive) {
Long minBits = (long)(min == null ? NumericUtils.floatToSortableInt(Float.NEGATIVE_INFINITY): NumericUtils.floatToSortableInt(Float.parseFloat(min)));
Long maxBits = (long)(max == null ? NumericUtils.floatToSortableInt(Float.POSITIVE_INFINITY): NumericUtils.floatToSortableInt(Float.parseFloat(max)));
return numericDocValuesRangeQuery(sf.getName(), minBits, maxBits, minInclusive, maxInclusive, true);
}
public static Query numericDocValuesRangeQuery(
String field,
Number lowerValue, Number upperValue,
boolean lowerInclusive, boolean upperInclusive,
boolean multiValued) {
long actualLowerValue = Long.MIN_VALUE;
if (lowerValue != null) {
actualLowerValue = lowerValue.longValue();
if (lowerInclusive == false) {
if (actualLowerValue == Long.MAX_VALUE) {
return new MatchNoDocsQuery();
}
++actualLowerValue;
}
}
long actualUpperValue = Long.MAX_VALUE;
if (upperValue != null) {
actualUpperValue = upperValue.longValue();
if (upperInclusive == false) {
if (actualUpperValue == Long.MIN_VALUE) {
return new MatchNoDocsQuery();
}
--actualUpperValue;
}
}
if (multiValued) {
// In multiValued case use SortedNumericDocValuesField, this won't work for Trie*Fields wince they use BinaryDV in the multiValue case
return SortedNumericDocValuesField.newRangeQuery(field, actualLowerValue, actualUpperValue);
} else {
return NumericDocValuesField.newRangeQuery(field, actualLowerValue, actualUpperValue);
}
}
}