Friday, July 6, 2012

Walking through the ChronoDB demo (2/2)


Important notice. On July 13, 2012, the ChronoDB project was renamed CrNiCKL, which is pronounced like "chronicle". All packages, demos included, have been renamed. The new project website is at http://agent.ch/timeseries/crnickl/. The old project remains accessible for a while at http://agent.ch/timeseries/chronodb/. The remainder of this article remains valid mutatis mutandis.

This post is the second of a two-part commentary on the ChronoDB demo. In the first part I explained the steps for setting up a ChronoDB database before it can be used to perform useful work.

Setting up the schema for the demo happens on the last line of our code snippet:

StocksAndForexDemo demo = new StocksAndForexDemo(args[0]); demo.setUpHyperSQLDatabase(); demo.setUpSchema(); Let's focus on this method. public void setUpSchema() throws Exception { StocksAndForexSchema schema = new StocksAndForexSchema(db); schema.createSchema(); // commit all changes db.commit(); } Whenever the symbol db appears in this post, it stands for the ChronoDB database. ch.agent.chronodb.api.Database db; The demo needs numerical series and textual attributes. So we need to create two value types. This is done in the StocksAndForexSchema constructor: db.createValueType("text", false, ValueType.StandardValueType.TEXT.name()) .applyUpdates(); db.createValueType("numeric", false, ValueType.StandardValueType.NUMBER.name()) .applyUpdates(); Here we use built-in support for numbers and texts provided by ChronoDB. In other cases we will provide a custom ValueScanner. But the constructor is not finished with its work. It needs to tell ChronoDB that we are going to have numeric series: UpdatableValueType<ValueType> uvtvt = db.getTypeBuiltInProperty() .getValueType().typeCheck(ValueType.class).edit(); uvtvt.addValue(uvtvt.getScanner().scan("numeric"), null); uvtvt.applyUpdates(); The method invocation applyUpdates() seen here and there consolidates all pending modifications to an object but does not commit them to permanent storage.

When the constructor is done, things get more specific, as can be seen from the code of createSchema:

public void createSchema() throws T2DBException { createCurrencyValueTypeAndProperty(); createSeriesUnitValueTypeAndProperty(); createTickerProperty(); createStocksSchema(); createExchangeRatesSchema(); createTopLevelChronicles(); } In the demo we have a class for currencies and we want to use a custom value scanner: UpdatableValueType<Currency> uvt = db.createValueType("Currency", true, "ch.agent.chronodb.demo.CurrencyValueScanner"); The Currency object in this demo is not very useful. In a real world application it would have more responsibilities, like computing exchange rates. Its purpose here is only to show how we set up ChronoDB to use problem-related classes. Not shown here is how to add currency values and how to create the currency property, as it's straightforward. We also skip the details of creating other value types and properties and turn to the creation of a schema for stocks.

We want to represent a stock using a chronicle with two attributes, ticker, and currency, and with three series, price, volume, and splits. Price and volume have a custom series attribute, unit. The first thing to do is to create a schema. The demo does not use the possibility to inherit from another schema.

UpdatableSchema schema = db.createSchema("Stocks", null); Attributes are then added to the schema. It is necessary to provide numbers for attributes and series. At first sight one would ask why are these numbers not hidden by the software. The answer is schema inheritance and the possibility to not only remove and modify attributes and series but also to insert them in precise positions. schema.addAttribute(1); schema.setAttributeProperty(1, db.getProperty("Ticker", true)); schema.addAttribute(2); schema.setAttributeProperty(2, db.getProperty("Currency", true)); The following piece of code defines the price series as a workweek numeric series, with a currency unit: schema.addSeries(1); schema.setSeriesName(1, "price"); schema.setSeriesDescription(1, "close price"); schema.setSeriesType(1, db.getValueType("numeric")); schema.setSeriesTimeDomain(1, Workday.DOMAIN); schema.addAttribute(1, 5); schema.setAttributeProperty(1, 5, db.getProperty("Unit", true)); schema.setAttributeDefault(1, 5, "currency"); By default a series does use sparse time series. The automatic use of sparse time series can be configured in the schema. This is done for splits series in the demo. Even if not configured, applications still have the possibility to force sparsity when getting data.

The last step in setting up the schema is to create top level chronicles. The demo uses two collections: stocks and exchange rates. The code below shows how to create the top chronicle hosting the exchange rate collection.

Schema forexSchema = db.getSchemas("Forex").iterator().next(); UpdatableChronicle forex = db.getTopChronicle().edit() .createChronicle("forex", false, "Exchange rate data", null, forexSchema); forex.applyUpdates(); To create a chronicle you need to have a parent chronicle. For a top-level chronicle, the parent is the top chronicle, which is virtual and which is named after the database, "demo" in this case.

With top-level chronicles created, the demo can go ahead. Once the required schemas have been set up, application rarely, if ever, need to do anything about them. Millions of chronicles can be created, and their attributes and series are automatically available without dong anything, except setting specific values. In many cases it is not even necessary to set values of attributes. Take the case of american stocks. It is a simple matter to define a schema for them, inheriting from the "Stocks" schema we made in the demo:

UpdatableSchema schema = db.createSchema("American stocks", "Stocks"); Property<Currency> currency = db.getProperty("Currency", true) .typeCheck(Currency.class); schema.setAttributeDefault(2, currency.scan("USD")); With this new schema, you can now create a chronicle collection for american stocks (perhaps a nested collection of the stocks collection), and all members will automatically be in USD.

As a final remark, it is important to note that as the default value of a chronicle attribute can always be overriden (it's named a default value after all), the value of a series attribute cannot. It keeps its default value, as defined in the schema. If you say in the schema that the unit of a series foo is bar then in all collections having this schema, the foo series unit will be bar. The same goes for built-in attributes: name, type, time domain, and sparsity of a series cannot be changed once defined. This does not restrict the modeling freedom. As the demo as shown you can have price series in different currencies. The price and volume have a unit ("in currency", and "in number of shares"), but the price series unit "in currency" simply tells to look at the chronicle's currency. So you will have your Toyotas in quoted in yens and your Renaults in euros.

No comments:

Post a Comment