Dance like NoSQL is watching

April 15th, 2017

Dance like NoSQL is watching

Here come a fast one that I have to get out of my head.

First, there was DBIx::NoSQL

Lately I’ve returned to DBIx::NoSQL, which provides a simple, easy way to create key/value stores. Simple? Yes, really dead simple. Like that kind of simple:

use DBIx::NoSQL;

my $store = DBIx::NoSQL->connect( 'store.sqlite' );

        #     model    id  
$store->set( Player => yenzie => {
    name => 'yenzie',
    alliance => 'Nefarious Coallition',
});

$store->set( Ship => enkidu => {
    name   => 'Enkidu',
    hull   => 10,
    coords => [ 10, 10 ],
});

In the background, DBIx::NoSQL creates a DBIx::Class schema. All data structures are serialized and dumped in a big happy table, while their ids are saved in per-model tables. Indexes can also be added to models whenever we want

$store->model('Player')->index('alliance');

When new indexes are created, DBIx::NoSQL alters the schema dynamically in consequence. And since the schema is DBIx::Class-based, its power is there for searches when needed.

my @players = $store->search('Player')->where({ 
    # needs to be the id or an indexed field
    alliance => { like => qr/evil/ }
)->order_by( 'name DESC' )->all;

In a nutshell, it’s a decent tool when setting up a MongoDB or CouchDB instance — let alone a full relational schema — sounds like too much work.

Then came along DBIx::NoSQL::Store::Manager

When I originally played with it I upped the antes and wrote DBIx::NoSQL::Store::Manager, which uses Moose objects as models. Which let you create the class MyStore::Model::Player, and the system will figure it out from there.

package MyStore::Model::Player;

use Moose;
use MooseX::MungeHas 'is_ro';

with 'DBIx::NoSQL::Store::Manager::Model';

has name     => ( traits  => [ 'StoreKey' ] );
has alliance => ( traits  => [ 'StoreIndex' ] );

1;

And now… lights on the dancefloor…

And so last week I was musing on how easy this allows to set up a quick’n’dirty NoSQL backend. Then I thought… and what if we push that DWIMery all the way to a REST interface?

So I fired up the ol’ Dancer environment, hacked for an hour or two, and now I have… no, let’s show, not tell.

##hackthrough

./config.yaml

I’m skipping the non-important parts of the config.yml. All that matter for us right now is that we serialize using JSON, and set a database and store manager class for the store plugin.

./store.perl

As you can see, the main store class is straightforward.

Mind you, in a future iteration I might streamline things further and just build the class automatically if no customization is required. But that’s a yak for a different day.

./player.perl

Model class. Still short and sweet.

./app.perl

The Dancer app. All of it.

No, really, that’s all of it.

##/hackthrough

And we DANCE!

##hackthrough

./post.bash

Player creation.

By the by, I’m using HTTP Pie as my REST tool. It’s really nice.

./get.bash

Yup, it’s there.

./put.bash

Updates work.

./put_bad.bash

… but not on ready-only values.

Right now the exception value is an ugly trace. It’ll eventually get prettier.

./put_bad2.bash

… or unknown attributes.

./delete.bash

We can delete too, natch.

##/hackthrough

C’mon. You have to admit. It’s all pretty sweet, isn’t?

Last notes

The code for the plugin can be found on GitHub, although it’s still purely in its Proof-of-Concept stage. I’ll try to groom it for CPAN if there is a demand.

I’m also in the midst of updating DBIx::NoSQL::Store::Manager itself, and I’m also making inquiries about DBIx::NoSQL, which would need to have a few things updated (mostly, it’s using Any::Moose, which has been deprecated since). I would also love to see DBIx::NoSQL adopting more backends. I heard smashing recommendations for Postgres and its JSON support, so the mix of the two could be interesting (well, to me, at least).