Currency conversion with the Units Ontology, SPARQLMotion and SPIN
As the Linked Data cloud is steaming ahead, the first large online stores (recently: BestBuy.com) are publishing their products in a machine-friendly RDF format. In order to exchange product information in a meaningful way, the various product vendors should use a shared (or at least mappable) vocabulary to represent prices, so that internet search engines and crawlers can better compare values. In the real world, three-letter currency codes such as USD, EUR and AUD are being used. These abbreviations are a standard vocabulary, but having just a string representation is an error-prone strategy - e.g. the string "AMD" could be either the Armenian Drams currency, or a computer chip manufacturer.
We have recently published a comprehensive units ontology (QUDT) that also includes all of the world's currencies. For each currency, a globally unique identifier (URI) is used. These URI resources also point back to the currency abbreviation, using a property called qud:abbreviation. A good strategy for ontology designers would be to use those URIs as ranges of their properties, instead of string codes. Using those standard URIs will have some benefits down the road...
This morning I have created a SPIN library of currency conversion functions that can be used to convert between various currencies, using the very latest conversion factors. These functions can be used in SPARQL queries or SPIN rules. For example, we have a function currencies:getRateByCodes that gets the latest exchange rate between two currencies, specified by the three-letter codes:
This particular SPIN function is backed by a SPARQLMotion script that takes two currency codes (arg1 and arg2) as input and then calls an external web service to retrieve the latest exchange rate. The web service delivers XML, and a simple XPath expression is used to extract the value into an xsd:float RDF literal:
We can drill into the Call web service module to see that it is simply calling a REST web service, inserting the two arguments into the URL string:
Based on this low-level function, we can define additional higher-level functions. The following screenshot shows the complete definition of a SPIN function that takes two qud:CurrencyUnits as input, then gets their respective abbreviations, and finally calls the getRateByCodes function:
Whenever the function is called, any SPIN-aware SPARQL engine will simply execute the SELECT query specified as the body of the function. This means that new functions are built by combining other functions. The same mechanism was used to define the convertLiteral function, as shown below:
The function above makes good use of the QUDT units ontology to shield the user from any low-level details. All the user needs to do is to specify the range of a property to be, say, unit:USDollar, and then all future values of that property will be stored as literals with that unit as datatype. The SPIN function can then look at the literal to find out about its currency. Then, the function can look up the unit's abbreviation and make the corresponding web service calls to fetch the current exchange rate. Finally, a simple multiplication returns the desired new value.
Once these functions are defined, they can be used in various places. For example, here is a class Product, which has two properties: one stores the price in US Dollars, and another represents the price in Australian Dollars. Only the USD price needs to be entered, because the class also has a SPIN rule attached to it which will automatically do this computation as an inference:
An example Product instance, with the inferred (blue) AUD price is shown below:
These rules can of course be generalized further into SPIN templates, or the range of the target property could be used instead of hard-coding the target currency anywhere. There are really endless possibilities here.