FileDocCategorySizeDatePackage
RangeFilter.javaAPI DocApache Lucene 2.1.07322Wed Feb 14 10:46:40 GMT 2007org.apache.lucene.search

RangeFilter.java

package org.apache.lucene.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.
 */

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermDocs;
import org.apache.lucene.index.TermEnum;

import java.io.IOException;
import java.util.BitSet;

/**
 * A Filter that restricts search results to a range of values in a given
 * field.
 * 
 * <p>
 * This code borrows heavily from {@link RangeQuery}, but is implemented as a Filter
 * 
 * </p>
 */
public class RangeFilter extends Filter {
    
    private String fieldName;
    private String lowerTerm;
    private String upperTerm;
    private boolean includeLower;
    private boolean includeUpper;

    /**
     * @param fieldName The field this range applies to
     * @param lowerTerm The lower bound on this range
     * @param upperTerm The upper bound on this range
     * @param includeLower Does this range include the lower bound?
     * @param includeUpper Does this range include the upper bound?
     * @throws IllegalArgumentException if both terms are null or if
     *  lowerTerm is null and includeLower is true (similar for upperTerm
     *  and includeUpper)
     */
    public RangeFilter(String fieldName, String lowerTerm, String upperTerm,
                       boolean includeLower, boolean includeUpper) {
        this.fieldName = fieldName;
        this.lowerTerm = lowerTerm;
        this.upperTerm = upperTerm;
        this.includeLower = includeLower;
        this.includeUpper = includeUpper;
        
        if (null == lowerTerm && null == upperTerm) {
            throw new IllegalArgumentException
                ("At least one value must be non-null");
        }
        if (includeLower && null == lowerTerm) {
            throw new IllegalArgumentException
                ("The lower bound must be non-null to be inclusive");
        }
        if (includeUpper && null == upperTerm) {
            throw new IllegalArgumentException
                ("The upper bound must be non-null to be inclusive");
        }
    }
    
    /**
     * Constructs a filter for field <code>fieldName</code> matching
     * less than or equal to <code>upperTerm</code>.
     */
    public static RangeFilter Less(String fieldName, String upperTerm) {
        return new RangeFilter(fieldName, null, upperTerm, false, true);
    }

    /**
     * Constructs a filter for field <code>fieldName</code> matching
     * greater than or equal to <code>lowerTerm</code>.
     */
    public static RangeFilter More(String fieldName, String lowerTerm) {
        return new RangeFilter(fieldName, lowerTerm, null, true, false);
    }
    
    /**
     * Returns a BitSet with true for documents which should be
     * permitted in search results, and false for those that should
     * not.
     */
    public BitSet bits(IndexReader reader) throws IOException {
        BitSet bits = new BitSet(reader.maxDoc());
        TermEnum enumerator =
            (null != lowerTerm
             ? reader.terms(new Term(fieldName, lowerTerm))
             : reader.terms(new Term(fieldName,"")));
        
        try {
            
            if (enumerator.term() == null) {
                return bits;
            }
            
            boolean checkLower = false;
            if (!includeLower) // make adjustments to set to exclusive
                checkLower = true;
        
            TermDocs termDocs = reader.termDocs();
            try {
                
                do {
                    Term term = enumerator.term();
                    if (term != null && term.field().equals(fieldName)) {
                        if (!checkLower || null==lowerTerm || term.text().compareTo(lowerTerm) > 0) {
                            checkLower = false;
                            if (upperTerm != null) {
                                int compare = upperTerm.compareTo(term.text());
                                /* if beyond the upper term, or is exclusive and
                                 * this is equal to the upper term, break out */
                                if ((compare < 0) ||
                                    (!includeUpper && compare==0)) {
                                    break;
                                }
                            }
                            /* we have a good term, find the docs */
                            
                            termDocs.seek(enumerator.term());
                            while (termDocs.next()) {
                                bits.set(termDocs.doc());
                            }
                        }
                    } else {
                        break;
                    }
                }
                while (enumerator.next());
                
            } finally {
                termDocs.close();
            }
        } finally {
            enumerator.close();
        }

        return bits;
    }
    
    public String toString() {
        StringBuffer buffer = new StringBuffer();
        buffer.append(fieldName);
        buffer.append(":");
        buffer.append(includeLower ? "[" : "{");
        if (null != lowerTerm) {
            buffer.append(lowerTerm);
        }
        buffer.append("-");
        if (null != upperTerm) {
            buffer.append(upperTerm);
        }
        buffer.append(includeUpper ? "]" : "}");
        return buffer.toString();
    }

    /** Returns true if <code>o</code> is equal to this. */
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof RangeFilter)) return false;
        RangeFilter other = (RangeFilter) o;

        if (!this.fieldName.equals(other.fieldName)
            || this.includeLower != other.includeLower
            || this.includeUpper != other.includeUpper
           ) { return false; }
        if (this.lowerTerm != null ? !this.lowerTerm.equals(other.lowerTerm) : other.lowerTerm != null) return false;
        if (this.upperTerm != null ? !this.upperTerm.equals(other.upperTerm) : other.upperTerm != null) return false;
        return true;
    }

    /** Returns a hash code value for this object.*/
    public int hashCode() {
      int h = fieldName.hashCode();
      h ^= lowerTerm != null ? lowerTerm.hashCode() : 0xB6ECE882;
      h = (h << 1) | (h >>> 31);  // rotate to distinguish lower from upper
      h ^= (upperTerm != null ? (upperTerm.hashCode()) : 0x91BEC2C2);
      h ^= (includeLower ? 0xD484B933 : 0)
         ^ (includeUpper ? 0x6AE423AC : 0);
      return h;
    }
}