Index: src/test/org/apache/lucene/search/TestFieldCacheRangeFilterPerformance.java =================================================================== --- src/test/org/apache/lucene/search/TestFieldCacheRangeFilterPerformance.java (revision 0) +++ src/test/org/apache/lucene/search/TestFieldCacheRangeFilterPerformance.java (revision 0) @@ -0,0 +1,118 @@ +package org.apache.lucene.search; + +import java.io.File; +import java.io.IOException; +import java.util.Date; +import java.util.Random; + +import junit.framework.TestCase; + +import org.apache.lucene.analysis.KeywordAnalyzer; +import org.apache.lucene.document.DateTools; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.DateTools.Resolution; +import org.apache.lucene.document.Field.Index; +import org.apache.lucene.document.Field.Store; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.search.RangeFilter; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.FSDirectory; +import org.apache.lucene.store.RAMDirectory; + +/** + * Compares performance between FieldCacheRangeFilterPerformance and standard RangeFilter + * Most of this or all of this code is coppied from TestMemoryCachedRangeFilterPerformance + * Since it tests everything I want to test + * + */ +public class TestFieldCacheRangeFilterPerformance extends TestCase { + + public static final long INTERVAL = 5 * 365 * 24 * 60 * 60 * 1000L; + + Random r = new Random(1); + + public void testPerformance() throws IOException { + RAMDirectory ramDir = new RAMDirectory(); +// Directory ramDir = FSDirectory.getDirectory(new File("/tmp/dateindex")); + IndexWriter writer = new IndexWriter(ramDir, new KeywordAnalyzer(), true); + + Date endInterval = new Date(); + + Date startInterval = new Date(endInterval.getTime() - (INTERVAL)); + + System.out.println("Start interval: " + startInterval.toString()); + System.out.println("End interval: " + endInterval.toString()); + + System.out.println("Creating RAMDirectory index..."); + for (int i=0; i<100000; i++) { + long newInterval = Math.round(r.nextDouble() * INTERVAL); + + Date curDate = new Date(startInterval.getTime() + newInterval); + String dateStr = DateTools.dateToString(curDate, Resolution.MINUTE); + + Document document = new Document(); + + document.add(new Field("id", String.valueOf(i), Store.YES, Index.NO)); + document.add(new Field("date", dateStr, Store.YES, Index.UN_TOKENIZED)); + + writer.addDocument(document); + } + + writer.optimize(); + writer.close(); + + IndexReader reader = IndexReader.open(ramDir); + + System.out.println("Reader opened with " + reader.maxDoc() + " documents. Creating RangeFilters..."); + + long s = System.currentTimeMillis(); + for (int i=0; i<1000; i++) { + // Generate random date interval + Date date1 = new Date(startInterval.getTime() + Math.round(r.nextDouble() * INTERVAL)); + Date date2 = new Date(startInterval.getTime() + Math.round(r.nextDouble() * INTERVAL)); + + String start, end; + if (date1.after(date2)) { + start = DateTools.dateToString(date2, Resolution.MINUTE); + end = DateTools.dateToString(date1, Resolution.MINUTE); + } else { + start = DateTools.dateToString(date1, Resolution.MINUTE); + end = DateTools.dateToString(date2, Resolution.MINUTE); + } + + RangeFilter filter = new RangeFilter("date", start, end, true, true); + filter.bits(reader); + } + long e = System.currentTimeMillis() - s; + System.out.println("Standard RangeFilter finished in " + e + "ms"); + + //By Running the query once we will prime the Field Cache and get it ready + FieldCacheRangeFilter warmfilter = new FieldCacheRangeFilter("date", 0L, 1L, true, true); + warmfilter.bits(reader); + + s = System.currentTimeMillis(); + for (int i=0; i<1000; i++) { + // Generate random date interval + Date date1 = new Date(startInterval.getTime() + Math.round(r.nextDouble() * INTERVAL)); + Date date2 = new Date(startInterval.getTime() + Math.round(r.nextDouble() * INTERVAL)); + + String start, end; + if (date1.after(date2)) { + start = DateTools.dateToString(date2, Resolution.MINUTE); + end = DateTools.dateToString(date1, Resolution.MINUTE); + } else { + start = DateTools.dateToString(date1, Resolution.MINUTE); + end = DateTools.dateToString(date2, Resolution.MINUTE); + } + + FieldCacheRangeFilter filter = new FieldCacheRangeFilter("date", Long.parseLong(start), Long.parseLong(end), true, true); + filter.bits(reader); + } + + e = System.currentTimeMillis() - s; + System.out.println("FieldCacheRangeFilter inished in " + e + "ms"); + } + +} Index: src/test/org/apache/lucene/search/TestFieldCacheRangeFilter.java =================================================================== --- src/test/org/apache/lucene/search/TestFieldCacheRangeFilter.java (revision 0) +++ src/test/org/apache/lucene/search/TestFieldCacheRangeFilter.java (revision 0) @@ -0,0 +1,148 @@ +package org.apache.lucene.search; + +import java.io.IOException; + +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BaseTestRangeFilter; +import org.apache.lucene.search.Hits; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.RangeFilter; +import org.apache.lucene.search.TermQuery; + + +/** + * Bulk of the code lifted from TestMemoryCachedRangeFilter + */ +public class TestFieldCacheRangeFilter extends BaseTestRangeFilter { + public TestFieldCacheRangeFilter(String name) { + super(name); + } + + public TestFieldCacheRangeFilter() { + super(); + } + + public void testRangeFilterId() throws IOException { + IndexReader reader = IndexReader.open(index); + IndexSearcher search = new IndexSearcher(reader); + + int medId = ((maxId - minId) / 2); + + long minIP = minId; + long maxIP = maxId; + long medIP = 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 FieldCacheRangeFilter("id", minIP, maxIP, T, T)); + assertEquals("find all", numDocs, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", minIP, maxIP, T, F)); + assertEquals("all but last", numDocs - 1, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", minIP, maxIP, F, T)); + assertEquals("all but first", numDocs - 1, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", minIP, maxIP, F, F)); + assertEquals("all but ends", numDocs - 2, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", medIP, maxIP, T, T)); + assertEquals("med and up", 1 + maxId - medId, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", minIP, medIP, T, T)); + assertEquals("up to med", 1 + medId - minId, result.length()); + + // unbounded id + + result = search.search(q, new FieldCacheRangeFilter("id", minIP, Long.MAX_VALUE, T, F)); + assertEquals("min and up", numDocs, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", Long.MIN_VALUE, maxIP, F, T)); + assertEquals("max and down", numDocs, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", minIP, Long.MAX_VALUE, F, F)); + assertEquals("not min, but up", numDocs - 1, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", Long.MIN_VALUE, maxIP, F, F)); + assertEquals("not max, but down", numDocs - 1, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", medIP, maxIP, T, F)); + assertEquals("med and up, not max", maxId - medId, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", minIP, medIP, F, T)); + assertEquals("not min, up to med", medId - minId, result.length()); + + // very small sets + + result = search.search(q, new FieldCacheRangeFilter("id", minIP, minIP, F, F)); + assertEquals("min,min,F,F", 0, result.length()); + result = search.search(q, new FieldCacheRangeFilter("id", medIP, medIP, F, F)); + assertEquals("med,med,F,F", 0, result.length()); + result = search.search(q, new FieldCacheRangeFilter("id", maxIP, maxIP, F, F)); + assertEquals("max,max,F,F", 0, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", minIP, minIP, T, T)); + assertEquals("min,min,T,T", 1, result.length()); + result = search.search(q, new FieldCacheRangeFilter("id", Long.MIN_VALUE, minIP, F, T)); + assertEquals("nul,min,F,T", 1, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", maxIP, maxIP, T, T)); + assertEquals("max,max,T,T", 1, result.length()); + result = search.search(q, new FieldCacheRangeFilter("id", maxIP, Long.MAX_VALUE, T, F)); + assertEquals("max,nul,T,T", 1, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", medIP, medIP, T, T)); + assertEquals("med,med,T,T", 1, result.length()); + + + //DO IT again for ints + // test id, bounded on both ends + result = search.search(q, new FieldCacheRangeFilter("id", (int)minIP, (int)maxIP, T, T)); + assertEquals("find all", numDocs, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", (int)minIP, (int)maxIP, T, F)); + assertEquals("all but last", numDocs - 1, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", (int)minIP, (int)maxIP, F, T)); + assertEquals("all but first", numDocs - 1, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", (int)minIP, (int)maxIP, F, F)); + assertEquals("all but ends", numDocs - 2, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", (int)medIP, (int)maxIP, T, T)); + assertEquals("med and up", 1 + maxId - medId, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", (int)minIP, (int)medIP, T, T)); + assertEquals("up to med", 1 + medId - minId, result.length()); + + //DO IT again for ints + // test id, bounded on both ends + result = search.search(q, new FieldCacheRangeFilter("id", (float)minIP, (float)maxIP, T, T)); + assertEquals("find all", numDocs, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", (float)minIP, (float)maxIP, T, F)); + assertEquals("all but last", numDocs - 1, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", (float)minIP, (float)maxIP, F, T)); + assertEquals("all but first", numDocs - 1, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", (float)minIP, (float)maxIP, F, F)); + assertEquals("all but ends", numDocs - 2, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", (float)medIP, (float)maxIP, T, T)); + assertEquals("med and up", 1 + maxId - medId, result.length()); + + result = search.search(q, new FieldCacheRangeFilter("id", (float)minIP, (float)medIP, T, T)); + assertEquals("up to med", 1 + medId - minId, result.length()); + + } + +} Index: src/java/org/apache/lucene/search/FieldCache.java =================================================================== --- src/java/org/apache/lucene/search/FieldCache.java (revision 525389) +++ src/java/org/apache/lucene/search/FieldCache.java (working copy) @@ -61,6 +61,13 @@ public int parseInt(String string); } + /** Interface to parse ints from document fields. + * @see #getLongs(IndexReader, String, IntParser) + */ + public interface LongParser { + /** Return an long representation of this field's value. */ + public long parseLong(String string); + } /** Interface to parse floats from document fields. * @see #getFloats(IndexReader, String, FloatParser) @@ -99,6 +106,33 @@ public int[] getInts (IndexReader reader, String field, IntParser parser) throws IOException; + + /** Checks the internal cache for an appropriate entry, and if none is + * found, reads the terms in field as integers and returns an array + * of size reader.maxDoc() of the value each document + * has in the given field. + * @param reader Used to get field values. + * @param field Which field contains the integers. + * @return The values in the given field for each document. + * @throws IOException If any error occurs. + */ + public long[] getLongs (IndexReader reader, String field) + throws IOException; + + /** Checks the internal cache for an appropriate entry, and if none is found, + * reads the terms in field as integers and returns an array of + * size reader.maxDoc() of the value each document has in the + * given field. + * @param reader Used to get field values. + * @param field Which field contains the integers. + * @param parser Computes integer for string values. + * @return The values in the given field for each document. + * @throws IOException If any error occurs. + */ + public long[] getLongs (IndexReader reader, String field, LongParser parser) + throws IOException; + + /** Checks the internal cache for an appropriate entry, and if * none is found, reads the terms in field as floats and returns an array * of size reader.maxDoc() of the value each document Index: src/java/org/apache/lucene/search/FieldCacheRangeFilter.java =================================================================== --- src/java/org/apache/lucene/search/FieldCacheRangeFilter.java (revision 0) +++ src/java/org/apache/lucene/search/FieldCacheRangeFilter.java (revision 0) @@ -0,0 +1,336 @@ +package org.apache.lucene.search; + +import java.io.IOException; +import java.util.BitSet; + +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.search.FieldCache.FloatParser; +import org.apache.lucene.search.FieldCache.IntParser; +import org.apache.lucene.search.FieldCache.LongParser; + +/** + * Uses the field cache to do Range Filters + * Lucene already has the Field cache to store term info on a given query + * This will use that same cache to do range filters by creating a pass through BitSet that + * will proxy to the field cache + * + * If you can convert any Field into a int, long or float and then you can use this filter to + * to do a range filter + * + * By default the filed will be converted from strings to values by using Long.parseLong() or + * Integer.parseInt() if your data is formatted differently then you will need to pass in a + * parser that will convert it correctly. + * + * Here is an example of a non standard parser that will turn a date string into a long + * This would convert a date of yyyy.MM.dd G 'at' HH:mm:ss z into a long and allow the + * user to do a range query on it + * + * FieldCacheRangeFilter filter = new FieldCacheRangeFilter("date",1L,500L,false,false); + * filter.setLongParser(new FieldCache.LongParser() { + * SimpleDateFormat dateFormater = new SimpleDateForamt("yyyy.MM.dd G 'at' HH:mm:ss z"); + * public long parseLong(String value) { + * return dateFormater.parse(value).getTime(); + * } + * }); + * + * + * @author Matt Ericson + * + */ +public class FieldCacheRangeFilter extends Filter { + + protected String fieldName; + protected long lowerTerm; + protected long upperTerm; + protected boolean includeLower; + protected boolean includeUpper; + protected int fcType; + + //If the user wants to use floats then the input should be floats + protected float floatLowerTerm; + protected float floatUpperTerm; + + + protected FloatParser floatParser = FieldCacheImpl.FLOAT_PARSER; + protected IntParser intParser = FieldCacheImpl.INT_PARSER; + protected LongParser longParser = FieldCacheImpl.LONG_PARSER; + + public static final int INT = 0; + public static final int LONG = 1; + public static final int FLOAT = 2; + + + /** + * Will make a new FieldCacheRangeFilter that will filter using Ints + * This will create a field cache entry for your field and save the values as ints + * @param fieldName The Field to query + * @param lowerTerm Lower value + * @param upperTerm Upper value + * @param includeLower boolean + * @param includeUpper boolean + */ + public FieldCacheRangeFilter(String fieldName, int lowerTerm, int upperTerm, + boolean includeLower, boolean includeUpper) { + this(fieldName,lowerTerm,upperTerm,includeLower,includeUpper,INT); + } + + /** + * Will make a new FieldCacheRangeFilter that will filter using Longs + * This will create a field cache entry for your field and save the values as Longs + * @param fieldName The Field to query + * @param lowerTerm Lower value + * @param upperTerm Upper value + * @param includeLower boolean + * @param includeUpper boolean + */ + public FieldCacheRangeFilter(String fieldName, long lowerTerm, long upperTerm, + boolean includeLower, boolean includeUpper) { + this(fieldName,lowerTerm,upperTerm,includeLower,includeUpper,LONG); + } + + /** + * Will make a new FieldCacheRangeFilter that will filter using Float + * This will create a field cache entry for your field and save the values as floats + * @param fieldName The Field to query + * @param lowerTerm Lower value + * @param upperTerm Upper value + * @param includeLower boolean + * @param includeUpper boolean + */ + public FieldCacheRangeFilter(String fieldName, float lowerTerm, float upperTerm, + boolean includeLower, boolean includeUpper) { + this(fieldName,lowerTerm,upperTerm,includeLower,includeUpper,FLOAT); + } + + protected FieldCacheRangeFilter(String fieldName, long lowerTerm, long upperTerm, + boolean includeLower, boolean includeUpper,int fcType) { + this.fieldName = fieldName; + this.lowerTerm = lowerTerm; + this.upperTerm = upperTerm; + this.includeLower = includeLower; + this.includeUpper = includeUpper; + this.fcType = fcType; + } + + protected FieldCacheRangeFilter(String fieldName, float lowerTerm, float upperTerm, + boolean includeLower, boolean includeUpper,int fcType) { + this.fieldName = fieldName; + this.floatLowerTerm = lowerTerm; + this.floatUpperTerm = upperTerm; + this.includeLower = includeLower; + this.includeUpper = includeUpper; + this.fcType = fcType; + } + /** + * Override the default Integer parser with this one + * @param intParser + */ + public void setIntParser(IntParser intParser) { + this.intParser = intParser; + } + + /** + * Override the default Float parser with this one + * @param intParser + */ + public void setFloatParser(FloatParser floatParser) { + this.floatParser = floatParser; + } + + /** + * Override the default Long parser with this one + * @param intParser + */ + public void setLongParser(LongParser longParser) { + this.longParser = longParser; + } + + /** + * Create a bit set that is just a wrapper to the filed cache + * Store all data as longs/ints or floats in the field cache + */ + public BitSet bits(IndexReader reader) throws IOException { + //This will allow the user to pick if they want ints ofr longs or floats + if (fcType == INT) { + final int[] values = FieldCache.DEFAULT.getInts(reader, fieldName,intParser); + final long fLowerTerm = lowerTerm; + final long fUpperTerm = upperTerm; + + //Do they work now and create the correct BitSet so that it will run faster + //If we do the check outside of the BitSet it will not need to check on every request + if (includeLower && includeUpper) { + return new BitSet(0) { + public boolean get(int bitIndex) { + if (values[bitIndex] >= fLowerTerm && values[bitIndex] <= fUpperTerm) { + return true; + } + return false; + } + + public void or(BitSet set) { throw new UnsupportedOperationException("can not or"); } + public void and(BitSet set) { throw new UnsupportedOperationException("can not and"); } + public void andNot(BitSet set) { throw new UnsupportedOperationException("can not andNot"); } + }; + } else if (includeLower) { + return new BitSet(0) { + public boolean get(int bitIndex) { + if (values[bitIndex] >= fLowerTerm && values[bitIndex] < fUpperTerm) { + return true; + } + return false; + } + + public void or(BitSet set) { throw new UnsupportedOperationException("can not or"); } + public void and(BitSet set) { throw new UnsupportedOperationException("can not and"); } + public void andNot(BitSet set) { throw new UnsupportedOperationException("can not andNot"); } + }; + } else if (includeUpper) { + return new BitSet(0) { + public boolean get(int bitIndex) { + if (values[bitIndex] > fLowerTerm && values[bitIndex] <= fUpperTerm) { + return true; + } + return false; + } + + public void or(BitSet set) { throw new UnsupportedOperationException("can not or"); } + public void and(BitSet set) { throw new UnsupportedOperationException("can not and"); } + public void andNot(BitSet set) { throw new UnsupportedOperationException("can not andNot"); } + }; + } else { + return new BitSet(0) { + public boolean get(int bitIndex) { + if (values[bitIndex] > fLowerTerm && values[bitIndex] < fUpperTerm) { + return true; + } + return false; + } + public void or(BitSet set) { throw new UnsupportedOperationException("can not or"); } + public void and(BitSet set) { throw new UnsupportedOperationException("can not and"); } + public void andNot(BitSet set) { throw new UnsupportedOperationException("can not andNot"); } + }; + } + //Will create a different Bit set for LONGS + } else if (fcType == LONG) { + + //First we get the Array + final long[] values = FieldCache.DEFAULT.getLongs(reader, fieldName,longParser); + final long fLowerTerm = lowerTerm; + final long fUpperTerm = upperTerm; + + //Do they work now and create the correct BitSet so that it will run faster + //If we do the check outside of the BitSet it will not need to check on every request + if (includeLower && includeUpper) { + return new BitSet(0) { + public boolean get(int bitIndex) { + if (values[bitIndex] >= fLowerTerm && values[bitIndex] <= fUpperTerm) { + return true; + } + return false; + } + + public void or(BitSet set) { throw new UnsupportedOperationException("can not or"); } + public void and(BitSet set) { throw new UnsupportedOperationException("can not and"); } + public void andNot(BitSet set) { throw new UnsupportedOperationException("can not andNot"); } + }; + } else if (includeLower) { + return new BitSet(0) { + public boolean get(int bitIndex) { + if (values[bitIndex] >= fLowerTerm && values[bitIndex] < fUpperTerm) { + return true; + } + return false; + } + + public void or(BitSet set) { throw new UnsupportedOperationException("can not or"); } + public void and(BitSet set) { throw new UnsupportedOperationException("can not and"); } + public void andNot(BitSet set) { throw new UnsupportedOperationException("can not andNot"); } + }; + } else if (includeUpper) { + return new BitSet(0) { + public boolean get(int bitIndex) { + if (values[bitIndex] > fLowerTerm && values[bitIndex] <= fUpperTerm) { + return true; + } + return false; + } + + public void or(BitSet set) { throw new UnsupportedOperationException("can not or"); } + public void and(BitSet set) { throw new UnsupportedOperationException("can not and"); } + public void andNot(BitSet set) { throw new UnsupportedOperationException("can not andNot"); } + }; + + } else { + return new BitSet(0) { + public boolean get(int bitIndex) { + if (values[bitIndex] > fLowerTerm && values[bitIndex] < fUpperTerm) { + return true; + } + return false; + } + public void or(BitSet set) { throw new UnsupportedOperationException("can not or"); } + public void and(BitSet set) { throw new UnsupportedOperationException("can not and"); } + public void andNot(BitSet set) { throw new UnsupportedOperationException("can not andNot"); } + }; + } + } else { + final float[] values = FieldCache.DEFAULT.getFloats(reader, fieldName,floatParser); + final float fLowerTerm = floatLowerTerm; + final float fUpperTerm = floatUpperTerm; + + //Do they work now and create the correct BitSet so that it will run faster + //If we do the check outside of the BitSet it will not need to check on every request + if (includeLower && includeUpper) { + return new BitSet(0) { + public boolean get(int bitIndex) { + if (values[bitIndex] >= fLowerTerm && values[bitIndex] <= fUpperTerm) { + return true; + } + return false; + } + public void or(BitSet set) { throw new UnsupportedOperationException("can not or"); } + public void and(BitSet set) { throw new UnsupportedOperationException("can not and"); } + public void andNot(BitSet set) { throw new UnsupportedOperationException("can not andNot"); } + }; + } else if (includeLower) { + return new BitSet(0) { + public boolean get(int bitIndex) { + if (values[bitIndex] >= fLowerTerm && values[bitIndex] < fUpperTerm) { + return true; + } + return false; + } + public void or(BitSet set) { throw new UnsupportedOperationException("can not or"); } + public void and(BitSet set) { throw new UnsupportedOperationException("can not and"); } + public void andNot(BitSet set) { throw new UnsupportedOperationException("can not andNot"); } + }; + } else if (includeUpper) { + return new BitSet(0) { + public boolean get(int bitIndex) { + if (values[bitIndex] > fLowerTerm && values[bitIndex] <= fUpperTerm) { + return true; + } + return false; + } + public void or(BitSet set) { throw new UnsupportedOperationException("can not or"); } + public void and(BitSet set) { throw new UnsupportedOperationException("can not and"); } + public void andNot(BitSet set) { throw new UnsupportedOperationException("can not andNot"); } + }; + + + } else { + return new BitSet(0) { + public boolean get(int bitIndex) { + if (values[bitIndex] > fLowerTerm && values[bitIndex] < fUpperTerm) { + return true; + } + return false; + } + public void or(BitSet set) { throw new UnsupportedOperationException("can not or"); } + public void and(BitSet set) { throw new UnsupportedOperationException("can not and"); } + public void andNot(BitSet set) { throw new UnsupportedOperationException("can not andNot"); } + }; + } + } + } +} Index: src/java/org/apache/lucene/search/FieldCacheImpl.java =================================================================== --- src/java/org/apache/lucene/search/FieldCacheImpl.java (revision 525389) +++ src/java/org/apache/lucene/search/FieldCacheImpl.java (working copy) @@ -131,13 +131,19 @@ } } - private static final IntParser INT_PARSER = new IntParser() { + public static final IntParser INT_PARSER = new IntParser() { public int parseInt(String value) { return Integer.parseInt(value); } }; - private static final FloatParser FLOAT_PARSER = new FloatParser() { + public static final LongParser LONG_PARSER = new LongParser() { + public long parseLong(String value) { + return Long.parseLong(value); + } + }; + + public static final FloatParser FLOAT_PARSER = new FloatParser() { public float parseFloat(String value) { return Float.parseFloat(value); } @@ -182,7 +188,47 @@ } }; + + // inherit javadocs + public long[] getLongs (IndexReader reader, String field) throws IOException { + return getLongs(reader, field, LONG_PARSER); + } + + // inherit javadocs + public long[] getLongs(IndexReader reader, String field, LongParser parser) + throws IOException { + return (long[]) longsCache.get(reader, new Entry(field, parser)); + } + + Cache longsCache = new Cache() { + + protected Object createValue(IndexReader reader, Object entryKey) + throws IOException { + Entry entry = (Entry) entryKey; + String field = entry.field; + LongParser parser = (LongParser) entry.custom; + final long[] retArray = new long[reader.maxDoc()]; + TermDocs termDocs = reader.termDocs(); + TermEnum termEnum = reader.terms (new Term (field, "")); + try { + do { + Term term = termEnum.term(); + if (term==null || term.field() != field) break; + long termval = parser.parseLong(term.text()); + termDocs.seek (termEnum); + while (termDocs.next()) { + retArray[termDocs.doc()] = termval; + } + } while (termEnum.next()); + } finally { + termDocs.close(); + termEnum.close(); + } + return retArray; + } + }; + // inherit javadocs public float[] getFloats (IndexReader reader, String field) throws IOException { return getFloats(reader, field, FLOAT_PARSER);