You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@lucene.apache.org by "Greg Fodor (JIRA)" <ji...@apache.org> on 2010/11/03 17:35:23 UTC

[jira] Issue Comment Edited: (SOLR-2202) Money FieldType

    [ https://issues.apache.org/jira/browse/SOLR-2202?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12927895#action_12927895 ] 

Greg Fodor edited comment on SOLR-2202 at 11/3/10 12:34 PM:
------------------------------------------------------------

This update to the patch includes a number of performance enhancements and is the version of the patch we will be likely to push to production.

First, this patch introduces the defaultCurrency parameter, which defaults to USD. The default currency allows you to omit the currency code in the field value (ie, "5000" instead of "5000,USD".) However, it plays a more pivotal role in improving performance.

The previous patches provided a native approach to constructing the trie bounding range by taking the max and min currency exchange rates for the target currency. This proved to be minimally useful since the relative value of currency units vary wildly and hence the bounding range often spanned the full document set.

The solution I took in this patch is to compute the bounding range by taking into account the "currency drift." Before getting to that, though, the indexing process was updated to include a new dynamic field that indexes the value of the field in the *default currency*, exchanged at the current rate at indexing time. (Additionally, a stored field is optionally created if the money field is marked as stored.)

Now, the appreciative and depreciative drifts of a currency C are defined to be the difference between the current exchange rate and the peak and trough exchange rates seen for the life of the index from C to the default currency. By taking the largest drifts (both appreciating and depreciating) across all currencies, a optimal and correct bounding range to use for a trie query against the default currency can be computed.

The historical max and min exchange rates relative to the default currency are now tracked by solr in a properties file. The properties file is named after the currency config file. For example, if the config file is "currency.xml", the properties file is "currency.xml.drift.properties". This file is designed to work correctly with replication, and is updated by Solr whenever the currency config file is loaded.

For example, if the default currency is USD, and over the life of the index the EUR exchange rate to USD is seen to be 1.1, 1.4, and 1.3, the max and min exchange rates are clearly 1.1 and 1.4. If the current exchange rate at query time is 1.3, the "depreciative drift" for EUR is 1.3 - 1.1 = 0.2, and the "appreciative drift" is 1.4 - 1.1 = 0.3. If the query being made is for ($100, $200), the lower bound is adjusted based upon the depreciative drift to $100 - (0.2 * $100) = $80, and the upper bound is adjusted based upon the appreciative drift to $200 + (0.3 * $200) = $260. 

This is an accurate bounding range for the indexed default currency value, since those are the max and min possible values that were indexed regardless of the time the indexing occured. So, the trie query is formed to query against the new dynamic field that indexes the original value as the default currency, over the bounds ($80, $260). The trie query is appended to a boolean clause that further constrains the results by doing the actual conversion to determine if the current exchange rate will still yield it as matching the user's range query.

Beyond this, I added some additional intra-query caching and changed the query construction from the FilteredQuery approach (which seemed to be inefficient in leveraging the trie query) to the BooleanQuery. You'll note that I rely upon the second clause in the BooleanQuery being scored first, which eliminates the expensive exchange rate conversions from happening for documents that fall outside the trie range. 

I ran into a limitation of the current resource loader API, however, in that it does not allow access to creating or writing new resources, which is needed to maintain the drift properties file. For now, I only support SolrResourceLoader which writes to the local filesystem by extracting the config directory. However, the new ZkResourceLoader is not supported, for example. A non-fatal warning is emitted to the log when this occurs. The side effect of this is that currency exchange rate drift will not be tracked, resulting in incorrect range and point queries if the currency.xml file is updated. It would be nice if it were possible to ask the ResourceLoader for an OutputStream to a new resource for this purpose.

Some limitations:
- The default currency cannot be changed after the initial index, otherwise the index effectively is corrupt since the value for the trie bound is indexed in the default currency.
- Loss or corruption of the drift file will cause erroneous range and point queries (documents will be omitted from the results, though no incorrect documents will appear.)
- As mentioned above, the only ResourceLoader supported are SolrResourceLoaders that respond to getConfigDir(). Please let me know if there is a safer, more canonical way to store and load Solr-maintained metadata that lives with the index.

Also note that this has been tested with replication. The only thing necessary for replication to work is that the currency.xml and currency.xml.drift.properties file be included as part of the replication. A limitation here is that if no documents are updated but the currency exchange rates change, the file will not be replicated due to Solr's policy of not replicating files without index changes. It would be useful to allow this behavior to be overridden. In our case this isn't a problem since our index churn is high enough that replication events happen regularly.

In the end these changes result in accurate currency range queries that perform nearly as fast as their non-currency counterparts. 


      was (Author: gfodor):
    This update to the patch includes a number of performance enhancements and is the version of the patch we will be likely to push to production.

First, this patch introduces the defaultCurrency parameter, which defaults to USD. The default currency allows you to omit the currency code in the field value (ie, "5000" instead of "5000,USD".) However, it plays a more pivotal role in improving performance.

The previous patches provided a native approach to constructing the trie bounding range by taking the max and min currency exchange rates for the target currency. This proved to be minimally useful since the relative value of currency units vary wildly and hence the bounding range often spanned the full document set.

The solution I took in this patch is to compute the bounding range by taking into account the "currency drift." Before getting to that, though, the indexing process was updated to include a new dynamic field that indexes the value of the field in the *default currency*, exchanged at the current rate at indexing time. (Additionally, a stored field is optionally created if the money field is marked as stored.)

Now, the appreciative and depreciative drifts of a currency C are defined to be the difference between the current exchange rate and the peak and trough exchange rates seen for the life of the index to from C to the default currency. By taking the largest drifts (both appreciating and depreciating) across all currencies, a optimal and correct bounding range to use for a trie query against the default currency can be computed.

The historical max and min exchange rates relative to the default currency are now tracked by solr in a properties file. The properties file is named after the currency config file. For example, if the config file is "currency.xml", the properties file is "currency.xml.drift.properties". This file is designed to work correctly with replication, and is updated by Solr whenever the currency config file is loaded.

For example, if the default currency is USD, and over the life of the index the EUR exchange rate to USD is seen to be 1.1, 1.4, and 1.3, the max and min exchange rates are clearly 1.1 and 1.4. If the current exchange rate at query time is 1.3, the "depreciative drift" for EUR is 1.3 - 1.1 = 0.2, and the "appreciative drift" is 1.4 - 1.1 = 0.3. If the query being made is for ($100, $200), the lower bound is adjusted based upon the depreciative drift to $100 - (0.2 * $100) = $80, and the upper bound is adjusted based upon the appreciative drift to $200 + (0.3 * $200) = $260. 

This is an accurate bounding range for the indexed default currency value, since those are the max and min possible values that were indexed regardless of the time the indexing occured. So, the trie query is formed to query against the new dynamic field that indexes the original value as the default currency, over the bounds ($80, $260). The trie query is appended to a boolean clause that further constrains the results by doing the actual conversion to determine if the current exchange rate will still yield it as matching the user's range query.

Beyond this, I added some additional intra-query caching and changed the query construction from the FilteredQuery approach (which seemed to be inefficient in leveraging the trie query) to the BooleanQuery. You'll note that I rely upon the second clause in the BooleanQuery being scored first, which eliminates the expensive exchange rate conversions from happening for documents that fall outside the trie range. 

I ran into a limitation of the current resource loader API, however, in that it does not allow access to creating or writing new resources, which is needed to maintain the drift properties file. For now, I only support SolrResourceLoader which writes to the local filesystem by extracting the config directory. However, the new ZkResourceLoader is not supported, for example. A non-fatal warning is emitted to the log when this occurs. The side effect of this is that currency exchange rate drift will not be tracked, resulting in incorrect range and point queries if the currency.xml file is updated. It would be nice if it were possible to ask the ResourceLoader for an OutputStream to a new resource for this purpose.

Some limitations:
- The default currency cannot be changed after the initial index, otherwise the index effectively is corrupt since the value for the trie bound is indexed in the default currency.
- Loss or corruption of the drift file will cause erroneous range and point queries (documents will be omitted from the results, though no incorrect documents will appear.)
- As mentioned above, the only ResourceLoader supported are SolrResourceLoaders that respond to getConfigDir(). Please let me know if there is a safer, more canonical way to store and load Solr-maintained metadata that lives with the index.

Also note that this has been tested with replication. The only thing necessary for replication to work is that the currency.xml and currency.xml.drift.properties file be included as part of the replication. A limitation here is that if no documents are updated but the currency exchange rates change, the file will not be replicated due to Solr's policy of not replicating files without index changes. It would be useful to allow this behavior to be overridden. In our case this isn't a problem since our index churn is high enough that replication events happen regularly.

In the end these changes result in accurate currency range queries that perform nearly as fast as their non-currency counterparts. 

  
> Money FieldType
> ---------------
>
>                 Key: SOLR-2202
>                 URL: https://issues.apache.org/jira/browse/SOLR-2202
>             Project: Solr
>          Issue Type: New Feature
>          Components: Schema and Analysis
>    Affects Versions: 1.5
>            Reporter: Greg Fodor
>         Attachments: SOLR-2022-solr-3.patch, SOLR-2202-lucene-1.patch, SOLR-2202-solr-1.patch, SOLR-2202-solr-2.patch, SOLR-2202-solr-4.patch, SOLR-2202-solr-5.patch, SOLR-2202-solr-6.patch
>
>
> Attached please find patches to add support for monetary values to Solr/Lucene with query-time currency conversion. The following features are supported:
> - Point queries (ex: "price:4.00USD")
> - Range quries (ex: "price:[$5.00 TO $10.00]")
> - Sorting.
> - Currency parsing by either currency code or symbol.
> - Symmetric & Asymmetric exchange rates. (Asymmetric exchange rates are useful if there are fees associated with exchanging the currency.)
> At indexing time, money fields can be indexed in a native currency. For example, if a product on an e-commerce site is listed in Euros, indexing the price field as "10.00EUR" will index it appropriately. By altering the currency.xml file, the sorting and querying against Solr can take into account fluctuations in currency exchange rates without having to re-index the documents.
> The new "money" field type is a polyfield which indexes two fields, one which contains the amount of the value and another which contains the currency code or symbol. The currency metadata (names, symbols, codes, and exchange rates) are expected to be in an xml file which is pointed to by the field type declaration in the schema.xml.
> The current patch is factored such that Money utility functions and configuration metadata lie in Lucene (see MoneyUtil and CurrencyConfig), while the MoneyType and MoneyValueSource lie in Solr. This was meant to mirror the work being done on the spacial field types.
> This patch has not yet been deployed to production but will be getting used to power the international search capabilities of the search engine at Etsy.

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@lucene.apache.org
For additional commands, e-mail: dev-help@lucene.apache.org