DealTaker.com > Inside DealTaker > PHP & MongoDB Sitting in a Tree: Part 1

Inside DealTaker

PHP & MongoDB Sitting in a Tree: Part 1

May 12, 2010 ellisgl

Yet another MongoDB and PHP tutorial, say it ain’t so! Well yes, but this tutorial aims to be more complete than the others out there. OK I’m going to assume you know what MongoDB is, so I’m not going to go over “what mongoDB is” or “what NoSQL is”. I’m going to do this series in a little different styling than my other tutorial series have been, so let just jump right in.

Here’s a couple links to get you up to speed:
Installing Mongo
MongoDB PHP Extension

If you have issues with MongoD not starting – something about “/data/db” missing, then you should create the directory. In *Nix: “mkdir /data/db”, in Win “mkdir c:data” then “mkdir c:datadb”.

Connecting
MySQL

[php]
<?php
$host = ‘localhost’;
$user = ‘root’;
$pass = “”;
$link = mysql_connect($host, $user, $pass, 1) or die(“Could not connect to: {$host}. Check your settings and try again.”);
[/php]

MongoDB

[php]
<?php
try
{
$link = new Mongo();
}
catch(MongoConnectionException $e)
{
die(‘Could not connect. Check to make sure MongoDB is running.’);
}
[/php]

As you can see, Mongo DB automatically will connect to the local host and default port. Yes, MySQL will do the same if you have the directives in your “php.ini” setup to do this. IE. “mysql.default_host”, “mysql.default_user” and “mysql.default_password”. You might notice that by default MongoDB doesn’t have a user name or password setup. I will go over how to set that up in part 2.

Creating and using a DB
MySQL

[php]$db = ‘testdb’;
$sql = “CREATE DATABASE `$db`”;

mysql_query($sql, $link);
mysql_select_db($db, $link);
[/php]

MongoDB

[php]$db = $link->testdb;
[/php]

In one line of code, MongoDB will create a DB automatically if it doesn’t already exists and select (use) it.

Create a Table / Collection
MySQL

[php]
// Create the Table
$tbl = ‘user’;
$sql = “CREATE TABLE `$tbl` (`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `login` VARCHAR (24)NOT NULL, `password` CHAR(32) NOT NULL, `email` VARCHAR(255)) TYPE=innodb;”;
mysql_query($sql, $link);
[/php]

MongoDB

[php]
// Create the collection (AKA Table)
$col = $db->user;
[/php]

Once again, MongoDB will automatically create something for us if it doesn’t exist. In this example, it create our “user” table. You might notice we didn’t define how the table is formatted, this is because MongoDB is schema-less and doesn’t need column definitions. “Tables” in MongoDB are called “collections”.

Insert Data
MySQL

[php]
// Insert a row into the table
$sql = “INSERT INTO `$tbl` SET `login` = ‘jsmith’, `password` = ’5f4dcc3b5aa765d61d8327deb882cf99′, `email` = ‘jsmith@example.com’”;
mysql_query($sql);

$sql = “INSERT INTO `$tbl` SET `login` = ‘psmith’, `password` = ’5f4dcc3b5aa765d61d8327deb882cf99′, `email` = ‘psmith@example.com’”;
mysql_query($sql);

// Get the ID of last insert
$id = mysql_insert_id($link);
[/php]

MongoDB

[php]
// Insert a document (row) into the collection (table)
$doc = array(‘login’ => ‘jsmith’, ‘password’ => ‘ 5f4dcc3b5aa765d61d8327deb882cf99′, ‘email’ => ‘jsmith@example.com’);
$col->insert($doc, true);

$doc = array(‘login’ => ‘psmith’, ‘password’ => ‘ 5f4dcc3b5aa765d61d8327deb882cf99′, ‘email’ => ‘psmith@example.com’);
$col->insert($doc, true);

// Get the id of last insert
$id = $doc['_id'];
[/php]

You might have noticed that we used an array to define our “row” of data, which are called “documents”. On the insert method, the second argument will set the query to do a “safe insert”. This will allow us to find out if the query executed properly. If not set, it will not get that information. So I would set it to true if you want to be able to debug your queries. If you are wondering about the password, it’s the MD5 hash for “password”.

Querying Data
MySQL

[php]
// Get all rows
$sql = “SELECT * FROM `$tbl`”;
$qry = mysql_query($sql, $link);
$cnt = mysql_num_rows($qry);

echo ‘All rows:<br/>’;

if($cnt > 0)
{
do
{
$row = mysql_fetch_assoc($qry);

echo ‘<pre>’;
print_r($row);
echo ‘</pre>’
}
}

// Query for the row matching the last insert ID
$sql = “SELECT * FROM `$tbl` WHERE `id` = {$id}”;
$qry = mysql_query($sql, $link);
$row = mysql_fetch_assoc($qry);

echo ‘Single row (id = $id):&lt;br/&gt;&lt;pre&gt;’;
print_r($row);
echo ‘</pre>
[/php]

MongoDB

[php]
// Get all documents
$res = $col->find();

echo ‘All documents:<br/>’;

foreach($res as $doc)
{
echo ‘<pre>’;
print_r($doc);
echo ‘</pre>’;
}

// Query for the document matching the last insert ID
$doc = $col->findone(array(‘_id’ => $id));

echo ‘Single document (_id = $id):<br/><pre>’;
print_r($doc);
[/php]

The MongoDB support in PHP has function to pull a single document, or all the documents. Once again, we use arrays to define something, in this example, we define our constraint.

Upading Data
MySQL

[php]
$sql = “UPDATE `$tbl` SET `password` = ‘b497dd1a701a33026f7211533620780d’ WHERE `id` = {$id}”;
$qry = mysql_query($sql, $link);

$sql = “SELECT * FROM `$tbl` WHERE `id` = {$id}”;
$qry = mysql_query($sql, $link);
$row = mysql_fetch_assoc($qry);

echo ‘Updated row (id = $id):<br/><pre>’;
print_r($row);
echo ‘</pre>
[/php]

MogoDB

[php]
// Update a document
$col->update(array(‘_id’ => $id), array(‘$set’ => array(‘password’ => ‘b497dd1a701a33026f7211533620780d’)));

// Query the updated docuemnt
$doc = $col->findone(array(‘_id’ => $id));

echo ‘Updated docuement:<br/><pre>’;
print_r($doc);
echo ‘</pre>’;
[/php]

The MongoDB extension has a function to perform updates. The first argument is an array of constraints, IE “WHERE {expression}”. The second is the data we want to update and what we want to update it with. Pretty simple, right?

Indexing data
MySQL

[php]
// Create a unique index
$sql = “ALTER TABLE `$db`.`$tbl` ADD UNIQUE `login` (`login`)”;
$qry = mysql_query($sql, $link);
[/php]

MongoDB

[php]
// Create a unique index
$col->ensureIndex(array(“login” => 1), array(“unique” => true, “dropDups” => true));
[/php]

MongoDB has method for creating index name “ensureIndex”. The first parameter is an array of what we want to index and the value is ether 1 or -1. 1 means that the index will sort the data for this in an ascending manor, while -1 will sort it in a descending manor. The second parameter are extra options. In this example I tell it to make what ever I’m indexing as a unique index, that way I don’t end up with multiple “jsmith” entries. Also, I’ve told it to drop any documents that have duplicates of the data in the “login” “column”. Another note is that “columns” in MongoDB should be called “elements”, since we are really dealing with arrays.

Lets make sure our index is working shall we?
MongoDb

[php]
// Test our unique index
try
{
$doc = array(‘login’ => ‘jsmith’, ‘password’ => ‘ 5f4dcc3b5aa765d61d8327deb882cf99′, ‘email’ => ‘jsmith@example.com’);
$col->insert($doc, true);
}
catch(MongoCursorException $e)
{
echo ‘Could not insert document. Double check values.<br />’;
}
[/php]

If all works, we should get an error message.

Limiting Results
MySQL

[php]
// Limits
$sql = “SELECT * FROM `$tbl` LIMIT 1″;
$qry = mysql_query($sql, $link);
$cnt = mysql_num_rows($qry);

echo ‘All rows – Limit by 1:<br/>’;

if($cnt > 0)
{
do
{
$row = mysql_fetch_assoc($qry);

echo ‘<pre>’;
print_r($row);
echo ‘</pre>’
}
}
[/php]

MongoDB

[php]
// Limits
$res = $col->find()->limit(1);

echo ‘All documents – Limited to 1:<br/>’;

foreach($res as $doc)
{
echo ‘<pre>’;
print_r($doc);
echo ‘</pre>’;
}
[/php]

Offsetting
MySQL

[php]
// Limits
$sql = “SELECT * FROM `$tbl` OFFSET 1 LIMIT 1″;
$qry = mysql_query($sql, $link);
$cnt = mysql_num_rows($qry);

echo ‘All rows – Limit by 1 – Offset by 1:<br/>’;

if($cnt > 0)
{
do
{
$row = mysql_fetch_assoc($qry);

echo ‘<pre>’;
print_r($row);
echo ‘</pre>’;
}
}
[/php]

MongoDB

[php]
// Offsets
$res = $col-&gt;find()-&gt;skip(1)-&gt;limit(1);

echo ‘All documents – Limited to 1, Offset by 1:<br/ >’;

foreach($res as $doc)
{
echo ‘<pre>’;
print_r($doc);
echo ‘</pre>’;
}
</pre>
[/php]

To offset a your query start point, we used the method “skip” with an integer value to create the offset.

Sorting
MySQL

[php]
// Sorting
$sql = “SELECT * FROM `$tbl` ORDERBY `login` DESC”;
$qry = mysql_query($sql, $link);
$cnt = mysql_num_rows($qry);

echo ‘All rows – Sorted by login descending:<br/>’;

if($cnt > 0)
{
do
{
$row = mysql_fetch_assoc($qry);

echo ‘<pre>’;
print_r($row);
echo ‘</pre>’
}while(–$cnt > 0);
}
[/php]

MongoDB

[php]
// Sorting
$res = $col->find()->sort(array(‘login’ => -1));

echo ‘All documents – Sorted by login descending:<br/>’;

foreach($res as $doc)
{
echo ‘<pre>’;
print_r($doc);
echo ‘</pre>’;
}
[/php]

Here we used the sort method, told it what element to sort on and which direction, -1 being descending. The record for “psmith” should show up first. A word of caution here, there is a 4MB limit if sorting on non-indexed element.

Advanced Queries
MySQL

[php]
// Advanced queries
// Add new column
$sql = “ALTER `$db`.`$tbl` ADD COLUMN `age` INT UNSIGNED”;
mysql_query($sql, $link);

// Add values
$sql = “UPDATE `$tbl` SET `age` = 40 WHERE `login` = ‘jsmith’”;
mysql_query($sql, $link);

$sql = “UPDATE `$tbl` SET `age` = 24 WHERE `login` = ‘psmith’”;
mysql_query($sql, $link);

// Query for anyone under 30
$sql = “SELECT * FROM `$tbl` WHERE `age` < 30″;
$qry = mysql_query($sql, $link);
$cnt = mysql_num_rows($qry);

echo ‘All documents – Anyone under 30:<br/>’;

if($cnt > 0)
{
do
{
$row = mysql_fetch_assoc($qry);

echo ‘<pre>’;
print_r($row);
echo ‘</pre>’;
}
}

// Query for anyone over 30
$sql = “SELECT * FROM `$tbl` WHERE `age` > 30″;
$qry = mysql_query($sql, $link);
$cnt = mysql_num_rows($qry);

echo ‘All documents – Anyone over 30:<br/>’;

if($cnt > 0)
{
do
{
$row = mysql_fetch_assoc($qry);

echo ‘<pre>’;
print_r($row);
echo ‘</pre>’;
}
}

// Query from anyone 25 and over
$sql = “SELECT * FROM `$tbl` WHERE `age` >= 25″;
$qry = mysql_query($sql, $link);
$cnt = mysql_num_rows($qry);

echo ‘All documents – Anyone 25 and over:<br/>’;

if($cnt > 0)
{
do
{
$row = mysql_fetch_assoc($qry);

echo ‘<pre>’;
print_r($row);
echo ‘</pre>’;
}
}
// Query for anyone 25 and under
$sql = “SELECT * FROM `$tbl` WHERE `age` <= 25″;
$qry = mysql_query($sql, $link);
$cnt = mysql_num_rows($qry);

echo ‘All documents – Anyone 25 and under:<br/>’;

if($cnt > 0)
{
do
{
$row = mysql_fetch_assoc($qry);

echo ‘<pre>’;
print_r($row);
echo ‘</pre>’;
}
}

// Query for anyone not 40
$sql = “SELECT * FROM `$tbl` WHERE `age` != 40″;
$qry = mysql_query($sql, $link);
$cnt = mysql_num_rows($qry);

echo ‘All documents – Anyone not 30:<br/>’;

if($cnt > 0)
{
do
{
$row = mysql_fetch_assoc($qry);

echo ‘<pre>’;
print_r($row);
echo ‘</pre>’;
}
}

// Query for anyone 20-50
$sql = “SELECT * FROM `$tbl` WHERE `age` >= 20 AND `age` <= 50″;
$qry = mysql_query($sql, $link);
$cnt = mysql_num_rows($qry);

echo ‘All documents – Anyone 20-50:<br/>’;

if($cnt > 0)
{
do
{
$row = mysql_fetch_assoc($qry);

echo ‘<pre>’;
print_r($row);
echo ‘</pre>’;
}
}
[/php]

MongoDB

[php]
// Advanced queries
// Add a new element and value
$col->update(array(‘login’ => ‘jsmith’), array(‘$set’ => array(‘age’ => 40)));
$col->update(array(‘login’ => ‘psmith’), array(‘$set’ => array(‘age’ => 24)));

// Confirm
$res = $col->find();

echo ‘All documents – Confirming age element:<br/>’;

foreach($res as $doc)
{
echo ‘<pre>’;
print_r($doc);
echo ‘</pre>’;
}

// Query for anyone under 30
$res = $col->find(array(‘age’ => array(‘$lt’ => 30)));

echo ‘All documents – Anyone under 30:<br/>’;

foreach($res as $doc)
{
echo ‘<pre>’;
print_r($doc);
echo ‘</pre>’;
}

// Query for anyone over 30
$res = $col->find(array(‘age’ => array(‘$gt’ => 30)));

echo ‘All documents – Anyone over 30:<br/>’;

foreach($res as $doc)
{
echo ‘<pre>’;
print_r($doc);
echo ‘</pre>’;
}

// Query from anyone 25 or over
$res = $col->find(array(‘age’ => array(‘$gte’ => 25)));

echo ‘All documents – Anyone 25 or older:<br/>’;

foreach($res as $doc)
{
echo ‘<pre>’;
print_r($doc);
echo ‘</pre>’;
}

// Query for anyone 25 and under
$res = $col->find(array(‘age’ => array(‘$lte’ => 25)));

echo ‘All documents – Anyone 25 or younger:<br/>’;

foreach($res as $doc)
{
echo ‘<pre>’;
print_r($doc);
echo ‘</pre>’;
}

// Query for anyone not 40
$res = $col->find(array(‘age’ => array(‘$ne’ => 40)));

echo ‘All documents – Anyone not 40:<br/>’;

foreach($res as $doc)
{
echo ‘<pre>’;
print_r($doc);
echo ‘</pre>’;
}

// Query for anyone 20-50
$res = $col->find(array(‘age’ => array(‘$gte’ => 20, ‘$lte’ => 50)));

echo ‘All documents – Anyone 20-50:<br/>’;

foreach($res as $doc)
{
echo ‘<pre>’;
print_r($doc);
echo ‘</pre>’;
}
[/php]

With MongoDB you can do “<,<=, >, >=” conditional operator based queries. Only thing is that you can’t just use “<,<=, >, >=” in your constraints, you have to use MongoDB’s version. Those are “$lt” (less than), “$lte” (less than or equal), “$gt” (greater than), “$gte” (greater than or equal). Yes, you need the dollar sign. Other conditional operators are “$ne” (not equal to), “$in” (like MySQL’s IN statement), “$nin” (NOT IN), “$mod” (Modulo operation), “$all” (like in, but all values much be matched to a document), “$size” (Match of number of elements), “$exists” (element exists), “$type” (matches element type – int, string, etc) and there are couple more.

Deleting a Row / Docuement
MySQL

[php]
// Delete a row
$sql = “DELETE FROM `$tbl` WHERE `login` = ‘psmith’”
mysql_query($sql, $link);
[/php]

MongoDB

[php]
// Delete a document
$col->remove(array(‘age’ => 24), array(‘justOne’ => true, ‘safe’ => true));

// Confirm delete
$res = $col->find();

echo ‘All documents:<br/>’;

foreach($res as $doc)
{
echo ‘<pre>’;
print_r($doc);
echo ‘</pre>’;
}
[/php]

Here we use the “remove” method. First argument has the constraints and the second tell it to only remove one item and also to run a “safe” query.

Dropping a Table / Collection
MySQL

[php]
// Drop table
$sql = “DROP TABLE `$db`.`$tbl`”;
mysql_query($sql, $link);
[/php]

MongoDB

[php]
// Drop collection
$col->drop();
[/php]

Dropping a database
MySQL

[php]
// Drop DB
$sql = “DROP DATABASE `$db`”;
mysql_query($sql, $link);
[/php]

MongoDB

[php]
// Drop DB
$db->drop();
[/php]

Disconnect
MySQL

[php]
// Disconnect
mysql_close($link);
[/php]

MongoDB

[php]
// Disconnect
$link->close();
[/php]

Until next time, when I will go over more stuff, happy coding.

Sources:
PHP.net, MongoDB

Part 2

Post a New Comment

Comments and Reviews:

  1.  
    Can't wait to read part 2, on joins, constr­aint integr­ity checks­ and transa­ctions­ ;) Seriou­sly, nice post.
  2.  
    @Niko: Thanks­!
  3.  
    seriou­sly nice post
  4.  
    The choice­ of the databa­se does not make PHP code better­.
  5.  
    @Dilip: Thanks­.
  6.  
    Awesom­e post, seeing­ the side by side compar­ison is insane­ly helpfu­l , I too look forwar­d to seeing­ part 2 - I'm curren­tly using Amazon­ Simple­DB for my new produc­t, but would love to move to Mongro­DB.. Thanks­ again dude, nice work. Brando­n
  7.  
    This is a bit obsess­ive-compul­sive, so feel free to skip over it. :) I know these are just exampl­es for compar­ison, but you really­ should­n't be using non-escape­d string­s in your MySQL exampl­es, or a databa­se API (mysql_­* functi­ons) that isn't develo­ped any more. People­ Google­ for usage exampl­es all the time, and they'll pick up a really­ unsafe­ habit if they copy from your mysql exampl­es. I'd sugges­t rewrit­ing the exampl­es with either­ mysqli­ or PDO and using prepar­ed statem­ents, just to reflec­t how things­ should­ actual­ly be done in the real world with both MongoD­B and MySQL.
  8.  
    Nice post, very useful­. I have a questi­on : can I publis­h a transl­ation of your post in french­ on my blog ? Thanks­.
  9.  
    @Brando­n: Thanks­! @Matt: Yeah, I should­ be slappe­d for not doing that.
  10.  
    @Kevina­rd: It's OK by me, just link back to this articl­e.
  11.  
  12.  
    @ellisg­l The french­ transl­ation is online­ : http://blog.kevino­neill.fr/post/2010/05/19/PHP-et-MongoD­B
  13.  
    @kevina­rd: Thanks­!
  14.  
    @Appath­ync: Thanks­!
  15.  
    All the basis, nothin­g specia­l (and very old MySQL code, but the way ;-)) but I'm expect­ing a lot from part 2, looks intere­sting !! Cheers­
  16.  
    Nice! Where was this tutori­al months­ ago when I needed­ help!!!! ;-)
  17.  
    Congra­tulati­on, you've just learn some SQL which seems you hate it ! Also from this exampl­es I unders­tand that you like ARRAYS­ which may not be so bad :)) if you knows how to handle­ them! Your tutori­al seems to counti­ng charac­ters in a statem­ent, so I really­ do not unders­tand what you want to explai­n us and which is the scope of it. If you do a google­ by "MYSQL DB LAYERS­" you'll find some "things­" which will help you to use MYSQL DB with a minimu­m of charac­ters and also using YOUR prefer­red data types for QUERIE­S. - DO NOT FORGET­ TO WRITE SOME MORE "Advanc­ed Querie­s" in PART 2 !
  18.  
    Thank you. Your artice­l is very good for mongo newbie­.
  19.  
    great articl­e. thank you for enligh­tening­ me.
© 2012 DealTaker Inc., a Media General company. All Rights Reserved.

Coupons, Coupon Codes & Promotional Codes