Save This Page
Home » lucene-3.0.1-src » org.apache » lucene » search » [javadoc | source]
    1   package org.apache.lucene.search;
    2   
    3   /**
    4    * Licensed to the Apache Software Foundation (ASF) under one or more
    5    * contributor license agreements.  See the NOTICE file distributed with
    6    * this work for additional information regarding copyright ownership.
    7    * The ASF licenses this file to You under the Apache License, Version 2.0
    8    * (the "License"); you may not use this file except in compliance with
    9    * the License.  You may obtain a copy of the License at
   10    *
   11    *     http://www.apache.org/licenses/LICENSE-2.0
   12    *
   13    * Unless required by applicable law or agreed to in writing, software
   14    * distributed under the License is distributed on an "AS IS" BASIS,
   15    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   16    * See the License for the specific language governing permissions and
   17    * limitations under the License.
   18    */
   19   
   20   import java.io.IOException;
   21   import java.text.Collator;
   22   
   23   import org.apache.lucene.index.Term;
   24   import org.apache.lucene.index.TermEnum;
   25   import org.apache.lucene.index.IndexReader;
   26   import org.apache.lucene.util.ToStringUtils;
   27   
   28   /**
   29    * A Query that matches documents within an exclusive range. A RangeQuery
   30    * is built by QueryParser for input like <code>[010 TO 120]</code> but only if the QueryParser has 
   31    * the useOldRangeQuery property set to true. The QueryParser default behaviour is to use
   32    * the newer ConstantScoreRangeQuery class. This is generally preferable because:
   33    * <ul>
   34    * 	<li>It is faster than RangeQuery</li>
   35    * 	<li>Unlike RangeQuery, it does not cause a BooleanQuery.TooManyClauses exception if the range of values is large</li>
   36    * 	<li>Unlike RangeQuery it does not influence scoring based on the scarcity of individual terms that may match</li>
   37    * </ul>
   38    * 
   39    * 
   40    * @see ConstantScoreRangeQuery
   41    * 
   42    *
   43    * @version $Id: RangeQuery.java 696056 2008-09-16 21:03:21Z gsingers $
   44    */
   45   public class RangeQuery extends Query
   46   {
   47       private Term lowerTerm;
   48       private Term upperTerm;
   49       private boolean inclusive;
   50       private Collator collator;
   51   
   52       /** Constructs a query selecting all terms greater than
   53        * <code>lowerTerm</code> but less than <code>upperTerm</code>.
   54        * There must be at least one term and either term may be null,
   55        * in which case there is no bound on that side, but if there are
   56        * two terms, both terms <b>must</b> be for the same field.
   57        *
   58        * @param lowerTerm The Term at the lower end of the range
   59        * @param upperTerm The Term at the upper end of the range
   60        * @param inclusive If true, both <code>lowerTerm</code> and
   61        *  <code>upperTerm</code> will themselves be included in the range.
   62        */
   63       public RangeQuery(Term lowerTerm, Term upperTerm, boolean inclusive)
   64       {
   65           if (lowerTerm == null && upperTerm == null)
   66           {
   67               throw new IllegalArgumentException("At least one term must be non-null");
   68           }
   69           if (lowerTerm != null && upperTerm != null && lowerTerm.field() != upperTerm.field())
   70           {
   71               throw new IllegalArgumentException("Both terms must be for the same field");
   72           }
   73   
   74           // if we have a lowerTerm, start there. otherwise, start at beginning
   75           if (lowerTerm != null) {
   76               this.lowerTerm = lowerTerm;
   77           }
   78           else {
   79               this.lowerTerm = new Term(upperTerm.field());
   80           }
   81   
   82           this.upperTerm = upperTerm;
   83           this.inclusive = inclusive;
   84       }
   85   
   86       /** Constructs a query selecting all terms greater than
   87        * <code>lowerTerm</code> but less than <code>upperTerm</code>.
   88        * There must be at least one term and either term may be null,
   89        * in which case there is no bound on that side, but if there are
   90        * two terms, both terms <b>must</b> be for the same field.
   91        * <p>
   92        * If <code>collator</code> is not null, it will be used to decide whether
   93        * index terms are within the given range, rather than using the Unicode code
   94        * point order in which index terms are stored.
   95        * <p>
   96        * <strong>WARNING:</strong> Using this constructor and supplying a non-null
   97        * value in the <code>collator</code> parameter will cause every single 
   98        * index Term in the Field referenced by lowerTerm and/or upperTerm to be
   99        * examined.  Depending on the number of index Terms in this Field, the 
  100        * operation could be very slow.
  101        *
  102        * @param lowerTerm The Term at the lower end of the range
  103        * @param upperTerm The Term at the upper end of the range
  104        * @param inclusive If true, both <code>lowerTerm</code> and
  105        *  <code>upperTerm</code> will themselves be included in the range.
  106        * @param collator The collator to use to collate index Terms, to determine
  107        *  their membership in the range bounded by <code>lowerTerm</code> and
  108        *  <code>upperTerm</code>.
  109        */
  110       public RangeQuery(Term lowerTerm, Term upperTerm, boolean inclusive,
  111                         Collator collator)
  112       {
  113           this(lowerTerm, upperTerm, inclusive);
  114           this.collator = collator;
  115       }
  116   
  117       public Query rewrite(IndexReader reader) throws IOException {
  118   
  119           BooleanQuery query = new BooleanQuery(true);
  120           String testField = getField();
  121           if (collator != null) {
  122               TermEnum enumerator = reader.terms(new Term(testField, ""));
  123               String lowerTermText = lowerTerm != null ? lowerTerm.text() : null;
  124               String upperTermText = upperTerm != null ? upperTerm.text() : null;
  125   
  126               try {
  127                   do {
  128                       Term term = enumerator.term();
  129                       if (term != null && term.field() == testField) { // interned comparison
  130                           if ((lowerTermText == null
  131                                || (inclusive ? collator.compare(term.text(), lowerTermText) >= 0
  132                                              : collator.compare(term.text(), lowerTermText) > 0))
  133                               && (upperTermText == null
  134                                   || (inclusive ? collator.compare(term.text(), upperTermText) <= 0
  135                                                 : collator.compare(term.text(), upperTermText) < 0))) {
  136                               addTermToQuery(term, query);
  137                           }
  138                       }
  139                   }
  140                   while (enumerator.next());
  141               }
  142               finally {
  143                   enumerator.close();
  144               }
  145           }
  146           else { // collator is null
  147               TermEnum enumerator = reader.terms(lowerTerm);
  148   
  149               try {
  150   
  151                   boolean checkLower = false;
  152                   if (!inclusive) // make adjustments to set to exclusive
  153                       checkLower = true;
  154   
  155                   do {
  156                       Term term = enumerator.term();
  157                       if (term != null && term.field() == testField) { // interned comparison
  158                           if (!checkLower || term.text().compareTo(lowerTerm.text()) > 0) {
  159                               checkLower = false;
  160                               if (upperTerm != null) {
  161                                   int compare = upperTerm.text().compareTo(term.text());
  162                                   /* if beyond the upper term, or is exclusive and
  163                                    * this is equal to the upper term, break out */
  164                                   if ((compare < 0) || (!inclusive && compare == 0))
  165                                       break;
  166                               }
  167                               addTermToQuery(term, query); // Found a match
  168                           }
  169                       }
  170                       else {
  171                           break;
  172                       }
  173                   }
  174                   while (enumerator.next());
  175               }
  176               finally {
  177                   enumerator.close();
  178               }
  179           }
  180           return query;
  181       }
  182   
  183       private void addTermToQuery(Term term, BooleanQuery query) {
  184           TermQuery tq = new TermQuery(term);
  185           tq.setBoost(getBoost()); // set the boost
  186           query.add(tq, BooleanClause.Occur.SHOULD); // add to query
  187       }
  188   
  189       /** Returns the field name for this query */
  190       public String getField() {
  191         return (lowerTerm != null ? lowerTerm.field() : upperTerm.field());
  192       }
  193   
  194       /** Returns the lower term of this range query */
  195       public Term getLowerTerm() { return lowerTerm; }
  196   
  197       /** Returns the upper term of this range query */
  198       public Term getUpperTerm() { return upperTerm; }
  199   
  200       /** Returns <code>true</code> if the range query is inclusive */
  201       public boolean isInclusive() { return inclusive; }
  202   
  203       /** Returns the collator used to determine range inclusion, if any. */
  204       public Collator getCollator() { return collator; }
  205   
  206   
  207       /** Prints a user-readable version of this query. */
  208       public String toString(String field)
  209       {
  210           StringBuffer buffer = new StringBuffer();
  211           if (!getField().equals(field))
  212           {
  213               buffer.append(getField());
  214               buffer.append(":");
  215           }
  216           buffer.append(inclusive ? "[" : "{");
  217           buffer.append(lowerTerm != null ? lowerTerm.text() : "null");
  218           buffer.append(" TO ");
  219           buffer.append(upperTerm != null ? upperTerm.text() : "null");
  220           buffer.append(inclusive ? "]" : "}");
  221           buffer.append(ToStringUtils.boost(getBoost()));
  222           return buffer.toString();
  223       }
  224   
  225       /** Returns true iff <code>o</code> is equal to this. */
  226       public boolean equals(Object o) {
  227           if (this == o) return true;
  228           if (!(o instanceof RangeQuery)) return false;
  229   
  230           final RangeQuery other = (RangeQuery) o;
  231           if (this.getBoost() != other.getBoost()) return false;
  232           if (this.inclusive != other.inclusive) return false;
  233           if (this.collator != null && ! this.collator.equals(other.collator)) 
  234               return false;
  235   
  236           // one of lowerTerm and upperTerm can be null
  237           if (this.lowerTerm != null ? !this.lowerTerm.equals(other.lowerTerm) : other.lowerTerm != null) return false;
  238           if (this.upperTerm != null ? !this.upperTerm.equals(other.upperTerm) : other.upperTerm != null) return false;
  239           return true;
  240       }
  241   
  242       /** Returns a hash code value for this object.*/
  243       public int hashCode() {
  244         int h = Float.floatToIntBits(getBoost());
  245         h ^= lowerTerm != null ? lowerTerm.hashCode() : 0;
  246         // reversible mix to make lower and upper position dependent and
  247         // to prevent them from cancelling out.
  248         h ^= (h << 25) | (h >>> 8);
  249         h ^= upperTerm != null ? upperTerm.hashCode() : 0;
  250         h ^= this.inclusive ? 0x2742E74A : 0;
  251         h ^= collator != null ? collator.hashCode() : 0; 
  252         return h;
  253       }
  254   }

Save This Page
Home » lucene-3.0.1-src » org.apache » lucene » search » [javadoc | source]