Relational Model Library

Description

A C++ library for efficient implementation of in-memory relational models.

Motivation

Relational models are a very effective way to store data.  They provide a natural syntax for querying and manipulating data, using high level query language such as SQL.  They are efficient since they manage collections of objects, and indexes are maintained and used implicitly.

Although the C++ Standard Library containers provide efficient collections via its containers library, its syntax is bulkier and less natural than higher level query languages. 

It is not possible to query standard containers, instead they must be iterated and post-filtered.  It is difficult to maintain keys, for example to have multiple keys into the same data, to modify the key of an item in a container, or to delete a row from multiple indexes.  When querying multiple tables using the Standard Library, the programmer must perform this in a low level way with nested loops, while a relational notation uses “joins” to combine several tables.

In relational databases, query analyzers transform queries expressed in a high level language into execution strategies for data retrieval.  Relational Model Library (RML) does this using template metaprogramming. 

RML allows programmers to write queries such as

      select( (customers, accounts),

customers.name == ”smith” &&

customers.customer_id == accounts.customer_id &&

accounts.value > 10 )

 

which is executed as

 

      for(std::pair<CustomerNameMap::const_iterator,

  CustomerNameMap::const_iterator>

r = customer_name_map.equal_range(“smith”);

r.first != r.second; ++r.first)

      {

CustomerAccountMap::const_iterator j =

customer_account_map.find(r.first->customer_id);

            if(j != customer_account_map.end() &&

j->account_value > 10)

            {

                  // Do stuff

            }

      }

           

Because this transformation is performed at compile-time, code generated by RML is as efficient as hand-crafted code.  The code is much more efficient than the C++ Standard Library when there are multiple keys per table, because RML provides an efficient multi-index implementation, similar to Boost.MultiIndex.  See the benchmarks.

In a nutshell

A table can be defined

 

      // Define the row

      RM_DEFINE_ROW_4(Customer,

            unique<int>, customer_id,

            indexed<string>, name,

            indexed<string>, postcode,

            float, balance)

 

      // Define the table

      table<Customer> customers;

 

Indexes are maintained automatically when the data is inserted, updated or deleted,

 

      // Insert a row

customers.insert(Customer(1, "Calum", "B28 0AP", 0.0));

 

      // Update a row using a condition

      customers.update_where( customers.balance = 100,

customers.customer_id == 1);

 

      // Erase all rows matching the condition

      customers.erase_where(customers.customer_id == 1);

 

Queries can be written using operator-overloaded syntax,

 

      select(customers,

customers.name == "Calum" &&

customers.balance < 0 )

 

More complex queries can be formulated by joining multiple tables together,

 

      select( (orders, customers, accounts),

            orders.date < “2002-05-21” &&

            orders.status = outstanding &&

            customers.customer_id = orders.customer_id &&

            accounts.customer_id == customers.id &&

            orders.value > accounts.balance)

 

Data can be loaded and saved to CSV files,

 

      // Save tables

      file << customers << accounts << orders;

 

      // Load tables

      file >> customers >> accounts >> orders;

Contact information

Home page: http://calumgrant.net/relational

Author: Calum Grant (http://calumgrant.net/)

Email: See http://calumgrant.net/ 

Please feel welcome to give feedback or ask for help.