Index: contrib/queries/src/java/org/apache/lucene/search/trie/TrieRangeFilter.java
===================================================================
--- contrib/queries/src/java/org/apache/lucene/search/trie/TrieRangeFilter.java (revision 737879)
+++ contrib/queries/src/java/org/apache/lucene/search/trie/TrieRangeFilter.java (working copy)
@@ -39,35 +39,57 @@
/**
* Universal constructor (expert use only): Uses already trie-converted min/max values.
* You can set min or max (but not both) to null to leave one bound open.
+ * With minInclusive and maxInclusive can be choosen, if the corresponding
+ * bound should be included or excluded from the range.
*/
- public TrieRangeFilter(final String field, final String min, final String max, final TrieUtils variant) {
+ public TrieRangeFilter(final String field, String min, String max,
+ final boolean minInclusive, final boolean maxInclusive, final TrieUtils variant
+ ) {
if (min==null && max==null) throw new IllegalArgumentException("The min and max values cannot be both null.");
this.trieVariant=variant;
+ this.field=field.intern();
+ // just for toString()
this.minUnconverted=min;
this.maxUnconverted=max;
- this.min=(min==null) ? trieVariant.TRIE_CODED_NUMERIC_MIN : min;
- this.max=(max==null) ? trieVariant.TRIE_CODED_NUMERIC_MAX : max;
- this.field=field.intern();
+ this.minInclusive=minInclusive;
+ this.maxInclusive=maxInclusive;
+ // encode bounds
+ this.min=(min==null) ? trieVariant.TRIE_CODED_NUMERIC_MIN : (
+ minInclusive ? min : variant.incrementTrieCoded(min)
+ );
+ this.max=(max==null) ? trieVariant.TRIE_CODED_NUMERIC_MAX : (
+ maxInclusive ? max : variant.decrementTrieCoded(max)
+ );
}
/**
* Universal constructor (expert use only): Uses already trie-converted min/max values.
* You can set min or max (but not both) to null to leave one bound open.
- *
This constructor uses the trie package returned by {@link TrieUtils#getDefaultTrieVariant()}.
+ * With minInclusive and maxInclusive can be choosen, if the corresponding
+ * bound should be included or excluded from the range.
+ *
This constructor uses the trie variant returned by {@link TrieUtils#getDefaultTrieVariant()}.
*/
- public TrieRangeFilter(final String field, final String min, final String max) {
- this(field,min,max,TrieUtils.getDefaultTrieVariant());
+ public TrieRangeFilter(final String field, final String min, final String max,
+ final boolean minInclusive, final boolean maxInclusive
+ ) {
+ this(field,min,max,minInclusive,maxInclusive,TrieUtils.getDefaultTrieVariant());
}
/**
* Generates a trie query using the supplied field with range bounds in numeric form (double).
* You can set min or max (but not both) to null to leave one bound open.
+ * With minInclusive and maxInclusive can be choosen, if the corresponding
+ * bound should be included or excluded from the range.
*/
- public TrieRangeFilter(final String field, final Double min, final Double max, final TrieUtils variant) {
+ public TrieRangeFilter(final String field, final Double min, final Double max,
+ final boolean minInclusive, final boolean maxInclusive, final TrieUtils variant
+ ) {
this(
field,
(min==null) ? null : variant.doubleToTrieCoded(min.doubleValue()),
(max==null) ? null : variant.doubleToTrieCoded(max.doubleValue()),
+ minInclusive,
+ maxInclusive,
variant
);
this.minUnconverted=min;
@@ -77,21 +99,31 @@
/**
* Generates a trie query using the supplied field with range bounds in numeric form (double).
* You can set min or max (but not both) to null to leave one bound open.
+ * With minInclusive and maxInclusive can be choosen, if the corresponding
+ * bound should be included or excluded from the range.
*
This constructor uses the trie variant returned by {@link TrieUtils#getDefaultTrieVariant()}.
*/
- public TrieRangeFilter(final String field, final Double min, final Double max) {
- this(field,min,max,TrieUtils.getDefaultTrieVariant());
+ public TrieRangeFilter(final String field, final Double min, final Double max,
+ final boolean minInclusive, final boolean maxInclusive
+ ) {
+ this(field,min,max,minInclusive,maxInclusive,TrieUtils.getDefaultTrieVariant());
}
/**
* Generates a trie query using the supplied field with range bounds in date/time form.
* You can set min or max (but not both) to null to leave one bound open.
+ * With minInclusive and maxInclusive can be choosen, if the corresponding
+ * bound should be included or excluded from the range.
*/
- public TrieRangeFilter(final String field, final Date min, final Date max, final TrieUtils variant) {
+ public TrieRangeFilter(final String field, final Date min, final Date max,
+ final boolean minInclusive, final boolean maxInclusive, final TrieUtils variant
+ ) {
this(
field,
(min==null) ? null : variant.dateToTrieCoded(min),
(max==null) ? null : variant.dateToTrieCoded(max),
+ minInclusive,
+ maxInclusive,
variant
);
this.minUnconverted=min;
@@ -101,21 +133,31 @@
/**
* Generates a trie query using the supplied field with range bounds in date/time form.
* You can set min or max (but not both) to null to leave one bound open.
+ * With minInclusive and maxInclusive can be choosen, if the corresponding
+ * bound should be included or excluded from the range.
*
This constructor uses the trie variant returned by {@link TrieUtils#getDefaultTrieVariant()}.
*/
- public TrieRangeFilter(final String field, final Date min, final Date max) {
- this(field,min,max,TrieUtils.getDefaultTrieVariant());
+ public TrieRangeFilter(final String field, final Date min, final Date max,
+ final boolean minInclusive, final boolean maxInclusive
+ ) {
+ this(field,min,max,minInclusive,maxInclusive,TrieUtils.getDefaultTrieVariant());
}
/**
* Generates a trie query using the supplied field with range bounds in integer form (long).
* You can set min or max (but not both) to null to leave one bound open.
+ * With minInclusive and maxInclusive can be choosen, if the corresponding
+ * bound should be included or excluded from the range.
*/
- public TrieRangeFilter(final String field, final Long min, final Long max, final TrieUtils variant) {
+ public TrieRangeFilter(final String field, final Long min, final Long max,
+ final boolean minInclusive, final boolean maxInclusive, final TrieUtils variant
+ ) {
this(
field,
(min==null) ? null : variant.longToTrieCoded(min.longValue()),
(max==null) ? null : variant.longToTrieCoded(max.longValue()),
+ minInclusive,
+ maxInclusive,
variant
);
this.minUnconverted=min;
@@ -125,10 +167,14 @@
/**
* Generates a trie query using the supplied field with range bounds in integer form (long).
* You can set min or max (but not both) to null to leave one bound open.
+ * With minInclusive and maxInclusive can be choosen, if the corresponding
+ * bound should be included or excluded from the range.
*
This constructor uses the trie variant returned by {@link TrieUtils#getDefaultTrieVariant()}.
*/
- public TrieRangeFilter(final String field, final Long min, final Long max) {
- this(field,min,max,TrieUtils.getDefaultTrieVariant());
+ public TrieRangeFilter(final String field, final Long min, final Long max,
+ final boolean minInclusive, final boolean maxInclusive
+ ) {
+ this(field,min,max,minInclusive,maxInclusive,TrieUtils.getDefaultTrieVariant());
}
//@Override
@@ -139,14 +185,21 @@
public String toString(final String field) {
final StringBuffer sb=new StringBuffer();
if (!this.field.equals(field)) sb.append(this.field).append(':');
- return sb.append('[').append(minUnconverted).append(" TO ").append(maxUnconverted).append(']').toString();
+ return sb.append(minInclusive ? '[' : '{')
+ .append((minUnconverted==null) ? "*" : minUnconverted.toString())
+ .append(" TO ")
+ .append((maxUnconverted==null) ? "*" : maxUnconverted.toString())
+ .append(maxInclusive ? ']' : '}').toString();
}
//@Override
public final boolean equals(final Object o) {
if (o instanceof TrieRangeFilter) {
TrieRangeFilter q=(TrieRangeFilter)o;
- // trieVariants are singleton per type, so no equals needed
+ /* trieVariants are singleton per type, so no equals needed.
+ * TrieRangeFilters with different datatypes but same encoded
+ * bounds (with increment/decrement for exclusive) are considered equal.
+ */
return (field==q.field && min.equals(q.min) && max.equals(q.max) && trieVariant==q.trieVariant);
} else return false;
}
@@ -282,6 +335,7 @@
// members
private final String field,min,max;
private final TrieUtils trieVariant;
+ private final boolean minInclusive,maxInclusive;
private Object minUnconverted,maxUnconverted;
private int lastNumberOfTerms=-1;
}
Index: contrib/queries/src/java/org/apache/lucene/search/trie/TrieRangeQuery.java
===================================================================
--- contrib/queries/src/java/org/apache/lucene/search/trie/TrieRangeQuery.java (revision 737879)
+++ contrib/queries/src/java/org/apache/lucene/search/trie/TrieRangeQuery.java (working copy)
@@ -34,69 +34,101 @@
/**
* Universal constructor (expert use only): Uses already trie-converted min/max values.
* You can set min or max (but not both) to null to leave one bound open.
+ * With minInclusive and maxInclusive can be choosen, if the corresponding
+ * bound should be included or excluded from the range.
*
This constructor uses the trie variant returned by {@link TrieUtils#getDefaultTrieVariant()}.
*/
- public TrieRangeQuery(final String field, final String min, final String max) {
- super(new TrieRangeFilter(field,min,max));
+ public TrieRangeQuery(final String field, final String min, final String max,
+ final boolean minInclusive, final boolean maxInclusive
+ ) {
+ super(new TrieRangeFilter(field,min,max,minInclusive,maxInclusive));
}
/**
* Universal constructor (expert use only): Uses already trie-converted min/max values.
* You can set min or max (but not both) to null to leave one bound open.
+ * With minInclusive and maxInclusive can be choosen, if the corresponding
+ * bound should be included or excluded from the range.
*/
- public TrieRangeQuery(final String field, final String min, final String max, final TrieUtils variant) {
- super(new TrieRangeFilter(field,min,max,variant));
+ public TrieRangeQuery(final String field, final String min, final String max,
+ final boolean minInclusive, final boolean maxInclusive, final TrieUtils variant
+ ) {
+ super(new TrieRangeFilter(field,min,max,minInclusive,maxInclusive,variant));
}
/**
* A trie query using the supplied field with range bounds in numeric form (double).
* You can set min or max (but not both) to null to leave one bound open.
+ * With minInclusive and maxInclusive can be choosen, if the corresponding
+ * bound should be included or excluded from the range.
*
This constructor uses the trie variant returned by {@link TrieUtils#getDefaultTrieVariant()}.
*/
- public TrieRangeQuery(final String field, final Double min, final Double max) {
- super(new TrieRangeFilter(field,min,max));
+ public TrieRangeQuery(final String field, final Double min, final Double max,
+ final boolean minInclusive, final boolean maxInclusive
+ ) {
+ super(new TrieRangeFilter(field,min,max,minInclusive,maxInclusive));
}
/**
* A trie query using the supplied field with range bounds in numeric form (double).
* You can set min or max (but not both) to null to leave one bound open.
+ * With minInclusive and maxInclusive can be choosen, if the corresponding
+ * bound should be included or excluded from the range.
*/
- public TrieRangeQuery(final String field, final Double min, final Double max, final TrieUtils variant) {
- super(new TrieRangeFilter(field,min,max,variant));
+ public TrieRangeQuery(final String field, final Double min, final Double max,
+ final boolean minInclusive, final boolean maxInclusive, final TrieUtils variant
+ ) {
+ super(new TrieRangeFilter(field,min,max,minInclusive,maxInclusive,variant));
}
/**
* A trie query using the supplied field with range bounds in date/time form.
* You can set min or max (but not both) to null to leave one bound open.
+ * With minInclusive and maxInclusive can be choosen, if the corresponding
+ * bound should be included or excluded from the range.
*
This constructor uses the trie variant returned by {@link TrieUtils#getDefaultTrieVariant()}.
*/
- public TrieRangeQuery(final String field, final Date min, final Date max) {
- super(new TrieRangeFilter(field,min,max));
+ public TrieRangeQuery(final String field, final Date min, final Date max,
+ final boolean minInclusive, final boolean maxInclusive
+ ) {
+ super(new TrieRangeFilter(field,min,max,minInclusive,maxInclusive));
}
/**
* A trie query using the supplied field with range bounds in date/time form.
* You can set min or max (but not both) to null to leave one bound open.
+ * With minInclusive and maxInclusive can be choosen, if the corresponding
+ * bound should be included or excluded from the range.
*/
- public TrieRangeQuery(final String field, final Date min, final Date max, final TrieUtils variant) {
- super(new TrieRangeFilter(field,min,max,variant));
+ public TrieRangeQuery(final String field, final Date min, final Date max,
+ final boolean minInclusive, final boolean maxInclusive, final TrieUtils variant
+ ) {
+ super(new TrieRangeFilter(field,min,max,minInclusive,maxInclusive,variant));
}
/**
* A trie query using the supplied field with range bounds in integer form (long).
* You can set min or max (but not both) to null to leave one bound open.
+ * With minInclusive and maxInclusive can be choosen, if the corresponding
+ * bound should be included or excluded from the range.
*
This constructor uses the trie variant returned by {@link TrieUtils#getDefaultTrieVariant()}.
*/
- public TrieRangeQuery(final String field, final Long min, final Long max) {
- super(new TrieRangeFilter(field,min,max));
+ public TrieRangeQuery(final String field, final Long min, final Long max,
+ final boolean minInclusive, final boolean maxInclusive
+ ) {
+ super(new TrieRangeFilter(field,min,max,minInclusive,maxInclusive));
}
/**
* A trie query using the supplied field with range bounds in integer form (long).
* You can set min or max (but not both) to null to leave one bound open.
+ * With minInclusive and maxInclusive can be choosen, if the corresponding
+ * bound should be included or excluded from the range.
*/
- public TrieRangeQuery(final String field, final Long min, final Long max, final TrieUtils variant) {
- super(new TrieRangeFilter(field,min,max,variant));
+ public TrieRangeQuery(final String field, final Long min, final Long max,
+ final boolean minInclusive, final boolean maxInclusive, final TrieUtils variant
+ ) {
+ super(new TrieRangeFilter(field,min,max,minInclusive,maxInclusive,variant));
}
/**
Index: contrib/queries/src/test/org/apache/lucene/search/trie/TestTrieRangeQuery.java
===================================================================
--- contrib/queries/src/test/org/apache/lucene/search/trie/TestTrieRangeQuery.java (revision 737879)
+++ contrib/queries/src/test/org/apache/lucene/search/trie/TestTrieRangeQuery.java (working copy)
@@ -58,7 +58,7 @@
TrieUtils.VARIANT_2BIT.addLongTrieCodedDocumentField(
doc, "field2", distance*l, true /*index it*/, Field.Store.YES
);
- // add ascending fields with a distance of 1 to test the correct splitting of range
+ // add ascending fields with a distance of 1 to test the correct splitting of range and inclusive/exclusive
TrieUtils.VARIANT_8BIT.addLongTrieCodedDocumentField(
doc, "ascfield8", l, true /*index it*/, Field.Store.NO
);
@@ -83,7 +83,7 @@
String field="field"+variant.TRIE_BITS;
int count=3000;
long lower=96666L, upper=lower + count*distance + 1234L;
- TrieRangeQuery q=new TrieRangeQuery(field, new Long(lower), new Long(upper), variant);
+ TrieRangeQuery q=new TrieRangeQuery(field, new Long(lower), new Long(upper), true, true, variant);
TopDocs topDocs = searcher.search(q, null, 10000, Sort.INDEXORDER);
System.out.println("Found "+q.getLastNumberOfTerms()+" distinct terms in range for field '"+field+"'.");
ScoreDoc[] sd = topDocs.scoreDocs;
@@ -111,7 +111,7 @@
String field="field"+variant.TRIE_BITS;
int count=3000;
long upper=(count-1)*distance + 1234L;
- TrieRangeQuery q=new TrieRangeQuery(field, null, new Long(upper), variant);
+ TrieRangeQuery q=new TrieRangeQuery(field, null, new Long(upper), true, true, variant);
TopDocs topDocs = searcher.search(q, null, 10000, Sort.INDEXORDER);
System.out.println("Found "+q.getLastNumberOfTerms()+" distinct terms in left open range for field '"+field+"'.");
ScoreDoc[] sd = topDocs.scoreDocs;
@@ -141,12 +141,34 @@
for (int i=0; i<50; i++) {
long lower=(long)(rnd.nextDouble()*10000L*distance);
long upper=(long)(rnd.nextDouble()*10000L*distance);
- TrieRangeQuery tq=new TrieRangeQuery(field, new Long(lower), new Long(upper), variant);
+ // test inclusive range
+ TrieRangeQuery tq=new TrieRangeQuery(field, new Long(lower), new Long(upper), true, true, variant);
RangeQuery cq=new RangeQuery(field, variant.longToTrieCoded(lower), variant.longToTrieCoded(upper), true, true);
cq.setConstantScoreRewrite(true);
TopDocs tTopDocs = searcher.search(tq, 1);
TopDocs cTopDocs = searcher.search(cq, 1);
assertEquals("Returned count for TrieRangeQuery and RangeQuery must be equal", tTopDocs.totalHits, cTopDocs.totalHits );
+ // test exclusive range
+ tq=new TrieRangeQuery(field, new Long(lower), new Long(upper), false, false, variant);
+ cq=new RangeQuery(field, variant.longToTrieCoded(lower), variant.longToTrieCoded(upper), false, false);
+ cq.setConstantScoreRewrite(true);
+ tTopDocs = searcher.search(tq, 1);
+ cTopDocs = searcher.search(cq, 1);
+ assertEquals("Returned count for TrieRangeQuery and RangeQuery must be equal", tTopDocs.totalHits, cTopDocs.totalHits );
+ // test left exclusive range
+ tq=new TrieRangeQuery(field, new Long(lower), new Long(upper), false, true, variant);
+ cq=new RangeQuery(field, variant.longToTrieCoded(lower), variant.longToTrieCoded(upper), false, true);
+ cq.setConstantScoreRewrite(true);
+ tTopDocs = searcher.search(tq, 1);
+ cTopDocs = searcher.search(cq, 1);
+ assertEquals("Returned count for TrieRangeQuery and RangeQuery must be equal", tTopDocs.totalHits, cTopDocs.totalHits );
+ // test right exclusive range
+ tq=new TrieRangeQuery(field, new Long(lower), new Long(upper), true, false, variant);
+ cq=new RangeQuery(field, variant.longToTrieCoded(lower), variant.longToTrieCoded(upper), true, false);
+ cq.setConstantScoreRewrite(true);
+ tTopDocs = searcher.search(tq, 1);
+ cTopDocs = searcher.search(cq, 1);
+ assertEquals("Returned count for TrieRangeQuery and RangeQuery must be equal", tTopDocs.totalHits, cTopDocs.totalHits );
}
}
@@ -171,9 +193,22 @@
if (lower>upper) {
long a=lower; lower=upper; upper=a;
}
- TrieRangeQuery tq=new TrieRangeQuery(field, new Long(lower), new Long(upper), variant);
+ // test inclusive range
+ TrieRangeQuery tq=new TrieRangeQuery(field, new Long(lower), new Long(upper), true, true, variant);
TopDocs tTopDocs = searcher.search(tq, 1);
assertEquals("Returned count of range query must be equal to inclusive range length", tTopDocs.totalHits, upper-lower+1 );
+ // test exclusive range
+ tq=new TrieRangeQuery(field, new Long(lower), new Long(upper), false, false, variant);
+ tTopDocs = searcher.search(tq, 1);
+ assertEquals("Returned count of range query must be equal to exclusive range length", tTopDocs.totalHits, upper-lower-1 );
+ // test left exclusive range
+ tq=new TrieRangeQuery(field, new Long(lower), new Long(upper), false, true, variant);
+ tTopDocs = searcher.search(tq, 1);
+ assertEquals("Returned count of range query must be equal to half exclusive range length", tTopDocs.totalHits, upper-lower );
+ // test right exclusive range
+ tq=new TrieRangeQuery(field, new Long(lower), new Long(upper), true, false, variant);
+ tTopDocs = searcher.search(tq, 1);
+ assertEquals("Returned count of range query must be equal to half exclusive range length", tTopDocs.totalHits, upper-lower );
}
}
@@ -199,7 +234,7 @@
if (lower>upper) {
long a=lower; lower=upper; upper=a;
}
- TrieRangeQuery tq=new TrieRangeQuery(field, new Long(lower), new Long(upper), variant);
+ TrieRangeQuery tq=new TrieRangeQuery(field, new Long(lower), new Long(upper), true, true, variant);
TopDocs topDocs = searcher.search(tq, null, 10000, new Sort(variant.getSortField(field, true)));
if (topDocs.totalHits==0) continue;
ScoreDoc[] sd = topDocs.scoreDocs;