| 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 <=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 & 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> |
| ///   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): --> |
| ///   <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): --> |
| ///   <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 < 100, it is |
| /// fair to use <see cref="int.MaxValue"/> (see below).</description></item> |
| /// <item><description>Steps <b>>=64</b> for <em>long/double</em> and <b>>=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 <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 </<= or >/>= 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 </<= or >/>= 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 </<= or >/>= 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 </<= or >/>= 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 </<= or >/>= 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 </<= or >/>= 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 </<= or >/>= 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 </<= or >/>= 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); |
| } |
| } |
| } |