Mongoose::Intro - an introduction
version 0.23
This module is an attempt to bring together the full power of Moose into one of the hottest new databases out there: MongoDB.
Before using this module you should take a little time to read on MongoDB.
KiokuDB is an awesome module that maps objects to data and caters to a wide variety of backends. Currently there's even a MongoDB backend that may suit your needs.
So, why use Mongoose instead?
You want your objects to have their own collection. KiokuDB stores all objects in a single collection. MongoDB performs best the more collections you have.
You want to be able to store relations as either embedded documents or foreign documents. KiokuDB embeds everything. Here you get to choose.
You want to abstract your data from your class representation. KiokuDB stores an extra field called __CLASS__ that ties data to its representation. It's not a bad decision, it's just a design choice.
You don't need to keep a scope. KiokuDB will keep objects in scope for you, avoiding breakage in relationships. Here nothing is kept in scope.
You don't plan to use transactions. KiokuDB is transaction ready. But MongoDB is not. So, what's the point here?
You feel adventurous.
If you don't need any of this, go grab KiokuDB instead. It's much more configurable, stable and you can painlessly switch backends in the future.
To use this module, you need:
Grab a pre-built copy for your OS from here, or build it from sources.
After intalling the software, start the Mongo daemon:
mongod -dbpath /path/to/data
cpan MongoDB cpan Mongoose
Set the MONGOOSEDB environment variable to your MongoDB connection in case it's not standard (localhost).
MONGOOSEDB
make export MONGOOSEDB=host,mongodb://localhost,db_name,mytestdb make test make install
Create some Moose classes to start using Mongoose;
package MyClass; use Moose; with 'Mongoose::Document'; has 'yada' => ( is=>'rw', isa=>'Str' );
Some of Mongoose features:
It's fast. Not as fast as working with MongoDB documents directly though. But it's way faster than any other ORM and relational mapping modules out there.
It handles most object relationships, circular references included.
No persistency. It doesn't manage states for your object. If you save your object twice, it writes twice to the database. In most cases, this is actually faster than trying to manage states.
Primary keys. This is quite a extraneuos concept for objects, and it's not mandatory. But it allows you to automatically control when new objects translate to new MongoDB documents, or just update them.
This is an experimental feature. There are other ways to do this anyway with the MongoDB built-in _id primary-key attribute.
_id
Schema-less data. MongoDB does not need a schema. You can create new attributes for your object and delete old ones at your leasure.
No data-object binding means that you may reuse collections, and peruse inheritance to great extent.
This is very much *BETA* software. In fact it's almost alpha, except that the API is so simple it will probably not change, so let's call it "beta".
This module intrusively imports singleton based methods into your class. It's the price to pay for a simpler user interface and less keystrokes.
Object expansion from the database is done using plain bless most of the time. Which means your attribute triggers, etc. will not be fired during expansion. There are exceptions to this rule though.
After saving or loading objects from the database, your object will have an extra attribute, _id. This is a unique identifier. The _id value can be overwritten if you wish.
There are only two steps to start using Mongoose in your code:
1) Create at least one class that consumes a Mongoose::Document role.
2) Connect to a Mongo database in your main program.
MongoDB does not require you to previously create a database, a collection or a document schema for your collection. This is done on the fly for you.
To make your Moose classes "Mongoable", all they need is to consume either one of two roles: Mongoose::Document or Mongoose::EmbeddedDocument. Read on for details on the difference.
There are two roles to make your Moose class a Mongoose document:
* Mongoose::Document
* Mongoose::EmbeddedDocument
The difference between these roles lies in the way objects of different classes will be joined and stored (collapsed) into the DB.
Read the MongoDB docs if you don't understand the difference.
Akin to a row in the relational model. Objects are stored into independent collections. Relationships are stored using references, MongoDB's foreign key system.
Tells Mongoose to store your class as an embedded document, inside a parent document.
This is usually faster than using document-to-document reference joins. But it's not meant for object reuse by foreign documents.
Both Document and EmbeddedDocument will import into your class the following methods:
Document
EmbeddedDocument
Saves the current object to the database, inserting the document if needed.
$person->save;
Deletes the correspondind document from the database.
$person->delete;
Wraps MongoDB's find method to return a cursor that expands data into objects.
my $cursor = Person->query({ age => { '$lt' => 30 } });
Finds exactly one document.
my $jack = Person->find_one({ first_name => 'Jack' });
Returns the MongoDB::Collection object supporting this class. It's a way to switch quickly back to MongoDB hash documents.
Person->find_one({ name=>'thyself' }); # isa Person # whereas Person->collection->find_one({ name=>'thyself' }); # ref = HASH
Not really a method but an attribute used by Mongoose (and MongoDB). Contains a unique MongoDB::OID instance.
Deleting or modifying this attribute may cause your object to be re-inserted on the next save, instead of being updated.
save
Mongoose comes with a default engine, Mongoose::Engine::Base that takes care of expanding and collapsing objects to and from the Mongo database.
Collapsing is the process of serializing classes.
Your Moose objects are collapsed by unblessing them until they become a hash. Relationships are mantained in the process.
Expansion is the process of inflating Mongo documents (plain hashes) into Moose objects.
This is done by inspecting the class attribute metadata. The base engine tries to do it's best identifying data types. The document is then blessed into your class. This is faster than calling new, but also means that no special class or attribute methods will be fired, such as default values, setters, triggers or coercion. You've been warned.
bless
new
Naturally, there are many cases where this guesswork is not enough. These may be addressed in the future using attribute traits, but should be fine for most trivial classes.
Mongoose roles are role parameterized for greater flexibility.
You can control the collection name for an individual class this way:
package My::Mumbo::Jumbo::Class; use Moose; with 'Mongoose::Document' => { -collection_name => 'mumbo_jumbo' };
By default, Mongoose will turn package names into collections this way:
Package name | Collection name ----------------------+---------------------- Person | person Humpty::Dumpty | humpty_dumpty HumptyDumpty | humpty_dumpty MyApp::Schema::Jumbo | my_app_schema_jumbo
You can change this standard anytime, by setting the Mongoose::naming anonymous sub to something of your liking:
Mongoose::naming
# remove prefix and return # a lower case collection name Mongoose->naming( sub{ my $pkg = shift; $pkg =~ s{^MyApp::Schema::}{}g; return lc $pkg; });
The standard way MongoDB deals with primary keys is by using the _id attribute. By default, a MongoDB::OID is assigned to each object you commit to the database with save.
Checkout this Devel::REPL example:
$ re.pl > use Person; > my $hurley = Person->new(name=>'Hurley'); $Person1 = Person=HASH(0x102099d08); > $hurley->dump; $VAR1 = bless( { 'name' => 'Hurley' }, 'Person' ); > $hurley->save; 4c683525a74100a8df000000 $ $hurley->dump; > $hurley->dump; $VAR1 = bless( { '_id' => bless( { 'value' => '4c683525a74100a8df000000' }, 'MongoDB::OID' ), 'name' => 'Hurley' }, 'Person' );
This is pretty standard MongoDB stuff.
Now, for a more control over your primary key, use the role parameter -pk.
-pk
package BankAccount; use Moose; with 'Mongoose::Document' => { -pk => [qw/ drivers_license /] }; has 'drivers_license' => (is=>'rw', isa=>'Int' );
That way, updates use the drivers_license field and inserts will fail if the primary key exists. (But be sure to set a unique index on your primary key.)
drivers_license
If you first had a class definition as such:
package Author; use Moose; with 'Mongoose::Document'; has 'name' => ( is=>'rw', isa=>'Str' );
Saved some objects into the DB:
Author->new( name=>'Mike Old Schema' )->save;
Then, later on, changed it to:
has 'first_name' => ( is=>'rw', isa=>'Str' );
When reading from the database, at expansion time, since the MongoDB document is just blessed into your class the old attribute will be loaded as if nothing had happened:
# load old schema document using the new schema: my $obj = Author->find_one({ name => 'Mike Old Schema' }); print Dump $obj; # prints --- !!perl/hash:MyTestApp::Schema::Author _id: !!perl/hash:MongoDB::OID value: 4c723348a741001455000000 name: Mike Old Schema
At this time, no BUILD or BUILDARGS methods are called, which could be used to rearrange the object into the new schema. This will be probably fixed soon.
BUILD
BUILDARGS
On the meanwhile, you can always invoke an "alignment" method after loading the data, or do a bulk migration:
Author->find->each( sub{ my $obj = shift; $obj->first_name( delete $obj->{name} ); $obj->save; });
Now head on to the Mongoose::Cookbook.
To install Mongoose, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Mongoose
CPAN shell
perl -MCPAN -e shell install Mongoose
For more information on module installation, please visit the detailed CPAN module installation guide.