blob: ebcf8cf2754aafbe9629616ece108bf283596bab [file] [log] [blame]
using Lucene.Net.Diagnostics;
using Lucene.Net.Documents;
using System;
using System.Collections.Generic;
using System.Globalization;
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>
/// <para>A <see cref="Query"/> that matches numeric values within a
/// specified range. To use this, you must first index the
/// numeric values using <see cref="Int32Field"/>,
/// <see cref="SingleField"/>, <see cref="Int64Field"/> or <see cref="DoubleField"/> (expert:
/// <see cref="Analysis.NumericTokenStream"/>). If your terms are instead textual,
/// you should use <see cref="TermRangeQuery"/>.
/// <see cref="NumericRangeFilter"/> is the filter equivalent of this
/// query.</para>
///
/// <para>You create a new <see cref="NumericRangeQuery{T}"/> with the static
/// factory methods, eg:
///
/// <code>
/// Query q = NumericRangeQuery.NewFloatRange("weight", 0.03f, 0.10f, true, true);
/// </code>
///
/// matches all documents whose <see cref="float"/> valued "weight" field
/// ranges from 0.03 to 0.10, inclusive.</para>
///
/// <para>The performance of <see cref="NumericRangeQuery{T}"/> is much better
/// than the corresponding <see cref="TermRangeQuery"/> because the
/// number of terms that must be searched is usually far
/// fewer, thanks to trie indexing, described below.</para>
///
/// <para>You can optionally specify a <a
/// href="#precisionStepDesc"><see cref="precisionStep"/></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 <c>Numeric*</c>
/// classes. See <a href="#precisionStepDesc">below</a> for
/// details.</para>
///
/// <para>This query defaults to
/// <see cref="MultiTermQuery.CONSTANT_SCORE_AUTO_REWRITE_DEFAULT"/>.
/// With precision steps of &lt;=4, this query can be run with
/// one of the <see cref="BooleanQuery"/> rewrite methods without changing
/// <see cref="BooleanQuery"/>'s default max clause count.</para>
///
/// <para/><h3>How it works</h3>
///
/// <para>See the publication about <a target="_blank" href="http://www.panfmp.org">panFMP</a>,
/// where this algorithm was described (referred to as <c>TrieRangeQuery</c>):
/// </para>
/// <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>
///
/// <para><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 <see cref="double"/>s, <see cref="long"/>s, <see cref="float"/>s, and <see cref="int"/>s are converted to
/// lexicographic sortable string representations and stored with different precisions
/// (for a more detailed description of how the values are stored,
/// see <see 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.</para>
///
/// <para>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).</para>
///
/// <a name="precisionStepDesc"><h3>Precision Step</h3></a>
/// <para/>You can choose any <see cref="precisionStep"/> 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 <see cref="Analysis.NumericTokenStream"/>):
/// <para>
/// &#160;&#160;indexedTermsPerValue = <b>ceil</b><big>(</big>bitsPerValue / precisionStep<big>)</big>
/// </para>
/// As the lower precision terms are shared by many values, the additional terms only
/// slightly grow the term dictionary (approx. 7% for <c>precisionStep=4</c>), but have a larger
/// impact on the postings (the postings file will have more entries, as every document is linked to
/// <c>indexedTermsPerValue</c> terms instead of one). The formula to estimate the growth
/// of the term dictionary in comparison to one term per value:
/// <para>
/// <!-- the formula in the alt attribute was transformed from latex to PNG with http://1.618034.com/latex.php (with 110 dpi): -->
/// &#160;&#160;<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}}" />
/// </para>
/// <para>On the other hand, if the <see cref="precisionStep"/> 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:
/// </para>
/// <para>
/// <!-- the formula in the alt attribute was transformed from latex to PNG with http://1.618034.com/latex.php (with 110 dpi): -->
/// &#160;&#160;<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)" />
/// </para>
/// <para>For longs stored using a precision step of 4, <c>maxQueryTerms = 15*15*2 + 15 = 465</c>, and for a precision
/// step of 2, <c>maxQueryTerms = 31*3*2 + 3 = 189</c>. But the faster search speed is reduced by more seeking
/// in the term enum of the index. Because of this, the ideal <see cref="precisionStep"/> 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.</para>
///
/// <para>Good values for <see cref="precisionStep"/> are depending on usage and data type:</para>
/// <list type="bullet">
/// <item><description>The default for all data types is <b>4</b>, which is used, when no <code>precisionStep</code> is given.</description></item>
/// <item><description>Ideal value in most cases for <em>64 bit</em> data types <em>(long, double)</em> is <b>6</b> or <b>8</b>.</description></item>
/// <item><description>Ideal value in most cases for <em>32 bit</em> data types <em>(int, float)</em> is <b>4</b>.</description></item>
/// <item><description>For low cardinality fields larger precision steps are good. If the cardinality is &lt; 100, it is
/// fair to use <see cref="int.MaxValue"/> (see below).</description></item>
/// <item><description>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 <see cref="TermRangeQuery"/>. But it can be used
/// to produce fields, that are solely used for sorting (in this case simply use <see cref="int.MaxValue"/> as
/// <see cref="precisionStep"/>). Using <see cref="Int32Field"/>,
/// <see cref="Int64Field"/>, <see cref="SingleField"/> or <see 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 <see cref="precisionStep"/>s.</description></item>
/// </list>
///
/// <para>Comparisons of the different types of RangeQueries on an index with about 500,000 docs showed
/// that <see cref="TermRangeQuery"/> in boolean rewrite mode (with raised <see cref="BooleanQuery"/> clause count)
/// took about 30-40 secs to complete, <see 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.</para>
///
/// @since 2.9
/// </summary>
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 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.GetEnumerator());
}
/// <summary>
/// Returns <c>true</c> if the lower endpoint is inclusive </summary>
public bool IncludesMin => minInclusive;
/// <summary>
/// Returns <c>true</c> if the upper endpoint is inclusive </summary>
public bool IncludesMax => maxInclusive;
/// <summary>
/// Returns the lower value of this range query </summary>
public T? Min => min;
/// <summary>
/// Returns the upper value of this range query </summary>
public T? Max => max;
/// <summary>
/// Returns the precision step. </summary>
public int PrecisionStep => 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> q)
{
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 <see cref="FilteredTermsEnum"/> for enumerating all terms that match the
/// sub-ranges for trie range queries, using flex API.
/// <para/>
/// WARNING: this term enumeration is not guaranteed to be always ordered by
/// <see cref="Index.Term.CompareTo(Index.Term)"/>.
/// The ordering depends on how <see cref="NumericUtils.SplitInt64Range(NumericUtils.Int64RangeBuilder, int, long, long)"/> and
/// <see cref="NumericUtils.SplitInt32Range(NumericUtils.Int32RangeBuilder, int, int, int)"/> generates the sub-ranges. For
/// <see cref="MultiTermQuery"/> ordering is not relevant.
/// </summary>
private sealed class NumericRangeTermsEnum : FilteredTermsEnum
{
private readonly NumericRangeQuery<T> outerInstance;
internal BytesRef currentLowerBound, currentUpperBound;
internal readonly Queue<BytesRef> rangeBounds = new Queue<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, CultureInfo.InvariantCulture);
}
else
{
if (Debugging.AssertsEnabled) Debugging.Assert(this.outerInstance.dataType == NumericType.DOUBLE);
minBound = (this.outerInstance.min == null) ? INT64_NEGATIVE_INFINITY : NumericUtils.DoubleToSortableInt64(Convert.ToDouble(this.outerInstance.min.Value, CultureInfo.InvariantCulture));
}
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, CultureInfo.InvariantCulture);
}
else
{
if (Debugging.AssertsEnabled) Debugging.Assert(this.outerInstance.dataType == NumericType.DOUBLE);
maxBound = (this.outerInstance.max == null) ? INT64_POSITIVE_INFINITY : NumericUtils.DoubleToSortableInt64(Convert.ToDouble(this.outerInstance.max, CultureInfo.InvariantCulture));
}
if (!this.outerInstance.maxInclusive && this.outerInstance.max != null)
{
if (maxBound == long.MinValue)
{
break;
}
maxBound--;
}
NumericUtils.SplitInt64Range(new Int64RangeBuilderAnonymousClass(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, CultureInfo.InvariantCulture);
}
else
{
if (Debugging.AssertsEnabled) Debugging.Assert(this.outerInstance.dataType == NumericType.SINGLE);
minBound = (this.outerInstance.min == null) ? INT32_NEGATIVE_INFINITY : NumericUtils.SingleToSortableInt32(Convert.ToSingle(this.outerInstance.min, CultureInfo.InvariantCulture));
}
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, CultureInfo.InvariantCulture);
}
else
{
if (Debugging.AssertsEnabled) Debugging.Assert(this.outerInstance.dataType == NumericType.SINGLE);
maxBound = (this.outerInstance.max == null) ? INT32_POSITIVE_INFINITY : NumericUtils.SingleToSortableInt32(Convert.ToSingle(this.outerInstance.max, CultureInfo.InvariantCulture));
}
if (!this.outerInstance.maxInclusive && this.outerInstance.max != null)
{
if (maxBound == int.MinValue)
{
break;
}
maxBound--;
}
NumericUtils.SplitInt32Range(new Int32RangeBuilderAnonymousClass(this), this.outerInstance.precisionStep, minBound, maxBound);
break;
}
default:
// should never happen
throw new ArgumentException("Invalid NumericType");
}
termComp = Comparer;
}
private class Int64RangeBuilderAnonymousClass : NumericUtils.Int64RangeBuilder
{
private readonly NumericRangeTermsEnum outerInstance;
public Int64RangeBuilderAnonymousClass(NumericRangeTermsEnum outerInstance)
{
this.outerInstance = outerInstance;
}
public override sealed void AddRange(BytesRef minPrefixCoded, BytesRef maxPrefixCoded)
{
outerInstance.rangeBounds.Enqueue(minPrefixCoded);
outerInstance.rangeBounds.Enqueue(maxPrefixCoded);
}
}
private class Int32RangeBuilderAnonymousClass : NumericUtils.Int32RangeBuilder
{
private readonly NumericRangeTermsEnum outerInstance;
public Int32RangeBuilderAnonymousClass(NumericRangeTermsEnum outerInstance)
{
this.outerInstance = outerInstance;
}
public override sealed void AddRange(BytesRef minPrefixCoded, BytesRef maxPrefixCoded)
{
outerInstance.rangeBounds.Enqueue(minPrefixCoded);
outerInstance.rangeBounds.Enqueue(maxPrefixCoded);
}
}
private void NextRange()
{
if (Debugging.AssertsEnabled) Debugging.Assert(rangeBounds.Count % 2 == 0);
currentLowerBound = rangeBounds.Dequeue();
if (Debugging.AssertsEnabled) Debugging.Assert(currentUpperBound == null || termComp.Compare(currentUpperBound, currentLowerBound) <= 0, "The current upper bound must be <= the new lower bound");
currentUpperBound = rangeBounds.Dequeue();
}
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
if (Debugging.AssertsEnabled) Debugging.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.Peek()) < 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;
}
}
}
/// <summary>
/// LUCENENET specific class to provide access to static factory metods of <see cref="NumericRangeQuery{T}"/>
/// without referring to its genereic closing type.
/// </summary>
public static class NumericRangeQuery
{
/// <summary>
/// Factory that creates a <see cref="NumericRangeQuery{T}"/>, that queries a <see cref="long"/>
/// range using the given <a href="#precisionStepDesc"><see cref="NumericRangeQuery{T}.precisionStep"/></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 <c>null</c>. By setting inclusive to <c>false</c>, 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 <see cref="NumericRangeQuery{T}"/>, that queries a <see cref="long"/>
/// range using the default <see cref="NumericRangeQuery{T}.precisionStep"/> <see 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 <c>null</c>. By setting inclusive to <c>false</c>, 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 <see cref="NumericRangeQuery{T}"/>, that queries a <see cref="int"/>
/// range using the given <a href="#precisionStepDesc"><see cref="NumericRangeQuery{T}.precisionStep"/></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 <c>null</c>. By setting inclusive to <c>false</c>, 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 <see cref="NumericRangeQuery{T}"/>, that queries a <see cref="int"/>
/// range using the default <see cref="NumericRangeQuery{T}.precisionStep"/> <see 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 <c>null</c>. By setting inclusive to <c>false</c>, 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 <see cref="NumericRangeQuery{T}"/>, that queries a <see cref="double"/>
/// range using the given <a href="#precisionStepDesc"><see cref="NumericRangeQuery{T}.precisionStep"/></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 <c>null</c>.
/// <see cref="double.NaN"/> will never match a half-open range, to hit <c>NaN</c> use a query
/// with <c>min == max == System.Double.NaN</c>. By setting inclusive to <c>false</c>, 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 <see cref="NumericRangeQuery{T}"/>, that queries a <see cref="double"/>
/// range using the default <see cref="NumericRangeQuery{T}.precisionStep"/> <see 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 <c>null</c>.
/// <see cref="double.NaN"/> will never match a half-open range, to hit <c>NaN</c> use a query
/// with <c>min == max == System.Double.NaN</c>. By setting inclusive to <c>false</c>, 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 <see cref="NumericRangeQuery{T}"/>, that queries a <see cref="float"/>
/// range using the given <a href="#precisionStepDesc"><see cref="NumericRangeQuery{T}.precisionStep"/></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 <c>null</c>.
/// <see cref="float.NaN"/> will never match a half-open range, to hit <c>NaN</c> use a query
/// with <c>min == max == System.Single.NaN</c>. By setting inclusive to <c>false</c>, 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 <see cref="NumericRangeQuery{T}"/>, that queries a <see cref="float"/>
/// range using the default <see cref="NumericRangeQuery{T}.precisionStep"/> <see 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 <c>null</c>.
/// <see cref="float.NaN"/> will never match a half-open range, to hit <c>NaN</c> use a query
/// with <c>min == max == System.Single.NaN</c>. By setting inclusive to <c>false</c>, 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);
}
}
}