MongoDB is a document-oriented database that stores documents in a JSON-like format (this format is known as BSON, for binary JSON). A database is an organized collection of data. Differently from a file, where data is stored in the order of writing, in a database the stored data can be organized in tables (like in relational databases) and retrieved at request (without reading the whole database). Traditional relational databases use the SQL language to store and access the stored information. You can learn about such databases if you take CS 304 Databases with Web Interfaces. MongoDB belongs to the family of NoSQL databases, meaning that it doesn't use the same language and principles found in the relational databases. NoSQL databases are used in real-time web applications and with big data, because they make certain operations faster than the traditional databases. There is a big debate among software developers on this issue. It is good if you learn both technologies (they are both widely used) and then reflect about advantanges and disadvantages of each, so that you can know where they are most useful.
Our reason for learning MongoDB in this class is that it is used by Meteor, the real-time JavaScript web application framework that we will start using soon.
Given that the only reason for learning MongoDB is to use it with Meteor, we will not be installing MongoDB separately. When we'll install Meteor, MongoDB will be part of it and Meteor will take care of the details of setting it up for our applications.
MongoDB has two parts: the MongoDB server and the MongoDB client. The MongoDB server is the part of MongoDB where the data are stored and queried, the MongoDB client is an application that allows us to enter data or query the chosen database. Our CS server, Tempest, has the MongoDB installed, where the MongoDB server runs uninterruptibly and we will simply run the MongoDB client from the command line.
To do that, you'll remotely connect to CS server using your credentials. The command
you'll need is ssh
(stands for secure shell) together with your account name
on the CS server, for example ssh eni@cs.wellesley.edu
. You'll be prompted
for your password. After entering it, you can type mongo
and start
the work in the client shell.
Mac machines already have ssh
installed, which you can start from a Terminal window.
If you have a Windows machine, you will need to install an application such
as PuTTY and learn how to connect
to the CS server, or simply use our Mac machines in SCI 257.
We will be using certain terms when talking about MongoDB and the data stored in it. Here is an overview:
// Examples of documents that can be stored in a collection about HP book characters {"name": "Harry Potter", "age": 17} {"name": "Ron Weasley", "age": 17, "house": "Gryffindor"} {"name": "Severus Snape", "age": 38, "profession": "Teacher", "alive": false}
The example above shows different documents that can be stored in the same collection. However, each document can have more fields, depending on the need.
Note: the following instructions can be entered in the mongo shell you opened in the CS server.
The server will have several databases, therefore, our first step will be
to choose the database we want to use. It doesn't matter whether a database
exists or not, once we enter the instruction:
use database_name_here
, we will be working with that database. Once
we have specified the database name, we can refer to it by simply prefixing
all other instructions with db
. For example, in the screenshot
below the following is happening:
I added one more document: db.sisters.insert({'name': 'Kate'})
. Now,
we can try to see what is stored in the collection, by using the method .find()
.
The result is shown in the screenshot below. You can notice that two documents were found,
and both of them have an additional field, "_id"
, which was automatically added
when the documents were entered in the database. Think of this ID as a unique way
to identify each document. In some cases, we might want to use values that we know as
the "_id" field (for example the Banner number of a student), but most of the time, we
will not care about its value.
If in certain occasions we are only interested to see one document in a collection,
we can use the method .findOne()
instead of .find()
. Try it out.
At this point make sure that you can do the following:
Just like in CSS & Javascript where we use "selectors" to find certain
DOM elements, mongodb has the same
concept known as "query selectors". A query selector will be a JSON object,
including the empty object {}
.
In order to test query selectors in MongoDB, I have inserted the following
12 documents to the collection unicorns
in the cs249test
database.
{name: 'Horny', dob: new Date(1992,2,13,7,47), loves: ['carrot','papaya'], weight: 600, gender: 'm', vampires: 63} {name: 'Aurora', dob: new Date(1991,0,24,13,0), loves: ['carrot','grape'], weight: 450, gender: 'f', vampires: 43} {name: 'Unicrom', dob: new Date(1973,1,9,22,10), loves: ['energon','redbull'], weight: 984, gender: 'm', vampires: 182} {name: 'Roooooodles', dob: new Date(1979,7,18,18,44), loves: ['apple'], weight: 575, gender: 'm', vampires: 99} {name: 'Solnara', dob: new Date(1985,6,4,2,1), loves: ['apple', 'carrot','chocolate'], weight: 550, gender: 'f', vampires: 80} {name: 'Aynaa', dob: new Date(1998,2,7,8,30), loves: ['strawberry','lemon'], weight: 733, gender: 'f', vampires: 40} {name: 'Kenny', dob: new Date(1997,6,1,10,42), loves: ['grape', 'lemon'], weight: 690, gender: 'm', vampires: 39} {name: 'Raleigh', dob: new Date(2005,4,3,0,57), loves: ['apple','sugar'], weight: 421, gender: 'm', vampires: 2} {name: 'Leia', dob: new Date(2001,9,8,14,53), loves: ['apple','watermelon'], weight: 601, gender: 'f', vampires: 33} {name: 'Pilot', dob: new Date(1997,2,1,5,3), loves: ['apple','watermelon'], weight: 650, gender: 'm', vampires: 54} {name: 'Nimue', dob: new Date(1999,11,20,16,15), loves: ['grape','carrot'], weight: 540, gender: 'f'} {name: 'Dunx', dob: new Date(1976,6,18,18,18), loves: ['grape','watermelon'], weight: 704, gender: 'm', vampires: 165}
To see them in the database, do the following:
use cs249test // change the database db.unicorns.find() // show all 12 documents of the collection
Here are some things that we will try on the console: (solutions are at the end of the document)
Find all female unicorns. The query selector will be {gender: 'f'}
and
we can use that in the .find()
method as an argument. You
should be able to see five documents as a result, if you type db.unicorns.find({gender: 'f'})
in the mongo shell.
Your Turn: Find a unicorn named Aurora.
The query selector that we just saw, {gender: 'f'}
is trying to find
all documents where a field has the desired value. Very often we want to find
documents where the values is greater than or less than a value, or even not equal.
We can use the five following relational operators to check for these cases:
$gt
for "greater than"$gte
for "greater than or equal"$lt
for "less than"$lte
for "less than or equal"$ne
for "not equal"The syntax for using these operators is shown in the example below:
db.unicorns.find({vampires: {$gt: 60}})
This query will return five unicorns who have fought more than 60 vampires.
Your turn:
So far, by using multiple fields in the query selector, we have been generating AND queries
(all the fields need to be true). But sometimes, we will be interested in documents that
fulfill at least one of the query parameters. For this, we can use the $or
operator. This operator requires its value to be an array of fields separated by
comma. Here is an example:
db.unicorns.find({gender: 'f', $or: [{loves: 'apple'}, {weight: {$lt : 500}}]})
This query selector says: find all female unicors who either love apples or weigh less than 500 lbs.
Notice that for values such as 'apple' that are part of an array, we can still use them as separate values in the query selector. If we want to have more than one value, we have to list them separately:
db.unicorns.find({gender: 'f', $or: [{loves: 'apple'}, {loves: 'grape'}, {weight: {$lt : 500}}]})
Another useful operator is $exists
which can take the values true / false
and will return documents that contain or not a certain field. For example, let's
find documents that don't contain the field 'vampire'.
db.unicorns.find({vampires: {$exists: false}})
The basic functions in a database are: create, read, update, and delete.
In MongoDB, creating documents is performed through .insert()
,
reading (or retrieving) is done through .find()
, updating is done
with .update()
and deleting is done with .drop()
.
We already saw .insert()
and .find()
and we will
see examples of .update()
and .drop()
when we'll work
with Meteor. For the moment, let's see some other operations that have to
do mostly with how the results are shown to us, such as filtering fields,
sorting, limiting or skipping, or counting.
You might have noticed so far that when we search for some documents, the entire
document (all its fields and values) is displayed. Sometimes, we might be
interested in seeing only one or a few fields. This is called a projection,
and can be realized by supplying a second argument to the method .find()
.
db.unicorns.find({gender: 'f'}, {name: 1, gender: 1})
This will do what we want (showing only name and gender), but the "_id" field is shown as well, so, we can request to not show it:
db.unicorns.find({gender: 'f'}, {name: 1, gender: 1, _id: 0})
Thus, using fields with the value 1 will include them in the projection, and using a field with the value 0 will exclude them in the projection. Sometimes, you only want to exclude less fields than you want to include, thus, it makes sense to know about how to do that.
We can sort elements by creating an object that has the field for sorting and a value of 1 or -1 for the order of sorting (ascending or descending). For example, let's sort the male unicorns by vampires count (decreasing):
db.unicorns.find({gender: 'm'}, {name: 1, vampires: 1, gender: 1, _id: 0}).sort({vampires: -1})
The sorting works for fields of different kinds, for example Date() objects too:
db.unicorns.find({gender: 'f'}, {name: 1, gender: 1, dob: 1, _id: 0}).sort({dob: 1})
We can limit the number of results we want to see by using the method .limit()
.
For example, we can only show the three oldest male unicorns:
db.unicorns.find({gender: 'm'}, {gender: 1, dob: 1, name: 1, _id: 0}).sort({dob: 1}).limit(3)
What is happening is that instead of the 7 male unicorns, only 3 are shown. In addition to
limiting results, we can skip some of them, by using the method .skip()
.
For example, we can decide to skip the first three oldest males and show the rest of them:
db.unicorns.find({gender: 'm'}, {gender: 1, dob: 1, name: 1, _id: 0}).sort({dob: 1}).skip(3
Combining .limit()
and .skip()
will allow us to show
documents grouped together (the first ten, the second ten, etc.)
Often, we are only interested in how many docoments fulfill some criteria. For example, if we want to know the total number of documents in the collection, we an write:
db.unicorns.count()
If we are interested in the number of documents found by a query selector:
db.unicorns.find({gender: 'f'}).count()
It is possible to use .count()
instead of .find()
, by
using the query selector as its arguments:
db.unicorns.count({gender: 'm', weight: {$gt: 600}})
Unicorn named Aurora: db.unicorns.find({name: 'Aurora'})
Male unicorns that weigh more than 550 lbs: db.unicorns.find({gender: 'm', weight: {$gt : 550}})
.
All unicorns that have fought at most 40 vampires: db.unicorns.find({vampires: {$lte: 40}})
Female unicorns that were born since 1999: db.unicorns.find({gender: 'f', dob: {new Date(1999,0,1)}})
These notes were based on the short book "The Little MongoDB book" by Karl Seguin.
You can see the whole list of query selector operators in the MongodDB documentation.
If you want to go in more depth with MongoDB, you should read the MongoDB Manual.