Lucene.Net  3.0.3
Lucene.Net is a .NET port of the Java Lucene Indexing Library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties
NumericRangeQuery.cs
Go to the documentation of this file.
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 using System;
19 using System.Collections.Generic;
20 using Lucene.Net.Index;
21 using NumericTokenStream = Lucene.Net.Analysis.NumericTokenStream;
22 using NumericField = Lucene.Net.Documents.NumericField;
23 using IndexReader = Lucene.Net.Index.IndexReader;
24 using Term = Lucene.Net.Index.Term;
25 using NumericUtils = Lucene.Net.Util.NumericUtils;
26 using StringHelper = Lucene.Net.Util.StringHelper;
27 using ToStringUtils = Lucene.Net.Util.ToStringUtils;
28 
29 namespace Lucene.Net.Search
30 {
31 
155  [Serializable]
156  public sealed class NumericRangeQuery<T> : MultiTermQuery
157  where T : struct, IComparable<T> // best equiv constraint for java's number class
158  {
159  internal NumericRangeQuery(System.String field, int precisionStep, int valSize, T? min, T? max, bool minInclusive, bool maxInclusive)
160  {
161  System.Diagnostics.Debug.Assert((valSize == 32 || valSize == 64));
162  if (precisionStep < 1)
163  throw new System.ArgumentException("precisionStep must be >=1");
164  this.field = StringHelper.Intern(field);
165  this.precisionStep = precisionStep;
166  this.valSize = valSize;
167  this.min = min;
168  this.max = max;
169  this.minInclusive = minInclusive;
170  this.maxInclusive = maxInclusive;
171 
172  // For bigger precisionSteps this query likely
173  // hits too many terms, so set to CONSTANT_SCORE_FILTER right off
174  // (especially as the FilteredTermEnum is costly if wasted only for AUTO tests because it
175  // creates new enums from IndexReader for each sub-range)
176  switch (valSize)
177  {
178 
179  case 64:
180  RewriteMethod = (precisionStep > 6)?CONSTANT_SCORE_FILTER_REWRITE:CONSTANT_SCORE_AUTO_REWRITE_DEFAULT;
181  break;
182 
183  case 32:
184  RewriteMethod = (precisionStep > 8)?CONSTANT_SCORE_FILTER_REWRITE:CONSTANT_SCORE_AUTO_REWRITE_DEFAULT;
185  break;
186 
187  default:
188  // should never happen
189  throw new System.ArgumentException("valSize must be 32 or 64");
190 
191  }
192 
193  // shortcut if upper bound == lower bound
194  if (min != null && min.Equals(max))
195  {
196  RewriteMethod = CONSTANT_SCORE_BOOLEAN_QUERY_REWRITE;
197  }
198  }
199 
200  //@Override
201  protected internal override FilteredTermEnum GetEnum(IndexReader reader)
202  {
203  return new NumericRangeTermEnum(this, reader);
204  }
205 
207  public string Field
208  {
209  get { return field; }
210  }
211 
213  public bool IncludesMin
214  {
215  get { return minInclusive; }
216  }
217 
219  public bool IncludesMax
220  {
221  get { return maxInclusive; }
222  }
223 
225  public T? Min
226  {
227  get { return min; }
228  }
229 
231  public T? Max
232  {
233  get { return max; }
234  }
235 
236  public override System.String ToString(System.String field)
237  {
238  System.Text.StringBuilder sb = new System.Text.StringBuilder();
239  if (!this.field.Equals(field))
240  sb.Append(this.field).Append(':');
241  return sb.Append(minInclusive ? '[' : '{').Append((min == null) ? "*" : min.ToString()).Append(" TO ").Append((max == null) ? "*" : max.ToString()).Append(maxInclusive ? ']' : '}').Append(ToStringUtils.Boost(Boost)).ToString();
242  }
243 
244  public override bool Equals(System.Object o)
245  {
246  if (o == this)
247  return true;
248  if (!base.Equals(o))
249  return false;
250  if (o is NumericRangeQuery<T>)
251  {
252  NumericRangeQuery<T> q = (NumericRangeQuery<T>)o;
253  return ((System.Object)field == (System.Object)q.field && (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);
254  }
255  return false;
256  }
257 
258  public override int GetHashCode()
259  {
260  int hash = base.GetHashCode();
261  hash += (field.GetHashCode() ^ 0x4565fd66 + precisionStep ^ 0x64365465);
262  if (min != null)
263  hash += (min.GetHashCode() ^ 0x14fa55fb);
264  if (max != null)
265  hash += (max.GetHashCode() ^ 0x733fa5fe);
266  return hash + (minInclusive.GetHashCode() ^ 0x14fa55fb) + (maxInclusive.GetHashCode() ^ 0x733fa5fe);
267  }
268 
269  // field must be interned after reading from stream
270  //private void ReadObject(java.io.ObjectInputStream in)
271  //{
272  // in.defaultReadObject();
273  // field = StringHelper.intern(field);
274  //}
275 
276 
277  [System.Runtime.Serialization.OnDeserialized]
278  internal void OnDeserialized(System.Runtime.Serialization.StreamingContext context)
279  {
280  field = StringHelper.Intern(field);
281  }
282 
283  // members (package private, to be also fast accessible by NumericRangeTermEnum)
284  internal System.String field;
285  internal int precisionStep;
286  internal int valSize;
287  internal T? min;
288  internal T? max;
289  internal bool minInclusive;
290  internal bool maxInclusive;
291 
301  private sealed class NumericRangeTermEnum:FilteredTermEnum
302  {
303  private class AnonymousClassLongRangeBuilder:NumericUtils.LongRangeBuilder
304  {
305  public AnonymousClassLongRangeBuilder(NumericRangeTermEnum enclosingInstance)
306  {
307  InitBlock(enclosingInstance);
308  }
309  private void InitBlock(NumericRangeTermEnum enclosingInstance)
310  {
311  this.enclosingInstance = enclosingInstance;
312  }
313  private NumericRangeTermEnum enclosingInstance;
314  public NumericRangeTermEnum Enclosing_Instance
315  {
316  get
317  {
318  return enclosingInstance;
319  }
320 
321  }
322  //@Override
323  public override void AddRange(System.String minPrefixCoded, System.String maxPrefixCoded)
324  {
325  Enclosing_Instance.rangeBounds.AddLast(minPrefixCoded);
326  Enclosing_Instance.rangeBounds.AddLast(maxPrefixCoded);
327  }
328  }
329  private class AnonymousClassIntRangeBuilder:NumericUtils.IntRangeBuilder
330  {
331  public AnonymousClassIntRangeBuilder(NumericRangeTermEnum enclosingInstance)
332  {
333  InitBlock(enclosingInstance);
334  }
335  private void InitBlock(NumericRangeTermEnum enclosingInstance)
336  {
337  this.enclosingInstance = enclosingInstance;
338  }
339  private NumericRangeTermEnum enclosingInstance;
340  public NumericRangeTermEnum Enclosing_Instance
341  {
342  get
343  {
344  return enclosingInstance;
345  }
346 
347  }
348  //@Override
349  public override void AddRange(System.String minPrefixCoded, System.String maxPrefixCoded)
350  {
351  Enclosing_Instance.rangeBounds.AddLast(minPrefixCoded);
352  Enclosing_Instance.rangeBounds.AddLast(maxPrefixCoded);
353  }
354  }
355  private void InitBlock(NumericRangeQuery<T> enclosingInstance)
356  {
357  this.enclosingInstance = enclosingInstance;
358  termTemplate = new Term(Enclosing_Instance.field);
359  }
360  private NumericRangeQuery<T> enclosingInstance;
361  public NumericRangeQuery<T> Enclosing_Instance
362  {
363  get
364  {
365  return enclosingInstance;
366  }
367 
368  }
369 
370  private IndexReader reader;
371  private LinkedList<string> rangeBounds = new LinkedList<string>();
372  private Term termTemplate;
373  private System.String currentUpperBound = null;
374 
375  private bool isDisposed;
376 
377  internal NumericRangeTermEnum(NumericRangeQuery<T> enclosingInstance, IndexReader reader)
378  {
379  InitBlock(enclosingInstance);
380  this.reader = reader;
381 
382  Type rangeType = Nullable.GetUnderlyingType(typeof(T?));
383  switch (Enclosing_Instance.valSize)
384  {
385  case 64: {
386  // lower
387  long minBound = System.Int64.MinValue;
388  if (rangeType == typeof(System.Int64))
389  {
390  // added in these checks to emulate java. passing null give it no type (in old code),
391  // but .net can identifies it with generics and sets the bounds to 0, causing tests to fail
392  if (Enclosing_Instance.min != null)
393  minBound = System.Convert.ToInt64(Enclosing_Instance.min);
394  }
395  else if (rangeType == typeof(System.Double))
396  {
397  if (Enclosing_Instance.min != null)
398  minBound = NumericUtils.DoubleToSortableLong(System.Convert.ToDouble(Enclosing_Instance.min));
399  }
400  if (!Enclosing_Instance.minInclusive && Enclosing_Instance.min != null)
401  {
402  if (minBound == System.Int64.MaxValue)
403  break;
404  minBound++;
405  }
406 
407  // upper
408  long maxBound = System.Int64.MaxValue;
409  if (rangeType == typeof(System.Int64))
410  {
411  if (Enclosing_Instance.max != null)
412  maxBound = System.Convert.ToInt64(Enclosing_Instance.max);
413  }
414  else if (rangeType == typeof(System.Double))
415  {
416  if (Enclosing_Instance.max != null)
417  maxBound = NumericUtils.DoubleToSortableLong(System.Convert.ToDouble(Enclosing_Instance.max));
418  }
419  if (!Enclosing_Instance.maxInclusive && Enclosing_Instance.max != null)
420  {
421  if (maxBound == System.Int64.MinValue)
422  break;
423  maxBound--;
424  }
425 
426  NumericUtils.SplitLongRange(new AnonymousClassLongRangeBuilder(this), Enclosing_Instance.precisionStep, minBound, maxBound);
427  break;
428  }
429 
430 
431  case 32: {
432  // lower
433  int minBound = System.Int32.MinValue;
434  if (rangeType == typeof(System.Int32))
435  {
436  if (Enclosing_Instance.min != null)
437  minBound = System.Convert.ToInt32(Enclosing_Instance.min);
438  }
439  else if (rangeType == typeof(System.Single))
440  {
441  if (Enclosing_Instance.min != null)
442  minBound = NumericUtils.FloatToSortableInt(System.Convert.ToSingle(Enclosing_Instance.min));
443  }
444  if (!Enclosing_Instance.minInclusive && Enclosing_Instance.min != null)
445  {
446  if (minBound == System.Int32.MaxValue)
447  break;
448  minBound++;
449  }
450 
451  // upper
452  int maxBound = System.Int32.MaxValue;
453  if (rangeType == typeof(System.Int32))
454  {
455  if (Enclosing_Instance.max != null)
456  maxBound = System.Convert.ToInt32(Enclosing_Instance.max);
457  }
458  else if (rangeType == typeof(System.Single))
459  {
460  if (Enclosing_Instance.max != null)
461  maxBound = NumericUtils.FloatToSortableInt(System.Convert.ToSingle(Enclosing_Instance.max));
462  }
463  if (!Enclosing_Instance.maxInclusive && Enclosing_Instance.max != null)
464  {
465  if (maxBound == System.Int32.MinValue)
466  break;
467  maxBound--;
468  }
469 
470  NumericUtils.SplitIntRange(new AnonymousClassIntRangeBuilder(this), Enclosing_Instance.precisionStep, minBound, maxBound);
471  break;
472  }
473 
474 
475  default:
476  // should never happen
477  throw new System.ArgumentException("valSize must be 32 or 64");
478 
479  }
480 
481  // seek to first term
482  Next();
483  }
484 
485  //@Override
486  public override float Difference()
487  {
488  return 1.0f;
489  }
490 
492  //@Override
493  public override bool EndEnum()
494  {
495  throw new NotSupportedException("not implemented");
496  }
497 
499  protected internal override void SetEnum(TermEnum tenum)
500  {
501  throw new NotSupportedException("not implemented");
502  }
503 
510  //@Override
511  protected internal override bool TermCompare(Term term)
512  {
513  return (term.Field == Enclosing_Instance.field && String.CompareOrdinal(term.Text, currentUpperBound) <= 0);
514  }
515 
517  //@Override
518  public override bool Next()
519  {
520  // if a current term exists, the actual enum is initialized:
521  // try change to next term, if no such term exists, fall-through
522  if (currentTerm != null)
523  {
524  System.Diagnostics.Debug.Assert(actualEnum != null);
525  if (actualEnum.Next())
526  {
527  currentTerm = actualEnum.Term;
528  if (TermCompare(currentTerm))
529  return true;
530  }
531  }
532  // if all above fails, we go forward to the next enum,
533  // if one is available
534  currentTerm = null;
535  while (rangeBounds.Count >= 2)
536  {
537  // close the current enum and read next bounds
538  if (actualEnum != null)
539  {
540  actualEnum.Close();
541  actualEnum = null;
542  }
543  string lowerBound = rangeBounds.First.Value;
544  rangeBounds.RemoveFirst();
545  this.currentUpperBound = rangeBounds.First.Value;
546  rangeBounds.RemoveFirst();
547  // create a new enum
548  actualEnum = reader.Terms(termTemplate.CreateTerm(lowerBound));
549  currentTerm = actualEnum.Term;
550  if (currentTerm != null && TermCompare(currentTerm))
551  return true;
552  // clear the current term for next iteration
553  currentTerm = null;
554  }
555 
556  // no more sub-range enums available
557  System.Diagnostics.Debug.Assert(rangeBounds.Count == 0 && currentTerm == null);
558  return false;
559  }
560 
562  protected override void Dispose(bool disposing)
563  {
564  if (isDisposed) return;
565 
566  rangeBounds.Clear();
567  currentUpperBound = null;
568 
569  isDisposed = true;
570  base.Dispose(disposing);
571  }
572  }
573  }
574 
575  public static class NumericRangeQuery
576  {
583  public static NumericRangeQuery<long> NewLongRange(System.String field, int precisionStep, long? min, long? max, bool minInclusive, bool maxInclusive)
584  {
585  return new NumericRangeQuery<long>(field, precisionStep, 64, min, max, minInclusive, maxInclusive);
586  }
587 
594  public static NumericRangeQuery<long> NewLongRange(System.String field, long? min, long? max, bool minInclusive, bool maxInclusive)
595  {
596  return new NumericRangeQuery<long>(field, NumericUtils.PRECISION_STEP_DEFAULT, 64, min, max, minInclusive, maxInclusive);
597  }
598 
605  public static NumericRangeQuery<int> NewIntRange(System.String field, int precisionStep, int? min, int? max, bool minInclusive, bool maxInclusive)
606  {
607  return new NumericRangeQuery<int>(field, precisionStep, 32, min, max, minInclusive, maxInclusive);
608  }
609 
616  public static NumericRangeQuery<int> NewIntRange(System.String field, int? min, int? max, bool minInclusive, bool maxInclusive)
617  {
618  return new NumericRangeQuery<int>(field, NumericUtils.PRECISION_STEP_DEFAULT, 32, min, max, minInclusive, maxInclusive);
619  }
620 
627  public static NumericRangeQuery<double> NewDoubleRange(System.String field, int precisionStep, double? min, double? max, bool minInclusive, bool maxInclusive)
628  {
629  return new NumericRangeQuery<double>(field, precisionStep, 64, min, max, minInclusive, maxInclusive);
630  }
631 
638  public static NumericRangeQuery<double> NewDoubleRange(System.String field, double? min, double? max, bool minInclusive, bool maxInclusive)
639  {
640  return new NumericRangeQuery<double>(field, NumericUtils.PRECISION_STEP_DEFAULT, 64, min, max, minInclusive, maxInclusive);
641  }
642 
649  public static NumericRangeQuery<float> NewFloatRange(System.String field, int precisionStep, float? min, float? max, bool minInclusive, bool maxInclusive)
650  {
651  return new NumericRangeQuery<float>(field, precisionStep, 32, min, max, minInclusive, maxInclusive);
652  }
653 
660  public static NumericRangeQuery<float> NewFloatRange(System.String field, float? min, float? max, bool minInclusive, bool maxInclusive)
661  {
662  return new NumericRangeQuery<float>(field, NumericUtils.PRECISION_STEP_DEFAULT, 32, min, max, minInclusive, maxInclusive);
663  }
664  }
665 }