blob: 2c97b1aa414f9f7dc7c854b0751158c19fcab7ed [file]
using Lucene.Net.Documents;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace Lucene.Net.Search
{
/*
* 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.
*/
using AttributeSource = Lucene.Net.Util.AttributeSource;
using BytesRef = Lucene.Net.Util.BytesRef;
using FilteredTermsEnum = Lucene.Net.Index.FilteredTermsEnum;
using NumericUtils = Lucene.Net.Util.NumericUtils;
using Terms = Lucene.Net.Index.Terms;
using TermsEnum = Lucene.Net.Index.TermsEnum;
using ToStringUtils = Lucene.Net.Util.ToStringUtils;
/// <summary>
/// <p>A <seealso cref="Query"/> that matches numeric values within a
/// specified range. To use this, you must first index the
/// numeric values using <seealso cref="Int32Field"/>, {@link
/// FloatField}, <seealso cref="Int64Field"/> or <seealso cref="DoubleField"/> (expert: {@link
/// NumericTokenStream}). If your terms are instead textual,
/// you should use <seealso cref="TermRangeQuery"/>. {@link
/// NumericRangeFilter} is the filter equivalent of this
/// query.</p>
///
/// <p>You create a new NumericRangeQuery with the static
/// factory methods, eg:
///
/// <pre class="prettyprint">
/// Query q = NumericRangeQuery.newFloatRange("weight", 0.03f, 0.10f, true, true);
/// </pre>
///
/// matches all documents whose float valued "weight" field
/// ranges from 0.03 to 0.10, inclusive.
///
/// <p>The performance of NumericRangeQuery is much better
/// than the corresponding <seealso cref="TermRangeQuery"/> because the
/// number of terms that must be searched is usually far
/// fewer, thanks to trie indexing, described below.</p>
///
/// <p>You can optionally specify a <a
/// href="#precisionStepDesc"><code>precisionStep</code></a>
/// when creating this query. this is necessary if you've
/// changed this configuration from its default (4) during
/// indexing. Lower values consume more disk space but speed
/// up searching. Suitable values are between <b>1</b> and
/// <b>8</b>. A good starting point to test is <b>4</b>,
/// which is the default value for all <code>Numeric*</code>
/// classes. See <a href="#precisionStepDesc">below</a> for
/// details.
///
/// <p>this query defaults to {@linkplain
/// MultiTermQuery#CONSTANT_SCORE_AUTO_REWRITE_DEFAULT}.
/// With precision steps of &lt;=4, this query can be run with
/// one of the BooleanQuery rewrite methods without changing
/// BooleanQuery's default max clause count.
///
/// <br><h3>How it works</h3>
///
/// <p>See the publication about <a target="_blank" href="http://www.panfmp.org">panFMP</a>,
/// where this algorithm was described (referred to as <code>TrieRangeQuery</code>):
///
/// <blockquote><strong>Schindler, U, Diepenbroek, M</strong>, 2008.
/// <em>Generic XML-based Framework for Metadata Portals.</em>
/// Computers &amp; Geosciences 34 (12), 1947-1955.
/// <a href="http://dx.doi.org/10.1016/j.cageo.2008.02.023"
/// target="_blank">doi:10.1016/j.cageo.2008.02.023</a></blockquote>
///
/// <p><em>A quote from this paper:</em> Because Apache Lucene is a full-text
/// search engine and not a conventional database, it cannot handle numerical ranges
/// (e.g., field value is inside user defined bounds, even dates are numerical values).
/// We have developed an extension to Apache Lucene that stores
/// the numerical values in a special string-encoded format with variable precision
/// (all numerical values like doubles, longs, floats, and ints are converted to
/// lexicographic sortable string representations and stored with different precisions
/// (for a more detailed description of how the values are stored,
/// see <seealso cref="NumericUtils"/>). A range is then divided recursively into multiple intervals for searching:
/// The center of the range is searched only with the lowest possible precision in the <em>trie</em>,
/// while the boundaries are matched more exactly. this reduces the number of terms dramatically.</p>
///
/// <p>For the variant that stores long values in 8 different precisions (each reduced by 8 bits) that
/// uses a lowest precision of 1 byte, the index contains only a maximum of 256 distinct values in the
/// lowest precision. Overall, a range could consist of a theoretical maximum of
/// <code>7*255*2 + 255 = 3825</code> distinct terms (when there is a term for every distinct value of an
/// 8-byte-number in the index and the range covers almost all of them; a maximum of 255 distinct values is used
/// because it would always be possible to reduce the full 256 values to one term with degraded precision).
/// In practice, we have seen up to 300 terms in most cases (index with 500,000 metadata records
/// and a uniform value distribution).</p>
///
/// <a name="precisionStepDesc"><h3>Precision Step</h3>
/// <p>You can choose any <code>precisionStep</code> when encoding values.
/// Lower step values mean more precisions and so more terms in index (and index gets larger). The number
/// of indexed terms per value is (those are generated by <seealso cref="NumericTokenStream"/>):
/// <p style="font-family:serif">
/// &nbsp;&nbsp;indexedTermsPerValue = <b>ceil</b><big>(</big>bitsPerValue / precisionStep<big>)</big>
/// </p>
/// As the lower precision terms are shared by many values, the additional terms only
/// slightly grow the term dictionary (approx. 7% for <code>precisionStep=4</code>), but have a larger
/// impact on the postings (the postings file will have more entries, as every document is linked to
/// <code>indexedTermsPerValue</code> terms instead of one). The formula to estimate the growth
/// of the term dictionary in comparison to one term per value:
/// <p>
/// <!-- the formula in the alt attribute was transformed from latex to PNG with http://1.618034.com/latex.php (with 110 dpi): -->
/// &nbsp;&nbsp;<img src="doc-files/nrq-formula-1.png" alt="\mathrm{termDictOverhead} = \sum\limits_{i=0}^{\mathrm{indexedTermsPerValue}-1} \frac{1}{2^{\mathrm{precisionStep}\cdot i}}" />
/// </p>
/// <p>On the other hand, if the <code>precisionStep</code> is smaller, the maximum number of terms to match reduces,
/// which optimizes query speed. The formula to calculate the maximum number of terms that will be visited while
/// executing the query is:
/// <p>
/// <!-- the formula in the alt attribute was transformed from latex to PNG with http://1.618034.com/latex.php (with 110 dpi): -->
/// &nbsp;&nbsp;<img src="doc-files/nrq-formula-2.png" alt="\mathrm{maxQueryTerms} = \left[ \left( \mathrm{indexedTermsPerValue} - 1 \right) \cdot \left(2^\mathrm{precisionStep} - 1 \right) \cdot 2 \right] + \left( 2^\mathrm{precisionStep} - 1 \right)" />
/// </p>
/// <p>For longs stored using a precision step of 4, <code>maxQueryTerms = 15*15*2 + 15 = 465</code>, and for a precision
/// step of 2, <code>maxQueryTerms = 31*3*2 + 3 = 189</code>. But the faster search speed is reduced by more seeking
/// in the term enum of the index. Because of this, the ideal <code>precisionStep</code> value can only
/// be found out by testing. <b>Important:</b> You can index with a lower precision step value and test search speed
/// using a multiple of the original step value.</p>
///
/// <p>Good values for <code>precisionStep</code> are depending on usage and data type:
/// <ul>
/// <li>The default for all data types is <b>4</b>, which is used, when no <code>precisionStep</code> is given.
/// <li>Ideal value in most cases for <em>64 bit</em> data types <em>(long, double)</em> is <b>6</b> or <b>8</b>.
/// <li>Ideal value in most cases for <em>32 bit</em> data types <em>(int, float)</em> is <b>4</b>.
/// <li>For low cardinality fields larger precision steps are good. If the cardinality is &lt; 100, it is
/// fair to use <seealso cref="int.MaxValue"/> (see below).
/// <li>Steps <b>&gt;=64</b> for <em>long/double</em> and <b>&gt;=32</b> for <em>int/float</em> produces one token
/// per value in the index and querying is as slow as a conventional <seealso cref="TermRangeQuery"/>. But it can be used
/// to produce fields, that are solely used for sorting (in this case simply use <seealso cref="int.MaxValue"/> as
/// <code>precisionStep</code>). Using <seealso cref="Int32Field"/>,
/// <seealso cref="Int64Field"/>, <seealso cref="SingleField"/> or <seealso cref="DoubleField"/> for sorting
/// is ideal, because building the field cache is much faster than with text-only numbers.
/// These fields have one term per value and therefore also work with term enumeration for building distinct lists
/// (e.g. facets / preselected values to search for).
/// Sorting is also possible with range query optimized fields using one of the above <code>precisionSteps</code>.
/// </ul>
///
/// <p>Comparisons of the different types of RangeQueries on an index with about 500,000 docs showed
/// that <seealso cref="TermRangeQuery"/> in boolean rewrite mode (with raised <seealso cref="BooleanQuery"/> clause count)
/// took about 30-40 secs to complete, <seealso cref="TermRangeQuery"/> in constant score filter rewrite mode took 5 secs
/// and executing this class took &lt;100ms to complete (on an Opteron64 machine, Java 1.5, 8 bit
/// precision step). this query type was developed for a geographic portal, where the performance for
/// e.g. bounding boxes or exact date/time stamps is important.</p>
///
/// @since 2.9
///
/// </summary>
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public sealed class NumericRangeQuery<T> : MultiTermQuery
where T : struct, IComparable<T> // best equiv constraint for java's number class
{
internal NumericRangeQuery(string field, int precisionStep, NumericType dataType, T? min, T? max, bool minInclusive, bool maxInclusive)
: base(field)
{
if (precisionStep < 1)
{
throw new System.ArgumentException("precisionStep must be >=1");
}
this.precisionStep = precisionStep;
this.dataType = dataType;
this.min = min;
this.max = max;
this.minInclusive = minInclusive;
this.maxInclusive = maxInclusive;
}
// LUCENENET NOTE: Static methods were moved into the NumericRangeQuery class
protected override TermsEnum GetTermsEnum(Terms terms, AttributeSource atts)
{
// very strange: java.lang.Number itself is not Comparable, but all subclasses used here are
if (min.HasValue && max.HasValue && (min.Value).CompareTo(max.Value) > 0)
{
return TermsEnum.EMPTY;
}
return new NumericRangeTermsEnum(this, terms.GetIterator(null));
}
/// <summary>
/// Returns <code>true</code> if the lower endpoint is inclusive </summary>
public bool IncludesMin
{
get { return minInclusive; }
}
/// <summary>
/// Returns <code>true</code> if the upper endpoint is inclusive </summary>
public bool IncludesMax
{
get { return maxInclusive; }
}
/// <summary>
/// Returns the lower value of this range query </summary>
public T? Min
{
get
{
return min;
}
}
/// <summary>
/// Returns the upper value of this range query </summary>
public T? Max
{
get
{
return max;
}
}
/// <summary>
/// Returns the precision step. </summary>
public int PrecisionStep
{
get
{
return precisionStep;
}
}
public override string ToString(string field)
{
StringBuilder sb = new StringBuilder();
if (!Field.Equals(field, StringComparison.Ordinal))
{
sb.Append(Field).Append(':');
}
return sb.Append(minInclusive ? '[' : '{').Append((min == null) ? "*" : min.ToString()).Append(" TO ").Append((max == null) ? "*" : max.ToString()).Append(maxInclusive ? ']' : '}').Append(ToStringUtils.Boost(Boost)).ToString();
}
public override bool Equals(object o)
{
if (o == this)
{
return true;
}
if (!base.Equals(o))
{
return false;
}
if (o is NumericRangeQuery<T>)
{
var q = (NumericRangeQuery<T>)o;
return ((q.min == null ? min == null : q.min.Equals(min)) && (q.max == null ? max == null : q.max.Equals(max)) && minInclusive == q.minInclusive && maxInclusive == q.maxInclusive && precisionStep == q.precisionStep);
}
return false;
}
public override int GetHashCode()
{
int hash = base.GetHashCode();
hash += precisionStep ^ 0x64365465;
if (min != null)
{
hash += min.GetHashCode() ^ 0x14fa55fb;
}
if (max != null)
{
hash += max.GetHashCode() ^ 0x733fa5fe;
}
return hash + (Convert.ToBoolean(minInclusive).GetHashCode() ^ 0x14fa55fb) + (Convert.ToBoolean(maxInclusive).GetHashCode() ^ 0x733fa5fe);
}
// members (package private, to be also fast accessible by NumericRangeTermEnum)
internal readonly int precisionStep;
internal readonly NumericType dataType;
internal readonly T? min, max;
internal readonly bool minInclusive, maxInclusive;
// used to handle float/double infinity correcty
/// <summary>
/// NOTE: This was LONG_NEGATIVE_INFINITY in Lucene
/// </summary>
internal static readonly long INT64_NEGATIVE_INFINITY = NumericUtils.DoubleToSortableInt64(double.NegativeInfinity);
/// <summary>
/// NOTE: This was LONG_NEGATIVE_INFINITY in Lucene
/// </summary>
internal static readonly long INT64_POSITIVE_INFINITY = NumericUtils.DoubleToSortableInt64(double.PositiveInfinity);
/// <summary>
/// NOTE: This was INT_NEGATIVE_INFINITY in Lucene
/// </summary>
internal static readonly int INT32_NEGATIVE_INFINITY = NumericUtils.SingleToSortableInt32(float.NegativeInfinity);
/// <summary>
/// NOTE: This was INT_POSITIVE_INFINITY in Lucene
/// </summary>
internal static readonly int INT32_POSITIVE_INFINITY = NumericUtils.SingleToSortableInt32(float.PositiveInfinity);
/// <summary>
/// Subclass of FilteredTermsEnum for enumerating all terms that match the
/// sub-ranges for trie range queries, using flex API.
/// <p>
/// WARNING: this term enumeration is not guaranteed to be always ordered by
/// <seealso cref="Term#compareTo"/>.
/// The ordering depends on how <seealso cref="NumericUtils#splitLongRange"/> and
/// <seealso cref="NumericUtils#splitIntRange"/> generates the sub-ranges. For
/// <seealso cref="MultiTermQuery"/> ordering is not relevant.
/// </summary>
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
private sealed class NumericRangeTermsEnum : FilteredTermsEnum
{
private readonly NumericRangeQuery<T> outerInstance;
internal BytesRef currentLowerBound, currentUpperBound;
internal readonly LinkedList<BytesRef> rangeBounds = new LinkedList<BytesRef>();
internal readonly IComparer<BytesRef> termComp;
internal NumericRangeTermsEnum(NumericRangeQuery<T> outerInstance, TermsEnum tenum)
: base(tenum)
{
this.outerInstance = outerInstance;
switch (this.outerInstance.dataType)
{
case NumericType.INT64:
case NumericType.DOUBLE:
{
// lower
long minBound;
if (this.outerInstance.dataType == NumericType.INT64)
{
minBound = (this.outerInstance.min == null) ? long.MinValue : Convert.ToInt64(this.outerInstance.min.Value);
}
else
{
Debug.Assert(this.outerInstance.dataType == NumericType.DOUBLE);
minBound = (this.outerInstance.min == null) ? INT64_NEGATIVE_INFINITY : NumericUtils.DoubleToSortableInt64(Convert.ToDouble(this.outerInstance.min.Value));
}
if (!this.outerInstance.minInclusive && this.outerInstance.min != null)
{
if (minBound == long.MaxValue)
{
break;
}
minBound++;
}
// upper
long maxBound;
if (this.outerInstance.dataType == NumericType.INT64)
{
maxBound = (this.outerInstance.max == null) ? long.MaxValue : Convert.ToInt64(this.outerInstance.max);
}
else
{
Debug.Assert(this.outerInstance.dataType == NumericType.DOUBLE);
maxBound = (this.outerInstance.max == null) ? INT64_POSITIVE_INFINITY : NumericUtils.DoubleToSortableInt64(Convert.ToDouble(this.outerInstance.max));
}
if (!this.outerInstance.maxInclusive && this.outerInstance.max != null)
{
if (maxBound == long.MinValue)
{
break;
}
maxBound--;
}
NumericUtils.SplitInt64Range(new Int64RangeBuilderAnonymousInnerClassHelper(this), this.outerInstance.precisionStep, minBound, maxBound);
break;
}
case NumericType.INT32:
case NumericType.SINGLE:
{
// lower
int minBound;
if (this.outerInstance.dataType == NumericType.INT32)
{
minBound = (this.outerInstance.min == null) ? int.MinValue : Convert.ToInt32(this.outerInstance.min);
}
else
{
Debug.Assert(this.outerInstance.dataType == NumericType.SINGLE);
minBound = (this.outerInstance.min == null) ? INT32_NEGATIVE_INFINITY : NumericUtils.SingleToSortableInt32(Convert.ToSingle(this.outerInstance.min));
}
if (!this.outerInstance.minInclusive && this.outerInstance.min != null)
{
if (minBound == int.MaxValue)
{
break;
}
minBound++;
}
// upper
int maxBound;
if (this.outerInstance.dataType == NumericType.INT32)
{
maxBound = (this.outerInstance.max == null) ? int.MaxValue : Convert.ToInt32(this.outerInstance.max);
}
else
{
Debug.Assert(this.outerInstance.dataType == NumericType.SINGLE);
maxBound = (this.outerInstance.max == null) ? INT32_POSITIVE_INFINITY : NumericUtils.SingleToSortableInt32(Convert.ToSingle(this.outerInstance.max));
}
if (!this.outerInstance.maxInclusive && this.outerInstance.max != null)
{
if (maxBound == int.MinValue)
{
break;
}
maxBound--;
}
NumericUtils.SplitInt32Range(new Int32RangeBuilderAnonymousInnerClassHelper(this), this.outerInstance.precisionStep, minBound, maxBound);
break;
}
default:
// should never happen
throw new System.ArgumentException("Invalid NumericType");
}
termComp = Comparer;
}
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
private class Int64RangeBuilderAnonymousInnerClassHelper : NumericUtils.Int64RangeBuilder
{
private readonly NumericRangeTermsEnum outerInstance;
public Int64RangeBuilderAnonymousInnerClassHelper(NumericRangeTermsEnum outerInstance)
{
this.outerInstance = outerInstance;
}
public override sealed void AddRange(BytesRef minPrefixCoded, BytesRef maxPrefixCoded)
{
outerInstance.rangeBounds.AddLast(minPrefixCoded);
outerInstance.rangeBounds.AddLast(maxPrefixCoded);
}
}
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
private class Int32RangeBuilderAnonymousInnerClassHelper : NumericUtils.Int32RangeBuilder
{
private readonly NumericRangeTermsEnum outerInstance;
public Int32RangeBuilderAnonymousInnerClassHelper(NumericRangeTermsEnum outerInstance)
{
this.outerInstance = outerInstance;
}
public override sealed void AddRange(BytesRef minPrefixCoded, BytesRef maxPrefixCoded)
{
outerInstance.rangeBounds.AddLast(minPrefixCoded);
outerInstance.rangeBounds.AddLast(maxPrefixCoded);
}
}
private void NextRange()
{
Debug.Assert(rangeBounds.Count % 2 == 0);
currentLowerBound = rangeBounds.First.Value;
rangeBounds.Remove(currentLowerBound);
Debug.Assert(currentUpperBound == null || termComp.Compare(currentUpperBound, currentLowerBound) <= 0, "The current upper bound must be <= the new lower bound");
currentUpperBound = rangeBounds.First.Value;
rangeBounds.Remove(currentUpperBound);
}
protected override sealed BytesRef NextSeekTerm(BytesRef term)
{
while (rangeBounds.Count >= 2)
{
NextRange();
// if the new upper bound is before the term parameter, the sub-range is never a hit
if (term != null && termComp.Compare(term, currentUpperBound) > 0)
{
continue;
}
// never seek backwards, so use current term if lower bound is smaller
return (term != null && termComp.Compare(term, currentLowerBound) > 0) ? term : currentLowerBound;
}
// no more sub-range enums available
Debug.Assert(rangeBounds.Count == 0);
currentLowerBound = currentUpperBound = null;
return null;
}
protected override sealed AcceptStatus Accept(BytesRef term)
{
while (currentUpperBound == null || termComp.Compare(term, currentUpperBound) > 0)
{
if (rangeBounds.Count == 0)
{
return AcceptStatus.END;
}
// peek next sub-range, only seek if the current term is smaller than next lower bound
if (termComp.Compare(term, rangeBounds.First.Value) < 0)
{
return AcceptStatus.NO_AND_SEEK;
}
// step forward to next range without seeking, as next lower range bound is less or equal current term
NextRange();
}
return AcceptStatus.YES;
}
}
}
public static class NumericRangeQuery
{
/// <summary>
/// Factory that creates a <code>NumericRangeQuery</code>, that queries a <code>long</code>
/// range using the given <a href="#precisionStepDesc"><code>precisionStep</code></a>.
/// You can have half-open ranges (which are in fact &lt;/&lt;= or &gt;/&gt;= queries)
/// by setting the min or max value to <code>null</code>. By setting inclusive to false, it will
/// match all documents excluding the bounds, with inclusive on, the boundaries are hits, too.
/// <para/>
/// NOTE: This was newLongRange() in Lucene
/// </summary>
public static NumericRangeQuery<long> NewInt64Range(string field, int precisionStep, long? min, long? max, bool minInclusive, bool maxInclusive)
{
return new NumericRangeQuery<long>(field, precisionStep, NumericType.INT64, min, max, minInclusive, maxInclusive);
}
/// <summary>
/// Factory that creates a <code>NumericRangeQuery</code>, that queries a <code>long</code>
/// range using the default <code>precisionStep</code> <seealso cref="NumericUtils#PRECISION_STEP_DEFAULT"/> (4).
/// You can have half-open ranges (which are in fact &lt;/&lt;= or &gt;/&gt;= queries)
/// by setting the min or max value to <code>null</code>. By setting inclusive to false, it will
/// match all documents excluding the bounds, with inclusive on, the boundaries are hits, too.
/// <para/>
/// NOTE: This was newLongRange() in Lucene
/// </summary>
public static NumericRangeQuery<long> NewInt64Range(string field, long? min, long? max, bool minInclusive, bool maxInclusive)
{
return new NumericRangeQuery<long>(field, NumericUtils.PRECISION_STEP_DEFAULT, NumericType.INT64, min, max, minInclusive, maxInclusive);
}
/// <summary>
/// Factory that creates a <code>NumericRangeQuery</code>, that queries a <code>int</code>
/// range using the given <a href="#precisionStepDesc"><code>precisionStep</code></a>.
/// You can have half-open ranges (which are in fact &lt;/&lt;= or &gt;/&gt;= queries)
/// by setting the min or max value to <code>null</code>. By setting inclusive to false, it will
/// match all documents excluding the bounds, with inclusive on, the boundaries are hits, too.
/// <para/>
/// NOTE: This was newIntRange() in Lucene
/// </summary>
public static NumericRangeQuery<int> NewInt32Range(string field, int precisionStep, int? min, int? max, bool minInclusive, bool maxInclusive)
{
return new NumericRangeQuery<int>(field, precisionStep, NumericType.INT32, min, max, minInclusive, maxInclusive);
}
/// <summary>
/// Factory that creates a <code>NumericRangeQuery</code>, that queries a <code>int</code>
/// range using the default <code>precisionStep</code> <seealso cref="NumericUtils#PRECISION_STEP_DEFAULT"/> (4).
/// You can have half-open ranges (which are in fact &lt;/&lt;= or &gt;/&gt;= queries)
/// by setting the min or max value to <code>null</code>. By setting inclusive to false, it will
/// match all documents excluding the bounds, with inclusive on, the boundaries are hits, too.
/// <para/>
/// NOTE: This was newIntRange() in Lucene
/// </summary>
public static NumericRangeQuery<int> NewInt32Range(string field, int? min, int? max, bool minInclusive, bool maxInclusive)
{
return new NumericRangeQuery<int>(field, NumericUtils.PRECISION_STEP_DEFAULT, NumericType.INT32, min, max, minInclusive, maxInclusive);
}
/// <summary>
/// Factory that creates a <code>NumericRangeQuery</code>, that queries a <code>double</code>
/// range using the given <a href="#precisionStepDesc"><code>precisionStep</code></a>.
/// You can have half-open ranges (which are in fact &lt;/&lt;= or &gt;/&gt;= queries)
/// by setting the min or max value to <code>null</code>.
/// <seealso cref="Double#NaN"/> will never match a half-open range, to hit {@code NaN} use a query
/// with {@code min == max == Double.NaN}. By setting inclusive to false, it will
/// match all documents excluding the bounds, with inclusive on, the boundaries are hits, too.
/// </summary>
public static NumericRangeQuery<double> NewDoubleRange(string field, int precisionStep, double? min, double? max, bool minInclusive, bool maxInclusive)
{
return new NumericRangeQuery<double>(field, precisionStep, NumericType.DOUBLE, min, max, minInclusive, maxInclusive);
}
/// <summary>
/// Factory that creates a <code>NumericRangeQuery</code>, that queries a <code>double</code>
/// range using the default <code>precisionStep</code> <seealso cref="NumericUtils#PRECISION_STEP_DEFAULT"/> (4).
/// You can have half-open ranges (which are in fact &lt;/&lt;= or &gt;/&gt;= queries)
/// by setting the min or max value to <code>null</code>.
/// <seealso cref="Double#NaN"/> will never match a half-open range, to hit {@code NaN} use a query
/// with {@code min == max == Double.NaN}. By setting inclusive to false, it will
/// match all documents excluding the bounds, with inclusive on, the boundaries are hits, too.
/// </summary>
public static NumericRangeQuery<double> NewDoubleRange(string field, double? min, double? max, bool minInclusive, bool maxInclusive)
{
return new NumericRangeQuery<double>(field, NumericUtils.PRECISION_STEP_DEFAULT, NumericType.DOUBLE, min, max, minInclusive, maxInclusive);
}
/// <summary>
/// Factory that creates a <code>NumericRangeQuery</code>, that queries a <code>float</code>
/// range using the given <a href="#precisionStepDesc"><code>precisionStep</code></a>.
/// You can have half-open ranges (which are in fact &lt;/&lt;= or &gt;/&gt;= queries)
/// by setting the min or max value to <code>null</code>.
/// <seealso cref="Float#NaN"/> will never match a half-open range, to hit {@code NaN} use a query
/// with {@code min == max == Float.NaN}. By setting inclusive to false, it will
/// match all documents excluding the bounds, with inclusive on, the boundaries are hits, too.
/// <para/>
/// NOTE: This was newFloatRange() in Lucene
/// </summary>
public static NumericRangeQuery<float> NewSingleRange(string field, int precisionStep, float? min, float? max, bool minInclusive, bool maxInclusive)
{
return new NumericRangeQuery<float>(field, precisionStep, NumericType.SINGLE, min, max, minInclusive, maxInclusive);
}
/// <summary>
/// Factory that creates a <code>NumericRangeQuery</code>, that queries a <code>float</code>
/// range using the default <code>precisionStep</code> <seealso cref="NumericUtils#PRECISION_STEP_DEFAULT"/> (4).
/// You can have half-open ranges (which are in fact &lt;/&lt;= or &gt;/&gt;= queries)
/// by setting the min or max value to <code>null</code>.
/// <seealso cref="Float#NaN"/> will never match a half-open range, to hit {@code NaN} use a query
/// with {@code min == max == Float.NaN}. By setting inclusive to false, it will
/// match all documents excluding the bounds, with inclusive on, the boundaries are hits, too.
/// <para/>
/// NOTE: This was newFloatRange() in Lucene
/// </summary>
public static NumericRangeQuery<float> NewSingleRange(string field, float? min, float? max, bool minInclusive, bool maxInclusive)
{
return new NumericRangeQuery<float>(field, NumericUtils.PRECISION_STEP_DEFAULT, NumericType.SINGLE, min, max, minInclusive, maxInclusive);
}
}
}