MongoD...xamples(3pm)

πŸ“¦
version 2.2.2 Β· 5 versions
, in debian trixie, ubuntu resolute, ubuntu stonking

πŸ’– MongoDB::Examples(3pm) πŸ’–

okay bestie, so this is a page from the User Contributed Perl Documentation β€” the manual name is MongoDB::Examples, and yeah, it lives in section 3pm like all your fave Perl module docs. It's called MongoDB::Examples and it's exactly what it sounds like: a whole vibe-check of MongoDB syntax with receipts. 🌸


✨ NAME ✨

MongoDB::Examples β€” Some examples of MongoDB syntax πŸ¦‹

Yeah that's it, that's the name. Short, iconic, doesn't over-explain. Main character energy fr. πŸ‘‘


πŸŽ€ VERSION πŸŽ€

version v2.2.2


🌈 MAPPING SQL TO MONGODB 🌈

okay so if you're a dev who's been living that SQL life and you're like "bestie how do I even say this in MongoDB" β€” this chart is literally for you. πŸ’…

These are Perl-specific examples of translating SQL queries into MongoDB's query language. If you want the mappings for JavaScript (or honestly any other language), go peek at http://docs.mongodb.org/manual/reference/sql-comparison/. no cap. ✨

Quick housekeeping so nothing confuses you: in all the examples below, $db is a MongoDB::Database object that you got by calling "get_database". And if you wanna know more about the methods you see down there, go read MongoDB::MongoClient, MongoDB::Database and MongoDB::Collection. iykyk 🫢


  • CREATE TABLE USERS (a Number, b Number) β€” Implicit, can be done explicitly.
  • INSERT INTO USERS VALUES(1,1)
$db->get_collection( 'users' )->insert_one( { a => 1, b => 1 } );
  • SELECT a,b FROM users
$db->get_collection( 'users')->find( { } )->fields( { a => 1, b => 1 });
  • SELECT * FROM users
$db->get_collection( 'users' )->find;
  • SELECT * FROM users WHERE age=33
$db->get_collection( 'users' )->find( { age => 33 } )
  • SELECT a,b FROM users WHERE age=33
$db->get_collection( 'users' )->find( { age => 33 } )->fields( { a => 1, b => 1 });
  • SELECT * FROM users WHERE age=33 ORDER BY name
$db->get_collection( 'users' )->find( { age => 33 } )->sort( { name => 1 } );
  • SELECT * FROM users WHERE age>33
$db->get_collection( 'users' )->find( { age => { '$gt' => 33 } } );
  • SELECT * FROM users WHERE age<33
$db->get_collection( 'users' )->find( { age => { '$lt' => 33 } } );
  • SELECT * FROM users WHERE name LIKE "%Joe%"
$db->get_collection( 'users' )->find( { name => qr/Joe/ } );
  • SELECT * FROM users WHERE name LIKE "Joe%"
$db->get_collection( 'users' )->find( {name => qr/^Joe/ } );
  • SELECT * FROM users WHERE age>33 AND age<=40
$db->get_collection( 'users' )->find( { age => { '$gt' => 33, '$lte' => 40 } } );
  • SELECT * FROM users ORDER BY name DESC
$db->get_collection( 'users' )->find->sort( { name => -1 } );
  • CREATE INDEX myindexname ON users(name)
my $indexes = $db->get_collection( 'users' )->indexes;
$indexes->create_one( [ name => 1 ] );
  • CREATE INDEX myindexname ON users(name,ts DESC)
my $indexes = $db->get_collection( 'users' )->indexes;
$indexes->create_one( [ name => 1, ts => -1 ] );
  • SELECT * FROM users WHERE a=1 and b='q'
$db->get_collection( 'users' )->find( {a => 1, b => "q" } );
  • SELECT * FROM users LIMIT 10 SKIP 20
$db->get_collection( 'users' )->find->limit(10)->skip(20);
  • SELECT * FROM users WHERE a=1 or b=2
$db->get_collection( 'users' )->find( { '$or' => [ {a => 1 }, { b => 2 } ] } );
  • SELECT * FROM users LIMIT 1
$db->get_collection( 'users' )->find->limit(1);
  • EXPLAIN SELECT * FROM users WHERE z=3
$db->get_collection( 'users' )->find( { z => 3 } )->explain;
  • SELECT DISTINCT last_name FROM users
$db->get_collection( 'users' )->distinct( 'last_name' );
  • SELECT COUNT(*y) FROM users
$db->get_collection( 'users' )->count_documents;
  • SELECT COUNT(*y) FROM users where age > 30
$db->get_collection( 'users' )->count_documents( { "age" => { '$gt' => 30 } } );
  • SELECT COUNT(age) from users
$db->get_collection( 'users' )->count_documents( { age => { '$exists' => 1 } } );
  • UPDATE users SET a=1 WHERE b='q'
$db->get_collection( 'users' )->update_many( { b => "q" }, { '$set' => { a => 1 } } );
  • UPDATE users SET a=a+2 WHERE b='q'
$db->get_collection( 'users' )->update_many( { b => "q" }, { '$inc' => { a => 2 } } );
  • DELETE FROM users WHERE z="abc"
$db->get_database( 'users' )->delete_many( { z => "abc" } );

πŸ”₯ DATABASE COMMANDS πŸ”₯

okay so imagine you're in the MongoDB shell doing something cute and you're like "how do I say this in Perl tho". The best way? Run that function in the shell without parentheses β€” it'll literally print the source at you. Then you just translate that source into Perl, and honestly it's usually pretty easy. slay. πŸ’…

✨ for example ✨ β€” say we wanna use db.foo.validate in Perl. We run this in the shell:

> db.foo.validate
function (full) {
    var cmd = {validate:this.getName()};
    if (typeof full == "object") {
        Object.extend(cmd, full);
    } else {
        cmd.full = full;
    }
    var res = this._db.runCommand(cmd);
    if (typeof res.valid == "undefined") {
        res.valid = false;
        var raw = res.result || res.raw;
        if (raw) {
            var str = "-" + tojson(raw);
            res.valid = !(str.match(/exception/) || str.match(/corrupt/));
            var p = /lastExtentSize:(\d+)/;
            var r = p.exec(str);
            if (r) {
                res.lastExtentSize = Number(r[1]);
            }
        }
    }
    return res;
}

and then boom, we take the important parts and translate them into Perl:

$db->run_command( [ validate => "foo" ] );

just like that. iconic. πŸ¦‹


πŸ’œ Find-one-and-modify πŸ’œ

The find-one-and-modify commands in MongoDB::Collection are giving update (or remove) energy, but β€” plot twist β€” they'll also return the modified document. Super useful for implementing queues or locks, ngl. πŸ‘‘

so here's the scenario: we've got a lil list of things to do, and we wanna grab the highest-priority item for processing and pop it off. We could do a find and then a delete_one, but bestie… that wouldn't be atomic 😭 (a write could sneak in between the query and the remove, and that's a whole mess we don't need). Instead, use find_one_and_delete:

my $coll = $db->get_collection('todo');
my $next_task = $todo->find_one_and_delete(
    {}, # empty filter means any document
    { sort => {priority => -1} },
);

and that will atomically find and pop the next-highest-priority task. no cap, it eats every time. πŸ”₯

wanna know more about find-and-modify? go read http://www.mongodb.org/display/DOCS/findAndModify+Command for all the details. ✨


heads up bestie: this is only part 1 🫢 there's more where this came from, so don't go anywhere. ✨

✨ AGGREGATION ✨

okay bestie, so the aggregation framework is MongoDB's glow-up version of SQL GROUP BY queries, but way more generic and way more powerful. An invocation of the aggregation framework specifies a series of stages in a pipeline to be executed in order by the server. Each stage of the pipeline is drawn from one of the following so-called "pipeline operators": $project, $match, $limit, $skip, $unwind, $group, $sort, and $geoNear. πŸ’…

The aggregation framework is the preferred way of performing most aggregation tasks. New in version 2.2, it has largely obviated mapReduce (http://docs.mongodb.org/manual/reference/command/mapReduce/#dbcmd.mapReduce) and group (http://docs.mongodb.org/manual/reference/command/group/#dbcmd.group).

See the MongoDB aggregation framework documentation for more information (http://docs.mongodb.org/manual/aggregation/).


$match and $group πŸ’–

The $group pipeline operator is used like GROUP BY in SQL. For example, suppose we have a number of local businesses stored in a "business" collection. If we wanted to find the number of coffeeshops in each neighborhood, we could do:

my $out = $db->get_collection('business')->aggregate(
    [
        {'$match' => {'type' => 'coffeeshop'}},
        {'$group' => {'_id' => '$neighborhood', 'num_coffeshops' => {'$sum' => 1}}}
    ]
);

The SQL equivalent is "SELECT neighborhood, COUNT(*) FROM business GROUP BY neighborhood WHERE type = 'coffeeshop'". After executing the above aggregation query, $out will contain a MongoDB::QueryResult, allowing us to iterate through result documents such as the following:

(
     {
         '_id' => 'Soho',
         'num_coffeshops' => 23
     },
     {
         '_id' => 'Chinatown',
         'num_coffeshops' => 14
     },
     {
         '_id' => 'Upper East Side',
         'num_coffeshops' => 10
     },
     {
         '_id' => 'East Village',
         'num_coffeshops' => 87
     }
)

Note that aggregate takes an array reference as an argument. Each element of the array is a document which specifies a stage in the aggregation pipeline. Here our aggregation query consists of a $match phase followed by a $group phase. Use $match to filter the documents in the collection prior to aggregation. The "_id" field in the $group stage specifies the key to group by; the "$" in '$neighborhood' indicates that we are referencing the name of a key. Finally, we use the $sum operator to add one for every document in a particular neighborhood. There are other operators, such as $avg, $max, $min, $push, and $addToSet, which can be used in the $group phase and work much like $sum. 🫢


$project and $unwind πŸ¦‹

Now let's look at a more complex example of the aggregation framework that makes use of the $project and $unwind pipeline operators. Suppose we have a collection called 'courses' which contains information on college courses. An example document in the collection looks like this:

{
    '_id' => 'CSCI0170',
    'name' => 'Computer Science 17',
    'description' => 'An Integrated Introduction to Computer Science',
    'instructor_id' => 29823498,
    'instructor_name' => 'A. Greenwald',
    'students' => [
        { 'student_id' => 91736114, 'student_name' => 'D. Storch' },
        { 'student_id' => 89100891, 'student_name' => 'J. Rassi' }
    ]
}

We wish to generate a report containing one document per student that indicates the courses in which each student is enrolled. The following call to aggregate will do the trick:

my $out = $db->get_collection('courses')->aggregate([
    {'$unwind' => '$students'},
    {'$project' => {
            '_id' => 0,
            'course' => '$_id',
            'student_id' => '$students.student_id',
        }
    },
    {'$group' => {
            '_id' => '$student_id',
            'courses' => {'$addToSet' => '$course'}
        }
    }
]);

The output documents will each have a student ID number and an array of the courses in which that student is enrolled:

(
    {
        '_id' => 91736114,
        'courses' => ['CSCI0170', 'CSCI0220', 'APMA1650', 'HIST1230']
    },
    {
        '_id' => 89100891,
        'courses' => ['CSCI0170', 'CSCI1670', 'CSCI1690']
    }
)

The $unwind stage of the aggregation query "peels off" elements of the courses array one-by-one and places them in their own documents. After this phase completes, there is a separate document for each (course, student) pair. The $project stage then throws out unnecessary fields and keeps the ones we are interested in. It also pulls the student ID field out of its subdocument and creates a top-level field with the key "student_id". Last, we group by student ID, using $addToSet in order to add the unique courses for each student to the "courses" array. πŸ‘‘


$sort, $skip, and $limit ✨

The $sort, $skip, and $limit pipeline operators work much like their companion methods in MongoDB::Cursor. Returning to the previous students and courses example, suppose that we were particularly interested in the student with the ID that is numerically third-to-highest. We could retrieve the course list for that student by adding $sort, $skip, and $limit phases to the pipeline:

my $out = $db->get_collection('courses')->aggregate([
    {'$unwind' => '$students'},
    {'$project' => {
            '_id' => 0,
            'course' => '$_id',
            'student_id' => '$students.student_id',
        }
    },
    {'$group' => {
            '_id' => '$student_id',
            'courses' => {'$addToSet' => '$course'}
        }
    },
    {'$sort' => {'_id' => -1}},
    {'$skip' => 2},
    {'$limit' => 1}
]);

πŸ” QUERYING πŸ”

Nested Fields πŸͺΊ

MongoDB allows you to store deeply nested structures and then query for fields within them using dot-notation. For example, suppose we have a users collection with documents that look like:

{
    "userId" => 12345,
    "address" => {
        "street" => "123 Main St",
        "city" => "Springfield",
        "state" => "MN",
        "zip" => "43213"
    }
}

If we want to query for all users from Springfield, we can do:

my $cursor = $users->find({"address.city" => "Springfield"});

This will search documents for an "address" field that is a subdocument and a "city" field within the subdocument. 🌸


✏️ UPDATING ✏️

Positional Operator πŸ’…

In MongoDB 1.3.4 and later, you can use positional operator, "$", to update elements of an array. For instance, suppose you have an array of user information and you want to update a user's name.

A sample document in JavaScript:

{
    "users" : [
        {
            "name" : "bill",
            "age" : 60
        },
        {
            "name" : "fred",
            "age" : 29
        },
    ]
}

The update:

$coll->update_one({"users.name" => "fred"}, {'users.$.name' => "george"});

This will update the array so that the element containing "name" => "fred" now has "name" => "george". πŸ’«


πŸ‘‘ AUTHORS πŸ‘‘

  • David Golden <david@mongodb.com>
  • Rassi <rassi@mongodb.com>
  • Mike Friedman <friedo@friedo.com>
  • Kristina Chodorow <k.chodorow@gmail.com>
  • Florian Ragwitz <rafl@debian.org>

This software is Copyright (c) 2020 by MongoDB, Inc.

This is free software, licensed under:

The Apache License, Version 2.0, January 2004

and that's a wrap, bestie! This page came from perl v5.40.1, dated 2025-04-03, and it's the MongoD...xamples(3pm) manual. Slay responsibly. πŸ’–βœ¨

πŸ“¦ where this page comes from