[box type="note" align="" class="" width=""]The following book excerpt is taken from the title Mastering MongoDB 3.x written by Alex Giamas. This book covers the fundamental as well as advanced tasks related to working with MongoDB.[/box]
There are two ways to connect your data to MongoDB. The first is using the driver for your programming language. The second is by using an ODM (Object Document Mapper) layer to map your model objects to MongoDB in a transparent way. In this post, we will cover both methods using two of the most popular languages for web application development:
Installing PyMongo can be done using pip or easy_install:
python -m pip install pymongo
python -m easy_install pymongo
Then in our class we can connect to a database:
>>> from pymongo import MongoClient
>>> client = MongoClient()
Connecting to a replica set needs a set of seed servers for the client to find out what the primary, secondary, or arbiter nodes in the set are:
client =
pymongo.MongoClient('mongodb://user:passwd@node1:p1,node2:p2/?replicaSet=rs name')
Using the connection string URL we can pass a username/password and replicaSet name all in a single string. Some of the most interesting options for the connection string URL are presented later.
Connecting to a shard requires the server host and IP for the mongo router, which is the mongos process.
Similar to Ruby's Mongoid, PyMODM is an ODM for Python that follows closely on Django's built-in ORM. Installing it can be done via pip:
pip install pymodm
Then we need to edit settings.py and replace the database engine with a dummy database:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.dummy'
}
}
And add our connection string anywhere in settings.py:
from pymodm import connect
connect("mongodb://localhost:27017/myDatabase", alias="MyApplication")
Here we have to use a connection string that has the following structure:
mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:port
N]]][/[database][?options]]
Options have to be pairs of name=value with an & between each pair. Some interesting pairs are:
Model classes need to inherit from MongoModel. A sample class will look like this:
from pymodm import MongoModel, fields
class User(MongoModel):
email = fields.EmailField(primary_key=True)
first_name = fields.CharField()
last_name = fields.CharField()
This has a User class with first_name, last_name, and email fields where email is the primary field.
Handling one-one and one-many relationships in MongoDB can be done using references or embedding. This example shows both ways: references for the model user and embedding for the comment model:
from pymodm import EmbeddedMongoModel, MongoModel, fields
class Comment(EmbeddedMongoModel):
author = fields.ReferenceField(User)
content = fields.CharField()
class Post(MongoModel):
title = fields.CharField()
author = fields.ReferenceField(User)
revised_on = fields.DateTimeField()
content = fields.CharField()
comments = fields.EmbeddedDocumentListField(Comment)
The MongoDB PHP driver was rewritten from scratch two years ago to support the PHP 5, PHP 7, and HHVM architectures. The current architecture is shown in the following diagram:
Currently we have official drivers for all three architectures with full support for the underlying functionality.
Installation is a two-step process. First we need to install the MongoDB extension. This extension is dependent on the version of PHP (or HHVM) that we have installed and can be done using brew in Mac. For example with PHP 7.0:
brew install php70-mongodb
Then, using composer (a widely used dependency manager for PHP):
composer require mongodb/mongodb
Connecting to the database can then be done by using the connection string URI or by passing an array of options. Using the connection string URI we have:
$client = new MongoDBClient($uri = 'mongodb://127.0.0.1/', array
$uriOptions = [], array $driverOptions = [])
For example, to connect to a replica set using SSL authentication:
$client = new
MongoDBClient('mongodb://myUsername:myPassword@rs1.example.com,rs2.example.com/?ssl=true&replicaSet=myReplicaSet&authSource=admin');
Or we can use the $uriOptions parameter to pass in parameters without using the connection string URL, like this:
$client = new MongoDBClient(
'mongodb://rs1.example.com,rs2.example.com/'
[
'username' => 'myUsername',
'password' => 'myPassword',
'ssl' => true,
'replicaSet' => 'myReplicaSet',
'authSource' => 'admin',
],
);
The set of $uriOptions and the connection string URL options available are analogous to the ones used for Ruby and Python.
Laravel is one of the most widely used MVC frameworks for PHP, similar in architecture to Django and Rails from the Python and Ruby worlds respectively. We will follow through configuring our models using a stack of Laravel, Doctrine, and MongoDB. This section assumes that Doctrine is installed and working with Laravel 5.x.
Doctrine entities are POPO (Plain Old PHP Objects) that, unlike Eloquent, Laravel's default ORM doesn't need to inherit from the Model class. Doctrine uses the Data Mapper pattern, whereas Eloquent uses Active Record. Skipping the get() set() methods, a simple class would look like:
use DoctrineORMMapping AS ORM;
use DoctrineCommonCollectionsArrayCollection;
/**
* @ORMEntity
* @ORMTable(name="scientist")
*/
class Scientist
{
/**
* @ORMId
* @ORMGeneratedValue
* @ORMColumn(type="integer")
*/
protected $id;
/**
* @ORMColumn(type="string")
*/
protected $firstname;
/**
* @ORMColumn(type="string")
*/
protected $lastname;
/**
* @ORMOneToMany(targetEntity="Theory", mappedBy="scientist",
cascade={"persist"})
* @var ArrayCollection|Theory[]
*/
protected $theories;
/**
* @param $firstname
* @param $lastname
*/
public function __construct($firstname, $lastname)
{
$this->firstname = $firstname;
$this->lastname = $lastname;
$this->theories = new ArrayCollection;
}
…
public function addTheory(Theory $theory)
{
if(!$this->theories->contains($theory)) {
$theory->setScientist($this);
$this->theories->add($theory);
}
}
This POPO-based model used annotations to define field types that need to be persisted in MongoDB. For example, @ORMColumn(type="string") defines a field in MongoDB with the string type firstname and lastname as the attribute names, in the respective lines.
There is a whole set of annotations available here http://doctrine-orm.readthedocs.io/en/latest/reference/annotations- reference.html . If we want to separate the POPO structure from annotations, we can also define them using YAML or XML instead of inlining them with annotations in our POPO model classes.
Modeling one-one and one-many relationships can be done via annotations, YAML, or XML. Using annotations, we can define multiple embedded subdocuments within our document:
/** @Document */
class User
{
// …
/** @EmbedMany(targetDocument="Phonenumber") */
private $phonenumbers = array();
// …
}
/** @EmbeddedDocument */
class Phonenumber
{
// …
}
Here a User document embeds many PhoneNumbers. @EmbedOne() will embed one subdocument to be used for modeling one-one relationships.
Referencing is similar to embedding:
/** @Document */
class User
{
// …
/**
* @ReferenceMany(targetDocument="Account")
*/
private $accounts = array();
// …
}
/** @Document */
class Account
{
// …
}
@ReferenceMany() and @ReferenceOne() are used to model one-many and one-one relationships via referencing into a separate collection.
We saw that the process of connecting data to MongoDB using Python and PHP is quite similar. We can accordingly define relationships as being embedded or referenced, depending on our design decision.
If you found this post useful, check out our book Mastering MongoDB 3.x for more tips and techniques on working with MongoDB efficiently.