001: package org.apache.lucene.search;
002:
003: /**
004: * Licensed to the Apache Software Foundation (ASF) under one or more
005: * contributor license agreements. See the NOTICE file distributed with
006: * this work for additional information regarding copyright ownership.
007: * The ASF licenses this file to You under the Apache License, Version 2.0
008: * (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: */
019:
020: import org.apache.lucene.index.IndexReader;
021: import org.apache.lucene.index.Term;
022: import org.apache.lucene.index.TermDocs;
023: import org.apache.lucene.index.TermEnum;
024:
025: import java.io.IOException;
026: import java.util.BitSet;
027:
028: /**
029: * A Filter that restricts search results to a range of values in a given
030: * field.
031: *
032: * <p>
033: * This code borrows heavily from {@link RangeQuery}, but is implemented as a Filter
034: *
035: * </p>
036: */
037: public class RangeFilter extends Filter {
038:
039: private String fieldName;
040: private String lowerTerm;
041: private String upperTerm;
042: private boolean includeLower;
043: private boolean includeUpper;
044:
045: /**
046: * @param fieldName The field this range applies to
047: * @param lowerTerm The lower bound on this range
048: * @param upperTerm The upper bound on this range
049: * @param includeLower Does this range include the lower bound?
050: * @param includeUpper Does this range include the upper bound?
051: * @throws IllegalArgumentException if both terms are null or if
052: * lowerTerm is null and includeLower is true (similar for upperTerm
053: * and includeUpper)
054: */
055: public RangeFilter(String fieldName, String lowerTerm,
056: String upperTerm, boolean includeLower, boolean includeUpper) {
057: this .fieldName = fieldName;
058: this .lowerTerm = lowerTerm;
059: this .upperTerm = upperTerm;
060: this .includeLower = includeLower;
061: this .includeUpper = includeUpper;
062:
063: if (null == lowerTerm && null == upperTerm) {
064: throw new IllegalArgumentException(
065: "At least one value must be non-null");
066: }
067: if (includeLower && null == lowerTerm) {
068: throw new IllegalArgumentException(
069: "The lower bound must be non-null to be inclusive");
070: }
071: if (includeUpper && null == upperTerm) {
072: throw new IllegalArgumentException(
073: "The upper bound must be non-null to be inclusive");
074: }
075: }
076:
077: /**
078: * Constructs a filter for field <code>fieldName</code> matching
079: * less than or equal to <code>upperTerm</code>.
080: */
081: public static RangeFilter Less(String fieldName, String upperTerm) {
082: return new RangeFilter(fieldName, null, upperTerm, false, true);
083: }
084:
085: /**
086: * Constructs a filter for field <code>fieldName</code> matching
087: * greater than or equal to <code>lowerTerm</code>.
088: */
089: public static RangeFilter More(String fieldName, String lowerTerm) {
090: return new RangeFilter(fieldName, lowerTerm, null, true, false);
091: }
092:
093: /**
094: * Returns a BitSet with true for documents which should be
095: * permitted in search results, and false for those that should
096: * not.
097: */
098: public BitSet bits(IndexReader reader) throws IOException {
099: BitSet bits = new BitSet(reader.maxDoc());
100: TermEnum enumerator = (null != lowerTerm ? reader
101: .terms(new Term(fieldName, lowerTerm)) : reader
102: .terms(new Term(fieldName, "")));
103:
104: try {
105:
106: if (enumerator.term() == null) {
107: return bits;
108: }
109:
110: boolean checkLower = false;
111: if (!includeLower) // make adjustments to set to exclusive
112: checkLower = true;
113:
114: TermDocs termDocs = reader.termDocs();
115: try {
116:
117: do {
118: Term term = enumerator.term();
119: if (term != null && term.field().equals(fieldName)) {
120: if (!checkLower || null == lowerTerm
121: || term.text().compareTo(lowerTerm) > 0) {
122: checkLower = false;
123: if (upperTerm != null) {
124: int compare = upperTerm.compareTo(term
125: .text());
126: /* if beyond the upper term, or is exclusive and
127: * this is equal to the upper term, break out */
128: if ((compare < 0)
129: || (!includeUpper && compare == 0)) {
130: break;
131: }
132: }
133: /* we have a good term, find the docs */
134:
135: termDocs.seek(enumerator.term());
136: while (termDocs.next()) {
137: bits.set(termDocs.doc());
138: }
139: }
140: } else {
141: break;
142: }
143: } while (enumerator.next());
144:
145: } finally {
146: termDocs.close();
147: }
148: } finally {
149: enumerator.close();
150: }
151:
152: return bits;
153: }
154:
155: public String toString() {
156: StringBuffer buffer = new StringBuffer();
157: buffer.append(fieldName);
158: buffer.append(":");
159: buffer.append(includeLower ? "[" : "{");
160: if (null != lowerTerm) {
161: buffer.append(lowerTerm);
162: }
163: buffer.append("-");
164: if (null != upperTerm) {
165: buffer.append(upperTerm);
166: }
167: buffer.append(includeUpper ? "]" : "}");
168: return buffer.toString();
169: }
170:
171: /** Returns true if <code>o</code> is equal to this. */
172: public boolean equals(Object o) {
173: if (this == o)
174: return true;
175: if (!(o instanceof RangeFilter))
176: return false;
177: RangeFilter other = (RangeFilter) o;
178:
179: if (!this .fieldName.equals(other.fieldName)
180: || this .includeLower != other.includeLower
181: || this .includeUpper != other.includeUpper) {
182: return false;
183: }
184: if (this .lowerTerm != null ? !this .lowerTerm
185: .equals(other.lowerTerm) : other.lowerTerm != null)
186: return false;
187: if (this .upperTerm != null ? !this .upperTerm
188: .equals(other.upperTerm) : other.upperTerm != null)
189: return false;
190: return true;
191: }
192:
193: /** Returns a hash code value for this object.*/
194: public int hashCode() {
195: int h = fieldName.hashCode();
196: h ^= lowerTerm != null ? lowerTerm.hashCode() : 0xB6ECE882;
197: h = (h << 1) | (h >>> 31); // rotate to distinguish lower from upper
198: h ^= (upperTerm != null ? (upperTerm.hashCode()) : 0x91BEC2C2);
199: h ^= (includeLower ? 0xD484B933 : 0)
200: ^ (includeUpper ? 0x6AE423AC : 0);
201: return h;
202: }
203: }
|