You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-commits@lucene.apache.org by yo...@apache.org on 2005/11/15 06:29:14 UTC
svn commit: r344312 - in /lucene/java/trunk: ./
src/java/org/apache/lucene/search/ src/test/org/apache/lucene/search/
Author: yonik
Date: Mon Nov 14 21:28:52 2005
New Revision: 344312
URL: http://svn.apache.org/viewcvs?rev=344312&view=rev
Log:
ConstantScoreRangeQuery addition: LUCENE-383
Added:
lucene/java/trunk/src/java/org/apache/lucene/search/ConstantScoreQuery.java
lucene/java/trunk/src/java/org/apache/lucene/search/ConstantScoreRangeQuery.java
lucene/java/trunk/src/test/org/apache/lucene/search/QueryUtils.java
lucene/java/trunk/src/test/org/apache/lucene/search/TestConstantScoreRangeQuery.java
Modified:
lucene/java/trunk/CHANGES.txt
Modified: lucene/java/trunk/CHANGES.txt
URL: http://svn.apache.org/viewcvs/lucene/java/trunk/CHANGES.txt?rev=344312&r1=344311&r2=344312&view=diff
==============================================================================
--- lucene/java/trunk/CHANGES.txt (original)
+++ lucene/java/trunk/CHANGES.txt Mon Nov 14 21:28:52 2005
@@ -169,6 +169,16 @@
These two new queries are not currently supported via QueryParser.
(Erik Hatcher)
+24. Added ConstantScoreQuery which wraps a filter and produces a score
+ equal to the query boost for every matching document.
+ (Yonik Seeley, LUCENE-383)
+
+25. Added ConstantScoreRangeQuery which produces a constant score for
+ every document in the range. One advantage over a normal RangeQuery
+ is that it doesn't expand to a BooleanQuery and thus doesn't have a maximum
+ number of terms the range can cover. Both endpoints may also be open.
+ (Yonik Seeley, LUCENE-383)
+
API Changes
1. Several methods and fields have been deprecated. The API documentation
Added: lucene/java/trunk/src/java/org/apache/lucene/search/ConstantScoreQuery.java
URL: http://svn.apache.org/viewcvs/lucene/java/trunk/src/java/org/apache/lucene/search/ConstantScoreQuery.java?rev=344312&view=auto
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/ConstantScoreQuery.java (added)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/ConstantScoreQuery.java Mon Nov 14 21:28:52 2005
@@ -0,0 +1,158 @@
+package org.apache.lucene.search;
+
+/**
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.index.IndexReader;
+
+import java.io.IOException;
+import java.util.BitSet;
+
+/**
+ * A query that wraps a filter and simply returns a constant score equal to the
+ * query boost for every document in the filter.
+ *
+ * @author yonik
+ * @version $Id$
+ */
+public class ConstantScoreQuery extends Query {
+ protected final Filter filter;
+
+ public ConstantScoreQuery(Filter filter) {
+ this.filter=filter;
+ }
+
+ public Query rewrite(IndexReader reader) throws IOException {
+ return this;
+ }
+
+ protected class ConstantWeight implements Weight {
+ private Searcher searcher;
+ private float queryNorm;
+ private float queryWeight;
+
+ public ConstantWeight(Searcher searcher) {
+ this.searcher = searcher;
+ }
+
+ public Query getQuery() {
+ return ConstantScoreQuery.this;
+ }
+
+ public float getValue() {
+ return queryWeight;
+ }
+
+ public float sumOfSquaredWeights() throws IOException {
+ queryWeight = getBoost();
+ return queryWeight * queryWeight;
+ }
+
+ public void normalize(float norm) {
+ this.queryNorm = norm;
+ queryWeight *= this.queryNorm;
+ }
+
+ public Scorer scorer(IndexReader reader) throws IOException {
+ return new ConstantScorer(getSimilarity(searcher), reader, this);
+ }
+
+ public Explanation explain(IndexReader reader, int doc) throws IOException {
+
+ ConstantScorer cs = (ConstantScorer)scorer(reader);
+ boolean exists = cs.bits.get(doc);
+
+ Explanation result = new Explanation();
+
+ if (exists) {
+ result.setDescription("ConstantScoreQuery(" + filter
+ + "), product of:");
+ result.setValue(queryWeight);
+ result.addDetail(new Explanation(getBoost(), "boost"));
+ result.addDetail(new Explanation(queryNorm,"queryNorm"));
+ } else {
+ result.setDescription("ConstantScoreQuery(" + filter
+ + ") doesn't match id " + doc);
+ result.setValue(0);
+ }
+ return result;
+ }
+ }
+
+ protected class ConstantScorer extends Scorer {
+ final BitSet bits;
+ final float theScore;
+ int doc=-1;
+
+ public ConstantScorer(Similarity similarity, IndexReader reader, Weight w) throws IOException {
+ super(similarity);
+ theScore = w.getValue();
+ bits = filter.bits(reader);
+ }
+
+ public boolean next() throws IOException {
+ doc = bits.nextSetBit(doc+1);
+ return doc >= 0;
+ }
+
+ public int doc() {
+ return doc;
+ }
+
+ public float score() throws IOException {
+ return theScore;
+ }
+
+ public boolean skipTo(int target) throws IOException {
+ doc = bits.nextSetBit(target); // requires JDK 1.4
+ return doc >= 0;
+ }
+
+ public Explanation explain(int doc) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+
+ protected Weight createWeight(Searcher searcher) {
+ return new ConstantScoreQuery.ConstantWeight(searcher);
+ }
+
+
+ /** Prints a user-readable version of this query. */
+ public String toString(String field)
+ {
+ return "ConstantScore(" + filter.toString()
+ + (getBoost()==1.0 ? ")" : "^" + getBoost());
+ }
+
+ /** Returns true if <code>o</code> is equal to this. */
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ConstantScoreQuery)) return false;
+ ConstantScoreQuery other = (ConstantScoreQuery)o;
+ return this.getBoost()==other.getBoost() && filter.equals(other.filter);
+ }
+
+ /** Returns a hash code value for this object. */
+ public int hashCode() {
+ // Simple add is OK since no existing filter hashcode has a float component.
+ return filter.hashCode() + Float.floatToIntBits(getBoost());
+ }
+
+}
+
+
Added: lucene/java/trunk/src/java/org/apache/lucene/search/ConstantScoreRangeQuery.java
URL: http://svn.apache.org/viewcvs/lucene/java/trunk/src/java/org/apache/lucene/search/ConstantScoreRangeQuery.java?rev=344312&view=auto
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/search/ConstantScoreRangeQuery.java (added)
+++ lucene/java/trunk/src/java/org/apache/lucene/search/ConstantScoreRangeQuery.java Mon Nov 14 21:28:52 2005
@@ -0,0 +1,138 @@
+package org.apache.lucene.search;
+
+/**
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.index.IndexReader;
+
+import java.io.IOException;
+
+/**
+ * A range query that returns a constant score equal to it's boost for
+ * all documents in the range.
+ * <p>
+ * It does not have an upper bound on the number of clauses covered in the range.
+ * <p>
+ * If an endpoint is null, it is said to be "open".
+ * Either or both endpoints may be open. Open endpoints may not be exclusive
+ * (you can't select all but the first or last term without explicitly specifying the term to exclude.)
+ *
+ * @author yonik
+ * @version $Id$
+ */
+
+public class ConstantScoreRangeQuery extends Query
+{
+ private final String fieldName;
+ private final String lowerVal;
+ private final String upperVal;
+ private final boolean includeLower;
+ private final boolean includeUpper;
+
+
+ public ConstantScoreRangeQuery(String fieldName, String lowerVal, String upperVal, boolean includeLower, boolean includeUpper)
+ {
+ // do a little bit of normalization...
+ // open ended range queries should always be inclusive.
+ if (lowerVal==null) {
+ includeLower=true;
+ } else if (includeLower && lowerVal.equals("")) {
+ lowerVal=null;
+ }
+ if (upperVal==null) {
+ includeUpper=true;
+ }
+
+
+ this.fieldName = fieldName.intern(); // intern it, just like terms...
+ this.lowerVal = lowerVal;
+ this.upperVal = upperVal;
+ this.includeLower = includeLower;
+ this.includeUpper = includeUpper;
+ }
+
+ /** Returns the field name for this query */
+ public String getField() { return fieldName; }
+ /** Returns the value of the lower endpoint of this range query, null if open ended */
+ public String getLowerVal() { return lowerVal; }
+ /** Returns the value of the upper endpoint of this range query, null if open ended */
+ public String getUpperVal() { return upperVal; }
+ /** Returns <code>true</code> if the lower endpoint is inclusive */
+ public boolean includesLower() { return includeLower; }
+ /** Returns <code>true</code> if the upper endpoint is inclusive */
+ public boolean includesUpper() { return includeUpper; }
+
+ public Query rewrite(IndexReader reader) throws IOException {
+ // Map to RangeFilter semantics which are slightly different...
+ RangeFilter rangeFilt = new RangeFilter(fieldName,
+ lowerVal!=null?lowerVal:"",
+ upperVal, lowerVal==""?false:includeLower, upperVal==null?false:includeUpper);
+ Query q = new ConstantScoreQuery(rangeFilt);
+ q.setBoost(getBoost());
+ return q;
+ }
+
+ /** Prints a user-readable version of this query. */
+ public String toString(String field)
+ {
+ StringBuffer buffer = new StringBuffer();
+ if (!getField().equals(field))
+ {
+ buffer.append(getField());
+ buffer.append(":");
+ }
+ buffer.append(includeLower ? '[' : '{');
+ buffer.append(lowerVal != null ? lowerVal : "*");
+ buffer.append(" TO ");
+ buffer.append(upperVal != null ? upperVal : "*");
+ buffer.append(includeUpper ? ']' : '}');
+ if (getBoost() != 1.0f)
+ {
+ buffer.append("^");
+ buffer.append(Float.toString(getBoost()));
+ }
+ return buffer.toString();
+ }
+
+ /** Returns true if <code>o</code> is equal to this. */
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ConstantScoreRangeQuery)) return false;
+ ConstantScoreRangeQuery other = (ConstantScoreRangeQuery) o;
+
+ if (this.fieldName != other.fieldName // interned comparison
+ || this.includeLower != other.includeLower
+ || this.includeUpper != other.includeUpper
+ ) { return false; }
+ if (this.lowerVal != null ? !this.lowerVal.equals(other.lowerVal) : other.lowerVal != null) return false;
+ if (this.upperVal != null ? !this.upperVal.equals(other.upperVal) : other.upperVal != null) return false;
+ return this.getBoost() == other.getBoost();
+ }
+
+ /** Returns a hash code value for this object.*/
+ public int hashCode() {
+ int h = Float.floatToIntBits(getBoost()) ^ fieldName.hashCode();
+ // hashCode of "" is 0, so don't use that for null...
+ h ^= lowerVal != null ? lowerVal.hashCode() : 0x965a965a;
+ // don't just XOR upperVal with out mixing either it or h, as it will cancel
+ // out lowerVal if they are equal.
+ h ^= (h << 17) | (h >>> 16); // a reversible (one to one) 32 bit mapping mix
+ h ^= (upperVal != null ? (upperVal.hashCode()) : 0x5a695a69);
+ h ^= (includeLower ? 0x665599aa : 0)
+ ^ (includeUpper ? 0x99aa5566 : 0);
+ return h;
+ }
+}
Added: lucene/java/trunk/src/test/org/apache/lucene/search/QueryUtils.java
URL: http://svn.apache.org/viewcvs/lucene/java/trunk/src/test/org/apache/lucene/search/QueryUtils.java?rev=344312&view=auto
==============================================================================
--- lucene/java/trunk/src/test/org/apache/lucene/search/QueryUtils.java (added)
+++ lucene/java/trunk/src/test/org/apache/lucene/search/QueryUtils.java Mon Nov 14 21:28:52 2005
@@ -0,0 +1,66 @@
+package org.apache.lucene.search;
+
+import junit.framework.TestCase;
+
+/**
+ * Copyright 2005 Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * @author yonik
+ */
+public class QueryUtils {
+
+ /** Check the types of things query objects should be able to do. */
+ public static void check(Query q) {
+ checkHashEquals(q);
+ }
+
+ /** check very basic hashCode and equals */
+ public static void checkHashEquals(Query q) {
+ Query q2 = (Query)q.clone();
+ checkEqual(q,q2);
+
+ Query q3 = (Query)q.clone();
+ q3.setBoost(7.21792348f);
+ checkUnequal(q,q3);
+
+ // test that a class check is done so that no exception is thrown
+ // in the implementation of equals()
+ Query whacky = new Query() {
+ public String toString(String field) {
+ return "My Whacky Query";
+ }
+ };
+ whacky.setBoost(q.getBoost());
+ checkUnequal(q, whacky);
+ }
+
+ public static void checkEqual(Query q1, Query q2) {
+ TestCase.assertEquals(q1, q2);
+ TestCase.assertEquals(q1.hashCode(), q2.hashCode());
+ }
+
+ public static void checkUnequal(Query q1, Query q2) {
+ TestCase.assertTrue(!q1.equals(q2));
+ TestCase.assertTrue(!q2.equals(q1));
+
+ // possible this test can fail on a hash collision... if that
+ // happens, please change test to use a different example.
+ TestCase.assertTrue(q1.hashCode() != q2.hashCode());
+ }
+
+}
Added: lucene/java/trunk/src/test/org/apache/lucene/search/TestConstantScoreRangeQuery.java
URL: http://svn.apache.org/viewcvs/lucene/java/trunk/src/test/org/apache/lucene/search/TestConstantScoreRangeQuery.java?rev=344312&view=auto
==============================================================================
--- lucene/java/trunk/src/test/org/apache/lucene/search/TestConstantScoreRangeQuery.java (added)
+++ lucene/java/trunk/src/test/org/apache/lucene/search/TestConstantScoreRangeQuery.java Mon Nov 14 21:28:52 2005
@@ -0,0 +1,347 @@
+package org.apache.lucene.search;
+
+/**
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.store.Directory;
+
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Term;
+
+import org.apache.lucene.analysis.WhitespaceAnalyzer;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+
+public class TestConstantScoreRangeQuery extends BaseTestRangeFilter {
+
+ /** threshold for comparing floats */
+ public static final float SCORE_COMP_THRESH = 1e-6f;
+
+ public TestConstantScoreRangeQuery(String name) {
+ super(name);
+ }
+ public TestConstantScoreRangeQuery() {
+ super();
+ }
+
+ Directory small;
+
+ void assertEquals(String m, float e, float a) {
+ assertEquals(m, e, a, SCORE_COMP_THRESH);
+ }
+
+ public void setUp() throws Exception {
+ super.setUp();
+
+ String[] data = new String [] {
+ "A 1 2 3 4 5 6",
+ "Z 4 5 6",
+ null,
+ "B 2 4 5 6",
+ "Y 3 5 6",
+ null,
+ "C 3 6",
+ "X 4 5 6"
+ };
+
+ small = new RAMDirectory();
+ IndexWriter writer = new IndexWriter(small,
+ new WhitespaceAnalyzer(),
+ true);
+
+ for (int i = 0; i < data.length; i++) {
+ Document doc = new Document();
+ doc.add(Field.Keyword("id",String.valueOf(i)));
+ doc.add(Field.Keyword("all","all"));
+ if (null != data[i]) {
+ doc.add(Field.Text("data",data[i]));
+ }
+ writer.addDocument(doc);
+ }
+
+ writer.optimize();
+ writer.close();
+ }
+
+
+
+ /** macro for readability */
+ public static Query csrq(String f, String l, String h,
+ boolean il, boolean ih) {
+ return new ConstantScoreRangeQuery(f,l,h,il,ih);
+ }
+
+ public void testBasics() throws IOException {
+ QueryUtils.check(csrq("data","1","6",T,T));
+ QueryUtils.check(csrq("data","A","Z",T,T));
+ QueryUtils.checkUnequal(csrq("data","1","6",T,T), csrq("data","A","Z",T,T));
+ }
+
+ public void testEqualScores() throws IOException {
+ // NOTE: uses index build in *this* setUp
+
+ IndexReader reader = IndexReader.open(small);
+ IndexSearcher search = new IndexSearcher(reader);
+
+ Hits result;
+
+ // some hits match more terms then others, score should be the same
+
+ result = search.search(csrq("data","1","6",T,T));
+ int numHits = result.length();
+ assertEquals("wrong number of results", 6, numHits);
+ float score = result.score(0);
+ for (int i = 1; i < numHits; i++) {
+ assertEquals("score for " + i +" was not the same",
+ score, result.score(i));
+ }
+
+ }
+
+ public void testBoost() throws IOException {
+ // NOTE: uses index build in *this* setUp
+
+ IndexReader reader = IndexReader.open(small);
+ IndexSearcher search = new IndexSearcher(reader);
+
+ // test for correct application of query normalization
+ // must use a non score normalizing method for this.
+ Query q = csrq("data","1","6",T,T);
+ q.setBoost(100);
+ search.search(q,null, new HitCollector() {
+ public void collect(int doc, float score) {
+ assertEquals("score for doc " + doc +" was not correct",
+ 1.0f, score);
+ }
+ });
+
+
+ //
+ // Ensure that boosting works to score one clause of a query higher
+ // than another.
+ //
+ Query q1 = csrq("data","A","A",T,T); // matches document #0
+ q1.setBoost(.1f);
+ Query q2 = csrq("data","Z","Z",T,T); // matches document #1
+ BooleanQuery bq = new BooleanQuery(true);
+ bq.add(q1, BooleanClause.Occur.SHOULD);
+ bq.add(q2, BooleanClause.Occur.SHOULD);
+
+ Hits hits = search.search(bq);
+ assertEquals(1, hits.id(0));
+ assertEquals(0, hits.id(1));
+ assertTrue(hits.score(0) > hits.score(1));
+
+ q1 = csrq("data","A","A",T,T); // matches document #0
+ q1.setBoost(10f);
+ q2 = csrq("data","Z","Z",T,T); // matches document #1
+ bq = new BooleanQuery(true);
+ bq.add(q1, BooleanClause.Occur.SHOULD);
+ bq.add(q2, BooleanClause.Occur.SHOULD);
+
+ hits = search.search(bq);
+ assertEquals(0, hits.id(0));
+ assertEquals(1, hits.id(1));
+ assertTrue(hits.score(0) > hits.score(1));
+ }
+
+
+ public void testBooleanOrderUnAffected() throws IOException {
+ // NOTE: uses index build in *this* setUp
+
+ IndexReader reader = IndexReader.open(small);
+ IndexSearcher search = new IndexSearcher(reader);
+
+ // first do a regular RangeQuery which uses term expansion so
+ // docs with more terms in range get higher scores
+
+ Query rq = new RangeQuery(new Term("data","1"),new Term("data","4"),T);
+
+ Hits expected = search.search(rq);
+ int numHits = expected.length();
+
+ // now do a boolean where which also contains a
+ // ConstantScoreRangeQuery and make sure hte order is the same
+
+ BooleanQuery q = new BooleanQuery();
+ q.add(rq, T, F);
+ q.add(csrq("data","1","6", T, T), T, F);
+
+ Hits actual = search.search(q);
+
+ assertEquals("wrong numebr of hits", numHits, actual.length());
+ for (int i = 0; i < numHits; i++) {
+ assertEquals("mismatch in docid for hit#"+i,
+ expected.id(i), actual.id(i));
+ }
+
+ }
+
+
+
+
+
+ public void testRangeQueryId() throws IOException {
+ // NOTE: uses index build in *super* setUp
+
+ IndexReader reader = IndexReader.open(index);
+ IndexSearcher search = new IndexSearcher(reader);
+
+ int medId = ((maxId - minId) / 2);
+
+ String minIP = pad(minId);
+ String maxIP = pad(maxId);
+ String medIP = pad(medId);
+
+ int numDocs = reader.numDocs();
+
+ assertEquals("num of docs", numDocs, 1+ maxId - minId);
+
+ Hits result;
+
+ // test id, bounded on both ends
+
+ result = search.search(csrq("id",minIP,maxIP,T,T));
+ assertEquals("find all", numDocs, result.length());
+
+ result = search.search(csrq("id",minIP,maxIP,T,F));
+ assertEquals("all but last", numDocs-1, result.length());
+
+ result = search.search(csrq("id",minIP,maxIP,F,T));
+ assertEquals("all but first", numDocs-1, result.length());
+
+ result = search.search(csrq("id",minIP,maxIP,F,F));
+ assertEquals("all but ends", numDocs-2, result.length());
+
+ result = search.search(csrq("id",medIP,maxIP,T,T));
+ assertEquals("med and up", 1+ maxId-medId, result.length());
+
+ result = search.search(csrq("id",minIP,medIP,T,T));
+ assertEquals("up to med", 1+ medId-minId, result.length());
+
+ // unbounded id
+
+ result = search.search(csrq("id",minIP,null,T,F));
+ assertEquals("min and up", numDocs, result.length());
+
+ result = search.search(csrq("id",null,maxIP,F,T));
+ assertEquals("max and down", numDocs, result.length());
+
+ result = search.search(csrq("id",minIP,null,F,F));
+ assertEquals("not min, but up", numDocs-1, result.length());
+
+ result = search.search(csrq("id",null,maxIP,F,F));
+ assertEquals("not max, but down", numDocs-1, result.length());
+
+ result = search.search(csrq("id",medIP,maxIP,T,F));
+ assertEquals("med and up, not max", maxId-medId, result.length());
+
+ result = search.search(csrq("id",minIP,medIP,F,T));
+ assertEquals("not min, up to med", medId-minId, result.length());
+
+ // very small sets
+
+ result = search.search(csrq("id",minIP,minIP,F,F));
+ assertEquals("min,min,F,F", 0, result.length());
+ result = search.search(csrq("id",medIP,medIP,F,F));
+ assertEquals("med,med,F,F", 0, result.length());
+ result = search.search(csrq("id",maxIP,maxIP,F,F));
+ assertEquals("max,max,F,F", 0, result.length());
+
+ result = search.search(csrq("id",minIP,minIP,T,T));
+ assertEquals("min,min,T,T", 1, result.length());
+ result = search.search(csrq("id",null,minIP,F,T));
+ assertEquals("nul,min,F,T", 1, result.length());
+
+ result = search.search(csrq("id",maxIP,maxIP,T,T));
+ assertEquals("max,max,T,T", 1, result.length());
+ result = search.search(csrq("id",maxIP,null,T,F));
+ assertEquals("max,nul,T,T", 1, result.length());
+
+ result = search.search(csrq("id",medIP,medIP,T,T));
+ assertEquals("med,med,T,T", 1, result.length());
+
+ }
+
+ public void testRangeQueryRand() throws IOException {
+ // NOTE: uses index build in *super* setUp
+
+ IndexReader reader = IndexReader.open(index);
+ IndexSearcher search = new IndexSearcher(reader);
+
+ String minRP = pad(minR);
+ String maxRP = pad(maxR);
+
+ int numDocs = reader.numDocs();
+
+ assertEquals("num of docs", numDocs, 1+ maxId - minId);
+
+ Hits result;
+ Query q = new TermQuery(new Term("body","body"));
+
+ // test extremes, bounded on both ends
+
+ result = search.search(csrq("rand",minRP,maxRP,T,T));
+ assertEquals("find all", numDocs, result.length());
+
+ result = search.search(csrq("rand",minRP,maxRP,T,F));
+ assertEquals("all but biggest", numDocs-1, result.length());
+
+ result = search.search(csrq("rand",minRP,maxRP,F,T));
+ assertEquals("all but smallest", numDocs-1, result.length());
+
+ result = search.search(csrq("rand",minRP,maxRP,F,F));
+ assertEquals("all but extremes", numDocs-2, result.length());
+
+ // unbounded
+
+ result = search.search(csrq("rand",minRP,null,T,F));
+ assertEquals("smallest and up", numDocs, result.length());
+
+ result = search.search(csrq("rand",null,maxRP,F,T));
+ assertEquals("biggest and down", numDocs, result.length());
+
+ result = search.search(csrq("rand",minRP,null,F,F));
+ assertEquals("not smallest, but up", numDocs-1, result.length());
+
+ result = search.search(csrq("rand",null,maxRP,F,F));
+ assertEquals("not biggest, but down", numDocs-1, result.length());
+
+ // very small sets
+
+ result = search.search(csrq("rand",minRP,minRP,F,F));
+ assertEquals("min,min,F,F", 0, result.length());
+ result = search.search(csrq("rand",maxRP,maxRP,F,F));
+ assertEquals("max,max,F,F", 0, result.length());
+
+ result = search.search(csrq("rand",minRP,minRP,T,T));
+ assertEquals("min,min,T,T", 1, result.length());
+ result = search.search(csrq("rand",null,minRP,F,T));
+ assertEquals("nul,min,F,T", 1, result.length());
+
+ result = search.search(csrq("rand",maxRP,maxRP,T,T));
+ assertEquals("max,max,T,T", 1, result.length());
+ result = search.search(csrq("rand",maxRP,null,T,F));
+ assertEquals("max,nul,T,T", 1, result.length());
+
+ }
+
+}