Bah ... i swear i ran "ant precommit" on this, and yet i just found doclint problems (merging to 4x) ... fix coming.
: Date: Mon, 04 Mar 2013 20:25:37 -0000 : From: [email protected] : Reply-To: [email protected] : To: [email protected] : Subject: svn commit: r1452483 - in /lucene/dev/trunk/solr: ./ : core/src/java/org/apache/solr/schema/ : core/src/java/org/apache/solr/search/ : core/src/test-files/solr/collection1/conf/ : core/src/test/org/apache/solr/schema/ : core/src/test/org/apache/solr/search/ : : Author: hossman : Date: Mon Mar 4 20:25:37 2013 : New Revision: 1452483 : : URL: http://svn.apache.org/r1452483 : Log: : SOLR-4138: CurrencyField fields can now be used in a ValueSources. There is also a new currency(field,[CODE]) function : : Modified: : lucene/dev/trunk/solr/CHANGES.txt : lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/CurrencyField.java : lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java : lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema15.xml : lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/AbstractCurrencyFieldTest.java : lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java : : Modified: lucene/dev/trunk/solr/CHANGES.txt : URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1452483&r1=1452482&r2=1452483&view=diff : ============================================================================== : --- lucene/dev/trunk/solr/CHANGES.txt (original) : +++ lucene/dev/trunk/solr/CHANGES.txt Mon Mar 4 20:25:37 2013 : @@ -98,6 +98,14 @@ New Features : Similarity when you know the optimal "Sweet Spot" of values for the field : length and TF scoring factors. (hossman) : : +* SOLR-4138: CurrencyField fields can now be used in a ValueSources to : + get the "raw" value (using the default number of fractional digits) in : + the default currency of the field type. There is also a new : + currency(field,[CODE]) function for generating a ValueSource of the : + "natural" value, converted to an optionally specified currency to : + override the default for the field type. : + (hossman) : + : Bug Fixes : ---------------------- : : : Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/CurrencyField.java : URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/CurrencyField.java?rev=1452483&r1=1452482&r2=1452483&view=diff : ============================================================================== : --- lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/CurrencyField.java (original) : +++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/CurrencyField.java Mon Mar 4 20:25:37 2013 : @@ -242,6 +242,67 @@ public class CurrencyField extends Field : return getRangeQuery(parser, field, valueDefault, valueDefault, true, true); : } : : + /** : + * <p> : + * Returns a ValueSource over this field in which the numeric value for : + * each document represents the indexed value as converted to the default : + * currency for the field, normalized to it's most granular form based : + * on the default fractional digits. : + * </p> : + * <p> : + * For example: If the default Currency specified for a field is : + * <code>USD</code>, then the values returned by this value source would : + * represent the equivilent number of "cents" (ie: value in dollars * 100) : + * after converting each document's native currency to USD -- because the : + * default fractional digits for <code>USD</code> is "<code>2</code>". : + * So for a document whose indexed value was currently equivilent to : + * "<code>5.43,USD</code>" using the the exchange provider for this field, : + * this ValueSource would return a value of "<code>543<code>" : + * </p> : + * : + * @see #PARAM_DEFAULT_CURRENCY : + * @see #DEFAULT_DEFAULT_CURRENCY : + * @see Currency#getDefaultFractionDigits : + * @see getConvertedValueSource : + */ : + public RawCurrencyValueSource getValueSource(SchemaField field, : + QParser parser) { : + field.checkFieldCacheSource(parser); : + return new RawCurrencyValueSource(field, defaultCurrency, parser); : + } : + : + /** : + * <p> : + * Returns a ValueSource over this field in which the numeric value for : + * each document represents the value from the underlying : + * <code>RawCurrencyValueSource</code> as converted to the specified target : + * Currency. : + * </p> : + * <p> : + * For example: If the <code>targetCurrencyCode</code> param is set to : + * <code>USD</code>, then the values returned by this value source would : + * represent the equivilent number of dollars after converting each : + * document's raw value to <code>USD</code>. So for a document whose : + * indexed value was currently equivilent to "<code>5.43,USD</code>" : + * using the the exchange provider for this field, this ValueSource would : + * return a value of "<code>5.43<code>" : + * </p> : + * : + * @param targetCurrencyCode The target currency for the resulting value source, if null the defaultCurrency for this field type will be used : + * @param source the raw ValueSource to wrap : + * @see #PARAM_DEFAULT_CURRENCY : + * @see #DEFAULT_DEFAULT_CURRENCY : + * @see getValueSource : + */ : + public ValueSource getConvertedValueSource(String targetCurrencyCode, : + RawCurrencyValueSource source) { : + if (null == targetCurrencyCode) { : + targetCurrencyCode = defaultCurrency; : + } : + return new ConvertedCurrencyValueSource(targetCurrencyCode, : + source); : + } : + : @Override : public Query getRangeQuery(QParser parser, SchemaField field, String part1, String part2, final boolean minInclusive, final boolean maxInclusive) { : final CurrencyValue p1 = CurrencyValue.parse(part1, defaultCurrency); : @@ -263,7 +324,7 @@ public class CurrencyField extends Field : // 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), : + (new RawCurrencyValueSource(field, currencyCode, parser), : p1 == null ? null : p1.getAmount() + "", : p2 == null ? null : p2.getAmount() + "", : minInclusive, maxInclusive); : @@ -277,7 +338,7 @@ public class CurrencyField extends Field : @Override : public SortField getSortField(SchemaField field, boolean reverse) { : // Convert all values to default currency for sorting. : - return (new CurrencyValueSource(field, defaultCurrency, null)).getSortField(reverse); : + return (new RawCurrencyValueSource(field, defaultCurrency, null)).getSortField(reverse); : } : : @Override : @@ -289,14 +350,128 @@ public class CurrencyField extends Field : return provider; : } : : - class CurrencyValueSource extends ValueSource { : + /** : + * <p> : + * A value source whose values represent the "normal" values : + * in the specified target currency. : + * </p> : + * @see RawCurrencyValueSource : + */ : + class ConvertedCurrencyValueSource extends ValueSource { : + private final Currency targetCurrency; : + private final RawCurrencyValueSource source; : + private final double rate; : + public ConvertedCurrencyValueSource(String targetCurrencyCode, : + RawCurrencyValueSource source) { : + this.source = source; : + this.targetCurrency = getCurrency(targetCurrencyCode); : + if (null == targetCurrency) { : + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Currency code not supported by this JVM: " + targetCurrencyCode); : + } : + // the target digits & currency of our source, : + // become the source digits & currency of ourselves : + this.rate = provider.getExchangeRate : + (source.getTargetCurrency().getCurrencyCode(), : + targetCurrency.getCurrencyCode()); : + } : + : + @Override : + public FunctionValues getValues(Map context, AtomicReaderContext reader) : + throws IOException { : + final FunctionValues amounts = source.getValues(context, reader); : + // the target digits & currency of our source, : + // become the source digits & currency of ourselves : + final String sourceCurrencyCode = source.getTargetCurrency().getCurrencyCode(); : + final int sourceFractionDigits = source.getTargetCurrency().getDefaultFractionDigits(); : + final double divisor = Math.pow(10D, targetCurrency.getDefaultFractionDigits()); : + return new FunctionValues() { : + @Override : + public boolean exists(int doc) { : + return amounts.exists(doc); : + } : + @Override : + public long longVal(int doc) { : + return (long) doubleVal(doc); : + } : + @Override : + public int intVal(int doc) { : + return (int) doubleVal(doc); : + } : + : + @Override : + public double doubleVal(int doc) { : + return CurrencyValue.convertAmount(rate, sourceCurrencyCode, amounts.longVal(doc), targetCurrency.getCurrencyCode()) / divisor; : + } : + : + @Override : + public float floatVal(int doc) { : + return CurrencyValue.convertAmount(rate, sourceCurrencyCode, amounts.longVal(doc), targetCurrency.getCurrencyCode()) / ((float)divisor); : + } : + : + @Override : + public String strVal(int doc) { : + return Double.toString(doubleVal(doc)); : + } : + : + @Override : + public String toString(int doc) { : + return name() + '(' + strVal(doc) + ')'; : + } : + }; : + } : + public String name() { : + return "currency"; : + } : + : + @Override : + public String description() { : + return name() + "(" + source.getField().getName() + "," + targetCurrency.getCurrencyCode()+")"; : + } : + : + @Override : + public boolean equals(Object o) { : + if (this == o) return true; : + if (o == null || getClass() != o.getClass()) return false; : + : + ConvertedCurrencyValueSource that = (ConvertedCurrencyValueSource) o; : + : + return !(source != null ? !source.equals(that.source) : that.source != null) && : + (rate == that.rate) && : + !(targetCurrency != null ? !targetCurrency.equals(that.targetCurrency) : that.targetCurrency != null); : + : + } : + : + @Override : + public int hashCode() { : + int result = targetCurrency != null ? targetCurrency.hashCode() : 0; : + result = 31 * result + (source != null ? source.hashCode() : 0); : + result = 31 * (int) Double.doubleToLongBits(rate); : + return result; : + } : + } : + : + /** : + * <p> : + * A value source whose values represent the "raw" (ie: normalized using : + * the number of default fractional digits) values in the specified : + * target currency). : + * </p> : + * <p> : + * For example: if the specified target currency is "<code>USD</code>" : + * then the numeric values are the number of pennies in the value : + * (ie: <code>$n * 100</code>) since the number of defalt fractional : + * digits for <code>USD</code> is "<code>2</code>") : + * </p> : + * @see ConvertedCurrencValueSource : + */ : + class RawCurrencyValueSource extends ValueSource { : private static final long serialVersionUID = 1L; : - private Currency targetCurrency; : + private final Currency targetCurrency; : private ValueSource currencyValues; : private ValueSource amountValues; : private final SchemaField sf; : : - public CurrencyValueSource(SchemaField sfield, String targetCurrencyCode, QParser parser) { : + public RawCurrencyValueSource(SchemaField sfield, String targetCurrencyCode, QParser parser) { : this.sf = sfield; : this.targetCurrency = getCurrency(targetCurrencyCode); : if (null == targetCurrency) { : @@ -309,6 +484,9 @@ public class CurrencyField extends Field : currencyValues = currencyField.getType().getValueSource(currencyField, parser); : amountValues = amountField.getType().getValueSource(amountField, parser); : } : + : + public SchemaField getField() { return sf; } : + public Currency getTargetCurrency() { return targetCurrency; } : : @Override : public FunctionValues getValues(Map context, AtomicReaderContext reader) throws IOException { : @@ -444,12 +622,13 @@ public class CurrencyField extends Field : } : : public String name() { : - return "currency"; : + return "rawcurrency"; : } : : @Override : public String description() { : - return name() + "(" + sf.getName() + ")"; : + return name() + "(" + sf.getName() + : + ",target="+targetCurrency.getCurrencyCode()+")"; : } : : @Override : @@ -457,7 +636,7 @@ public class CurrencyField extends Field : if (this == o) return true; : if (o == null || getClass() != o.getClass()) return false; : : - CurrencyValueSource that = (CurrencyValueSource) o; : + RawCurrencyValueSource that = (RawCurrencyValueSource) o; : : return !(amountValues != null ? !amountValues.equals(that.amountValues) : that.amountValues != null) && : !(currencyValues != null ? !currencyValues.equals(that.currencyValues) : that.currencyValues != null) && : : Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java : URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java?rev=1452483&r1=1452482&r2=1452483&view=diff : ============================================================================== : --- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java (original) : +++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java Mon Mar 4 20:25:37 2013 : @@ -392,6 +392,21 @@ public abstract class ValueSourceParser : return f.getType().getValueSource(f, fp); : } : }); : + addParser("currency", new ValueSourceParser() { : + @Override : + public ValueSource parse(FunctionQParser fp) throws SyntaxError { : + : + String fieldName = fp.parseArg(); : + SchemaField f = fp.getReq().getSchema().getField(fieldName); : + if (! (f.getType() instanceof CurrencyField)) { : + throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, : + "Currency function input must be the name of a CurrencyField: " + fieldName); : + } : + CurrencyField ft = (CurrencyField) f.getType(); : + String code = fp.hasMoreArguments() ? fp.parseArg() : null; : + return ft.getConvertedValueSource(code, ft.getValueSource(f, fp)); : + } : + }); : : addParser(new DoubleParser("rad") { : @Override : : Modified: lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema15.xml : URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema15.xml?rev=1452483&r1=1452482&r2=1452483&view=diff : ============================================================================== : --- lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema15.xml (original) : +++ lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema15.xml Mon Mar 4 20:25:37 2013 : @@ -45,6 +45,7 @@ : <fieldType name="tfloat" class="solr.TrieFloatField" precisionStep="8" positionIncrementGap="0"/> : <fieldType name="tlong" class="solr.TrieLongField" precisionStep="8" positionIncrementGap="0"/> : <fieldType name="tdouble" class="solr.TrieDoubleField" precisionStep="8" positionIncrementGap="0"/> : + <fieldType name="currency" class="solr.CurrencyField" currencyConfig="currency.xml" multiValued="false" /> : : <!-- numeric field types that manipulate the value into : a string value that isn't human readable in it's internal form, : @@ -518,6 +519,7 @@ : <field name="pointD" type="xyd" indexed="true" stored="true" multiValued="false"/> : <field name="point_hash" type="geohash" indexed="true" stored="true" multiValued="false"/> : <field name="store" type="location" indexed="true" stored="true"/> : + <field name="amount" type="currency" indexed="true" stored="true" multiValued="false"/> : : <!-- to test uniq fields --> : <field name="uniq" type="string" indexed="true" stored="true" multiValued="true"/> : : Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/AbstractCurrencyFieldTest.java : URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/AbstractCurrencyFieldTest.java?rev=1452483&r1=1452482&r2=1452483&view=diff : ============================================================================== : --- lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/AbstractCurrencyFieldTest.java (original) : +++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/AbstractCurrencyFieldTest.java Mon Mar 4 20:25:37 2013 : @@ -297,6 +297,118 @@ public abstract class AbstractCurrencyFi : assertQ(req("fl", "*,score", "q", "*:*", "sort", field()+" asc", "limit", "1"), "//int[@name='id']='3'"); : } : : + public void testFunctionUsage() throws Exception { : + clearIndex(); : + for (int i = 1; i <= 8; i++) { : + // "GBP" currency code is 1/2 of a USD dollar, for testing. : + assertU(adoc("id", "" + i, field(), (((float)i)/2) + ",GBP")); : + } : + for (int i = 9; i <= 11; i++) { : + assertU(adoc("id", "" + i, field(), i + ",USD")); : + } : + : + assertU(commit()); : + : + // direct value source usage, gets "raw" form od default curency : + // default==USD, so raw==penies : + assertQ(req("fl", "id,func:field($f)", : + "f", field(), : + "q", "id:5"), : + "//*[@numFound='1']", : + "//doc/float[@name='func' and .=500]"); : + assertQ(req("fl", "id,func:field($f)", : + "f", field(), : + "q", "id:10"), : + "//*[@numFound='1']", : + "//doc/float[@name='func' and .=1000]"); : + assertQ(req("fl", "id,score,"+field(), : + "q", "{!frange u=500}"+field()) : + ,"//*[@numFound='5']" : + ,"//int[@name='id']='1'" : + ,"//int[@name='id']='2'" : + ,"//int[@name='id']='3'" : + ,"//int[@name='id']='4'" : + ,"//int[@name='id']='5'" : + ); : + assertQ(req("fl", "id,score,"+field(), : + "q", "{!frange l=500 u=1000}"+field()) : + ,"//*[@numFound='6']" : + ,"//int[@name='id']='5'" : + ,"//int[@name='id']='6'" : + ,"//int[@name='id']='7'" : + ,"//int[@name='id']='8'" : + ,"//int[@name='id']='9'" : + ,"//int[@name='id']='10'" : + ); : + : + // use the currency function to convert to default (USD) : + assertQ(req("fl", "id,func:currency($f)", : + "f", field(), : + "q", "id:10"), : + "//*[@numFound='1']", : + "//doc/float[@name='func' and .=10]"); : + assertQ(req("fl", "id,func:currency($f)", : + "f", field(), : + "q", "id:5"), : + "//*[@numFound='1']", : + "//doc/float[@name='func' and .=5]"); : + assertQ(req("fl", "id,score"+field(), : + "f", field(), : + "q", "{!frange u=5}currency($f)") : + ,"//*[@numFound='5']" : + ,"//int[@name='id']='1'" : + ,"//int[@name='id']='2'" : + ,"//int[@name='id']='3'" : + ,"//int[@name='id']='4'" : + ,"//int[@name='id']='5'" : + ); : + assertQ(req("fl", "id,score"+field(), : + "f", field(), : + "q", "{!frange l=5 u=10}currency($f)") : + ,"//*[@numFound='6']" : + ,"//int[@name='id']='5'" : + ,"//int[@name='id']='6'" : + ,"//int[@name='id']='7'" : + ,"//int[@name='id']='8'" : + ,"//int[@name='id']='9'" : + ,"//int[@name='id']='10'" : + ); : + : + // use the currency function to convert to MXN : + assertQ(req("fl", "id,func:currency($f,MXN)", : + "f", field(), : + "q", "id:5"), : + "//*[@numFound='1']", : + "//doc/float[@name='func' and .=10]"); : + assertQ(req("fl", "id,func:currency($f,MXN)", : + "f", field(), : + "q", "id:10"), : + "//*[@numFound='1']", : + "//doc/float[@name='func' and .=20]"); : + assertQ(req("fl", "*,score,"+field(), : + "f", field(), : + "q", "{!frange u=10}currency($f,MXN)") : + ,"//*[@numFound='5']" : + ,"//int[@name='id']='1'" : + ,"//int[@name='id']='2'" : + ,"//int[@name='id']='3'" : + ,"//int[@name='id']='4'" : + ,"//int[@name='id']='5'" : + ); : + assertQ(req("fl", "*,score,"+field(), : + "f", field(), : + "q", "{!frange l=10 u=20}currency($f,MXN)") : + ,"//*[@numFound='6']" : + ,"//int[@name='id']='5'" : + ,"//int[@name='id']='6'" : + ,"//int[@name='id']='7'" : + ,"//int[@name='id']='8'" : + ,"//int[@name='id']='9'" : + ,"//int[@name='id']='10'" : + ); : + : + } : + : @Test : public void testMockFieldType() throws Exception { : clearIndex(); : : Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java : URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java?rev=1452483&r1=1452482&r2=1452483&view=diff : ============================================================================== : --- lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java (original) : +++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java Mon Mar 4 20:25:37 2013 : @@ -679,6 +679,13 @@ public class QueryEqualityTest extends S : "field('foo_i\')", : "foo_i"); : } : + public void testFuncCurrency() throws Exception { : + assertFuncEquals("currency(\"amount\")", : + "currency('amount\')", : + "currency(amount)", : + "currency(amount,USD)", : + "currency('amount',USD)"); : + } : : public void testTestFuncs() throws Exception { : assertFuncEquals("sleep(1,5)", "sleep(1,5)"); : : : -Hoss --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
