Data Access

The Data Access essentially is UBStores persistence layer – data items can be be saved and retrieved from here. Every UBStore node has one data access.

The idea is to abstract away how data is actually stored. While the default implementation (MemoryDataAccess) stores data items in an in-memory list, it would be possible to build implementations that store data in relational databases, key-value stores, on the file system, the cloud, you name it.

Obtaining the data access

The easiest way to access the data access is via the IUBStore.getDataAccess() method. However, since it is implemented as an OSGi service, it is also possible to get a reference using OSGi services, be it programmatically or by using declarative services.

Storing items

Data Items can be stored with the IDataAccess.put() method. It takes either a single data item or a list of data items (bulk put). Obviously, the idea is to later be able to retrieve stored data items exactly as they were put in.

Conditions

Data Items that are stored inside the data access need to fulfil certain conditions, or else the data access will (should) reject it and throw an Exception.

  1. The key must be neither null nor empty.
  2. The type must be neither null nor empty.
  3. Transaction and Validtime intervals must be left closed and right open.
  4. The Value must not be null.

Although the SimpleDataItem, UBStores default implementation of IDataItem prevents items 1, 2 and 4, other implementations might not, and custom implementations of the data access should check for these conditions before accepting a put operation.

Creating storable data items

While it is perfectly possible to create data items that meet the aforementioned conditions, it can be a tedious and self-repeating process, especially if generating multiple data items at once.

The UBStore helper bundle contains a factory that helps creating storable data items easily. Have a look at the Build.dataItem() method.

Retrieving items

The IDataAccess.get() method is the counterpart to the put() methods: it is used to retrieve stored items.

The get() method takes exactly one data item as a parameter, the so-called query item. While the query comes in the same object as the data, the semantics are a little bit different. Each of the fields (key, type, metadata and value) are understood as query, not as an exact value:

Note that none of the query item fields (except the value) may be null.

Example

Query:

Key: .*
Type: car
Metadata: color -> (red|blue)
Valid Time: unbounded
Transaction Time: [4,8]

Will return all data items that are of type car and contain a metadata entry with key color and value red or blue. The transaction time has to overlap the interval [4,8], e.g. both data items with transaction time intervals [3,5[ and [5,10[ would match.

Warning
Although a little bit counter intuitive, if the key had been left empty (""), it would not match anything (except empty string, which is forbidden to store as a key). We chose to strictly adhere to regex matching semantics for consistency reasons as well as avoid forcing implementations of IDataAccess to check for specific corner cases.

Creating request data items

Sadly, it can be tedious to formulate queries manually using plain data items. For example, in order to search for all data items with a specific type, one has to set the key to .* and both the valid and transaction times to unbounded intervals. To simplify formulating queries, the UBStore helper bundle contains a facility that helps formulating queries easily, for example by defaulting all data item fields that you do not specify explicitly to values that match everything. Have a look at the Build.request() method.

Deleting data

Deleting data is done via the IDataAccess.remove(IDataItem) method. The parameter is a query data item, and the method deletes all stored data items that would be returned if the same query data item had been put into the get(IDataItem) method.