org.pageseeder.flint.lucene.query.NumericRange Maven / Gradle / Ivy
/*
* Copyright 2015 Allette Systems (Australia)
* http://www.allette.com.au
*
* Licensed 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.pageseeder.flint.lucene.query;
import org.apache.lucene.document.DoublePoint;
import org.apache.lucene.document.FloatPoint;
import org.apache.lucene.document.IntPoint;
import org.apache.lucene.document.LongPoint;
import org.apache.lucene.search.Query;
import org.pageseeder.flint.catalog.Catalog;
import org.pageseeder.flint.catalog.Catalogs;
import org.pageseeder.flint.indexing.FlintField.NumericType;
import org.pageseeder.flint.lucene.util.Beta;
import org.pageseeder.xmlwriter.XMLWriter;
import java.io.IOException;
/**
* Create a range parameter using numeric values.
*
* This class simply reflects a numeric range query.
* This is API is still experimental and subject to change in Lucene, any change in Lucene may also
* be reflected in this API.
*
* @param The number type for this numeric range
*
* @author Christophe Lauret (Weborganic)
* @author Jean-Baptiste Reure (Weborganic)
*
* @version 22 September 2010
*/
@Beta
public final class NumericRange implements SearchParameter {
/**
* The numeric field.
*/
private final String _field;
/**
* The minimum value, if null
, then no lower limit.
*/
private final T _min;
/**
* The maximum value, if null
, then no upper limit.
*/
private final T _max;
/**
* Whether the minimum value should be included; ignored if the minimum value is null
*/
private final boolean _minInclusive;
/**
* Whether the maximum value should be included; ignored if the maximum value is null
*/
private final boolean _maxInclusive;
/**
* The actual Lucene query (lazy initialised)
*/
private volatile Query _query;
// Implementation methods ----------------------------------------------------------------------
/**
* Creates a new numeric range parameter.
*
* @param field the numeric field to search
* @param min the minimum value in the range (can be null
)
* @param max the maximum value in the range (can be null
)
* @param minInclusive true
to include values matching the lower limit in the range;
* false
to exclude it.
* @param maxInclusive true
to include values matching the upper limit in the range;
* false
to exclude it.
*/
private NumericRange(String field, T min, T max, boolean minInclusive, boolean maxInclusive) {
if (field == null) throw new NullPointerException("field");
this._field = field;
this._min = min;
this._max = max;
this._maxInclusive = maxInclusive;
this._minInclusive = minInclusive;
}
/**
* Returns the value of the lower limit of the range.
*
* @return A minimum value or null
.
*/
public T min() {
return this._min;
}
/**
* Returns the value of the upper limit for the range.
*
* @return A maximum value or null
.
*/
public T max() {
return this._max;
}
/**
* Returns the name of the numeric field to search.
*
* @return The name of the numeric field to search.
*/
public String field() {
return this._field;
}
/**
* Indicates whether the lower limit is inclusive.
*
* @return true
to include the lower limit in the range; false
to included it.
*/
public boolean includesMin() {
return this._minInclusive;
}
/**
* Indicates whether the upper limit is inclusive.
*
* @return true
to include the upper limit in the range; false
to included it.
*/
public boolean includesMax() {
return this._maxInclusive;
}
/**
* Returns true
if this search query does not contain any parameter.
*
* @return true
if this search query does not contain any parameter;
* false
otherwise.
*/
@Override
public boolean isEmpty() {
return this._min == null && this._max == null;
}
/**
* Generates the Query
object corresponding to a numeric range search query.
*
* Returns a NumericRangeQuery
based on the values in this object.
*
* @return a NumericRangeQuery
or null
if empty.
*/
@Override
public Query toQuery() {
if (this._min == null && this._max == null) return null;
if (this._query == null) {
this._query = toNumericRangeQuery(this._field, this._min, this._max, this._minInclusive, this._maxInclusive);
}
return this._query;
}
/**
* Serialises the search query as XML.
*
* @param xml The XML writer.
*
* @throws IOException Should there be any I/O exception while writing the XML.
*/
@Override
public void toXML(XMLWriter xml) throws IOException {
xml.openElement("numeric-range", false);
xml.attribute("type", (this._min != null ? this._min.getClass().getName() : this._max.getClass().getName()).replaceFirst("^(.+\\.)", "").toLowerCase());
xml.attribute("field", this._field);
if (this._min != null) {
xml.attribute("min", this._min.toString());
xml.attribute("min-included", Boolean.toString(this._minInclusive));
}
if (this._max != null) {
xml.attribute("max", this._max.toString());
xml.attribute("max-included", Boolean.toString(this._maxInclusive));
}
xml.closeElement();
}
// Private helpers ------------------------------------------------------------------------------
/**
* Returns the numeric range query that corresponds to the specified parameters.
*
* @param field the numeric field
* @param min the lower limit (can be null)
* @param max the upper limit (can be null)
* @param minInclusive true
to include the minimum value in the range; false
to excluded it.
* @param maxInclusive true
to include the maximum value in the range; false
to excluded it.
*
* @return the corresponding NumericRangeQuery
*/
private static Query toNumericRangeQuery(String field, Number min, Number max, boolean minInclusive, boolean maxInclusive) {
// Long
if (min instanceof Long || (min == null && max instanceof Long)) {
long minLong = min == null ? Long.MIN_VALUE : minInclusive ? (Long) min : Math.addExact((Long) min, 1);
long maxLong = max == null ? Long.MAX_VALUE : maxInclusive ? (Long) max : Math.addExact((Long) max, -1);
return LongPoint.newRangeQuery(field, minLong, maxLong);
}
// Integer
if (min instanceof Integer || (min == null && max instanceof Integer)) {
int minLong = min == null ? Integer.MIN_VALUE : minInclusive ? (Integer) min : Math.addExact((Integer) min, 1);
int maxLong = max == null ? Integer.MAX_VALUE : maxInclusive ? (Integer) max : Math.addExact((Integer) max, -1);
return IntPoint.newRangeQuery(field, minLong, maxLong);
}
// Double
if (min instanceof Double || (min == null && max instanceof Double)) {
double minLong = min == null ? Double.MIN_VALUE : minInclusive ? (Double) min : DoublePoint.nextUp((Double) min);
double maxLong = max == null ? Double.MAX_VALUE : maxInclusive ? (Double) max : DoublePoint.nextDown((Double) max);
return DoublePoint.newRangeQuery(field, minLong, maxLong);
}
// Float
if (min instanceof Float || (min == null && max instanceof Float)) {
float minLong = min == null ? Float.MIN_VALUE : minInclusive ? (Float) min : FloatPoint.nextUp((Float) min);
float maxLong = max == null ? Float.MAX_VALUE : maxInclusive ? (Float) max : FloatPoint.nextDown((Float) max);
return FloatPoint.newRangeQuery(field, minLong, maxLong);
}
// Should never happen
return null;
}
// factory methods ------------------------------------------------------------------------------
/**
* Factory that creates a NumericRangeParameter
, that queries a double range using
* the default precisionStep.
*
* @param field the numeric field to search
* @param min the minimum value in the range (can be null
)
* @param max the maximum value in the range (can be null
)
* @param minInclusive true
to include values matching the lower limit in the range;
* false
to exclude it.
* @param maxInclusive true
to include values matching the upper limit in the range;
* false
to exclude it.
*
* @return a new range.
*/
public static NumericRange newDoubleRange(String field, Double min, Double max,
boolean minInclusive, boolean maxInclusive) {
return new NumericRange<>(field, min, max, minInclusive, maxInclusive);
}
/**
* Factory that creates a NumericRangeParameter
, that queries a float range using the
* default precisionStep.
*
* @param field the numeric field to search
* @param min the minimum value in the range (can be null
)
* @param max the maximum value in the range (can be null
)
* @param minInclusive true
to include values matching the lower limit in the range;
* false
to exclude it.
* @param maxInclusive true
to include values matching the upper limit in the range;
* false
to exclude it.
*
* @return a new range.
*/
public static NumericRange newFloatRange(String field, Float min, Float max,
boolean minInclusive, boolean maxInclusive) {
return new NumericRange<>(field, min, max, minInclusive, maxInclusive);
}
/**
* Factory that creates a NumericRangeParameter
, that queries an int range using the
* default precisionStep.
*
* @param field the numeric field to search
* @param min the minimum value in the range (can be null
)
* @param max the maximum value in the range (can be null
)
* @param minInclusive true
to include values matching the lower limit in the range;
* false
to exclude it.
* @param maxInclusive true
to include values matching the upper limit in the range;
* false
to exclude it.
*
* @return a new range.
*/
public static NumericRange newIntRange(String field, Integer min, Integer max,
boolean minInclusive, boolean maxInclusive) {
return new NumericRange<>(field, min, max, minInclusive, maxInclusive);
}
/**
* Factory that creates a NumericRangeParameter
, that queries a long range using the
* default precisionStep.
*
* @param field the numeric field to search
* @param min the minimum value in the range (can be null
)
* @param max the maximum value in the range (can be null
)
* @param minInclusive true
to include values matching the lower limit in the range;
* false
to exclude it.
* @param maxInclusive true
to include values matching the upper limit in the range;
* false
to exclude it.
*
* @return a new range.
*/
public static NumericRange newLongRange(String field, Long min, Long max,
boolean minInclusive, boolean maxInclusive) {
return new NumericRange<>(field, min, max, minInclusive, maxInclusive);
}
/**
* Factory that creates a NumericRangeParameter
, that queries a long range using the
* default precisionStep.
*
* @param field the numeric field to search
* @param min the minimum value in the range (can be null
)
* @param max the maximum value in the range (can be null
)
* @param minInclusive true
to include values matching the lower limit in the range;
* false
to exclude it.
* @param maxInclusive true
to include values matching the upper limit in the range;
* false
to exclude it.
*
* @return a new range.
*/
public static NumericRange> newNumberRange(String field, String catalog, Number min, Number max,
boolean minInclusive, boolean maxInclusive) {
Catalog thecatalog = Catalogs.getCatalog(catalog);
if (thecatalog == null) return null;
NumericType nt = thecatalog.getNumericType(field);
if (nt == null) return null;
switch (nt) {
case DOUBLE: return newDoubleRange(field, (Double) min, (Double) max, minInclusive, maxInclusive);
case FLOAT : return newFloatRange(field, (Float) min, (Float) max, minInclusive, maxInclusive);
case INT : return newIntRange(field, (Integer) min, (Integer) max, minInclusive, maxInclusive);
case LONG : return newLongRange(field, (Long) min, (Long) max, minInclusive, maxInclusive);
}
return null;
}
}