Index: contrib/miscellaneous/src/test/org/apache/lucene/misc/RuntimeChainedFilterTest.java
===================================================================
--- contrib/miscellaneous/src/test/org/apache/lucene/misc/RuntimeChainedFilterTest.java (revision 0)
+++ contrib/miscellaneous/src/test/org/apache/lucene/misc/RuntimeChainedFilterTest.java (revision 0)
@@ -0,0 +1,161 @@
+package org.apache.lucene.misc;
+
+/**
+ * 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 junit.framework.TestCase;
+import java.util.Calendar;
+import java.util.Date;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.analysis.WhitespaceAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.search.*;
+
+public class RuntimeChainedFilterTest extends TestCase {
+ public static final int MAX = 500;
+
+ private RAMDirectory directory;
+ private IndexSearcher searcher;
+ private Query query;
+ // private DateFilter dateFilter; DateFilter was deprecated and removed
+ private RangeFilter dateFilter;
+ private QueryFilter bobFilter;
+ private QueryFilter sueFilter;
+ private FieldCacheRangeFilter idFilter;
+
+ public void setUp() throws Exception {
+ directory = new RAMDirectory();
+ IndexWriter writer =
+ new IndexWriter(directory, new WhitespaceAnalyzer(), true);
+
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(1041397200000L); // 2003 January 01
+
+ for (int i = 0; i < MAX; i++) {
+ Document doc = new Document();
+ doc.add(new Field("key", "" + (i + 1), Field.Store.YES, Field.Index.UN_TOKENIZED));
+ doc.add(new Field("owner", (i < MAX / 2) ? "bob" : "sue", Field.Store.YES, Field.Index.UN_TOKENIZED));
+ doc.add(new Field("date", cal.getTime().toString(), Field.Store.YES, Field.Index.UN_TOKENIZED));
+ writer.addDocument(doc);
+
+ cal.add(Calendar.DATE, 1);
+ }
+
+ writer.close();
+
+ searcher = new IndexSearcher(directory);
+
+ // query for everything to make life easier
+ BooleanQuery bq = new BooleanQuery();
+ bq.add(new TermQuery(new Term("owner", "bob")), BooleanClause.Occur.SHOULD);
+ bq.add(new TermQuery(new Term("owner", "sue")), BooleanClause.Occur.SHOULD);
+ query = bq;
+
+ // date filter matches everything too
+ Date pastTheEnd = parseDate("2099 Jan 1");
+ // dateFilter = DateFilter.Before("date", pastTheEnd);
+ // just treat dates as strings and select the whole range for now...
+ dateFilter = new RangeFilter("date","","ZZZZ",true,true);
+
+ bobFilter = new QueryFilter(
+ new TermQuery(new Term("owner", "bob")));
+ sueFilter = new QueryFilter(
+ new TermQuery(new Term("owner", "sue")));
+ idFilter = new FieldCacheRangeFilter("key",1, MAX / 2 , true , true);
+ }
+
+ public void testSingleFilter() throws Exception {
+ RuntimeChainedFilter chain = new RuntimeChainedFilter(
+ new Filter[] {dateFilter});
+
+ Hits hits = searcher.search(query, chain);
+ assertEquals(MAX, hits.length());
+
+ chain = new RuntimeChainedFilter(new Filter[] {bobFilter});
+ hits = searcher.search(query, chain);
+ assertEquals(MAX / 2, hits.length());
+
+ chain = new RuntimeChainedFilter(
+ new Filter[] {idFilter});
+
+ hits = searcher.search(query, chain);
+ assertEquals(MAX / 2, hits.length());
+ }
+
+ public void testOR() throws Exception {
+ RuntimeChainedFilter chain = new RuntimeChainedFilter(
+ new Filter[] {sueFilter, bobFilter});
+
+ Hits hits = searcher.search(query, chain);
+ assertEquals("OR matches all", MAX, hits.length());
+ }
+
+ public void testAND() throws Exception {
+ RuntimeChainedFilter chain = new RuntimeChainedFilter(
+ new Filter[] {dateFilter, bobFilter}, RuntimeChainedFilter.AND);
+
+ Hits hits = searcher.search(query, chain);
+ assertEquals("AND matches just bob", MAX / 2, hits.length());
+ assertEquals("bob", hits.doc(0).get("owner"));
+ }
+
+ public void testXOR() throws Exception {
+ RuntimeChainedFilter chain = new RuntimeChainedFilter(
+ new Filter[]{dateFilter, bobFilter}, RuntimeChainedFilter.XOR);
+
+ Hits hits = searcher.search(query, chain);
+ assertEquals("XOR matches sue", MAX / 2, hits.length());
+ assertEquals("sue", hits.doc(0).get("owner"));
+ }
+
+ public void testANDNOT() throws Exception {
+ RuntimeChainedFilter chain = new RuntimeChainedFilter(
+ new Filter[]{dateFilter, sueFilter},
+ new int[] {RuntimeChainedFilter.AND, RuntimeChainedFilter.ANDNOT});
+
+ Hits hits = searcher.search(query, chain);
+ assertEquals("ANDNOT matches just bob",
+ MAX / 2, hits.length());
+ assertEquals("bob", hits.doc(0).get("owner"));
+ }
+
+ public void testFieldCacheRangeFilter() throws Exception
+ {
+ RuntimeChainedFilter chain = new RuntimeChainedFilter(
+ new Filter[] {idFilter, bobFilter}, RuntimeChainedFilter.AND);
+
+ Hits hits = searcher.search(query, chain);
+ assertEquals("FieldCacheRangeFilter matches just bob", MAX / 2, hits.length());
+ assertEquals("bob", hits.doc(0).get("owner"));
+
+ chain = new RuntimeChainedFilter(
+ new Filter[] {idFilter, sueFilter}, RuntimeChainedFilter.AND);
+ hits = searcher.search(query, chain);
+ assertEquals("FieldCacheRangeFilter NONE matche sue", 0, hits.length());
+
+ }
+
+ private Date parseDate(String s) throws ParseException {
+ return new SimpleDateFormat("yyyy MMM dd").parse(s);
+ }
+
+}
Index: contrib/miscellaneous/src/java/org/apache/lucene/misc/ChainedFilter.java
===================================================================
--- contrib/miscellaneous/src/java/org/apache/lucene/misc/ChainedFilter.java (revision 527270)
+++ contrib/miscellaneous/src/java/org/apache/lucene/misc/ChainedFilter.java (working copy)
@@ -106,11 +106,11 @@
public static int DEFAULT = OR;
/** The filter chain */
- private Filter[] chain = null;
+ protected Filter[] chain = null;
- private int[] logicArray;
+ protected int[] logicArray;
- private int logic = -1;
+ protected int logic = -1;
/**
* Ctor.
Index: contrib/miscellaneous/src/java/org/apache/lucene/misc/RuntimeChainedFilter.java
===================================================================
--- contrib/miscellaneous/src/java/org/apache/lucene/misc/RuntimeChainedFilter.java (revision 0)
+++ contrib/miscellaneous/src/java/org/apache/lucene/misc/RuntimeChainedFilter.java (revision 0)
@@ -0,0 +1,223 @@
+package org.apache.lucene.misc;
+
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001-2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache Lucene" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache Lucene", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+
+import java.io.IOException;
+import java.util.BitSet;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.AbstractGetOnlyBitSet;
+import org.apache.lucene.search.Filter;
+
+
+/**
+ * A chained filter that does not do its ANDing or ORing until you call
+ * BitSet.get() this will allow and BitSet implementing AbstractGetOnlyBitSet to work correctly with a
+ * Chained Filter
+ *
+ * It might be a bit slower then the Chained Filter as it has to do the AND or OR or XOR or AND NOT at runtime
+ * but if your # of matches are small then it might run faster as it only cacluates the bits that it needs to look at
+ * this will also not create new BitSets and save Memory
+ *
+ * @author Matt Ericson
+ *
+ */
+
+public class RuntimeChainedFilter extends ChainedFilter {
+
+ public RuntimeChainedFilter(Filter[] chain) {
+ super(chain);
+ }
+
+ public RuntimeChainedFilter(Filter[] chain, int[] logicArray) {
+ super(chain, logicArray);
+ }
+
+ public RuntimeChainedFilter(Filter[] chain, int logic) {
+ super(chain, logic);
+ }
+
+ /**
+ * Create a new chained Bit Set that will know how to
+ */
+ public BitSet bits(IndexReader reader) throws IOException
+ {
+ if (logic != -1)
+ return getChainedBitSet(reader,logic);
+ else if (logicArray != null)
+ return getChainedBitSet(reader, logicArray);
+ else
+ return getChainedBitSet(reader, DEFAULT);
+ }
+
+
+ protected BitSet[] getBits(IndexReader reader) throws IOException {
+ if (chain.length > 0) {
+ BitSet[] sets = new BitSet[chain.length];
+ for(int i =0 ; i < chain.length; i ++) {
+ sets[i] = chain[i].bits(reader);
+ }
+ return sets;
+ }
+ return null;
+ }
+
+ protected BitSet getChainedBitSet(IndexReader reader, int logic) throws IOException {
+ BitSet[] sets = getBits(reader);
+ return new ChainedBitSet(sets,logic);
+ }
+
+ protected BitSet getChainedBitSet(IndexReader reader, int[] logicArray) throws IOException {
+ BitSet[] sets = getBits(reader);
+ if (logicArray.length != sets.length) {
+ throw new IllegalArgumentException("Invalid number of elements in logic array");
+ }
+ return new ChainedBitSet(sets,logicArray);
+ }
+
+ /**
+ * Will act as a normal bit set but have an array of bit sets that it can act on
+ *
+ * Does the chain with the bit sets and allows users to use non-standard bit sets
+ * @author Matt Ericson
+ *
+ */
+ protected class ChainedBitSet extends AbstractGetOnlyBitSet {
+
+ protected BitSet[] sets;
+ protected int logic = -1;
+ protected int[] logicArray;
+
+ public ChainedBitSet(BitSet[] sets, int logic ) {
+ super(0);
+ this.sets = sets;
+ this.logic = logic;
+ setBitSetLength();
+ }
+
+ public ChainedBitSet(BitSet[] sets, int[] logicArray ) {
+ super(0);
+ this.sets = sets;
+ this.logicArray = logicArray;
+ this.logic = -1;
+ setBitSetLength();
+ }
+
+
+ public boolean get(int bitIndex) {
+
+ if (logic > -1 ) {
+ return getBooleanFromBitSet(bitIndex,logic);
+ } else if (logicArray != null) {
+ return getBooleanFromBitSet(bitIndex,logicArray);
+ } else {
+ return getBooleanFromBitSet(bitIndex,DEFAULT);
+ }
+ }
+
+
+ protected boolean getBooleanFromBitSet(int bitIndex , int logic) {
+ boolean result = false;
+ for (int i = 0 ; i < sets.length; i++) {
+ if (logic == AND && i == 0 ) {
+ result = sets[i].get(bitIndex);
+ } else {
+ result = doLogic(result,sets[i].get(bitIndex) , logic) ;
+ }
+ }
+ return result;
+ }
+
+ protected boolean getBooleanFromBitSet(int bitIndex , int[] logicArray) {
+ boolean result = false;
+ for (int i = 0 ; i < sets.length; i++) {
+ if (logicArray[0] == AND && i == 0 ) {
+ result = sets[i].get(bitIndex);
+ } else {
+ result = doLogic(result,sets[i].get(bitIndex) , logicArray[i]) ;
+ }
+ }
+ return result;
+ }
+
+
+ protected boolean doLogic (boolean current, boolean fromBitSet, int logic)
+ {
+ switch (logic)
+ {
+ case OR:
+ return current | fromBitSet;
+ case AND:
+ return current & fromBitSet;
+ case ANDNOT:
+ return current & !fromBitSet;
+ case XOR:
+ return current ^ fromBitSet;
+ default:
+ return current | fromBitSet;
+ }
+ }
+
+ protected void setBitSetLength () {
+ for (int i = 0; i < sets.length; i ++) {
+ if (sets[i].length() > length ) {
+ this.length = sets[i].length();
+ }
+ }
+ }
+ }
+}
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,141 @@
+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 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 FieldCacheRangeFilter and standard RangeFilter
+ */
+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");
+ searcher.close();
+
+ //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);
+ }
+
+ long e1 = System.currentTimeMillis() - s;
+ System.out.println("FieldCacheRangeFilter finished in " + e1 + "ms");
+ searcher.close();
+ assertEquals("New FieldCacheFilter is faster then RangeFilter ", true, e1 < e);
+ }
+
+}
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,181 @@
+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 java.io.IOException;
+import java.util.BitSet;
+
+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 TestRangeFilter
+ */
+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());
+
+
+ //Test the Bit sets
+ FieldCacheRangeFilter filter = new FieldCacheRangeFilter("id", medIP, maxIP, T, T);
+ BitSet fcBits = filter.bits(reader);
+ BitSet bits = (BitSet)fcBits.clone();
+
+ assertEquals("They have the same size", bits.length(), fcBits.length());
+ for (int i = 0; i < fcBits.length() ; i++) {
+ assertEquals("All Bits are they same ", bits.get(i), fcBits.get(i));
+ assertEquals("Next clear bit ", bits.nextClearBit(i), fcBits.nextClearBit(i));
+ assertEquals("Next Set bit ", bits.nextSetBit(i), fcBits.nextSetBit(i));
+
+ }
+ assertEquals("med and up", 1 + maxId - medId, result.length());
+
+ }
+
+}
Index: src/java/org/apache/lucene/search/FieldCache.java
===================================================================
--- src/java/org/apache/lucene/search/FieldCache.java (revision 527270)
+++ 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,318 @@
+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 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 AbstractGetOnlyBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (bitIndex < length && bitIndex >= 0 && values[bitIndex] >= fLowerTerm && values[bitIndex] <= fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+
+ };
+ } else if (includeLower) {
+ return new AbstractGetOnlyBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (bitIndex < length && bitIndex >= 0 && values[bitIndex] >= fLowerTerm && values[bitIndex] < fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+
+ };
+ } else if (includeUpper) {
+ return new AbstractGetOnlyBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (bitIndex < length && bitIndex >= 0 && values[bitIndex] > fLowerTerm && values[bitIndex] <= fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+
+ };
+ } else {
+ return new AbstractGetOnlyBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (bitIndex < length && bitIndex >= 0 && 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 AbstractGetOnlyBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (bitIndex < length && bitIndex >= 0 && values[bitIndex] >= fLowerTerm && values[bitIndex] <= fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+
+ };
+ } else if (includeLower) {
+ return new AbstractGetOnlyBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (bitIndex < length && bitIndex >= 0 && values[bitIndex] >= fLowerTerm && values[bitIndex] < fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+
+ };
+ } else if (includeUpper) {
+ return new AbstractGetOnlyBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (bitIndex < length && bitIndex >= 0 && values[bitIndex] > fLowerTerm && values[bitIndex] <= fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+
+ };
+
+ } else {
+ return new AbstractGetOnlyBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (bitIndex < length && bitIndex >= 0 && 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 AbstractGetOnlyBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (bitIndex < length && bitIndex >= 0 && values[bitIndex] >= fLowerTerm && values[bitIndex] <= fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+ };
+ } else if (includeLower) {
+ return new AbstractGetOnlyBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (bitIndex < length && bitIndex >= 0 && values[bitIndex] >= fLowerTerm && values[bitIndex] < fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+ };
+ } else if (includeUpper) {
+ return new AbstractGetOnlyBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (bitIndex < length && bitIndex >= 0 && values[bitIndex] > fLowerTerm && values[bitIndex] <= fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+ };
+
+
+ } else {
+ return new AbstractGetOnlyBitSet(values.length) {
+ public boolean get(int bitIndex) {
+ if (bitIndex < length && bitIndex >= 0 && values[bitIndex] > fLowerTerm && values[bitIndex] < fUpperTerm) {
+ return true;
+ }
+ return false;
+ }
+ };
+ }
+ }
+ }
+
+}
Index: src/java/org/apache/lucene/search/AbstractGetOnlyBitSet.java
===================================================================
--- src/java/org/apache/lucene/search/AbstractGetOnlyBitSet.java (revision 0)
+++ src/java/org/apache/lucene/search/AbstractGetOnlyBitSet.java (revision 0)
@@ -0,0 +1,193 @@
+package org.apache.lucene.search;
+
+
+/* ====================================================================
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001-2003 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ * if any, must include the following acknowledgment:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowledgment may appear in the software itself,
+ * if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Apache" and "Apache Software Foundation" and
+ * "Apache Lucene" must not be used to endorse or promote products
+ * derived from this software without prior written permission. For
+ * written permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ * "Apache Lucene", nor may "Apache" appear in their name, without
+ * prior written permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+
+import java.util.BitSet;
+
+
+/**
+ * Abstract Version of a GetOnlyBitSet
+ * This will allow users to create bit sets that are not real bit sets since
+ * this class will use get() for every operation
+ *
+ * It handles all of the functions like isEmpty() or nextClearBit()
+ *
+ * The constructor needs to take the logical length of the BitSet
+ * So it knows how big the array is to loop over with each function
+ *
+ * @author Matt Ericson
+ */
+public abstract class AbstractGetOnlyBitSet extends BitSet {
+
+ protected int length;
+
+ public AbstractGetOnlyBitSet(int length) {
+ super(0);
+ this.length = length;
+ }
+
+ /**
+ * Will use get to decide if the two sets intersects
+ */
+ public boolean intersects(BitSet set) {
+ for (int i =0; i < length; i++) {
+ if (get(i) && set.get(i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * All sub classes must implement get
+ */
+ public abstract boolean get(int bitIndex);
+
+ /**
+ * Will make a real bit set and set all the fields to be the same they are in this
+ * BitSet.
+ */
+ public Object clone() {
+ return convertToBitSet();
+ }
+
+ /**
+ * Turn this FC Bit Set and make a real bit set so all other functions can have access to it
+ * @return
+ */
+ public BitSet convertToBitSet() {
+ BitSet returnSet = new BitSet(length);
+ for (int i =0; i < length; i++) {
+ returnSet.set(i,get(i));
+ }
+ return returnSet;
+ }
+
+ /*
+ * 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 ) {
+ throw new IndexOutOfBoundsException ("Indxex " + fromIndex + " is invalid");
+ }
+ if (fromIndex > length()) {
+ return -1;
+ }
+
+ int i = 0;
+ for (i = fromIndex; i < length; i ++) {
+ if (!get(i)) {
+ return i;
+ }
+ }
+ return i;
+ }
+
+ /*
+ * 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 ) {
+ throw new IndexOutOfBoundsException ("Indxex " + fromIndex + " is invalid");
+ }
+ if (fromIndex > length()) {
+ return -1;
+ }
+ int i = 0;
+ for (i = fromIndex ; i < length; i ++) {
+ if (get(i)) {
+ return i;
+ }
+ }
+ return i;
+ }
+
+ 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 527270)
+++ src/java/org/apache/lucene/search/FieldCacheImpl.java (working copy)
@@ -131,13 +131,19 @@
}
}
- private static final IntParser INT_PARSER = new IntParser() {
+ static final IntParser INT_PARSER = new IntParser() {
public int parseInt(String value) {
return Integer.parseInt(value);
}
};
- private static final FloatParser FLOAT_PARSER = new FloatParser() {
+ static final LongParser LONG_PARSER = new LongParser() {
+ public long parseLong(String value) {
+ return Long.parseLong(value);
+ }
+ };
+
+ 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);