GSP
Quick Navigator

Search Site

Unix VPS
A - Starter
B - Basic
C - Preferred
D - Commercial
MPS - Dedicated
Previous VPSs
* Sign Up! *

Support
Contact Us
Online Help
Handbooks
Domain Status
Man Pages

FAQ
Virtual Servers
Pricing
Billing
Technical

Network
Facilities
Connectivity
Topology Map

Miscellaneous
Server Agreement
Year 2038
Credits
 

USA Flag

 

 

Man Pages
Mongoose::Cookbook(3) User Contributed Perl Documentation Mongoose::Cookbook(3)

Mongoose::Cookbook - recipes, recipes

Here we go.

First connect to a database before starting to use your classes.

    use Mongoose;
    Mongoose->db('mydb'); # looks for a localhost connection

    # or, for more control:

    Mongoose->db(
        host    => 'mongodb://mongodb.server:4000',
        db_name => 'mydb'
    );

This is done globally here for simplicity sake, but multiple connections and databases are also supported.

This is a work in progress. Right now this syntax is supported:

    # No class, this will be default db
    Mongoose->db( db_name => 'mydbone', ... );

    # A db for one class
    Mongoose->db( class => 'Person', db_name => 'mydbone', ... );

    # A db/host for several classes
    Mongoose->db( class => ['Address', 'Telephone'], db_name => 'mydbtwo', host => 'mongodb://host:27017' );

To quickly load your Mongoose classes (or any kind of package for that matter), use the "load_schema" method:

    package main;
    Mongoose->load_schema( search_path => 'MyApp::Schema', shorten => 1 );

Use it only once in your program. Your modules will be "require"d and may be used from anywhere else.

If set to 1, the "shorten" option will alias "MyApp::Schema::MyClass" into "MyClass" for convenience. Be careful not to shadow other classes by using this option.

In case your class has attributes you don't want to store in the database.

    package Person;
    use Moose;
    with 'Mongoose::Document';

    has name   => ( is => 'rw', isa => 'Str', required => 1 );
    has age    => ( is => 'rw', isa => 'Int', default  => 40 );
    has salary => ( is => 'rw', isa => 'Int', traits => ['DoNotMongoSerialize'] );

This can be accomplished several ways.

ArrayRef

Use the "ArrayRef" Moose type.

    package Person;
    use Moose;
    with 'Mongoose::Document';

    has name     => ( is => 'rw', isa => 'Str', required => 1 );
    has accounts => ( is => 'rw', isa => 'ArrayRef[Account]' );

Then, define the Account class, either as a document or embedded document, depending on how you want it stored.

    package Account;
    use Moose;
    with Mongoose::EmbeddedDocument;

    has amount => ( is => 'rw', isa => 'Int', required => 1 );

But this has a memory and performance cost, since all related rows will be loaded in memory during object expansion.

To avoid loading related rows, use a Mongoose::Join parameterized type.

Mongoose::Join

Establishing a Mongoose::Join relationship will load relationships lazily:

    package Person;
    use Moose; with 'Mongoose::Document';

    has name     => ( is => 'rw', isa => 'Str', required => 1 );
    has accounts => ( is => 'rw', isa => 'Mongoose::Join[Account]' );

Then retrieve data with a cursor:

    my $large_acc = $person->accounts->find({ amount => { '$gt' => 100000 } });
    while( my $account = $large_acc->next ) {
        ...
    }

Use Mongoose::Class instead of "Moose".

    package Article;
    use Mongoose::Class; with 'Mongoose::Document';

    has        title    => ( is => 'rw', isa => 'Str', required => 1 );
    has_one    author   => 'Author';
    has_many   comments => 'Comment';
    belongs_to acc      => 'Account';

Normalization is a relational concept, not natural to the document-oriented MongoDB, but an useful approach that should sometimes be taken into consideration.

Sometimes it may just be more adequate than storing relationships directly in objects:

    package Authorship;
    use Mongoose::Class; with 'Mongoose::Document';

    has_one  author   => 'Author';
    has_many articles => 'Article';

    # or even:
    #  has_one article => 'Article';

    package main;

    # create

    my $authorship = Authorship->new;
    $authorship->author( Author->new );
    $authorship->articles->add( Article->new );
    $authorship->articles->add( Article->new );

    # find

    my $articles = Authorship->find_one({ author => $author->_id });
    while( my $article = $articles->next ) {
        ...
    }

To make a long package name shorter, use:

    package My::Mumbo::Jumbo::Class;
    with 'Mongoose::Document' => { -as => 'Mumbo' };

    # then in your code

    my $obj = Mumbo->find_one({ flavor=>'gum' })

    print ref $obj;
    # prints 'My::Mumbo::Jumbo::Class'

    print $obj->isa('Mumbo')
    # prints 1

In case you don't want a "find_one" or "save" method polluting your class.

    package BankAccount;
    with 'Mongoose::Document' => {
        -alias    => { 'find_one' => '_find_one' },
        -excludes => { 'find_one' },
    };

By default when collapsing data into the DB, Mongoose always sends to the MongoDB driver an unblessed version of your attribute.

But there are special types of objects that the Perl MongoDB driver may expect to receive blessed, such as a "MongoDB::Code" object.

To skip the Mongoose Engine collapsing process altogether, use the "Raw" trait. That way, the MongoDB driver will receive the attribute as is.

    has code => ( is => 'rw', isa => 'MongoDB::Code', traits => ['Raw'] );

DateTime, DateTime::Tiny and BSON::Time objects are passed untouched to the MongoDB driver, so using the Raw helper is not necesary and not recommended.

This attribute:

    has date => (
        is      => 'rw',
        isa     => 'DateTime',
        default => sub{ DateTime->now }
    );

Is collapsed exactly the same as:

    has date => (
        is      => 'rw',
        isa     => 'DateTime',
        traits  => ['Raw'],
        default => sub{ DateTime->now }
    );

Which in turn is stored as:

    {
        "_id" : ObjectId("4c750574a74100a588000000"),
        "date" : ISODate("2012-12-20T05:00:49Z")
    }

Either way the object will be expanded back from the database into a "BSON::TIme" object. If you want to have it back as a DateTime object as in the old versions, you should not use the Raw trait and make sure your attribute 'isa' DateTime:

    has date => (
        is      => 'rw',
        isa     => 'DateTime',
        default => sub{ DateTime->now }
    );

Just use MongoDB's "find" standard syntax:

    # sorting

    Person->find->sort({ name => 1 })->each(sub{
        my $person = shift;
        print $person->name;
    });

    # paging

    Person->find->sort({ name => 1 })->skip(20)->limit(20)->each(sub{
        my $person = shift;
        print $person->name;
    });

Support for the FileHandle Moose type is done making use of MooseDB::GridFSBucket.

    package Thing;
    use Mongoose::Class; with 'Mongoose::Document';
    has 'file' => ( is=>'rw', isa=>'FileHandle' );

Then store it using a "FileHandle" type object:

    require IO::File;
    my $fh = new IO::File "myfile.txt", "r";
    my $t = Thing->new( file=>$fh );
    $t->save;

The file is stored in Mongo's GridFS using the field name and "_id" as filename. A special reference is created in your document to point to the GridFS file. The "thing" collection BSON may look like this:

    { "file":
        { "$ref": "FileHandle",
          "$id": ObjectId("4c7619d02f2e70d7a4140000") }
    }

When expanding the doc, the file attibute becomes a Mongoose::File, a wrapper of MongoDB::GridFSBucket::DownloadStream, which can be easily slurped or streamed.

    my $file = Thing->find_one->file;
    print $file->slurp;

This is asymmetric, which means it's probably best if you check the attribute if it isa "Mongoose::File" before using it as such, specially in your class methods.

    package Thing;
    # ...
    sub my_file_method {
        my $self = shift;
        if( $self->file->isa('Mongoose::File') ) {
            my $data = $file->slurp;
            # do stuff
        } else {
            # probably an IO::File filehandle still
        }
    }

This less-than-optimal asymmetric behavior may change in the future.

To skip Mongoose and have direct access to MongoDB, use the "db" and "collection" methods on your class:

    # finds and expands documents into objects

    my $cur = Person->find;

    # or just get the plain documents (hashrefs)

    my $cur = Person->collection->find;

    # get the MongoDB::Database object for your class

    my $db = Person->db;
    $db->run_command({ shutdown => 1 });

This can be useful for performance reasons, when you don't need to expand objects, or need to access MongoDB functionality not available in Mongoose. It's also useful for testing, to compare if some set of query results returned by Mongoose are identical to the straight MongoDB driver's results.

If you're passing around the "_id" attribute from your objects in, ie, a webapp, you're probably turning it into a string.

The MongoDB way of retrieving an "_id" from a string is a little annoying to type:

    $author_id = "4dd77f4ebf4342d711000000";
    $author = Author->find_one( BSON::OID->new( oid => pack("H*",$author_id) ) );

So, there is a shorthand variation of "find_one" to help you on this:

    $author = Author->find_one('4dd77f4ebf4342d711000000');

Will automatically search by "_id" on the given value by first turning it into a "BSON::OID".

2018-10-08 perl v5.32.1

Search for    or go to Top of page |  Section 3 |  Main Index

Powered by GSP Visit the GSP FreeBSD Man Page Interface.
Output converted with ManDoc.