CHAPTER 6

image

PHP and MongoDB

Through Chapters 1 to 5, you’ve learned how to perform all sorts of actions in the MongoDB shell. For example, you’ve learned how to add, modify, and delete a document. You’ve also learned about the workings of DBRef and GridFS, including how to use them.

So far, however, most of the things you’ve learned about have taken place in the MongoDB shell. It is a very capable application, but the MongoDB software also comes with a vast number of additional drivers (see Chapter 2 for more information on these) that let you step outside the shell to accomplish many other sorts of tasks programmatically.

One such tool is the PHP driver, which allows you to extend your PHP installation to connect, modify, and manage your MongoDB databases when you want to use PHP rather than the shell. This can be helpful when you need to design a web application or don’t have access to the MongoDB shell. As this chapter will demonstrate, most of the actions you can perform with the PHP driver closely resemble functions you can execute in the MongoDB shell; however, the PHP driver requires that the options be specified in an array, rather than between two curly brackets. Similarities notwithstanding, you will need to be aware of quite a few howevers when working with the PHP driver. This chapter will walk you through the benefits of using PHP with MongoDB, as well as how to overcome the aforementioned howevers.

This chapter brings you back to the beginning in many ways. You will start by learning to navigate the database and use collections in PHP. Next you will learn how to insert, modify, and delete posts in PHP. You will also learn how to use GridFS and DBRef again; this time, however, the focus will be on how to use them in PHP, rather than the theory behind these technologies.

Comparing Documents in MongoDB and PHP

As you’ve learned previously, a document in a MongoDB collection is stored using a JSON-like format that consists of keys and values. This is similar to the way PHP defines an associative array, so it shouldn’t be too difficult to get used to this format.

For example, assume a document looks like the following in the MongoDB shell:

contact = ( {
    "First Name" : "Philip",
    "Last Name" : "Moran",
    "Address" : [
        {
        "Street" : "681 Hinkle Lake Road",
        "Place" : "Newton",
        "Postal Code" : "MA 02160",
        "Country" : "USA"
        }
    ],
    "E-Mail" : [
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "[email protected]"
    ],
    "Phone" : "617-546-8428",
    "Age" : 60
})

The same document would look like this when contained in an array in PHP:

$contact = array(
    "First Name" => "Philip",
    "Last Name" => "Moran",
    "Address" => array(
        "Street" => "681 Hinkle Lake Road",
        "Place" => "Newton",
        "Postal Code" => "MA 02160",
        "Country" => "USA"
    )
    ,
    "E-Mail" => array(
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "[email protected]"
    ),
    "Phone" => "617-546-8428",
    "Age" => 60
);

The two versions of the document look a lot alike. The obvious difference is that the colon (:) is replaced as the key/value separator by an arrow-like symbol (=>) in PHP. You will get used to these syntactical differences relatively quickly.

MongoDB Classes

The PHP driver version 1.6 for MongoDB contains four core classes, a few others for dealing with GridFS, and several more to represent MongoDB datatypes. The core classes make up the most important part of the driver. Together, these classes allow you to execute a rich set of commands. The four core classes available are as follows:

  • MongoClient: Initiates a connection to the database and provides database server commands such as connect(), close(), listDBs(), selectDBs(), and selectCollection().
  • MongoDB: Interacts with the database and provides commands such as createCollection(), selectCollection(), createDBRef(), getDBRef(), drop(), and getGridFS().
  • MongoCollection: Interacts with the collection. It includes commands such as count(), find(), findOne(), insert(), remove(), save(), and update().
  • MongoCursor: Interacts with the results returned by a find() command and includes commands such as getNext(), count(), hint(), limit(), skip(), and sort().

In this chapter, we’ll look at all of the preceding commands; without a doubt, you’ll use these commands the most.

Image Note  This chapter will not discuss the preceding commands grouped by class; instead, the commands will be sorted in as logical an order as possible.

Connecting and Disconnecting

Let’s begin by examining how to use the MongoDB driver to connect to and select a database and a collection. You establish connections using the Mongo class, which is also used for database server commands. The following example shows how to quickly connect to your database in PHP:

// Connect to the database
$c = new MongoClient();
// Select the database you want to connect to, e.g. contacts
$c->contacts;

The Mongo class also includes the selectDB() function, which you can use to select a database:

// Connect to the database
$c = new MongoClient();
// Select the database you want to connect to, e.g. contacts
$c->selectDB("contacts");

The next example shows how to select the collection you want to work with. The same rules apply as when working in the shell: if you select a collection that does not yet exist, it will be created when you save data to it. The process for selecting the collection you want to connect to is similar to that for connecting to the database; in other words, you use the (->) syntax to literally point to the collection in question, as in the following example:

// Connect to the database
$c = new MongoClient();
// Selecting the database (’contacts’) and collection (’people’) you want
// to connect to
$c->contacts->people;

The selectCollection() function also lets you select—or switch—collections, as in the following example:

// Connect to the database
$c = new MongoClient();
// Selecting the database (’contacts’) and collection (’people’) you want
// to connect to
$c-> selectDB("contacts")->selectCollection("people");

Before you can select a database or a collection, you sometimes need to find the desired database or collection. The Mongo class includes two additional commands for listing the available databases, as well as the available collections. You can acquire a list of available databases by invoking the listDBs() function and printing the output (which will be placed in an array):

// Connecting to the database
$c = new MongoClient();
// Listing the available databases
print_r($c->listDBs());

Likewise, you can use listCollections() to get a list of available collections in a database:

// Connecting to the database
$c = new MongoClient();
// Listing the available collections within the ’contacts’ database
print_r($c->contacts->listCollections());

Image Note  The print_r command used in this example is a PHP command that prints the contents of an array. The listDBs() function returns an array directly, so the command can be used as a parameter of the print_r function.

The MongoClient class also contains a close() function that you can use to disconnect the PHP session from the database server. However, using it is generally not required, except in unusual circumstances, because the driver will automatically close the connection to the database cleanly whenever the Mongo object goes out of scope.

Sometimes you may not want to forcibly close a connection. For example, you may not be sure of the actual state of the connection, or you may wish to ensure that a new connection can be established. In this case, you can use the close() function, as shown in the following example:

// Connecting to the database
$c = new MongoClient();
// Closing the connection
$c->close();

Inserting Data

So far you’ve seen how to establish a connection to the database. Now it’s time to learn how to insert data into your collection. The process for doing this is no different in PHP than when using the MongoDB shell. The process has two steps. First, you define the document in a variable. Second, you insert it using the insert() function.

Defining a document is not specifically related to MongoDB—instead, you create an array with keys and values stored in it, as in the following example:

$contact = array(
    "First Name" => "Philip",
    "Last Name" => "Moran",
    "Address" => array(
        "Street" => "681 Hinkle Lake Road",
        "Place" => "Newton",
        "Postal Code" => "MA 02160",
        "Country" => "USA"
    )
    ,
    "E-Mail" => array(
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "[email protected]",
        "[email protected]"
    ),
    "Phone" => "617-546-8428",
    "Age" => 60
);

Image Warning  Strings sent to the database need to be UTF-8 formatted to prevent an exception from occurring.

Once you’ve assigned your data properly to a variable—called $contact in this case—you can use the insert() function to insert it into the MongoCollection class:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’
$collection = $c->contacts->people;
// Insert the document ’$contact’ into the people collection ’$collection’
$collection->insert($contact);

The insert() function takes five options, specified in an array: fsync, j, w, wTimeoutMS, and socketTimeoutMS. The fsync option can be set to TRUE or FALSE; FALSE is the default value for this option. If set to TRUE, fsync forces the data to be written to the hard disk before it indicates the insertion was a success. This option will override any setting for the option w, setting it to 0. Generally, you will want to avoid using this option. The j option can be set to TRUE or FALSE, where FALSE is the default. If set, the j option will force the data to be written to the journal before indicating the insertion was a success. If you are unfamiliar with journaling, think of it as a log file that keeps track of the changes made to your data, before it is finally written to disk. This ensures that, were mongod to stop unexpectedly, it would be able to recover the changes written to the journal, thereby preventing your data from entering an inconsistent state.

The w option can be used to acknowledge or unacknowledge a write operation (making this option also applicable for remove() and update() operations). If w is set to 0, the write operation will not be acknowledged; set it to 1 and the write will be acknowledged by the (primary) server. When working with replica sets, w can also be set to n, ensuring that the primary server acknowledges the write operation when successfully replicated to n nodes. The w option can also be set to ’majority’—a reserved string—ensuring that the majority of the replica set will acknowledge the write, or to a specific tag, ensuring that those tagged nodes will acknowledge the write. For this option, the default setting is also 1. The wTimeoutMS option can be used to specify how long the server is to wait for receiving acknowledgment (in milliseconds). By default, this option is set to 10000. Lastly, the socketTimeoutMS option allows you to specify how long (in milliseconds) the client needs to wait for a response from the database. By default, this option is set to 30000.

Image Warning  The wTimeoutMS and socketTimeoutMs options determine how long the client will wait for a response, but do not interrupt any operations executed server-side when the timeout expires. As such, operations may complete after the timeout, but the application will not know about this, having given up waiting for a response.

The following example illustrates how to use the w and wTimeoutMS options to insert data:

// Define another contact
$contact = array(
        "First Name" => "Victoria",
        "Last Name" => "Wood",
        "Address" => array(
                        "Street" => "50 Ash lane",
                        "Place" => "Ystradgynlais",
                        "Postal Code" => "SA9 6XS",
                        "Country" => "UK"
        )
        ,
        "E-Mail" => array(
                "[email protected]",
                "[email protected]"
        ),
        "Phone" => "078-8727-8049",
        "Age" => 28
);
// Connect to the database
$c = new MongoClient();
// Select the collection ’people’
$collection = $c->contacts->people;
// Specify the w and wTimeoutMS options
$options = array("w" => 1, "wTimeoutMS" => 5000);
// Insert the document ’$contact’ into the people collection ’$collection’
$collection->insert($contact,$options);

And that’s all there is to inserting data into your database with the PHP driver. For the most part, you will probably be working on defining the array that contains the data, rather than injecting the data into the array.

Listing Your Data

Typically, you will use the find() function to query for data. It takes a parameter that you use to specify your search criteria; once you specify your criteria, you execute find() to get the results. By default, the find() function simply returns all documents in the collection. This is similar to the shell examples discussed in Chapter 4. Most of the time, however, you will not want to do this. Instead, you will want to define specific information for which to return results. The next sections will cover commonly used options and parameters that you can use with the find() function to filter your results.

Returning a Single Document

Listing a single document is easy: simply executing the findOne() function without any parameters specified will grab the first document it finds in the collection. The findOne function stores the returned information in an array and leaves it up to you to print out again, as in this example:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Find the very first document within the collection, and print it out
// using print_r
print_r($collection->findOne());

As noted previously, it’s easy to list a single document in a collection: all you will need to do is define the findOne() function itself. Naturally, you can use the findOne() function with additional filters. For instance, if you know the last name of a person you’re looking for, you can specify it as an option in the findOne() function:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Define the last name of the person in the $lastname variable
$lastname = array("Last Name" => "Moran");
// Find the very first person in the collection with the last name "Moran"
print_r($collection->findOne($lastname));

Of course, many more options exist for filtering the data; you’ll learn more about these additional options later in this chapter. Let’s begin by looking at some sample output returned by using the print_r() command (the example adds a few line breaks for the sake of making the code easier to read):

Array (
    [_id] => MongoId Object ( )
    [First Name] => Philip
    [Last Name] => Moran
    [Address] => Array (
        [Street] => 681 Hinkle Lake Road
        [Place] => Newton
        [Postal Code] => MA 02160
        [Country] => USA
    )
    [E-Mail] => Array (
        [0] => [email protected]
        [1] => [email protected]
        [2] => [email protected]
        [3] => [email protected]
        [4] => [email protected]
        [5] => [email protected]
        [6] => [email protected]
        [7] => [email protected]
    )
    [Phone] => 617-546-8428
    [Age] => 60
)

Listing All Documents

While you can use the findOne() function to list a single document, you will use the find() function for pretty much everything else. Don’t misunderstand, please: you can find a single document with the find() function by limiting your results; but if you are unsure about the number of documents to be returned, or if you are expecting more than one document to be returned, then the find() function is your friend.

As detailed in the previous chapters, the find() function has many, many options that you can use to filter your results to suit just about any circumstance you can imagine. We’ll start off with a few simple examples and build from there.

First, let’s see how you can display all the documents in a certain collection using PHP and the find() function. The only thing that you should be wary of when printing out multiple documents is that each document is returned in an array, and that each array needs to be printed individually. You can do this using PHP’s while() function. As just indicated, you will need to instruct the function to print each document before proceeding with the next one. The getNext() command gets the next document in the cursor from MongoDB; this command effectively returns the next object in the cursor and advances the cursor. The following snippet lists all the documents found in a collection:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Execute the query and store it under the $cursor variable
$cursor = $collection->find();
// For each document it finds within the collection, print the contents
while ($document = $cursor->getNext())
{
        print_r($document);
}

Image Note  You can implement the syntax for the preceding example several different ways. For example, a faster way to execute the preceding command would look like this: $cursor = $c->contacts->people->find(). For the sake of clarity, however, code examples like this one will be split up into two lines in this chapter, leaving more room for comments.

At this stage, the resulting output would still show only two arrays, assuming you have added the documents described previously in this chapter (and nothing else). If you were to add more documents, then each document would be printed in its own array. Granted, this doesn’t look pretty; however, that’s nothing you can’t fix with a little additional code.

Using Query Operators

Whatever you can do in the MongoDB shell, you can also accomplish using the PHP driver. As you’ve seen in the previous chapters, the shell includes dozens of options for filtering your results. For example, you can use dot notation; sort or limit the results; skip, count, or group a number of items; or even use regular expressions, among many other things. The following sections will walk you through how to use most of these options with the PHP driver.

Querying for Specific Information

As you might remember from Chapter 4, you can use dot notation to query for specific information in an embedded object in a document. For instance, if you want to find one of your contacts for which you know a portion of the address details, you can use dot notation to find this, as in the following example:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Use dot notation to search for a document in which the place
// is set to "Newton"
$address = array("Address.Place" => "Newton");
// Execute the query and store it under the $cursor variable
$cursor = $collection->find($address);
// For each document it finds within the collection, print the ID
// and its contents
while ($document = $cursor->getNext())
{
    print_r($document);
}

In a similar fashion, you can search for information in a document’s array by specifying one of the items in that array, such as an e-mail address. Because an e-mail address is (usually) unique, the findOne() function will suffice in this example:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Define the e-mail address you want to search for under $email
$email = array("E-Mail" => "[email protected]");
// Find the very first person in the collection matching the e-mail address
print_r($collection->findOne($email));

As expected, this example returns the first document that matches the e-mail address [email protected]—the address of Victoria Wood in this case. The document is returned in the form of an array:

Array (
    [_id] => MongoId Object ( )
    [First Name] => Victoria
    [Last Name] => Wood
    [Address] => Array (
        [Street] => 50 Ash lane
        [Place] => Ystradgynlais
        [Postal Code] => SA9 6XS
        [Country] => UK
    )
    [E-Mail] => Array (
        [0] => [email protected]
        [1] => [email protected]
    )
    [Phone] => 078-8727-8049
    [Age] => 28
)

Sorting, Limiting, and Skipping Items

The MongoCursor class provides sort(), limit(), and skip() functions, which allow you to sort your results, limit the total number of returned results, and skip a specific number of results, respectively. Let’s use the PHP driver to examine each function and how it is used.

PHP’s sort() function takes one array as a parameter. In that array, you can specify the field by which it should sort the documents. As when using the shell, you use the value 1 to sort the results in ascending order and -1 to sort the results in descending order. Note that you execute these functions on an existing cursor—that is, against the results of a previously executed find() command.

The following example sorts your contacts based on their age in ascending order:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Execute the query and store it under the $cursor variable
$cursor = $collection->find();
// Use the sort command to sort all results in $cursor, based on their age
$cursor->sort(array(’Age’ => 1));
// Print the results
while($document = $cursor->getNext())
{
    print_r($document);
}

You execute the limit() function on the actual cursor; this takes a stunning total of one parameter, which specifies the number of results you would like to have returned. The limit() command returns the first number of n items it finds in the collection that match your search criteria. The following example returns only one document (granted, you could use the findOne() function for this instead, but limit() does the job):

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Execute the query and store it under the $cursor variable
$cursor = $collection->find();
// Use the limit function to limit the number of results to 1
$cursor->limit(1);
//Print the result
while($document = $cursor->getNext())
{
    print_r($document);
}

Finally, you can use the skip() function to skip the first n results that match your criteria. This function also works on a cursor:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Execute the query and store it under the $cursor variable
$cursor = $collection->find();
// Use the skip function to skip the first result found
$cursor->skip(1);
// Print the result
while($document = $cursor->getNext())
{
    print_r($document);
}

Counting the Number of Matching Results

You can use PHP’s count() function to count the number of documents matching your criteria and return the number of items in an array. This function is part of the MongoCursor class and thus operates on the cursor. The following example shows how to get a count of contacts in the collection for people who live in the United States:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’$collection = $c->contacts->people;
// Specify the search parameters
$country = array("Address.Country" => "USA");
// Execute the query and store under the $cursor variable for further processing
$cursor = $collection->find($country);
// Count the results and return the value
print_r($cursor->count());

This query returns one result. Such counts can be useful for all sorts of operations, whether it’s counting comments, the total number of registered users, or anything else.

Grouping Data with the Aggregation Framework

The aggregation framework is easily one of the more powerful features built into MongoDB, as it allows you to calculate aggregated values without needing to use the Map/Reduce functionality. One of the most useful pipeline operators the framework includes is the $group operator, which can loosely be compared to SQL’s GROUP BY functionality. This operator allows you to calculate aggregate values based on a collection of documents. For example, the aggregation function $max can be used to find and return a group’s highest value; the $min function to find and return the lowest value, and $sum to calculate the total number of occurrences of a given value.

Let’s say that you want to get a list of all contacts in your collection, grouped by the country where they live. The aggregation framework lets you do this easily. Let’s take a look at an example:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Execute the query and store it under the $result variable
$result = $collection->aggregate(array(
        ’$group’ => array(
                ’_id’ => ’$Address.Country’,
                ’total’ => array(’$sum’ => 1)
        )
));
// Count the results and return the value
print_r($result);

As you can see, the aggregate function accepts one (or more) array with pipeline operators (in this case, the $group operator). Here, you can specify how the resulting output is returned, and any optional aggregation functions to execute: in this case the $sum function. In this example a unique document is returned for every unique country found, represented by the document’s _id field. Next, the total count of each country is summarized using the $sum function and returned using the total field. Note that the $sum function is represented by an array and given the value of 1, as you want every match to increase the total by 1.

You might wonder what the resulting output will look like. Here’s an example of the output, given that there are two contacts living in the United Kingdom and one in the United States:

Array (
    [result] => Array (
        [0] => Array (
            [_id] => UK [total] => 2
        )
        [1] => Array (
            [_id] => USA [total] => 1
        )
    )
    [ok] => 1
)

This example is but a simple one, but the aggregation framework is quite powerful indeed, as you will see when we look into it more closely in Chapter 8.

Specifying the Index with Hint

You use PHP’s hint() function to specify which index should be used when querying for data; doing so can help you increase query performance in case the query planner isn’t able to consistently choose a good index. Bear in mind, however, that using hint() can harm performance if the use of a poor index is forced.

For instance, assume you have thousands of contacts in your collection, and you generally search for a person based on last name. In this case, it’s recommended that you create an index on the Last Name key in the collection.

Image Note  The hint() example shown next will not return anything if an index is not created first.

To use the hint() function, you must apply it to the cursor, as in the following example:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Execute the query and store it under the $cursor variable
$cursor = $collection->find(array("Last Name" => "Moran"));
// Use the hint function to specify which index to use
$cursor->hint(array("Last Name" => -1));
//Print the result
while($document = $cursor->getNext())
{
    print_r($document);
}

Image Note  See Chapter 4 for more details on how to create an index. It is also possible to use the PHP driver’s createIndex() function to create an index, as discussed there.

Refining Queries with Conditional Operators

You can use conditional operators to refine your queries. PHP comes with a nice set of default conditional operators, such as < (less than), > (greater than), <= (less than or equal to), and >= (greater than or equal to). Now for the bad news: you cannot use these operators with the PHP driver. Instead, you will need to use MongoDB’s version of these operators. Fortunately, MongoDB itself comes with a vast set of conditional operators (you can find more information about these operators in Chapter 4). You can use all of these operators when querying for data through PHP, passing them on through the find() function.

While you can use all of these operators with the PHP driver, you must use specific syntax to do so; that is, you must place them in an array and pass this array to the find() function. The following sections will walk you through how to use several commonly used operators.

Using the $lt, $gt, $lte, and $gte Operators

MongoDB’s $lt, $gt, $lte, and $gte operators allow you to perform the same actions as the <, >, <=, and >= operators, respectively. These operators are useful in situations where you want to search for documents that store integer values.

You can use the $lt (less than) operator to find any kind of data for which the integer value is less than n, as shown in the following example:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the conditional operator
$cond = array(’Age’ => array(’$lt’ => 30));
// Execute the query and store it under the $cursor variable
$cursor = $collection->find($cond);
//Print the results
while($document = $cursor->getNext())
{
    print_r($document);
}

The resulting output shows only one result in the current documents: the contact information for Victoria Wood, who happens to be younger than 30:

Array (
    [_id] => MongoId Object ( )
    [First Name] => Victoria
    [Last Name] => Wood
        Address] => Array (
        [Street] => 50 Ash lane
        [Place] => Ystradgynlais
        [Postal Code] => SA9 6XS
        [Country] => UK
    )
    [E-Mail] => Array (
        [0] => [email protected]
        [1] => [email protected]
    )
    [Phone] => 078-8727-8049
    [Age] => 28
)

Similarly, you can use the $gt operator to find any contacts who are older than 30. This following example does that by changing the $lt variable to $gt (greater than), instead:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the conditional operator
$cond = array(’Age’ => array(’$gt’ => 30));
// Execute the query and store it under the $cursor variable
$cursor = $collection->find($cond);
//Print the results
while($document = $cursor->getNext())
{
    print_r($document);
}

This will return the document for Philip Moran because he’s older than 30:

Array (
    [_id] => MongoId Object ( )
    [First Name] => Philip
    [Last Name] => Moran
    [Address] => Array (
        [Street] => 681 Hinkle Lake Road
        [Place] => Newton
        [Postal Code] => MA 02160
        [Country] => USA
    )
    [E-Mail] => Array (
        [0] => [email protected]
        [1] => [email protected]
        [2] => [email protected]
        [3] => [email protected]
        [4] => [email protected]
        [5] => [email protected]
        [6] => [email protected]
        [7] => [email protected]
    )
    [Phone] => 617-546-8428
    [Age] => 60
)

You can use the $lte operator to specify that the value must either match exactly or be lower than the value specified. Remember: $lt will find anyone who is younger than 30, but not anyone who is exactly 30. The same goes for the $gte operator, which finds any value that is greater than or equal to the integer specified. Now let’s look at a pair of examples.

The first example will return both items from your collection to your screen:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the conditional operator
$cond = array(’Age’ => array(’$lte’ => 60));
// Execute the query and store it under the $cursor variable
$cursor = $collection->find($cond);
//Print the results
while($document = $cursor->getNext())
{
    print_r($document);
}

The second example will display only one document because the collection only holds one contact who is either 60 or older:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the conditional operator
$cond = array(’Age’ => array(’$gte’ => 60));
// Execute the query and store it under the $cursor variable
$cursor = $collection->find($cond);
//Print the results
while($document = $cursor->getNext())
{
    print_r($document);
}

Finding Documents That Don’t Match a Value

You can use the $ne (not equals) operator to find any documents that don’t match the value specified in the $ne operator. The syntax for this operator is straightforward. The next example will display any contact whose age is not equal to 28:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the conditional operator
$cond = array(’Age’ => array(’$ne’ => 28));
// Execute the query and store it under the $cursor variable
$cursor = $collection->find($cond);
//Print the results
while($document = $cursor->getNext())
{
    print_r($document);
}

Matching Any of Multiple Values with $in

The $in operator lets you search for documents that match any of several possible values added to an array, as in the following example:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the conditional operator
$cond = array(’Address.Country’ => array(’$in’ => array("USA","UK")));
// Execute the query and store it under the $cursor variable
$cursor = $collection->find($cond);
//Print the results
while($document = $cursor->getNext())
{
    print_r($document);
}

The resulting output would show any contact information from any person you add, whether that person lives in the United States or the United Kingdom. Note that the list of possibilities is actually added in an array; it cannot be typed in “just like that.”

Matching All Criteria in a Query with $all

Like the $in operator, the $all operator lets you compare against multiple values in an additional array. The difference is that the $all operator requires that all items in the array match a document before it returns any results. The following example shows how to conduct such a query:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the conditional operator
$cond = array(’E-Mail’ => array(’$all’ => array("[email protected]","[email protected]")));
// Execute the query and store it under the $cursor variable
$cursor = $collection->find($cond);
//Print the results
while($document = $cursor->getNext())
{
    print_r($document);
}

Searching for Multiple Expressions with $or

You can use the $or operator to specify multiple expressions a document can contain to return a match. The difference between the two operators is that the $in operator doesn’t allow you to specify both a key and value, whereas the $or operator does. You can combine the $or operator with any other key/value combination. Let’s look at two examples.

The first example searches for and returns any document that contains either an Age key with the integer value of 28 or an Address.Country key with the value of USA:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the conditional operator
$cond = array(’$or’ => array(
    array("Age" => 28),
    array("Address.Country" => "USA")
) );
// Execute the query and store it under the $cursor variable
$cursor = $collection->find($cond);
//Print the results
while($document = $cursor->getNext())
{
    print_r($document);
}

The second example searches for and returns any document that has the Address.Country key set to USA (mandatory), as well as a key/value set either to "Last Name" : "Moran" or to "E-Mail" : "[email protected]":

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the conditional operator
$cond = array(
    "Address.Country" => "USA",
    ’$or’ => array(
        array("Last Name" => "Moran"),
        array("E-Mail" => "[email protected]")
    )
);
// Execute the query and store it under the $cursor variable
$cursor = $collection->find($cond);
//Print the results
while($document = $cursor->getNext())
{
    print_r($document);
}

The $or operator allows you to conduct two searches at once and then combine the resulting output, even if the searches have nothing in common.

Retrieving a Specified Number of Items with $slice

You can use the $slice projection operator to retrieve a specified number of items from an array in your document. This function is similar to the skip() and limit() functions detailed previously in this chapter. The difference is that the skip() and limit() functions work on full documents, whereas the $slice operator allows you to work on an array rather than a single document.

The $slice projection operator is a great method for limiting the number of items per page (this is generally known as paging). The next example shows how to limit the number of e-mail addresses returned from one of the contacts specified earlier (Philip Moran); in this case, you only return the first three e-mail addresses:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify our search operator
$query = array("Last Name" => "Moran");
// Create a new object from an array using the $slice operator
$cond = (object)array(’E-Mail’ => array(’$slice’ => 3));
// Execute the query and store it under the $cursor variable
$cursor = $collection->find($query, $cond);
// For each document it finds within the collection, print the contents
while ($document = $cursor->getNext())
{
        print_r($document);
}

Similarly, you can get only the last three e-mail addresses in the list by making the integer negative, as shown in the following example:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify our search operator
$query = array("Last Name" => "Moran");
// Specify the conditional operator
$cond = (object)array(’E-Mail’ => array(’$slice’ => -3));
// Execute the query and store it under the $cursor variable
$cursor = $collection->find($query, $cond);
// For each document it finds within the collection, print the contents
while ($document = $cursor->getNext())
{
        print_r($document);
}

Or, you can skip the first two entries and limit the results to three:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify our search operator
$query = array("Last Name" => "Moran");
// Specify the conditional operator
$cond = (object)array(’E-Mail’ => array(’$slice’ => array(2, 3)));
// Execute the query and store it under the $cursor variable
$cursor = $collection->find($query, $cond);
// For each document it finds within the collection, print the contents
while ($document = $cursor->getNext())
{
        print_r($document);
}

The $slice operator is a great method for limiting the number of items in an array; you’ll definitely want to keep this operator in mind when programming with the MongoDB driver and PHP.

Determining Whether a Field Has a Value

You can use the $exists operator to return a result based on whether a field holds a value (regardless of the value of this field). As illogical as this may sound, it’s actually very handy. For example, you can search for contacts where the Age field has not been set yet; or you can search for contacts for whom you have a street name.

The following example returns any contacts that do not have an Age field set:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the conditional operator
$cond = array(’Age’ => array(’$exists’ => false));
// Execute the query and store it under the $cursor variable
$cursor = $collection->find($cond);
//Print the results
while($document = $cursor->getNext())
{
    print_r($document);
}

Similarly, the next example returns any contacts that have the Street field set:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the conditional operator
$cond = array("Address.Street" => array(’$exists’ => true));
// Execute the query and store it under the $cursor variable
$cursor = $collection->find($cond);
//Print the results
while($document = $cursor->getNext())
{
    print_r($document);
}

Regular Expressions

Regular expressions are neat. You can use them for just about everything (except for making coffee, perhaps); and they can greatly simplify your life when searching for data. The PHP driver comes with its own class for regular expressions: the MongoRegex class. You can use this class to create regular expressions, and then use them to find data.

The MongoRegex class knows six regular expression flags that you can use to query your data. You may already be familiar with some of them:

  • i: Triggers case insensitivity.
  • m: Searches for content that is spread over multiple lines (line breaks).
  • x: Allows your search to contain #comments.
  • l: Specifies a locale.
  • s: Also known as dotall, "." can be specified to match everything, including new lines.
  • u: Matches Unicode.

Now let’s take a closer look at how to use regular expressions in PHP to search for data in your collection. Obviously, this is best demonstrated with a simple example.

Let’s assume you want to search for a contact about whom you have very little information. For example, you may vaguely recall the place where the person lives and that it contains something like stradgynl in the middle somewhere. Regular expressions give you a simple yet elegant way to search for such a person:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the regular expression
$regex = new MongoRegex("/stradgynl/i");
// Execute the query and store it under the $cursor variable
$cursor = $collection->find(array("Address.Place" => $regex));
//Print the results
while($document = $cursor->getNext())
{
    print_r($document);
}

When creating a PHP application, you’ll typically want to search for specific data. In the preceding example, you would probably replace the text ("stradgynl", in this case) with a $_POST variable.

Modifying Data with PHP

If we lived in a world where all data remained static and humans never made any typos, we would never need to update our documents. But the world is a little more flexible than that, and there are times when we make mistakes that we’d like to correct.

For such situations, you can use a set of modifier functions in MongoDB to update (and therefore change) your existing data. You can do this in several ways. For example, you might use the update() function to update existing information, and then use the save() function to save your changes. The following sections look at a handful of these and other modifier operators, illustrating how to use them effectively.

Updating via update()

As detailed in Chapter 4, you use the update() function to perform most document updates. Like the version of update() in the MongoDB shell, the update() function that comes with the PHP driver allows you to use an assortment of modifier operators to update your documents quickly and easily. PHP’s version of the update() function operates almost identically; nevertheless, using the PHP version successfully requires a significantly different approach. The upcoming section will walk you through how to use the function successfully with PHP.

PHP’s update() function takes a minimum of two parameters: the first describes the object(s) to update, and the second describes the object you want to update the matching record(s) with. Additionally, you can specify a third parameter for an expanded set of options.

The options parameter provides seven additional flags you can use with the update() function; this list explains what they are and how to use them:

  • upsert: If set to true, this Boolean option causes a new document to be created if the search criteria are not matched.
  • multiple: If set to true, this Boolean option causes all documents matching the search criteria to be updated.
  • fsync: If set to true, this Boolean option causes the data to be synced to disk before returning a success. If this option is set to true, then it’s implied that w is set to 0, even if it’s set otherwise. It defaults to false.
  • w: If set to 0, the update operation will not be acknowledged. When working with replica sets, w can also be set to n, ensuring that the primary server acknowledges the update operation when successfully replicated to n nodes. It can also be set to ’majority’—a reserved string—to ensure that the majority of replica nodes will acknowledge the update or to a specific tag, ensuring that those nodes tagged will acknowledge the update. This option defaults to 1, acknowledging the update operation.
  • j: If set to true, this Boolean option will force the data to be written to the journal before indicating the update was a success. It defaults to false.
  • wTimeoutMS: Used to specify how long the server is to wait for receiving acknowledgment (in milliseconds). It defaults to 10000.
  • socketTimeoutMS: Used to specific how long the server is to wait for socket communication (in milliseconds). It defaults to 30000.

Now let’s look at a common example that changes Victoria Wood’s first name to “Vicky” without using any of the modifier operators (these will be discussed momentarily):

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the search criteria
$criteria = array("Last Name" => "Wood");
// Specify the information to be changed
$update = array(
        "First Name" => "Vicky",
        "Last Name" => "Wood",
        "Address" => array(
                        "Street" => "50 Ash lane",
                        "Place" => "Ystradgynlais",
                        "Postal Code" => "SA9 6XS",
                        "Country" => "UK"
        )
        ,
        "E-Mail" => array(
                "[email protected]",
                "[email protected]"
        ),
        "Phone" => "078-8727-8049",
        "Age" => 28
);
// Options
$options = array("upsert" => true);
// Perform the update
$collection->update($criteria,$update,$options);
// Show the result
print_r($collection->findOne($criteria));

The resulting output would look like this:

Array (
    [_id] => MongoId Object ()
    [First Name] => Vicky
    [Last Name] => Wood
    [Address] => Array (
        [Street] => 50 Ash lane
        [Place] => Ystradgynlais
        [Postal Code] => SA9 6XS
        [Country] => UK
    )
    [E-Mail] => Array (
        [0] => [email protected]
        [1] => [email protected]
    )
    [Phone] => 078-8727-8049
    [Age] => 28
)

This is a lot of work just to change one value—not exactly what you’d want to be doing to make a living. However, this is precisely what you would have to do if you didn’t use PHP’s modifier operators. Now let’s look at how you can use these operators in PHP to make life easier and consume less time.

Image Warning  If you don’t specify any of the conditional operators when applying the change, the data in the matching document(s) will be replaced by the information in the array. Generally, it’s best to use $set if you want to change only one field.

Saving Time with Update Operators

The update operations are going to save you loads of typing. As you’ll probably agree, the preceding example is just not feasible to work with. Fortunately, the PHP driver includes about half a dozen update operators for quickly modifying your data, without going through the trouble of writing it out fully. The purpose of each operator will be briefly summarized again, although you are probably familiar with most of them at this point (you can find more information about all the update operators discussed in this section in Chapter 4). However, the way you use them in PHP differs significantly, as do the options associated with them. We’ll look at examples for each of these operators, so you can familiarize yourself with their syntax in PHP.

Image Note  None of the update operators that follow will include PHP code to review the changes made; rather, the examples that follow only apply the changes. It’s suggested that you fire up the MongoDB shell alongside of the PHP code, so you can perform searches and confirm that the desired changes have been applied. Alternatively, you can write additional PHP code to perform these checks.

Increasing the Value of a Specific Key with $inc

The $inc operator allows you to increase the value of a specific key by n, assuming that the key exists. If the key does not exist, it will be created instead. The following example increases the age of each person younger than 40 by three years:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Search for anyone that’s younger than 40
$criteria = array("Age" => array(’$lt’ => 40));
// Use $inc to increase their age by 3 years
$update = array(’$inc’ => array(’Age’ => 3));
// Options
$options = array("upsert" => true);
// Perform the update
$collection->update($criteria,$update,$options);

Changing the Value of a Key with $set

The $set operator lets you change the value of a key while ignoring any other fields. As noted previously, this would have been a much better choice for updating Victoria’s first name to "Vicky" in the earlier example. The following example shows how to use the $set operator to change the contact’s name to "Vicky":

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the search criteria
$criteria = array("Last Name" => "Wood");
// Specify the information to be changed
$update = array(’$set’ => array("First Name" => "Vicky"));
// Options
$options = array("upsert" => true);
// Perform the update
$collection->update($criteria,$update,$options);

You can also use $set to add a field for every occurrence found matching your query:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the search criteria using regular expressions
$criteria = array("E-Mail" => new MongoRegex("/@office.com/i"));
// Add “Category => Work” into every occurrence found
$update = array(’$set’ => array(’Category’ => ’Work’));
// Options
$options = array(’upsert’ => true, ’multi’ => true);
// Perform the upsert via save()
$collection->update($criteria,$update,$options);

Deleting a Field with $unset

The $unset operator works similarly to the $set operator. The difference is that $unset lets you delete a given field from a document. For instance, the following example removes the Phone field and its associated data from the contact information for Victoria Wood:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the search criteria
$criteria = array("Last Name" => "Wood");
// Specify the information to be removed
$update = array(’$unset’ => array("Phone" => 1));
// Perform the update
$collection->update($criteria,$update);

Renaming a Field with $rename

The $rename operator can be used to rename a field. This can be helpful when you’ve accidently made a typo or simply wish to change its name to a more accurate one. The operator will search for the given field name within each document and its underlying arrays and subdocuments.

Image Warning  Be careful when using this operator. If the document already contains a field that has the new name, that field will be deleted, after which the old field name will be renamed to the new one as specified.

Let’s look at an example where the First Name and Last Name fields will be renamed to Given Name and Family Name, respectively, for Vicky Wood:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the search criteria
$criteria = array("Last Name" => "Wood");
// Specify the information to be changed
$update = array(’$rename’ => array("First Name" => "Given Name", "Last Name" => "Family Name"));
// Perform the update
$collection->update($criteria,$update);

Changing the Value of a Key During Upsert with $setOnInsert

MongoDB’s $setOnInsert operator can be used to assign a specific value only in case the update function performs an insert when using the upsert operator. This might sound a bit confusing at first, but you can think of this operator as a conditional statement that only sets the given value when upsert inserts a document, rather than updates one. Let’s look at an example to clarify how this works. First, we’ll perform an upsert that matches an existing document, thus ignoring the $setOnInsert criteria specified:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the search criteria
$criteria = array("Family Name" => "Wood");
// Specify the information to be set on upsert-inserts only
$update = array(’$setOnInsert’ => array("Country" => "Unknown"));
// Specify the upsert options
$options = array("upsert" => true);
// Perform the update
$collection->update($criteria,$update,$options);

Next, let’s look at an example where an upsert performs an insert as the document does not yet exist. Here you’ll find that the $setOnInsert criteria given will be successfully applied:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the search criteria
$criteria = array("Family Name" => "Wallace");
// Specify the information to be set on upsert-inserts only
$update = array(’$setOnInsert’ => array("Country" => "Unknown"));
// Specify the upsert options
$options = array("upsert" => true);
// Perform the update
$collection->update($criteria,$update,$options);

This piece of code will search for any document where the Family Name field (remember we renamed it previously) is set to "Wallace". If it’s not found, an upsert will be done, as a result of which the Country field will be set to "Unknown", creating the following empty-looking document:

{
    "_id" : ObjectId("1"),
    "Country" : "Unknown",
    "Last Name" : "Wallace"
}

Appending a Value to a Specified Field with $push

MongoDB’s $push operator lets you append a value to a specified field. If the field is an existing array, the data will be added; if the field does not exist, it will be created. If the field exists, but it is not an array, then an error condition will be raised. The following example shows how to use $push to add some data into an existing array:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the search criteria
$criteria = array("Family Name" => "Wood");
// Specify the information to be added
$update = array(’$push’ => array("E-Mail" => "[email protected]"));
// Perform the update
$collection->update($criteria,$update);

Adding Multiple Values to a Key with $push and $each

The $push operator also lets you append multiple values to a key. For this, the $each modifier needs to be added. The values, presented in an array, will be added in case they do not exist yet within the given field. As the $push operator is being used, the same general rules apply: if the field exists, and it is an array, then the data will be added; if it does not exist, then it will be created; if it exists, but it isn’t an array, then an error condition will be raised. The following example illustrates how to use the $each modifier:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the search criteria
$criteria = array("Family Name" => "Wood");
// Specify the information to be added
$update = array(
    ’$push’ => array(
        "E-Mail" => array(
            ’$each’ => array(
                "[email protected]",
                "[email protected]"
            )
        )
    )
);
// Perform the update
$collection->update($criteria,$update);

Adding Data to an Array with $addToSet

The $addToSet operator is similar to the $push operator, with one important difference: $addToSet ensures that data are added to an array only if the data are not in there. The $addToSet operator takes one array as a parameter:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the search criteria
$criteria = array("Family Name" => "Wood");
// Specify the information to be added (successful because it doesn’t exist yet)
$update = array(’$addToSet’ => array("E-Mail" => "[email protected]"));
// Perform the update
$collection->update($criteria,$update);

Similarly, you can add a number of items that don’t exist by combining the $addToSet operator with the $each operator:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the search criteria
$criteria = array("Family Name" => "Wood");
// Specify the information to be added (partially successful; some
// examples were already there)
$update = array(
    ’$addToSet’ => array
        (
        "E-Mail" => array
            (
            ’$each’ => array
                (
                "[email protected]",
                "[email protected]",
                "[email protected]"
                )
            )
        )
);
// Perform the update
$collection->update($criteria,$update);

Removing an Element from an Array with $pop

MongoDB’s $pop operator lets you remove an element from an array. Keep in mind that you can remove only the first or last element in the array—and nothing in between. You remove the first element by specifying a value of -1; similarly, you remove the last element by specifying a value of 1:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the search criteria
$criteria = array("Family Name" => "Wood");
// Pop out the first e-mail address found in the list
$update = array(’$pop’ => array("E-Mail" => -1));
// Perform the update
$collection->update($criteria,$update);

Image Note  Specifying a value of -2 or 1000 wouldn’t change which element is removed. Any negative number will remove the first element, whereas any positive number removes the last element. Using a value of 0 removes the last element from the array.

Removing Each Occurrence of a Value with $pull

You can use MongoDB’s $pull operator to remove each occurrence of a given value from an array. For example, this is handy if you’ve accidentally added duplicates to an array when using $push or $pushAll. The following example removes any duplicate occurrence of an e-mail address:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the search criteria
$criteria = array("Family Name" => "Wood");
// Pull out each occurrence of the e-mail address "[email protected]"
$update = array(’$pull’ => array("E-Mail" => "[email protected]"));
// Perform the update
$collection->update($criteria,$update);

Removing Each Occurrence of Multiple Elements with $pullAll

Similarly, you can use the $pullAll operator to remove each occurrence of multiple elements from your documents, as shown in the following example:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the search criteria
$criteria = array("Family Name" => "Wood");
// Pull out each occurrence of the e-mail addresses below
$update = array(
         ’$pullAll’ => array(
                   "E-Mail" => array("[email protected]","[email protected]")
         )
);
// Perform the update
$collection->update($criteria,$update);

Upserting Data with save()

Like the insert() function, the save() function allows you to insert data into your collection. The only difference is that you can also use save() to update a field that already holds data. As you might recall, this is called an upsert. The way you execute the save() function shouldn’t come as a surprise at this point. Like the save() function in the MongoDB shell, PHP’s save() takes two parameters: an array that contains the information you wish to save and any options for the save. The following options can be used:

  • fsync: If set to true, this Boolean option causes the data to be synced to disk before returning a success. If this option is set to true, then it’s implied that w is set to 0, even if it’s set otherwise.
  • w: If set to 0, the save operation will not be acknowledged. When working with replica sets, w can also be set to n, ensuring that the primary server acknowledges the save operation when successfully replicated to n nodes. It can also be set to ’majority’—a reserved string—to ensure that the majority of replica nodes will acknowledge the save, or to a specific tag, ensuring that those nodes tagged will acknowledge the save. This option defaults to 1, acknowledging the save operation.
  • j: If set to true, this Boolean option will force the data to be written to the journal before indicating the save was a success. It defaults to false.
  • wTimeoutMS: Used to specify how long the server is to wait for receiving acknowledgment (in milliseconds). It defaults to 10000.
  • socketTimeoutMS: Used to specify how long to wait for socket communication to the server. It defaults to 30000.

The syntax for PHP’s save() version is similar to that in the MongoDB shell, as the following example illustrates:

// Specify the document to be saved
$contact = array(
        "Given Name" => "Kenji",
        "Family Name" => "Kitahara",
        "Address" => array(
                        "Street" => "149 Bartlett Avenue",
                        "Place" => "Southfield",
                        "Postal Code" => "MI 48075",
                        "Country" => "USA"
        )
        ,
        "E-Mail" => array(
                "[email protected]",
                "[email protected]"
        ),
        "Phone" => "248-510-1562",
        "Age" => 34
);

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’
$collection = $c->contacts->people;
// Save via the save() function
$options = array("fsync" => true);
// Specify the save() options
$collection->save($contact,$options);

// Realizing you forgot something, let’s upsert this contact:
$contact[’Category’] = ’Work’;
// Perform the upsert
$collection->save($contact);

Modifying a Document Atomically

Like the save() and update() functions, the findAndModify() function can be invoked from the PHP driver. Remember that you can use the findAndModify() function to modify a document atomically and return the results after the update executes successfully. You use the findAndModify() function to update a single document—and nothing more. You may recall that, by default, the document returned will not show the modifications made—returning the document with the modifications made would require specifying an additional argument: the new parameter.

The findAndModify function takes four parameters: query, update, fields, and options. Some of these are optional, depending on your actions. For example, when specifying the update criteria, the fields and options are optional. However, when you wish to use the remove option, the update and fields parameters need to be specified (using null, for example). The following list details the available parameters:

  • query: Specifies a filter for the query. If this parameter isn’t specified, then all documents in the collection will be seen as possible candidates, and the first document encountered will be updated or removed.
  • update: Specifies the information to update the document. Note that any of the modifier operators specified previously can be used to accomplish this.
  • fields: Specifies the fields you would like to see returned, rather than the entire document. This parameter behaves identically to the fields parameter in the find() function. Note that the _id field will always be returned, even if that field isn’t included in your list of fields to return.
  • options: Specifies the options to apply. The following options can be used:
    • sort: Sorts the matching documents in a specified order.
    • remove: If set to true, the first matching document will be removed.
    • update: If set to true, an update will be performed on the selected document.
    • new: If set to true, returns the updated document, rather than the selected document. Note that this parameter is not set by default, which might be a bit confusing in some circumstances.
    • upsert: If set to true, performs an upsert.

Now let’s look at a set of examples that illustrate how to use these parameters. The first example searches for a contact with the last name "Kitahara" and adds an e-mail address to his contact card by combining an update() with the $push operator. The new parameter is not set in the following example, so the resulting output still displays the old information:

// Connect to the database
$c = new MongoClient();
// Specify the database and collection in which to work
$collection = $c->contacts->people;
// Specify the search criteria
$criteria = array("Family Name" => "Kitahara");
// Specify the update criteria
$update = array(’$push’ => array("E-Mail" => "[email protected]"));
// Perform a findAndModify()
$collection->findAndModify($criteria,$update);

The result returned looks like this:

Array (
    [value] => Array (
        [Given Name] => Kenji
        [Family Name] => Kitahara
        [Address] => Array (
            [Street] => 149 Bartlett Avenue
            [Place] => Southfield
            [Postal Code] => MI 48075
            [Country] => USA
            )
            [E-Mail] => Array (
                [0] => [email protected]
                [1] => [email protected]
           )
            [Phone] => 248-510-1562
            [Age] => 34
            [_id] => MongoId Object ( )
            [Category] => Work
    )
    [ok] => 1
)

The following example shows how to use the remove and sort parameters:

// Connect to the database
$c = new MongoClient();
// Specify the database and collection in which to work
$collection = $c->contacts->people;
// Specify the search criteria
$criteria = array("Category" => "Work");
// Specify the options
$options = array("sort" => array("Age" => -1), "remove" => true);
// Perform a findAndModify()
$collection->findAndModify($criteria,null,null,$options);

Processing Data in Bulk

The MongoDB PHP driver also allows you to perform multiple write operations in bulk. Similar to how this is done on the MongoDB shell, you will first need to define your dataset as well as write options before writing it all in a single go using the execute() command. Bulk write operations are limited to a single collection only, and can be used to insert, update, or remove data using the MongoInsertBatch, MongoUpdateBatch, or MongoDeleteBatch class, respectively.

Before you can write your data in bulk, you will first need to define your connectivity details, the type of batch operation to be executed, and the dataset—or array—that will hold your data. For example, if you wish to batch insert a set of documents, you will use the MongoInsertBatch class to create a new instance of the class as follows:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;

// Create the array containing bulk insert operations
$bulk = new MongoInsertBatch($collection);

Next, you will need to define your dataset to be inserted. Here, you can create a single array to store all your to-be-inserted documents in, prior to inserting each document into the previously created MongoInsertBatch instance, like so:

// Initialize the data set to be inserted
$data = array();
// Add your data
$data[] = array(
        "First Name" => "Nick",
        "Last Name" => "Scheffer",
        "E-Mail" => array(
                "[email protected]",
                "[email protected]"
        ),
);
$data[] = array(
        "First Name" => "Max",
        "Last Name" => "Scheffer",
        "E-Mail" => array(
                "[email protected]",
                "[email protected]"
        ),
);

When your dataset has been defined you will need to iterate over it using the foreach() command to add each of the documents to the previously created MongoInsertBatch instance—called $bulk—using the add() function. Let’s look at an example:

// Insert each document defined in the dataset ’$data’
foreach($data as $document) {
        $bulk->add($document);
}

Now that your dataset has been filled and your bulk operation has been defined, you are but one step away from executing it all in a single go.

Image Note  Your bulk instance can contain a maximum of 1000 documents, or up to 16777216 bytes of data, by default. MongoDB will automatically split and process your list into separate groups of 1000 operations or less when your list exceeds this.

Executing Bulk Operations

Before executing your previously defined bulk operation, you may first want to specify the operation’s write options. These write options are similar to the ones previously discussed, with the exception of the ordered write option, used to tell MongoDB how the data are to be written: ordered or unordered. When executing the operation in an ordered fashion, MongoDB will go over the list of operations serially. That is, were an error to occur while processing one of the write operations, the remaining operations would not be processed. In contrast, using an unordered write operation, MongoDB will execute the operations in a parallel manner. Were an error to occur during one of the writing operations here, MongoDB would continue to process the remaining write operations. A complete list of the write options for bulk operations are listed below:

  • continueOnError: If set to true, bulk inserts would continue to be processed even if one fails. It defaults to false.
  • w: If set to 0, the save operation will not be acknowledged. When working with replica sets, w can also be set to n, ensuring the primary server acknowledges the save operation when successfully replicated to n nodes. It can also be set to ’majority’—a reserved string—to ensure that the majority of replica nodes will acknowledge the save, or to a specific tag, ensuring those nodes tagged will acknowledge the save. This option defaults to 1, acknowledging the save operation.
  • wTimeoutMS: Used to specify how long the server is to wait for receiving acknowledgment (in milliseconds). It defaults to 10000.
  • socketTimeoutMS: Used to specify how long to wait for socket communication to the server. It defaults to 30000.
  • ordered: Used to determine if MongoDB should process this batch sequentially—one item at a time—or if it can rearrange the operations. It defaults to true.
  • fsync: If set to true, this Boolean option causes the data to be synced to disk before returning a success. If this option is set to true, then it’s implied that w is set to 0, even if it’s set otherwise.
  • j: If set to true, this Boolean option will force the data to be written to the journal before indicating the save was a success. It defaults to false.

Having determined your write options, you can finally execute the bulk operation using the execute() command on the previously created $bulk instance, providing the write operations as an option:

// Specify the write options
$options = array("w" => 1);

// Execute the batch operation
$result = $bulk->execute($options);

Evaluating the Output

If you wish to review the output of the bulk operations executed to ensure all write operations went well, you may print the output generated by the execute() function and stored in the $result variable using PHP’s var_dump() function in your PHP document:

 // Return the results
var_dump($retval);

If both documents were inserted properly, your output will look as follows:

array(2) {
  ["nInserted"]=>
  int(2)
  ["ok"]=>
  bool(true)
}

Here, the nInserted key will report the number of documents inserted (two). Similarly, nModified and nRemoved will report the number of documents changed or removed when using the MongoUpdateBatch and MongoDeleteBatch classes, respectively. Finally, the "ok" key will tell you if the operations executed successfully.

Bulk operations can be extremely useful for processing a large set of data in a single go without influencing the available dataset beforehand.

Deleting Data

You can use the remove() function to remove a document like the one in the preceding example from the MongoDB shell. The PHP driver also includes a remove() function you can use to remove data. The PHP version of this function takes two parameters: one contains the description of the record or records to remove, while the other specifies the additional write options governing the removal process.

There are five options available:

  • justOne: If set to true, at most only one record matching the criteria must be removed.
  • fsync: If set to true, this Boolean option causes the data to be synced to disk before returning a success. If this option is set to true, then it’s implied that w is set to 0, even if it’s set otherwise.
  • w: If set to 0, the save operation will not be acknowledged. When working with replica sets, w can also be set to n, ensuring the primary server acknowledges the save operation when successfully replicated to n nodes. It can also be set to ’majority’—a reserved string—to ensure that the majority of replica nodes will acknowledge the save, or to a specific tag, ensuring those nodes tagged will acknowledge the save. This option defaults to 1, acknowledging the save operation.
  • j: If set to true, this Boolean option will force the data to be written to the journal before indicating the save was a success. It defaults to false.
  • wTimeoutMS: Used to specify how long the server is to wait for receiving acknowledgment (in milliseconds). It defaults to 10000.
  • socketTimeoutMS: Used to specify how long the server is to wait for socket communication (in milliseconds). It defaults to 30000.

Now let’s look at a couple of code examples that illustrate how to remove a document:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the search criteria
$criteria = array("Family Name" => "Wallace");
// Specify the options
$options = array(’justOne’ => true, ’w’ => 0);
// Perform the removal
$collection->remove($criteria,$options);

Similarly, the next example removes multiple documents at the same time:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$collection = $c->contacts->people;
// Specify the search criteria using regular expressions
$criteria = array("E-Mail" => new MongoRegex("/@office.com/i"));
// Specify the options
$options = array("justOne" => false);
// Perform the removal
$collection->remove($criteria,$options);

Image Warning  When you remove a document, remember that any reference to that document will remain in the database. Make sure that you also manually delete or update references to the deleted document; otherwise, these references will return null when evaluated.

Similarly, you can drop an entire collection using the drop() function. The following example returns an array with the removal results:

// Connect to the database
$c = new MongoClient();
// Select the collection to remove
$collection = $c->contacts->people;
// Remove the collection and return the results
print_r($collection->drop());

The results returned look like this:

Array (
    [nIndexesWas] => 1
    [msg] => indexes dropped for collection
    [ns] => contacts.people
    [ok] => 1
)

Last but not least, you can use PHP to drop entire databases. You accomplish this by using the drop() function in the MongoDB class, as shown in the following example:

// Connect to the database
$c = new MongoClient();
// Select the database to remove
$db = $c->contacts;
// Remove the database and return the results
print_r($db->drop());

The results returned show the name of the database dropped:

Array (
[dropped] => contacts
[ok] => 1
)

DBRef

DBRef enables you to create links between two documents stored in different locations; this functionality lets you implement behavior similar to that found in a relational database. This functionality can be particularly handy if you want to store the addresses from the people in an addresses collection, rather than include that information in your people collection.

There are two ways to do this. First, you can use a simple link (known as manual referencing); in this case, you include the _id of a document into another document. Second, you can use DBRef to create such links automatically.

First, let’s look at how you implement manual referencing. In the following example, you add a contact and, under its address information, specify the _id of another document:

// Connect to the database
$c = new MongoClient();
$db = $c->contacts;
// Select the collections we want to store our contacts and addresses in
$people = $db->people;
$addresses = $db->addresses;
// Specify an address
$address = array(
    "Street" => "St. Annastraat 44",
    "Place" => "Monster",
    "Postal Code" => "2681 SR",
    "Country" => "Netherlands"
);
// Save the address
$addresses->insert($address);
// Add a contact living at the address
$contact = array(
    "First Name" => "Melvyn",
    "Last Name" => "Babel",
    "Age" => 35,
    "Address" => $address[’_id’]
);
$people->insert($contact);

Now assume you want to find the preceding contact’s address information. To do this, simply query for the Object ID in the address field; you can find this information in the addresses collection (assuming you know the name of this collection).

This works, but the preferred method for referencing another document relies on DBRef. This is because DBRef relies on a common format that the database and all the drivers understand. We’ll look at a DBRef version of the preceding example momentarily. Before doing so, however, let’s take a look at the create() function of the DBRef class; you will use this class to create the desired reference.

The create() function takes three parameters:

  • collection: Specifies the name of the collection where the information resides (without the database name).
  • id: Specifies the ID of the document to link to.
  • database: Specifies the name of the database in which the document resides.

The following example uses the create() function to create a reference to an address in another document:

// Connect to the database
$c = new MongoClient();
$db = $c->contacts;

// Select the collections we want to store our contacts and addresses in
$people = $db->people;
$addresses = $db->addresses;

// Specify an address
$address = array(
    "Street" => "WA Visser het Hooftlaan 2621",
    "Place" => "Driebergen",
    "Postal Code" => "3972 SR",
    "Country" => "Netherlands"
);

// Save the address
$addresses->insert($address);

// Create a reference to the address
$addressRef = MongoDBRef::create($addresses->getName(), $address[’_id’]);

// Add a contact living at the address
$contact = array(
    "First Name" => "Ivo",
    "Last Name" => "Lauw",
    "Age" => 24,
    "Address" => $addressRef
);
$people->insert($contact);

Image Note  The getName() function in this example is used to get the name of the collection.

Retrieving the Information

So far you’ve used DBRef to create a reference. Now it’s time to look at how to retrieve the information referenced, so that you can display the contents again correctly. You do this using MongoDBRef’s get() function.

MongoDBRef’s get() function takes two parameters. The first parameter specifies the database to use, while the second provides the reference to fetch:

// Connect to the database
$c = new MongoClient();
// Select the collection ’people’ from the database ’contacts’
$people = $c->contacts->people;
// Define the search parameters
$lastname = array("Last Name" => "Lauw");
// Find our contact, and store under the $person variable
$person = $people->findOne(array("Last Name" => "Lauw"));
// Dereference the address
$address = MongoDBRef::get($people->db, $person[’Address’]);
// Print out the address from the matching contact
print_r($address);

The resulting output shows the document being referred to:

Array (
    [_id] => MongoId Object ( )
    [Street] => WA Visser het Hooftlaan 2621
    [Place] => Driebergen
    [Postal Code] => 3972 SR
    [Country] => Netherlands
)

DBRef provides a great way to store data you want to reference, not least because it permits flexibility in the collection and database names.

GridFS and the PHP Driver

Chapter 5 elaborated on GridFS and its benefits. For example, it explained how to use this technology to store and retrieve data, in addition to other GridFS-related techniques. In this section, you’ll learn how to use the PHP driver to store and retrieve files using GridFS.

The PHP driver contains its own classes for dealing with GridFS; here are three of its most important classes and what they do:

  • MongoGridFS: Stores and retrieves files from the database. This class contains several methods, including delete(), find(), storeUpload(), and about a half dozen others.
  • MongoGridFSFile: Works on a specific file in the database. It includes functions such as __construct(), getFilename(), getSize(), and write().
  • MongoGridFSCursor: Works on the cursor. It contains a handful of functions, such as __construct(), current(), getNext(), and key().

Let’s have a look at how we can use PHP to upload files into the database.

Image Note  The code in the following example will not work without an HTML form that uploads data. Such code is beyond the scope of this chapter, however, so it is not shown here.

Storing Files

You use the storeUpload() function to store files into your database with GridFS. This function takes two parameters: one indicates the field name of the file to be uploaded, and the other can optionally be used to specify the file’s metadata. Once used, the function reports back the _id of the file stored.

The following simple code example shows how to use the storeUpload() function:

// Connect to the database
$c = new MongoClient();
// Select the name of the database
$db = $c->contacts;
// Define the GridFS class to ensure we can handle the files
$gridFS = $db->getGridFS();
// Specify the HTML field’s name attribute
$file = ’fileField’;
// Specify the file’s metadata (optional)
$metadata = array(’uploadDate’ => date());
// Upload the file into the database
$id = $gridFS->storeUpload($file,$metadata);

And that’s all there is to it. As you can see, $id is used as a parameter to store the file in the database. You might also use this parameter to reference the data with DBRef.

Adding More Metadata to Stored Files

Sometimes you may want to add more metadata to your stored files. By default, the only other data that’s added is the _id field, which you might use to reference the data when you’re storing a picture to a contact card. Unfortunately, that might prove to be more of a restriction than a benefit when you want to start searching for your data through these tags.

The following example shows how to store metadata for your uploaded data. This example builds on the previous code block and the $id parameter in particular. Obviously, you can customize this yourself, using any other desired search criteria:

// Specify the metadata to be added
$metadata = array(’$set’ => array("Tag" => "Avatar"));
// Specify the search criteria to which to apply the metadata
$criteria = array(’_id’ => $id);
// Insert the metadata
$db->grid->update($criteria, $metadata);

Retrieving Files

Of course, the ability to store your files in a database wouldn’t do you any good if you weren’t able to retrieve these files later. Retrieving files is about as hard (read: easy!) as storing them. Let’s look at two examples: the first retrieves the file names stored, while the second retrieves the files themselves.

The following example shows how to retrieve the file names stored, which you accomplish using the getFilename() function:

// Connect to the database
$c = new MongoClient();
$db = $c->contacts;
// Initialize GridFS
$gridFS = $db->getGridFS();
// Find all files in the GridFS storage and store under the $cursor parameter
$cursor = $gridFS->find();
// Return all the names of the files
foreach ($cursor as $object) {
    echo "Filename:".$object->getFilename();
}

That was easy! Of course, this example assumes that you have some data stored in your database. You might also want to add more search parameters to the find() function after you’ve added a little more data or if you want to search for more specific data. Note that the find() function searches through the metadata added to each uploaded file (as detailed earlier in this chapter).

You might wonder how you go about retrieving the files themselves. After all, retrieving the data is probably what you’ll be using the most in the end. You accomplish this by using the getBytes() function to send it to your browser. The next example uses the getBytes() function to retrieve a previously stored image. Note that you can retrieve the _id parameter by querying the database (the following example just makes up some parameters). Also, it’s mandatory to specify the content type because, logically, this is not recognized by the browser when you build up the data again:

// Connect to the database
$c = new MongoClient();
// Specify the database name
$db = $c->contacts;
// Initialize the GridFS files collection
$gridFS = $db->getGridFS();
// Specify the search parameter for the file
$id = new MongoId(’4c555c70be90968001080000’);
// Retrieve the file
$file = $gridFS->findOne(array(’_id’ => $id));
// Specify the header for the file and write the data to it
header(’Content-Type: image/jpeg’);
echo $file->getBytes();
exit;

Deleting Data

You can ensure that any previously stored data are removed by using the delete() function. This function takes one parameter: the _id of the file itself. The following example illustrates how to use the delete() function to delete the file matching the Object ID 4c555c70be90968001080000:

// Connect to the database
$c = new MongoClient();
// Specify the database name
$db = $c->contacts;
// Initialize GridFS
$gridFS = $db->getGridFS();
// Specify the file via it’s ID
$id = new MongoId(’4c555c70be90968001080000’);
$file = $gridFS->findOne(array(’_id’ => $id));
// Remove the file using the remove() function
$gridFS->delete($id);

Summary

In this chapter, we’ve taken an in-depth look at how to work with MongoDB’s PHP driver. For example, you’ve seen how to use the most commonly used functions with the PHP driver, including the insert(), update(), and modify() functions. You’ve also learned how to search your documents by using the PHP driver’s equivalent of the find() function. Finally, you’ve learned how to leverage DBRef’s functionality, as well as how to store and retrieve files with GridFS.

One chapter couldn’t possibly cover everything there is to know about using the PHP driver for MongoDB; nonetheless, this chapter should provide the necessary basics to perform most of the actions you’d want to accomplish with this driver. Along the way, you’ve also learned enough to use the server-side commands whenever the going gets a little more complicated.

In the next chapter, you’ll explore the same concepts, but for the Python driver instead.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.21.247.9