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,124 @@
+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.index.Term;
+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...");
+
+ IndexSearcher searcher = new IndexSearcher(reader);
+
+ 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);
+ Query q = new TermQuery(new Term("id", Integer.toString(i)));
+ searcher.search(q,filter);
+ }
+ 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);
+ searcher = new IndexSearcher(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);
+ }
+
+ Query q = new TermQuery(new Term("id", Integer.toString(i)));
+ FieldCacheRangeFilter filter = new FieldCacheRangeFilter("date", Long.parseLong(start), Long.parseLong(end), true, true);
+ searcher.search(q,filter);
+ }
+
+ 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,391 @@
+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 FieldCacheBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (values[bitIndex] >= fLowerTerm && values[bitIndex] <= fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+
+ };
+ } else if (includeLower) {
+ return new FieldCacheBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (values[bitIndex] >= fLowerTerm && values[bitIndex] < fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+
+ };
+ } else if (includeUpper) {
+ return new FieldCacheBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (values[bitIndex] > fLowerTerm && values[bitIndex] <= fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+
+ };
+ } else {
+ return new FieldCacheBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (values[bitIndex] > fLowerTerm && values[bitIndex] < fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+ };
+ }
+ //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 FieldCacheBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (values[bitIndex] >= fLowerTerm && values[bitIndex] <= fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+
+ };
+ } else if (includeLower) {
+ return new FieldCacheBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (values[bitIndex] >= fLowerTerm && values[bitIndex] < fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+
+ };
+ } else if (includeUpper) {
+ return new FieldCacheBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (values[bitIndex] > fLowerTerm && values[bitIndex] <= fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+
+ };
+
+ } else {
+ return new FieldCacheBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (values[bitIndex] > fLowerTerm && values[bitIndex] < fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+ };
+ }
+ } 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 FieldCacheBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (values[bitIndex] >= fLowerTerm && values[bitIndex] <= fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+ };
+ } else if (includeLower) {
+ return new FieldCacheBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (values[bitIndex] >= fLowerTerm && values[bitIndex] < fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+ };
+ } else if (includeUpper) {
+ return new FieldCacheBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (values[bitIndex] > fLowerTerm && values[bitIndex] <= fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+ };
+
+
+ } else {
+ return new FieldCacheBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (values[bitIndex] > fLowerTerm && values[bitIndex] < fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+ };
+ }
+ }
+ }
+ /**
+ * This class will handle all of the other functions that are not boolean get(int bitIndex)
+ * Puting them all one place so I only have to write them once
+ *
+ * The constructor needs to take the logical length of the BitSet
+ * So it knows how big the array is to loop over
+ *
+ * @author Matt Ericson
+ */
+ protected class FieldCacheBitSet extends BitSet {
+
+ protected int length;
+
+ public FieldCacheBitSet(int length) {
+ super(0);
+ this.length = length;
+ }
+
+ public boolean intersects(BitSet set) {
+ for (int i =0; i < length; i++) {
+ if (get(i) && set.get(i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*
+ * Will return the if the set is Empty
+ * (non-Javadoc)
+ * @see java.util.BitSet#isEmpty()
+ */
+ public boolean isEmpty() {
+ for (int i =0; i < length; i++) {
+ if (get(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /*
+ * Will return the next Not set Bit
+ * (non-Javadoc)
+ * @see java.util.BitSet#nextClearBit(int)
+ */
+ public int nextClearBit(int fromIndex) {
+ int length = length();
+ if (fromIndex < 0 || fromIndex > size()) {
+ throw new IndexOutOfBoundsException ("Indxex " + fromIndex + " is invalid");
+ }
+ for (int i = fromIndex +1; i < length; i ++) {
+ if (!get(i)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /*
+ * Will return the next Set bit
+ * (non-Javadoc)
+ * @see java.util.BitSet#nextSetBit(int)
+ */
+ public int nextSetBit(int fromIndex) {
+ int length = length();
+ if (fromIndex < 0 || fromIndex > size()) {
+ throw new IndexOutOfBoundsException ("Indxex " + fromIndex + " is invalid");
+ }
+ for (int i = fromIndex +1; i < length; i ++) {
+ if (get(i)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+ public int size() { return length;}
+ public int length() { return length;}
+
+ //The Following are all Unsupported Operations as they cant be used with this type of filter
+ public void set(int bitIndex) { throw new UnsupportedOperationException("can not set"); }
+ public void set(int bitIndex,boolean value) { throw new UnsupportedOperationException("can not set"); }
+ public void set(int bitIndex,int toIndex, boolean value) { throw new UnsupportedOperationException("can not set"); }
+ public void set(int bitIndex,int toIndex) { throw new UnsupportedOperationException("can not set"); }
+ public void flip(int bit) { throw new UnsupportedOperationException("can not flip"); }
+ public void flip(int bit, int toindex) { throw new UnsupportedOperationException("can not flip"); }
+ 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"); }
+ public void xor(BitSet set) { throw new UnsupportedOperationException("can not xor"); }
+ }
+}
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);