The Power of the Sun, in the Palm of your Hand

Over the weekend I worked on a new Perl library, MongoDB::QueryBuilder, a tool which is designed to simplify composing complex and dynamic queries for MongoDB in a chainable and object-oriented fashion.

Seldomly do we see examples of querying a MongoDB datastore of significant size and complexity, however, as your application grows and your data evolves, the questions you’ll want to ask your datastore will become more intricate.

For example. Let’s suppose we have an inventory management application that tracks equipment used in our business. Over time the business grows as does the database and (for whatever reason) we want to ask the database to “give us the (serial number, asset type, and the location manager’s name and phone number) closest to one-of-the-three latitude/longitude coordinates we will provide where the asset has been inspected and placed in a bin-location”. Oh, and limit that result-set by 100 documents.

Using MongoDB::QueryBuilder, the abstraction might resemble the following:

use MongoDB::QueryBuilder;

my $query = MongoDB::QueryBuilder->new(
    only => [
        'serial_number',
        'asset.type.name',
        'asset.location.phone',
        'asset.location.manager.name'
    ],
    and_where => [
        'asset.location.bin$ne' => undef,
        '$or' => [ # this condition will short-circuit
            {'asset.location.latlng$near' => [$a, $b]},
            {'asset.location.latlng$near' => [$c, $d]},
            {'asset.location.latlng$near' => [$e, $f]}
        ]
    ],
    limit => 100
);

Needless to say, manually producing the code to query the database would be a bit more involved. Another noteworthy feature is that the query-builder object is chainable which allows you to create classes that will return a result-set.

For example. Considering the aforementioned scenario.

package MyApp::Inventory::Assets;

use MongoDB::QueryBuilder;

has 'query';
sub _build_query { MongoDB::QueryBuilder->new }

sub has_been_inspected {
    my $self = shift;
    $self->query->and_where('asset.location.bin$ne' => undef);
    return $self;
}

sub is_located_near {
    my $self = shift;
    $self->query->and_where('asset.location.latlng$near' => [@_]);
    return $self;
}

sub limit {
    my $self = shift;
    $self->query->limit($_[0] || 100);
    return $self;
}

package main;

my $assets = MyApp::Inventory::Assets->new;

my $cursor = $assets->limit(25)->has_been_inspected;

while (my $asset = $cursor->next) {
    say $asset->{serial_number};
}

.. I rushed through the demonstration, sorry. I’m extremely busy these days. I hope this module will be of as-much use to others as it is to me (at-the-moment).

The end.

Leave a comment

About Al Newkirk

user-pic ... proud Perl hacker, ask me anything!