ehatcher 2004/11/23 06:17:18 Modified: . CHANGES.txt src/java/org/apache/lucene/search DateFilter.java src/java/org/apache/lucene/document DateField.java Added: src/java/org/apache/lucene/search RangeFilter.java src/test/org/apache/lucene/search BaseTestRangeFilter.java TestRangeFilter.java Log: Added RangeFilter and tests contributed by Chris M Hostetter.
Deprecated DateFilter and DateField. Revision Changes Path 1.125 +4 -1 jakarta-lucene/CHANGES.txt Index: CHANGES.txt =================================================================== RCS file: /home/cvs/jakarta-lucene/CHANGES.txt,v retrieving revision 1.124 retrieving revision 1.125 diff -u -r1.124 -r1.125 --- CHANGES.txt 19 Nov 2004 21:04:17 -0000 1.124 +++ CHANGES.txt 23 Nov 2004 14:17:18 -0000 1.125 @@ -47,6 +47,8 @@ 9. Added javadocs-internal to build.xml - bug #30360 (Paul Elschot via Otis) + +10. Added RangeFilter. (Chris M Hostetter via Erik) API Changes @@ -67,6 +69,7 @@ 4. Add a serializable Parameter Class to standardize parameter enum classes in BooleanClause and Field. (Christoph) + Bug fixes 1.11 +4 -1 jakarta-lucene/src/java/org/apache/lucene/search/DateFilter.java Index: DateFilter.java =================================================================== RCS file: /home/cvs/jakarta-lucene/src/java/org/apache/lucene/search/DateFilter.java,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- DateFilter.java 29 Mar 2004 22:48:03 -0000 1.10 +++ DateFilter.java 23 Nov 2004 14:17:18 -0000 1.11 @@ -30,7 +30,10 @@ * A Filter that restricts search results to a range of time. * * <p>For this to work, documents must have been indexed with a - * [EMAIL PROTECTED] DateField}. + * [EMAIL PROTECTED] DateField}.</p> + * + * @deprecated Instead, use [EMAIL PROTECTED] RangeFilter} combined with + * [EMAIL PROTECTED] org.apache.lucene.document.DateTools}. */ public class DateFilter extends Filter { String field; 1.1 jakarta-lucene/src/java/org/apache/lucene/search/RangeFilter.java Index: RangeFilter.java =================================================================== package org.apache.lucene.search; /** * Copyright 2004 The Apache Software Foundation * * Licensed 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 java.util.BitSet; import java.io.IOException; import org.apache.lucene.search.Filter; import org.apache.lucene.index.Term; import org.apache.lucene.index.TermDocs; import org.apache.lucene.index.TermEnum; import org.apache.lucene.index.IndexReader; /** * A Filter that restricts search results to a range of values in a given * field. * * <p> * This code borrows heavily from [EMAIL PROTECTED] RangeQuery}, but implemented as a Filter * (much like [EMAIL PROTECTED] DateFilter}) * </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? */ 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>field</code> matching * less than or equal to <code>value</code> */ public static RangeFilter Less(String fieldName, String upperTerm) { return new RangeFilter(fieldName, null, upperTerm, false, true); } /** * Constructs a filter for field <code>field</code> matching * greater than or equal to <code>lower</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(); } } 1.1 jakarta-lucene/src/test/org/apache/lucene/search/BaseTestRangeFilter.java Index: BaseTestRangeFilter.java =================================================================== package org.apache.lucene.search; import java.util.Random; import junit.framework.TestCase; import org.apache.lucene.analysis.SimpleAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.store.RAMDirectory; public class BaseTestRangeFilter extends TestCase { public static final boolean F = false; public static final boolean T = true; RAMDirectory index = new RAMDirectory(); Random rand = new Random(101); // use a set seed to test is deterministic int maxR = Integer.MIN_VALUE; int minR = Integer.MAX_VALUE; int minId = 0; int maxId = 10000; static final int intLength = Integer.toString(Integer.MAX_VALUE).length(); /** * a simple padding function that should work with any int */ public static String pad(int n) { StringBuffer b = new StringBuffer(40); String p = "0"; if (n < 0) { p = "-"; n = Integer.MAX_VALUE + n + 1; } b.append(p); String s = Integer.toString(n); for (int i = s.length(); i <= intLength; i++) { b.append("0"); } b.append(s); return b.toString(); } public BaseTestRangeFilter(String name) { super(name); build(); } public BaseTestRangeFilter() { build(); } private void build() { try { /* build an index */ IndexWriter writer = new IndexWriter(index, new SimpleAnalyzer(), T); for (int d = minId; d <= maxId; d++) { Document doc = new Document(); doc.add(Field.Keyword("id",pad(d))); int r= rand.nextInt(); if (maxR < r) { maxR = r; } if (r < minR) { minR = r; } doc.add(Field.Keyword("rand",pad(r))); doc.add(Field.Keyword("body","body")); writer.addDocument(doc); } writer.optimize(); writer.close(); } catch (Exception e) { throw new RuntimeException("can't build index", e); } } public void testPad() { int[] tests = new int[] { -9999999, -99560, -100, -3, -1, 0, 3, 9, 10, 1000, 999999999 }; for (int i = 0; i < tests.length - 1; i++) { int a = tests[i]; int b = tests[i+1]; String aa = pad(a); String bb = pad(b); String label = a + ":" + aa + " vs " + b + ":" + bb; assertEquals("length of " + label, aa.length(), bb.length()); assertTrue("compare less than " + label, aa.compareTo(bb) < 0); } } } 1.1 jakarta-lucene/src/test/org/apache/lucene/search/TestRangeFilter.java Index: TestRangeFilter.java =================================================================== package org.apache.lucene.search; import java.io.IOException; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Term; /** * A basic 'positive' Unit test class for the RangeFilter class. * * <p> * NOTE: at the moment, this class only tests for 'positive' results, * it does not verify the results to ensure their are no 'false positives', * nor does it adequately test 'negative' results. It also does not test * that garbage in results in an Exception. */ public class TestRangeFilter extends BaseTestRangeFilter { public TestRangeFilter(String name) { super(name); } public TestRangeFilter() { super(); } public void testRangeFilterId() throws IOException { IndexReader reader = IndexReader.open(index); IndexSearcher search = new IndexSearcher(reader); int medId = ((maxId - minId) / 2); String minIP = pad(minId); String maxIP = pad(maxId); String medIP = pad(medId); int numDocs = reader.numDocs(); assertEquals("num of docs", numDocs, 1+ maxId - minId); Hits result; Query q = new TermQuery(new Term("body","body")); // test id, bounded on both ends result = search.search(q,new RangeFilter("id",minIP,maxIP,T,T)); assertEquals("find all", numDocs, result.length()); result = search.search(q,new RangeFilter("id",minIP,maxIP,T,F)); assertEquals("all but last", numDocs-1, result.length()); result = search.search(q,new RangeFilter("id",minIP,maxIP,F,T)); assertEquals("all but first", numDocs-1, result.length()); result = search.search(q,new RangeFilter("id",minIP,maxIP,F,F)); assertEquals("all but ends", numDocs-2, result.length()); result = search.search(q,new RangeFilter("id",medIP,maxIP,T,T)); assertEquals("med and up", 1+ maxId-medId, result.length()); result = search.search(q,new RangeFilter("id",minIP,medIP,T,T)); assertEquals("up to med", 1+ medId-minId, result.length()); // unbounded id result = search.search(q,new RangeFilter("id",minIP,null,T,F)); assertEquals("min and up", numDocs, result.length()); result = search.search(q,new RangeFilter("id",null,maxIP,F,T)); assertEquals("max and down", numDocs, result.length()); result = search.search(q,new RangeFilter("id",minIP,null,F,F)); assertEquals("not min, but up", numDocs-1, result.length()); result = search.search(q,new RangeFilter("id",null,maxIP,F,F)); assertEquals("not max, but down", numDocs-1, result.length()); result = search.search(q,new RangeFilter("id",medIP,maxIP,T,F)); assertEquals("med and up, not max", maxId-medId, result.length()); result = search.search(q,new RangeFilter("id",minIP,medIP,F,T)); assertEquals("not min, up to med", medId-minId, result.length()); // very small sets result = search.search(q,new RangeFilter("id",minIP,minIP,F,F)); assertEquals("min,min,F,F", 0, result.length()); result = search.search(q,new RangeFilter("id",medIP,medIP,F,F)); assertEquals("med,med,F,F", 0, result.length()); result = search.search(q,new RangeFilter("id",maxIP,maxIP,F,F)); assertEquals("max,max,F,F", 0, result.length()); result = search.search(q,new RangeFilter("id",minIP,minIP,T,T)); assertEquals("min,min,T,T", 1, result.length()); result = search.search(q,new RangeFilter("id",null,minIP,F,T)); assertEquals("nul,min,F,T", 1, result.length()); result = search.search(q,new RangeFilter("id",maxIP,maxIP,T,T)); assertEquals("max,max,T,T", 1, result.length()); result = search.search(q,new RangeFilter("id",maxIP,null,T,F)); assertEquals("max,nul,T,T", 1, result.length()); result = search.search(q,new RangeFilter("id",medIP,medIP,T,T)); assertEquals("med,med,T,T", 1, result.length()); } public void testRangeFilterRand() throws IOException { IndexReader reader = IndexReader.open(index); IndexSearcher search = new IndexSearcher(reader); String minRP = pad(minR); String maxRP = pad(maxR); int numDocs = reader.numDocs(); assertEquals("num of docs", numDocs, 1+ maxId - minId); Hits result; Query q = new TermQuery(new Term("body","body")); // test extremes, bounded on both ends result = search.search(q,new RangeFilter("rand",minRP,maxRP,T,T)); assertEquals("find all", numDocs, result.length()); result = search.search(q,new RangeFilter("rand",minRP,maxRP,T,F)); assertEquals("all but biggest", numDocs-1, result.length()); result = search.search(q,new RangeFilter("rand",minRP,maxRP,F,T)); assertEquals("all but smallest", numDocs-1, result.length()); result = search.search(q,new RangeFilter("rand",minRP,maxRP,F,F)); assertEquals("all but extremes", numDocs-2, result.length()); // unbounded result = search.search(q,new RangeFilter("rand",minRP,null,T,F)); assertEquals("smallest and up", numDocs, result.length()); result = search.search(q,new RangeFilter("rand",null,maxRP,F,T)); assertEquals("biggest and down", numDocs, result.length()); result = search.search(q,new RangeFilter("rand",minRP,null,F,F)); assertEquals("not smallest, but up", numDocs-1, result.length()); result = search.search(q,new RangeFilter("rand",null,maxRP,F,F)); assertEquals("not biggest, but down", numDocs-1, result.length()); // very small sets result = search.search(q,new RangeFilter("rand",minRP,minRP,F,F)); assertEquals("min,min,F,F", 0, result.length()); result = search.search(q,new RangeFilter("rand",maxRP,maxRP,F,F)); assertEquals("max,max,F,F", 0, result.length()); result = search.search(q,new RangeFilter("rand",minRP,minRP,T,T)); assertEquals("min,min,T,T", 1, result.length()); result = search.search(q,new RangeFilter("rand",null,minRP,F,T)); assertEquals("nul,min,F,T", 1, result.length()); result = search.search(q,new RangeFilter("rand",maxRP,maxRP,T,T)); assertEquals("max,max,T,T", 1, result.length()); result = search.search(q,new RangeFilter("rand",maxRP,null,T,F)); assertEquals("max,nul,T,T", 1, result.length()); } } 1.9 +2 -0 jakarta-lucene/src/java/org/apache/lucene/document/DateField.java Index: DateField.java =================================================================== RCS file: /home/cvs/jakarta-lucene/src/java/org/apache/lucene/document/DateField.java,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- DateField.java 5 Sep 2004 21:27:29 -0000 1.8 +++ DateField.java 23 Nov 2004 14:17:18 -0000 1.9 @@ -36,6 +36,8 @@ * Note: dates before 1970 cannot be used, and therefore cannot be * indexed when using this class. See [EMAIL PROTECTED] DateTools} for an * alternative without such a limitation. + * + * @deprecated Use [EMAIL PROTECTED] DateTools} instead. */ public class DateField { --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]