SOLR-4504: Fixed CurrencyField range queries to correctly exclude documents w/o values

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1450304 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Chris M. Hostetter 2013-02-26 17:46:17 +00:00
parent ec2978de6d
commit fe0ca7336f
3 changed files with 61 additions and 8 deletions

View File

@ -173,6 +173,9 @@ Bug Fixes
AnalysisSPILoader when doing concurrent core loads in multicore
Solr configs. (Uwe Schindler, Hossman)
* SOLR-4504: Fixed CurrencyField range queries to correctly exclude
documents w/o values (hossman)
Optimizations
----------------------

View File

@ -24,6 +24,9 @@ import org.apache.lucene.queries.function.FunctionValues;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.FieldValueFilter;
import org.apache.lucene.queries.ChainedFilter;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.response.TextResponseWriter;
@ -240,10 +243,19 @@ public class CurrencyField extends FieldType implements SchemaAware, ResourceLoa
public Query getRangeQuery(QParser parser, SchemaField field, final CurrencyValue p1, final CurrencyValue p2, final boolean minInclusive, final boolean maxInclusive) {
String currencyCode = (p1 != null) ? p1.getCurrencyCode() :
(p2 != null) ? p2.getCurrencyCode() : defaultCurrency;
final CurrencyValueSource vs = new CurrencyValueSource(field, currencyCode, parser);
return new SolrConstantScoreQuery(new ValueSourceRangeFilter(vs,
p1 == null ? null : p1.getAmount() + "" , p2 == null ? null : p2.getAmount() + "", minInclusive, maxInclusive));
// ValueSourceRangeFilter doesn't check exists(), so we have to
final Filter docsWithValues = new FieldValueFilter(getAmountField(field).getName());
final Filter vsRangeFilter = new ValueSourceRangeFilter
(new CurrencyValueSource(field, currencyCode, parser),
p1 == null ? null : p1.getAmount() + "",
p2 == null ? null : p2.getAmount() + "",
minInclusive, maxInclusive);
final Filter docsInRange = new ChainedFilter
(new Filter [] { docsWithValues, vsRangeFilter }, ChainedFilter.AND);
return new SolrConstantScoreQuery(docsInRange);
}
@Override
@ -315,8 +327,21 @@ public class CurrencyField extends FieldType implements SchemaAware, ResourceLoa
}
}
@Override
public boolean exists(int doc) {
return amounts.exists(doc);
}
@Override
public long longVal(int doc) {
long amount = amounts.longVal(doc);
// bail fast using whatever ammounts defaults to if no value
// (if we don't do this early, currencyOrd may be < 0,
// causing index bounds exception
if ( ! exists(doc) ) {
return amount;
}
if (!initializedCache) {
for (int i = 0; i < fractionDigitCache.length; i++) {
fractionDigitCache[i] = -1;
@ -325,7 +350,6 @@ public class CurrencyField extends FieldType implements SchemaAware, ResourceLoa
initializedCache = true;
}
long amount = amounts.longVal(doc);
int currencyOrd = currencies.ordVal(doc);
if (currencyOrd == targetCurrencyOrd) {

View File

@ -109,9 +109,28 @@ public class CurrencyFieldTest extends SolrTestCaseJ4 {
@Test
public void testCurrencyRangeSearch() throws Exception {
clearIndex();
final int emptyDocs = atLeast(50); // times 2
final int negDocs = atLeast(5);
assertU(adoc("id", "0", "amount", "0,USD")); // 0
// lots of docs w/o values
for (int i = 100; i <= 100 + emptyDocs; i++) {
assertU(adoc("id", "" + i));
}
// docs with values in ranges we'll query
for (int i = 1; i <= 10; i++) {
assertU(adoc("id", "" + i, "amount", i + ",USD"));
}
// more docs w/o values
for (int i = 500; i <= 500 + emptyDocs; i++) {
assertU(adoc("id", "" + i));
}
// some negative values
for (int i = -100; i > -100 - negDocs; i--) {
assertU(adoc("id", "" + i, "amount", i + ",USD"));
}
assertU(adoc("id", "40", "amount", "0,USD")); // 0
assertU(commit());
@ -145,22 +164,22 @@ public class CurrencyFieldTest extends SolrTestCaseJ4 {
// Open ended ranges without currency
assertQ(req("fl", "*,score", "q",
"amount:[* TO *]"),
"//*[@numFound='10']");
"//*[@numFound='" + (2 + 10 + negDocs) + "']");
// Open ended ranges with currency
assertQ(req("fl", "*,score", "q",
"amount:[*,EUR TO *,EUR]"),
"//*[@numFound='10']");
"//*[@numFound='" + (2 + 10 + negDocs) + "']");
// Open ended start range without currency
assertQ(req("fl", "*,score", "q",
"amount:[* TO 5,USD]"),
"//*[@numFound='5']");
"//*[@numFound='" + (2 + 5 + negDocs) + "']");
// Open ended start range with currency (currency for the * won't matter)
assertQ(req("fl", "*,score", "q",
"amount:[*,USD TO 5,USD]"),
"//*[@numFound='5']");
"//*[@numFound='" + (2 + 5 + negDocs) + "']");
// Open ended end range
assertQ(req("fl", "*,score", "q",
@ -170,6 +189,7 @@ public class CurrencyFieldTest extends SolrTestCaseJ4 {
@Test
public void testCurrencyPointQuery() throws Exception {
clearIndex();
assertU(adoc("id", "" + 1, "amount", "10.00,USD"));
assertU(adoc("id", "" + 2, "amount", "15.00,EUR"));
assertU(commit());
@ -184,6 +204,8 @@ public class CurrencyFieldTest extends SolrTestCaseJ4 {
@Ignore
public void testPerformance() throws Exception {
clearIndex();
Random r = random();
int initDocs = 200000;
@ -225,6 +247,8 @@ public class CurrencyFieldTest extends SolrTestCaseJ4 {
@Test
public void testCurrencySort() throws Exception {
clearIndex();
assertU(adoc("id", "" + 1, "amount", "10.00,USD"));
assertU(adoc("id", "" + 2, "amount", "15.00,EUR"));
assertU(adoc("id", "" + 3, "amount", "7.00,EUR"));
@ -238,6 +262,8 @@ public class CurrencyFieldTest extends SolrTestCaseJ4 {
@Test
public void testMockFieldType() throws Exception {
clearIndex();
assertU(adoc("id", "1", "mock_amount", "1.00,USD"));
assertU(adoc("id", "2", "mock_amount", "1.00,EUR"));
assertU(adoc("id", "3", "mock_amount", "1.00,NOK"));